Improving summing algorithm for 2048 game developed with PHP - php

I challenged myself to improve my skills by doing some individual projects and for now I am stuck with the 2048.
I have the text file which stores the present values of the game.
128 64 32 16 32 8 8 8 16 4 4 4 8 2 2 2
<?php
$logs = file("logs-2048.txt");
if ($_SERVER["REQUEST_METHOD"] == "POST") {
file_put_contents('logs-2048.txt', '');
$tableValues = [];
$arrayLine = explode(" ", $logs[0]);
for($g=0; $g<4; $g++) {
$newLine = [];
for($h=0; $h<4; $h++) {
array_push($newLine, htmlspecialchars($arrayLine[$g*4+$h]));
}
array_push($tableValues, $newLine);
}
$direction = $_POST["action-joueur"];
$string = "";
if($direction == "^") {
for($g = 0; $g < 3; $g++) {
for ($h = 0; $h < 4; $h++) {
for ($i = 0; $i < 3; $i++) {
if ($tableValues[$i][$h] == $tableValues[$i+1][$h] || $tableValues[$i][$h] == 0) {
$tableValues[$i][$h] += $tableValues[$i+1][$h];
$tableValues[$i+1][$h] = 0;
}
}
}
}
} else if ($direction == "v") {
//Almost simillar stuff as for ^
} else if ($direction == "<") {
//Almost similar stuff as for ^
} else if ($direction == ">") {
//Almost similar stuff as for ^
}
$arrayEmpty = [];
for($g = 0; $g < 4; $g++) {
for($h = 0; $h < 4; $h++) {
if($tableValues[$g][$h] == 0) {
array_push($arrayEmpty, [$g, $h]);
}
}
}
$findRand = mt_rand(0, count($arrayEmpty)-1);
$tableValues[$arrayEmpty[$findRand][0]][$arrayEmpty[$findRand][1]] = 2;
for($g = 0; $g < 4; $g++) {
for ($h = 0; $h < 4; $h++) {
$string = $string.$tableValues[$g][$h]." ";
}
}
file_put_contents("logs-2048.txt", $string);
}
?>
The first loop is to parse the array that I take from the file, and convert it to a 2 dimensional array(table).
The problem is in the next 4 nested loops which does the summing, I can't figure how can I do other way then just use another loop(first one for each nested loop) which goes 3 times back if some numbers were stuck somewhere adding themselves (EX. 2 2 2 2 in first column => 4 2 2 0 => 4 4 0 0 => 8 0 0 0).
I am 100% sure that the algorithms for summing is terribly inefficient, but I can't figure out what solution I can provide to make it faster and more intuitive, then have 4 for's... I tried also to look at different resources on internet but it's pretty messed up for me because I am starting with php.
Yes, I know that php isn't the best way to do, I'll do myself with
Javascript, but I need it for learning, any ideas or tips to improve the algorithm will be greatly appreciated.

Related

Generate List of Unique Four-Digit Numbers Without Repeating Digits and Without Forward-Sequential Digits

I had a need to generate a list of four-digit numbers for use as codes. The digits should not repeat, and each next digit should not be sequential. There were some questions that were similar but not enough for me to answer. I chose to share my function instead. It did not matter if reverse numbers were in the list e.g. 1357 > 7531.
It occurred to me that it there may be an opportunity for a recursive function, possibly to return five or six-digit numbers. Improvements to my function are most welcome.
public function codeList() {
$data = [];
for ($ii=0; $ii < 10; $ii++) {
for ($jj=0; $jj < 10; $jj++) {
for ($kk=0; $kk < 10; $kk++) {
for ($ll=0; $ll < 10; $ll++) {
$str = "{$ii}{$jj}{$kk}{$ll}";
$arr = str_split($str);
if (count($arr) === count(array_unique($arr))) {
if (($arr[0] + 1 != $arr[1]) && ($arr[1] + 1 != $arr[2]) && ($arr[2] + 1 != $arr[3])) {
$data[] = $str;
}
}
}
}
}
}
return $data;
} # END FUNCTION codeList

Get lowest price on sum of combinations in given array

