如何存储深度未知的递归函数的结果?

时间:2015-11-07 14:26:16

标签: php arrays function recursion depth

我最近遇到了一款游戏,它生成一个NxN(2x2,4x4 ......)电路板,带有编号的磁贴(1,2,3 ......),每个数字决定了允许的移动。如果可能的话,目标是清空整个棋盘,而每个棋子只能使用一次。

小板看起来像这样:

| 1 1 |  
| 1 1 |

更大的董事会:

| 1 2 2 3 |  
| 2 1 1 1 |  
| 3 2 1 3 |  
| 2 2 3 1 | 

由于我经常偶然发现无法解决问题的情况,我尝试编写一个简短的脚本,检查在使用电路板之前是否可以找到解决方案。

虽然这部分有效,但检查每个可能的组合并分别存储所有结果变得更加复杂,我甚至不确定这是否可行。

以下是简化示例:

$tiles[1][1] = 'p1';
$tiles[2][1] = 'p1';
$tiles[1][2] = 'p1';
$tiles[2][2] = 'p1';

$moves = array(
                array('x' => -1, 'y' => -1),
                array('x' =>  0, 'y' => -1),
                array('x' =>  1, 'y' => -1),
                array('x' => -1, 'y' =>  0),
                array('x' =>  1, 'y' =>  0),
                array('x' => -1, 'y' =>  1),
                array('x' =>  0, 'y' =>  1),
                array('x' =>  1, 'y' =>  1)
            );

function nextMove($x, $y, &$visited)
{
    global $moves;

    $max_loops = count($moves);
    $visited[] = $x. ', ' .$y;

    for ($j = 0; $j < $max_loops; $j++)
    {
        $new_x = $x + $moves[$j]['x'];
        $new_y = $y + $moves[$j]['y'];

        if (($new_x >= 1) && ($new_x <= 2) && ($new_y >= 1) && ($new_y <= 2))
        {
            $new_pos = $new_x. ', ' .$new_y;

            if (!in_array($new_pos, $visited))
            {
                $j = $max_loops - 1;

                nextMove($new_x, $new_y, $visited);
            }
        }
    }
}

nextMove(1, 1, $visited, $moves_done);
var_dump($visited);

所以,这里发生的事情很简单:

使用初始坐标调用函数,计算最大移动量(对于该图块)并将当前位置添加到$visited数组。

之后,for循环遍历可能移动的数组,生成新坐标并检查它们是否在棋盘上或者之前是否已经“访问过”。如果找到有效的移动,则使用新坐标再次调用该函数。

(正如您所看到的那样$j = $max_loops - 1;结束循环,以避免在$visited中添加错误的值,如果以后未找到有效的移动的话

这就是现在发生的事情

array(4) {
  [0]=>
  string(4) "1, 1"
  [1]=>
  string(4) "1, 2"
  [2]=>
  string(4) "2, 1"
  [3]=>
  string(4) "2, 2"
}

到目前为止,脚本正在运行,但只要没有可能的移动,脚本就会结束,不会检查其他组合(使用$j = $max_loops - 1;)或向$visited添加错误的坐标。为了避免这种情况,我要么必须单独存储结果,要么更改功能,这就是我遇到的问题。

一个大问题是未知数量的可能移动,因为有时游戏在2次移动后结束,有时候可以清除棋盘。

有没有办法让函数迭代所有可能的组合并单独存储/返回它们(可能太多了,无法处理,但如果只能使用1-2个tile,可能会很有趣) OR 至少检查整个电路板是否可以清除并返回解决方案(这很重要,因为结果应该存储并在以后使用)?

脚本以这种方式工作:

array(4) {
  [0]=>
  string(4) "1, 1"
  [1]=>
  string(4) "1, 2"
  [2]=>
  string(4) "2, 1"
  [3]=>
  string(4) "2, 2"
}
array(4) {
  [0]=>
  string(4) "1, 1"
  [1]=>
  string(4) "1, 2"
  [2]=>
  string(4) "2, 2"
  [3]=>
  string(4) "2, 1"
}
array(4) {
  [0]=>
  string(4) "1, 1"
  [1]=>
  string(4) "2, 1"
  [2]=>
  string(4) "1, 2"
  [3]=>
  string(4) "2, 2"
}
array(4) {
  [0]=>
  string(4) "1, 1"
  [1]=>
  string(4) "2, 1"
  [2]=>
  string(4) "2, 2"
  [3]=>
  string(4) "1, 2"
}
array(4) {
  [0]=>
  string(4) "1, 1"
  [1]=>
  string(4) "2, 2"
  [2]=>
  string(4) "2, 1"
  [3]=>
  string(4) "1, 2"
}
array(4) {
  [0]=>
  string(4) "1, 1"
  [1]=>
  string(4) "2, 2"
  [2]=>
  string(4) "1, 2"
  [3]=>
  string(4) "2, 1"
}

[...]

1 个答案:

答案 0 :(得分:1)

好的,这就是我想出来的:

<?php

$width = 2;
$height = 2;

$tiles[2][1] = 1;
$tiles[1][2] = 1;
$tiles[1][1] = 1;
$tiles[2][2] = 1;

$directions = array(
                array('x' => -1, 'y' => 0),
                array('x' => -1, 'y' => 1),
                array('x' => 0, 'y' => 1),
                array('x' => 1, 'y' => 1),
                array('x' => 1, 'y' => 0),
                array('x' => 1, 'y' => -1),
                array('x' => 0, 'y' => -1),
                array('x' => -1, 'y' => -1)
            );

$valid_paths = array();

function nextMoves($position, $visited = array()){
    global $directions, $tiles, $valid_paths, $height, $width;

    $visited[] = $position;
    if(count($visited) == $width * $height){
        $valid_paths[] = $visited;
        return;
    }

    $next_moves = array();
    $position = explode(',', $position);
    $x = $position[0];
    $y = $position[1];

    $tile_value = $tiles[$x][$y];

    foreach($directions as $direction){
        $new_x = $x + $direction['x'] * $tile_value;
        $new_y = $y + $direction['y'] * $tile_value;
        $new_pos = "$new_x,$new_y";

        if (($new_x >= 1) && ($new_x <= $width) && ($new_y >= 1) && ($new_y <= $height) && !in_array($new_pos, $visited)){
            $next_moves[] = $new_pos;
        }
    }

    foreach($next_moves as $next_move){
        nextMoves($next_move, $visited);
    }   
}

nextMoves('1,1');

echo "<pre>";
var_dump($valid_paths);
echo "</pre>";

?>

<强>解释

  1. 函数nextMoves将从全局变量$valid_paths添加从给定位置开始的所有有效路径。
  2. 首先检查是否已访问过所有图块。如果这是真的,它会将$visited数组添加到$valid_paths
  3. 如果未访问过所有图块,则会将当前$position添加到$visited阵列。
  4. 接下来,它会迭代每个$direction,如果可以在那个方向上移动到网格中的图块并且没有访问它,则会将新图块添加为有效的下一步。
  5. 完成计算后,所有可能的有效下一步操作都会针对每个新$position$visted来重新开始。
  6. 我已经改进了一些代码。它可以生成具有一定数量的可能答案的有效电路板。我不会在这里发布代码,因为它与答案无关。您可以在此PHPFiddle中查看改进的代码。