Every possible combos for an island in Hashi game - php

Here's the game : Hashiwokakero.
To solve it, I need to find all possible combinations of links + islands around every island.
Constraints are, 1 or 2 links, and one combination is enough links created for the targeted island.
For example, I have a island ['A' => 3], which means it needs 3 links to be solved, and it has 3 neighbors ['B', 'C', 'D'].
I'd like to find an algorithm which would produce such an array :
[
['B' => 1, 'C' => 1, 'D' => 1],
['B' => 1, 'C' => 2],
['B' => 1, 'D' => 2],
['B' => 2, 'C' => 1],
['B' => 2, 'D' => 1],
['C' => 1, 'D' => 2],
['C' => 2, 'D' => 1]
];
Thanks.

If you want to find all combinations of links (0, 1, or 2) per neighbor, with a fixed total number of links, then you could use the following recursive function:
function getPossibleLinks($value, $neighbors) {
if ($value == 0) return [[]];
$max = min(2, $value);
$min = 2 - min(count($neighbors) * 2 - $value, 2);
if ($min > 2) {
throw new Exception('Not possible to assign that many links');
}
$results = [];
for ($count = $min; $count <= $max; $count++) {
$nextResults = getPossibleLinks($value - $count, array_slice($neighbors, 0, -1));
foreach($nextResults as $result) {
if ($count) $result[end($neighbors)] = $count;
$results[] = $result;
}
}
return $results;
}
You would need to pass it the number of links as first argument ($value), and the array of neighbors as an array of strings.
Here is an example call:
$results = getPossibleLinks(3, ["B", "C", "D"]);
After this call, $results will have this content:
[
['B' => 2, 'C' => 1],
['B' => 1, 'C' => 2],
['B' => 2, 'D' => 1],
['B' => 1, 'C' => 1, 'D' => 1],
['C' => 2, 'D' => 1],
['B' => 1, 'D' => 2],
['C' => 1, 'D' => 2]
]
See it run on eval.in.

Related

How to find the highest value in a associative array in php

$characters = [
"Character1" => ["Strength" => 1, "Dexterity" => 2,
"Intelligence" => 6, "Wisdom" => 6, "Charisma" => 3],
"Character2" => ["Strength" => 5, "Dexterity" => 4,
"Intelligence" => 1, "Wisdom" => 1, "Charisma" => 6],
"Character3" => ["Strength" => 6, "Dexterity" => 5,
"Intelligence" => 5, "Wisdom" => 1, "Charisma" => 5]
"Character4" => ["Strength" => 1, "Dexterity" => 2,
"Intelligence" => 4, "Wisdom" => 3, "Charisma" => 3]
];
So my question is as follows, how can I get the highest quality of each character. Like for Character1 it would be either Intelligence or Wisdom. I would like to echo the name of the character, the best quality and the value associated with the quality.
foreach($attributs as $s_key => $value)
{
if($value > $max)
$max = max($attributs);
$maxkey = $s_key;
}
echo "<li>$max , $maxkey</li>";
echo "</ul>";
I also have this code, but it only gives me the highest number for each character without giving me the name of the quality associated with the number.
foreach($characters as $names => $quality){
echo "$names";
echo max($quality);
}
I know none of them work at all, but that's as close as I could get to what I wanted.
You can find the highest value of an array using max function, meanwhile the key using array_search(). This will fail if you have 2 skills with the same value, assuming that "Intellegence" is 6 and "Wisdom" is 6 only "Intellegence" will be shown. Do you have to shown both of them or only one ?
$characters = [
"Character1" => ["Strength" => 1, "Dexterity" => 2, "Intelligence" => 6, "Wisdom" => 6, "Charisma" => 3],
"Character2" => ["Strength" => 5, "Dexterity" => 4, "Intelligence" => 1, "Wisdom" => 1, "Charisma" => 6],
"Character3" => ["Strength" => 6, "Dexterity" => 5, "Intelligence" => 5, "Wisdom" => 1, "Charisma" => 5],
"Character4" => ["Strength" => 1, "Dexterity" => 2, "Intelligence" => 4, "Wisdom" => 3, "Charisma" => 3]
];
foreach ($characters as $key => $character){
$value = max($character);
$skill = array_search($value, $character);
$characters[$key]['highest_skill'] = ['skill' => $skill, 'value' => $value];
}
print_r($characters["Character1"]);
// this will be shown
Array ( [Strength] => 1 [Dexterity] => 2 [Intelligence] => 6 [Wisdom] => 6 [Charisma] => 3 [highest_skill] => Array ( [skill] => Intelligence [value] => 6 ) )
Use a combination of max and array_search to get the highest for each character
Optionally, use a temp object were we will 'save' the highest key and value. This extra loop is shown in the demo, but can be removed if you move the echo after the array_search
<?php
$characters = [
"Character1" => ["Strength" => 1, "Dexterity" => 2, "Intelligence" => 6, "Wisdom" => 6, "Charisma" => 3],
"Character2" => ["Strength" => 5, "Dexterity" => 4, "Intelligence" => 1, "Wisdom" => 1, "Charisma" => 6],
"Character3" => ["Strength" => 6, "Dexterity" => 5, "Intelligence" => 5, "Wisdom" => 1, "Charisma" => 5],
"Character4" => ["Strength" => 1, "Dexterity" => 2, "Intelligence" => 4, "Wisdom" => 3, "Charisma" => 3]
];
$res = new \stdClass();
foreach ($characters as $name => $char) {
$highest = array_search(max($char), $char);
$res->{$name} = [ $highest, $char[$highest] ];
}
foreach ($res as $name => $val) {
echo "Highest for {$name} is {$val[0]}! Value: {$val[1]}" . PHP_EOL;
}
Output:
Highest for Character1 is Intelligence! Value: 6
Highest for Character2 is Charisma! Value: 6
Highest for Character3 is Strength! Value: 6
Highest for Character4 is Intelligence! Value: 4
Try it online!
Note: If there are multiple keys with the same (highest) value, the first will be used!
Though Ostone0 is right in his approach, but I assume it does not need to be this complicated. Just try the following to get what you want. It is easier and much more straightforward.
foreach ($characters as $character => $names) {
$quality = array_search(max($names), $names);
$max = max($names);
echo 'The highest value for '. $character . ' is ' . $max . ' for the quality ' . $quality. PHP_EOL ;
}
This will output the following:
The highest value for Character1 is 6 for the quality Intelligence
The highest value for Character2 is 6 for the quality Charisma
The highest value for Character3 is 6 for the quality Strength
The highest value for Character4 is 4 for the quality Intelligence
Try it online!