This code is working fine when the array length is 8 or 10 only. When we are checking this same code for more than 10 array length.it get loading not showing the results.
How do reduce my code. If you have algorithm please share. Please help me.
This program working flow:
$allowed_per_room_accommodation =[2,3,6,5,3,5,2,5,4];
$allowed_per_room_price =[10,30,60,40,30,50,20,60,80];
$search_accommodation = 10;
i am get subsets = [5,5],[5,3,2],[6,4],[6,2,2],[5,2,3],[3,2,5]
Show lowest price room and then equal of 10 accommodation; output like as [5,3,2];
<?php
$dp=array(array());
$GLOBALS['final']=[];
$GLOBALS['room_key']=[];
function display($v,$room_key)
{
$GLOBALS['final'][] = $v;
$GLOBALS['room_key'][] = $room_key;
}
function printSubsetsRec($arr, $i, $sum, $p,$dp,$room_key='')
{
// If we reached end and sum is non-zero. We print
// p[] only if arr[0] is equal to sun OR dp[0][sum]
// is true.
if ($i == 0 && $sum != 0 && $dp[0][$sum]) {
array_push($p,$arr[$i]);
array_push($room_key,$i);
display($p,$room_key);
return $p;
}
// If $sum becomes 0
if ($i == 0 && $sum == 0) {
display($p,$room_key);
return $p;
}
// If given sum can be achieved after ignoring
// current element.
if (isset($dp[$i-1][$sum])) {
// Create a new vector to store path
// if(!is_array(#$b))
// $b = array();
$b = $p;
printSubsetsRec($arr, $i-1, $sum, $b,$dp,$room_key);
}
// If given $sum can be achieved after considering
// current element.
if ($sum >= $arr[$i] && isset($dp[$i-1][$sum-$arr[$i]]))
{
if(!is_array($p))
$p = array();
if(!is_array($room_key))
$room_key = array();
array_push($p,$arr[$i]);
array_push($room_key,$i);
printSubsetsRec($arr, $i-1, $sum-$arr[$i], $p,$dp,$room_key);
}
}
// Prints all subsets of arr[0..n-1] with sum 0.
function printAllSubsets($arr, $n, $sum,$get=[])
{
if ($n == 0 || $sum < 0)
return;
// Sum 0 can always be achieved with 0 elements
// $dp = new bool*[$n];
$dp = array();
for ($i=0; $i<$n; ++$i)
{
// $dp[$i][$sum + 1]=true;
$dp[$i][0] = true;
}
// Sum arr[0] can be achieved with single element
if ($arr[0] <= $sum)
$dp[0][$arr[0]] = true;
// Fill rest of the entries in dp[][]
for ($i = 1; $i < $n; ++$i) {
for ($j = 0; $j < $sum + 1; ++$j) {
// echo $i.'d'.$j.'.ds';
$dp[$i][$j] = ($arr[$i] <= $j) ? (isset($dp[$i-1][$j])?$dp[$i-1][$j]:false) | (isset($dp[$i-1][$j-$arr[$i]])?($dp[$i-1][$j-$arr[$i]]):false) : (isset($dp[$i - 1][$j])?($dp[$i - 1][$j]):false);
}
}
if (isset($dp[$n-1][$sum]) == false) {
return "There are no subsets with";
}
$p;
printSubsetsRec($arr, $n-1, $sum, $p='',$dp);
}
$blockSize = array('2','3','6','5','3','5','2','5','4');
$blockvalue = array('10','30','60','40','30','50','20','60','80');
$blockname = array("map","compass","water","sandwich","glucose","tin","banana","apple","cheese");
$processSize = 10;
$m = count($blockSize);
$n = count($processSize);
// sum of sets in array
printAllSubsets($blockSize, $m, $processSize);
$final_subset_room = '';
$final_set_room_keys = '';
$final_set_room =[];
if($GLOBALS['room_key']){
foreach ($GLOBALS['room_key'] as $set_rooms_key => $set_rooms) {
$tot = 0;
foreach ($set_rooms as $set_rooms) {
$tot += $blockvalue[$set_rooms];
}
$final_set_room[$set_rooms_key] = $tot;
}
asort($final_set_room);
$final_set_room_first_key = key($final_set_room);
$final_all_room['set_room_keys'] = $GLOBALS['room_key'][$final_set_room_first_key];
$final_all_room_price['set_room_price'] = $final_set_room[$final_set_room_first_key];
}
if(isset($final_all_room_price)){
asort($final_all_room_price);
$final_all_room_first_key = key($final_all_room_price);
foreach ($final_all_room['set_room_keys'] as $key_room) {
echo $blockname[$key_room].'---'. $blockvalue[$key_room];
echo '<br>';
}
}
else
echo 'No Results';
?>
I'm assuming your task is, given a list rooms, each with the amount of people it can accommodate and the price, to accommodate 10 people (or any other quantity).
This problem is similar to 0-1 knapsack problem which is solvable in polynomial time. In knapsack problem one aims to maximize the price, here we aim to minimize it. Another thing that is different from classic knapsack problem is that full room cost is charged even if the room is not completely occupied. It may reduce the effectiveness of the algorithm proposed at Wikipedia. Anyway, the implementation isn't going to be straightforward if you have never worked with dynamic programming before.
If you want to know more, CLRS book on algorithms discusses dynamic programming in Chapter 15, and knapsack problem in Chapter 16. In the latter chapter they also prove that 0-1 knapsack problem doesn't have trivial greedy solution.

