如何在“五子棋”游戏中检测斜线胜利(PHP)

3

我需要编写一个简单的"五子棋"游戏。我已成功检测到了行胜利和列胜利,如下所示:

对于行:

$same=0;
    for($i=1;$i<=$size;$i++) for($j=1;$j<=$size;$j++){ /wins in a row
        if((@$_SESSION["pos"][$i][$j] == @$_SESSION["pos"][$i][$j+1]) && @$_SESSION["pos"][$i][$j]!=0 ) ++$same; else $same=0;
        if($same==4){
            if($_SESSION["pos"][$i][$j]==1) $winner="First"; //will be read from DB
            if($_SESSION["pos"][$i][$j]==2) $winner="Second"; //will be read from DB
            print $winner.' player WON!<br />';  
        }
}

对于列,情况相同,但是方向相反,所以很容易。现在我的问题是,如何检测对角线胜利?我尝试搜索了一下,但是没有找到任何我可以使用(或理解)的东西。我只能使用PHP。

What is what:
$same = same symbol counter
$_SESSION["pos"][$i][$j] = the 2D array of coordinates of the board (starting from 1,1 not 0,0)
indexes are the coordinates, values can be 0 (empty space) or 1 (symbol1) or 2 (symbol2)
$size = size of the board (always N x N)

您不需要给我一个完整的代码,只需给我一些东西来理解如何实现它。


2
这些讲师变得非常有想象力。 - Ed Heal
3个回答

0

让我们的游戏区域宽度为5。这有点棘手或者说是hack,但我们可以将这样的数组作为一个长字符串来处理,并查找子字符串,如XXXXX,或00000(用于行检查),或X....{5次}(用于列检查)或X.....{5次}(用于\对角线检查),以及X...{5次}用于/对角线。

所以我们只需要用任何符号替换.并找到这样的模式。这个任务是我们可以使用正则表达式的时候。我们只需要用preg_match()匹配这些模式。

我不知道有什么更快的方法,但这似乎很原始(例如只针对X检查):

<?php
$area = array(
    array('x','x','x','x','x'),
    array('0','0','x','x','0'),
    array('x','x','x','x','0'),
    array('0','0','x','x','x'),
    array('x','0','x','0','x'),
    );

//get the long string of that array
$fullstring = '';
foreach($area as $string) {
    $fullstring .= implode('', $string)."|";
}

//check long string for such patterns
$win_by_column = preg_match('/x.{5}x.{5}x.{5}x.{5}x/i', $fullstring); //5 game area width
$win_by_row = preg_match('/x{5}/i', $fullstring); //5 same in rows
$win_by_bs_diagonal = preg_match('/x.{6}x.{6}x.{6}x.{6}x/i', $fullstring); // \ diagonal, 5 is $area width + 1
$win_by_s_diagonal = preg_match('/x.{4}x.{4}x.{4}x.{4}x/i', $fullstring); // / diagonal, 4 is $area width - 1.

//output the results
var_dump($win_by_column);
var_dump($win_by_row);
var_dump($win_by_bs_diagonal);
var_dump($win_by_s_diagonal);
?>

如果您不熟悉正则表达式,这也是一个很好的开始;)

更新。我已经更新了代码。我在数组字符串中插入了字符串分隔符。因此,现在我们需要检查列检查中的5个点(任何符号)和对角线检查中的6个点。这些硬编码值分别计算为宽度和宽度+1。为了方便阅读和理解,我没有在模式中插入$area宽度。


我只需要将X更改为我的符号,并使用变量来表示区域大小,它就可以完美地检测出对角线为5的情况。谢谢! - user3055100
我将你的“4-check”更改为“5-check”,以便在这个例子中更简单。我相信如果你使用“4-check”,你还需要检查行、列和对角线的两侧未关闭情况。好问题! - V G
1
如果我正确地阅读了你的代码,它将错误地检测到像(row 1: 0xxxx, row 2: x0000)中的5个x这样的包装行。如果棋盘更大,对角线也会受到同样的问题影响(这也是为什么你简单的 / 对角线检查不起作用的原因)。要修复它,您可以在行之间添加分隔符字符(并将正则表达式中的偏移量增加一)。 - Ilmari Karonen
对角线检查之所以有效是因为硬编码的5意味着游戏区域宽度,而不是连续5个棋子。 - V G
通过这个改变,你现在可以摆脱 $fullstring_rev 变量,并且只需要匹配 /x.{4}x.{4}x.{4}x.{4}x/$fullstring - Ilmari Karonen

0

你可以像检测行和列一样检测对角线:把对角线看作是一个倾斜的行(或列)。也就是说,不要只看单元格 [$i] [$j] ,而是查看 [$i + $j] [$j] (对于正斜率的对角线)或 [$i-$j] [$j] (对于负斜率)。

例如,以下是如何检测负对角线的示例:

$grid = $_SESSION["pos"];
for ($i = 1; $i <= $size; $i++) {
    $same = 0;
    $prev = 0;
    for ($j = 1; $j <= $size && $i-$j >= 1; $j++) {
        $cur = @$grid[$i-$j][$j];
        if ($cur == $prev && $cur != 0) ++$same;
        else $same = 0;
        if ($same == 4) {
            $winner = ($cur == 1 ? "First" : "Second"); //will be read from DB
            print "$winner player WON!<br />";  
        }
        $prev = $cur;
    }
}

将其更改为检查正对角线留作练习。


既然我看到了解决方案,它比我想象的要容易。我自己只是想不出来。感谢您的回答。 - user3055100

0
你可以通过两个单独的for循环来检查对角线:
$area = array(
    array('x','x','x','x','x'),
    array('0','x','x','x','0'),
    array('x','x','x','x','0'),
    array('0','x','x','x','x'),
    array('x','0','x','0','x'),
);

$diagonal1 = 0;
for($i = 0; $i < 5; $i++){

   if($area[$i][$i] == 'x'){
       $diagonal1 ++;
   }
}

if($diagonal1 == 5){
    echo 'Diagonal win';
}

// other diagonal
$diagonal2 = 0;
for($i = 0; $i < 5; $i++){

    if($area[$i][4 - $i] == 'x'){
        $diagonal2 ++;
    }
}

if($diagonal2 == 5){
    echo 'Diagonal win';
}

如果我们有一个“5 * 5”的棋盘,这很好,但我的棋盘是“n * n”,所以仅检查两个主对角线是不够的。感谢您的回答。 - user3055100
是的,但如果您的板是正方形,则可以计算要在for循环中迭代的数字并动态检查它。 - PKolos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接