Group rows in a multidimensional array and sum the "count" elements in each group [duplicate]

This question already has answers here:
Group multidimensional array data based on two column values and sum values of one column in each group
(5 answers)
Closed 7 months ago.
I have an array of associative arrays and I would like to group them by the rows by the values for A, B, and C and sum the count value for each group.
$array = [
['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'test1', 'C' => 2, 'count' => 1],
];
I need a result like this:
[
["A" => "O", "B" => "O", "C" => 1, "count" => 2],
["A" => "Test", "B" => "Test", "C" => 1, "count" => 2],
["A" => "Test", "B" => "test1", "C" => 2, "count" => 1]
]
In order to make this you need to loop through the array and check where the attributes "A", "B", "C" are equal. I tried doing this, but I couldn't fix it.
$countedArray[0] = $array[0];
foreach ($array as $item) {
$occKey = array_filter(
$countedArray,
function ($countedItem, $key) use ($array) {
if ($countedItem['A'] == $item['A']
&& $countedItem['B'] == $item['B']
&& $countedItem['C'] == $item['C']
) {
$countedItem[$key]['count'] = countedItem[$key]['count'] + 1
} else {
array_push(
$countedArray,
[
'A' => $item['A'],
'B' => $item['B'],
'C' => $item['C'],
'count' => 1
]
);
}
},
ARRAY_FILTER_USE_BOTH
);
}
I've done my best to make it less verbose. I welcome any suggestions. Here's my proposed solution:
function sumOccurrences(array $original): array
{
$summed = [];
foreach ($original as $value) {
// here we get the array without the 'count' key - everything we need to compare
$comparisonElement = array_filter($value, function ($key) {
return $key !== 'count';
}, ARRAY_FILTER_USE_KEY);
// we search with strict comparison (third param - true) - see reasoning below
$foundAt = array_search($comparisonElement, array_column($summed, 'element'), true);
if ($foundAt === false) {
// we separate the values we compare and the count for easier handling
$summed[] = ['element' => $comparisonElement, 'count' => $value['count']];
} else {
// if we've run into an existing element, just increase the count
$summed[$foundAt]['count'] += $value['count'];
}
}
// since we separated count from the values for comparison, we have to merge them now
return array_map(function ($a) {
// $a['count'] is wrapped in an array as it's just an integer
return array_merge($a['element'], ['count' => $a['count']]);
}, $summed);
}
In order to make it less verbose, I've opted to compare arrays directly. Other than being less verbose, another benefit is that this will work if additional key => value pairs are introduced to the array without any addition to logic. Everything that is not count gets compared, no matter how many pairs exist. It will also cover any nested arrays (for example 'C' => ['D' => 1]).
But, this comes at a price - we must use strict comparison because loose can give undesired results (for example, ['a'] == [0] will return true). Strict comparison also means that it won't work if any values are objects (strict comparison means it's checking for the same instance) and that arrays will only be matched if they have the same key => value pairs in the same order. This solution assumes that your array (and any nested ones) are already sorted.
If that is not the case, we'd have to sort it before comparing. Normally, ksort would do the job, but to support nested arrays, we'd have to devise a recursive sort by key:
function ksortRecursive(array &$array): void
{
ksort($array);
foreach ($array as &$value) {
if (is_array($value)) {
ksortRecursive($value);
}
}
}
and call it before we do array_search.
Now if we assume a starting array like in your example, the following should give you the desired result:
$original = [
['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'test1', 'C' => 2, 'count' => 1],
];
var_dump(sumOccurrences($original));
The accepted answer is working waaaaay too hard for what is a basic task.
You merely need to use temporary, composite keys (based on the first three elements' values) to form grouped results. When a new row matches a pre-existing group, simply add its count to the stored count for the group. When the loop finishes, call array_values() to re-index the first level of the result array.
Code: (Demo)
$array = [
['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
['A' => 'Test', 'B' => 'test1', 'C' => 2, 'count' => 1],
];
$result = [];
foreach ($array as $row) {
$key = implode('~', array_slice($row, 0, 3));
if (!isset($result[$key])) {
$result[$key] = $row;
} else {
$result[$key]['count'] += $row['count'];
}
}
var_export(array_values($result));

PHP - check if multi array keys exist

How I can check if multi array keys exist?
Example:
$array = array(
array('first_id' => 2, 'second_id' => 4, 'third_id' => 6),
array('first_id' => 3, 'second_id' => 5, 'third_id' => 7)
);
And now I want to check if in array exist row with params:
first_id = 3,
second_id = 5,
third_id = 6.
in this example, I should get no results, becase third_id = 6 is not exist (it exist but with first_id = 2 and second_id = 4).
How I can check it in easy way in PHP?
Thanks.
PHP's native array equality check will return true for arrays that have the same keys and values, so you should just be able to use in_array for this - it will take care of the "depth" automatically:
$set = [
['first_id' => 2, 'second_id' => 4, 'third_id' => 6],
['first_id' => 3, 'second_id' => 5, 'third_id' => 7]
];
$tests = [
['first_id' => 3, 'second_id' => 5, 'third_id' => 7],
['first_id' => 3, 'second_id' => 5, 'third_id' => 6],
['first_id' => 2, 'second_id' => 4, 'third_id' => 6],
['first_id' => 2, 'second_id' => 5, 'third_id' => 6],
];
foreach ($tests as $test) {
var_dump(in_array($test, $set));
}
bool(true)
bool(false)
bool(true)
bool(false)
See https://eval.in/936215
If it's important that the array keys are also in the right order, add the third paramater true to the in_array call. This will use strict equality, rather than loose, and require the arrays to be ordered identically. See the information about equality here: http://php.net/manual/en/language.operators.array.php
You can use isset, array_search, and array_filter
for only one liner try this..
$array = array(
array('first_id' => 2, 'second_id' => 4, 'third_id' => 6),
array('first_id' => 3, 'second_id' => 5, 'third_id' => 7)
);
$first_id = 2;
$second_id = 4;
$third_id = 6;
//check and get array index if exist
$index = array_keys(array_filter($array, function($item) use ($first_id,
$second_id, $third_id) { return $item['first_id'] === $first_id &&
$item['second_id'] === $second_id && $item['third_id'] === $third_id; }));
//print out that array index
print_r($array[$index[0]]);

Fill 2d array with rows for missing periods of array

I have an array that contains periods from 1 - 13. Sometimes the array doesn't contain data for all periods and I need to fill in the missing ones, for example:
$array = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
For this case I need to run a php loop to fill in the missing first 6 periods with 0 y values. I've tried a variety of loops but with no joy.
Desired output:
[
['period' => 1, 'y' => 0],
['period' => 2, 'y' => 0],
['period' => 3, 'y' => 0],
['period' => 4, 'y' => 0],
['period' => 5, 'y' => 0],
['period' => 6, 'y' => 0],
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
]
You can get good semantics with using the standard array methods. For example:
<?php
$in = [
['period' => 7, 'y' => 20],
['period' => 8, 'y' => 20.50],
['period' => 9, 'y' => 7020],
['period' => 10, 'y' => 6520],
['period' => 11, 'y' => 65920],
['period' => 12, 'y' => 62820],
['period' => 13, 'y' => 6120],
];
// collect available periods
$available = array_column($in, 'period');
// calculate missing periods
$missing = array_diff(range(1, 13), $available);
// transform missing to correct format
$addition = array_map(function ($period) { return ['period' => $period, 'y' => 0]; }, $missing);
// add missing to input
$out = array_merge($in, $addition);
// sort by period
usort($out, function ($a, $b) {
return $a['period'] <=> $b['period'];
});
// done
print_r($out);
demo: https://3v4l.org/2fDYW
start by filling the whole array you need with all period with a value of 0.
Then for each period you get in your data array, use the period id to update the value of period in the right place in the array.
Hope it helps...
You can try this
for ($i=1 $<=13, $i++) {
$exits = false;
foreach ($array as $key => $value) {
if ($value['period'] == $key) {
$exits = true;
}
}
if ($exits) {
$array[] = ['period' => $i , 'y' => 0];
}
}
This should solve your problem:
Let's say $p_array is your array that contains periods from 1 - 13.
// get all periods number that is not missed
$not_missed_periods = array();
foreach ($p_array as $p_each)
{
$not_missed_periods[] = $p_each['period'];
}
// loop for checking all valid periods i.e 1-13
for ($i=1; $i<=13; $i++)
{
// check if $i OR period is not in $not_missed_periods
if (!in_array($i, $not_missed_periods)) {
$p_array[] = array('period' => $i, 'y' => 0);
}
}
print_r($p_array);
Assuming your $array is sorted by period.
You can create a new array that copies the content or your $array and set a new content for missing periods.
$new_array = [];
for ($i = 1, $j = 0; $i <= 13; $i++) {
if ($array[$j]['period'] == $i) {
$new_array[] = $array[$j]; //copy contents
$j++;
} else {
$new_array[] = [ 'period' => $i, 'y' => 0 ]; // set new contents
}
}
Use array_column() to generate a lookup array from your input array -- effectively applying associative, numeric, first-level keys without disturbing the original row data.
Then iterate from 1 to 13. If the current iteration's integer is found in the lookup, then push the found row; otherwise push the default row containing the incremented value.
Code: (Demo)
$lookup = array_column($data, null, 'period');
$result = [];
for ($i = 1; $i <= 13; ++$i) {
$result[] = $lookup[$i] ?? ['period' => $i, 'y' => 0];
}
var_export($result);

Simplify foreach array builder with isset tests for keys

For the following dataset ($data), I'm building a multidimensional array. For each unique key (a) there will be an array of b values associated with it.
$data = [
['a' => 1, 'b' => 23],
['a' => 1, 'b' => 97],
['a' => 2, 'b' => 23],
['a' => 1, 'b' => 47],
['a' => 3, 'b' => 23],
];
$result = [];
foreach ($data as $d) {
if (!isset($result[$d['a']]))
$result[$d['a']] = [];
$result[$d['a']][] = $d['b'];
}
print_r($result);
/*
Output should be:
[
1 => [23, 97, 47],
2 => [23],
3 => [23],
]
*/
The if (!isset($result[$d['a']])) $result[$d['a']] = []; part is ugly. Is there a sexier/more-efficient way to build this desired output?
Update:
Thanks everyone, looks like I could just go (without even initializing $result = [];)
foreach ($data as $d)
$result[$d['a']][] = $d['b'];
That check is not needed at all. If it is not a array, it will created when the first item is added.
So the "sexier" solution is to remove the if and initialization...
Dont have to use the isset() function.
try this code to make it "sexier".
$data = [
['a' => 1, 'b' => 23],
['a' => 1, 'b' => 97],
['a' => 2, 'b' => 23],
['a' => 1, 'b' => 47],
['a' => 3, 'b' => 23],
];
$result = [];
foreach ($data as $d) {
$result[$d['a']][]= $d['b'];
}
print_r($result);

Categories