PHP - Find turning points in array data (high and low) based on range

$data = array(5,0,15,20,22,14,13,15,12,22,40,25);
Hi , i want to traverse the data points above and find the turning points based on a range.
The way i'm tackling it so far is simply taking the $array[$i] - $array[$i-1] , and if the absolute difference is greater than the range - i'm taking it as a turning point . however - the logic is flawed as if it moved slightly up and then back down - it breaks the cycle.
The 3 down values should have been enough to make X , a turning point downwards , but because they individually do not meet the range - they are discarded .
Any solutions ?
if($diff >= 0)
{
$diff_up = $diff_up + $diff;
}
else
{
$diff_down = $diff_down + abs($diff);
}
if((($diff_up-$diff_down) >=$range) && ($pivot_type != "UP"))
{
echo "Pivot UP at : ".$current;
break;
}
else if((($diff_down-$diff_up) >$range) && ($pivot_type != "DOWN"))
{
echo "Pivot DOWN at : ".$current;
break;
}
What you are looking for is all local minima and maxima, This is a good article.
I made this (with inspiration from:
get extremes from list of numbers):
<?php
$data = array(5,0,15,20,22,14,13,15,12,22,40,25);
function minima_and_maxima(array $array){
$maxima = [];
$minima = [];
$maxima[] = $array[0];
for($i = 1; $i < count($array) - 1; $i++){
$more_than_last = $array[$i] > $array[$i-1];
$more_than_next = $array[$i] > $array[$i+1];
$next_is_equal = $array[$i] == $array[$i+1];
if($next_is_equal) {
continue;
}
if ($i == 0) {
if ($more_than_next) {
$maxima[] = $array[$i];
} else {
$minima[] = $array[$i];
}
} elseif ($i == count($array)-1) {
if ($more_than_last) {
$maxima[] = $array[$i];
} else {
$minima[] = $array[$i];
}
} else {
if ($more_than_last && $more_than_next) {
$maxima[] = $array[$i];
} elseif (!$more_than_last && !$more_than_next) {
$minima[] = $array[$i];
}
}
}
for ($i = 0; $i < count($maxima); $i++) {
$current_maxima = $maxima[$i];
$next_maxima = $maxima[$i+1];
if ($current_maxima > $next_maxima) {
unset($maxima[$i+1]);
}
}
for ($i = 0; $i < count($minima); $i++) {
$current_minima = $minima[$i];
$next_minima = $minima[$i+1];
if ($next_minima < $current_minima) {
unset($minima[$i]);
}
}
return [
'maxima' => array_values($maxima),
'minima' => array_values($minima),
];
}
function get_turning_points($data)
{
$mins_and_maxs = minima_and_maxima($data);
$turning_points = [];
for ($i = 0; $i < count($mins_and_maxs['maxima']) - 1; $i++) {
$turning_points[] = $mins_and_maxs['maxima'][$i];
$turning_points[] = $mins_and_maxs['minima'][$i];
}
$turning_points[] = $mins_and_maxs['maxima'][count($mins_and_maxs['maxima'])-1];
return $turning_points;
}
print_r(get_turning_points($data));
This gives you:
Array
(
[0] => 5
[1] => 0
[2] => 22
[3] => 12
[4] => 40
)
Demo: https://eval.in/832708
Hope this helps :)

Php string with separator to page list

