我最近遇到了一款游戏,它生成一个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"
}
[...]
答案 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>";
?>
<强>解释强>
nextMoves
将从全局变量$valid_paths
添加从给定位置开始的所有有效路径。$visited
数组添加到$valid_paths
$position
添加到$visited
阵列。$direction
,如果可以在那个方向上移动到网格中的图块并且没有访问它,则会将新图块添加为有效的下一步。$position
新$visted
来重新开始。我已经改进了一些代码。它可以生成具有一定数量的可能答案的有效电路板。我不会在这里发布代码,因为它与答案无关。您可以在此PHPFiddle中查看改进的代码。