I try to recreate what we see when we printing page on office or adobe.
For example, when you want to print page 1 to 5 you write : 1-5 and if you want to print a page outside you write : 1-5,8
At the moment I explode string by ',' :
1-5 / 8
Then explode each result by '-' and if I've got result I loop from first page to last and create variable with comma :
1,2,3,4,5,8
Finally I explode by ',' and use array unique to erase double value.
It take some times to achieve this especially when there's a lot of '-'.
Maybe someone got a easier solution to so this ?
Thank
Edit :
$pages = "1-4,6-8,14,16,18-20";
$pages_explode = explode(',',$pages);
foreach($pages_explode as $page){
$page_explode = explode('-',$page);
if(!empty($page_explode[1])){
for ($i=$page_explode[0]; $i<=$page_explode[1] ; $i++) {
$page_final .= $i.',';
}
}else{
$page_final .= $page_explode[0].',';
}
}
$page_final = explode(',',$page_final);
$page_final = array_unique ($page_final);
foreach($page_final as $value){
echo $value.'<br>';
}
Is it a code golf challenge?
Well a basic approach seems fine to me :
$input = '1-5,6-12,8';
$patterns = explode(',', $input);
$pages = [];
foreach ($patterns as $pattern) {
if (2 == count($range = explode('-', $pattern))) {
$pages = array_merge($pages, range($range[0], $range[1]));
} else {
$pages[] = (int)$pattern;
}
}
$uniquePages = array_unique($pages);
var_dump($uniquePages);
Outputs :
array (size=12)
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5
5 => int 6
6 => int 7
7 => int 8
8 => int 9
9 => int 10
10 => int 11
11 => int 12
Having to remove duplicates suggests that you have overlapping ranges in your strings.
Eg: 1-5,2-9,7-15,8,10
You seems to process all these without considering the overlapping areas and finally attempt to remove duplicates by the expensive array_unique function.
Your code should instead remember the minimum and maximum of the resulting range and not process anything that overlaps this range.
Following is a sample code which demonstrates the idea. But its certainly faster than the code you have suggested in your question. You should add parts there to process additional types of delimiters, if any, in your requirement.
<?php
$ranges = "1-5,3-7,6-10,8,11";
$min = 10000;
$max = -1;
$numbers = array(); //Your numbers go here
//Just a utility function to generate numbers from any range and update margins
function generateNumbers($minVal, $maxVal) {
global $min, $max, $numbers;
for ($i = $minVal; $i <= $maxVal; $i++) {
array_push($numbers, $i);
if ($i < $min)
$min = $i;
if ($i > $max)
$max = $i;
}
}
//Seperate ranges
$sets = explode(",", $ranges);
//Go through each range
foreach($sets as $aSet) {
//Extract the range or get individual numbers
$range = explode("-", $aSet);
if (count($range) == 1) { //its an individual number. So check margins and insert
$aSet = intval($aSet);
if ($aSet < $min){
array_push($numbers, $aSet);
$min = $aSet;
}
if ($aSet > $max){
array_push($numbers, $aSet);
$max = $aSet;
}
continue; // <----- For single numbers it ends here
}
//Its a range
$rangeLow = intval($range[0]);
$rangeHigh = intval($range[1]);
//Adjusting numbers to omit cases when ranges fall right on the margins
if ($rangeLow == $min){
$rangeLow++;
}
else
if ($rangeLow == $max) {
$rangeLow--;
}
if ($rangeHigh == $min){
$rangeHigh++;
}
else
if ($rangeHigh == $max) {
$rangeHigh--;
}
//Check if below or above the generated range
if (($rangeLow < $min && $rangeHigh < $min) || ($rangeLow > $max && $rangeHigh > $max)) {
generateNumbers($rangeLow, $rangeHigh);
};
//Check if across the lower edge of the generated range
if ($rangeLow < $min && $rangeHigh > $min && $rangeHigh < $max) {
generateNumbers($rangeLow, $min - 1);
};
//Check if across the upper edge of the generated range
if ($rangeLow > $min && $rangeLow < $max && $rangeHigh > $max) {
generateNumbers($max + 1, $rangeHigh);
};
}
//Now just sort the array
print_r($numbers);
?>

2D Cluster algorithm

I am analyzing images and need a fast clustering algorithm which searches for the center of the biggest center.
A sample data set might look like this:
$x = array(6,9,7,0,0,0,4,0,0,6,6,3);
As you see there are 3 clusters in the array.
The result I am looking for is the array position of the center of the cluster with the highest sum.
In the example above this would be 1 as $x[1] is the center of the biggest cluster 6+9+7(=22).
Any ideas?
Whichever way you go, you'll have to walk through the array at least once. The following algorithm does this in one pass without any additional sorting/searching - although I admit that it still may not be the most efficient one. Note that if the biggest cluster has an even number of elements, then it'll return the "lower" mid-point (e.g. for a cluster with indices 0, 1, 2, 3 it will return 1) - this can be easily adjusted in the last line of computation ($mid = ...).
$input = array(6,9,7,0,0,0,4,0,0,6,6,3);
$clust = array(0, 0, 0);
$st = -1;
$sum = 0;
for($i=0; $i<count($input); $i++) {
if($input[$i] == 0) {
if($i == 0) {
continue;
}
elseif($input[$i - 1] == 0) {
continue;
}
else {
if($clust[2] < $sum) {
$clust = array($st, $i - 1, $sum);
}
}
}
else {
if($i == 0 || $input[$i - 1] == 0) {
$st = $i;
$sum = 0;
}
$sum += $input[$i];
}
}
if(end($input) != 0 && $clust[2] < $sum) {
$clust = array($st, $i - 1, $sum);
}
if($clust[2] > 0) {
$mid = (int)(($clust[1] - $clust[0]) / 2);
echo $clust[0] ."->". $mid ."->" . $clust[1] ." = ". $clust[2];
}
else {
echo "No clusters found";
}

Categories