class Board
{
const MARK_0 = 0;
const MARK_X = 1;
/** @var int */
private $sizeX;
/** @var int */
private $sizeY;
/** @var int */
private $requiredMarks;
/** @var array */
private $map = [];
/**
* @param int $sizeX
* @param int $sizeY
*/
public function __construct (int $sizeX = 3, int $sizeY = 3)
{
$this->sizeX = $sizeX;
$this->sizeY = $sizeY;
$this->requiredMarks = $sizeX;
}
/**
* @return int
*/
public function getSizeX() : int
{
return $this->sizeX;
}
/**
* @return int
*/
public function getSizeY() : int
{
return $this->sizeY;
}
/**
* @return int
*/
public function getRequiredMarks() : int
{
return $this->requiredMarks;
}
/**
* @param int $count
*/
public function setRequiredMarks (int $count) : void
{
$this->requiredMarks = $count;
}
/**
* @param int $x
* @param int $y
* @param int $mark
*/
public function setMark (int $x, int $y, int $mark) : void
{
$this->map[$x][$y] = $mark;
}
/**
* @param int $x
* @param int $y
*
* @return int|null
*/
public function getMark (int $x, int $y) : ?int
{
return $this->map[$x][$y] ?? null;
}
/**
* @return int|null
*/
public function checkWin() : ?int
{
foreach([self::MARK_0, self::MARK_X] as $mark)
{
if(/* $this->checkLanes($mark) || */ $this->checkDiagonals($mark))
{
return $mark;
}
}
return null;
}
/**
* @param int $mark
*
* @return bool
*/
private function checkDiagonals (int $mark) : bool
{
$sizeX = $this->getSizeX();
$sizeY = $this->getSizeY();
$required = $this->getRequiredMarks();
$size = max($sizeX, $sizeY);
for($k = $required - $size; $k <= ($size - $required); $k++)
{
$score1 = 0;
$score2 = 0;
$startI = max(0, $k);
$endI = min($size, $size + $k);
for($i = $startI; $i < $endI; $i++)
{
if($this->getMark($i, $k + $i) === $mark)
{
if(++$score1 >= $required)
{
return true;
}
}
else
{
$score1 = 0;
}
if($this->getMark($i, $size - 1 + $k - $i) === $mark)
{
if(++$score2 >= $required)
{
return true;
}
}
else
{
$score2 = 0;
}
}
}
return false;
}
}
$b = new Board (4, 4);
$b->setRequiredMarks(3);
$b->setMark(0, 1, Board::MARK_X);
$b->setMark(1, 2, Board::MARK_X);
$b->setMark(2, 3, Board::MARK_X);
$winner = $b->checkWin();
if($winner === null)
{
$winner = "nobody";
}
elseif($winner === Board::MARK_X)
{
$winner = "X";
}
else
{
$winner = "0";
}
var_dump($winner);
如何修复函数“ checkDiagonals”,以便正确处理photo中的对角线并返回正确的结果?
如果像photo中那样检查对角线,则它可以正常工作。
我想不出一种用于检查对角线的算法,因此我从这里取了它:https://stackoverflow.com/a/34257658/10261980
已注释的函数“ checkLanes”正常工作,因此已从代码中隐藏。
答案 0 :(得分:0)
以下是您的算法当前正在检查的对角线的坐标:
x: 0, y: -1
x: 1, y: 0
x: 2, y: 1
x: 0, y: 0
x: 1, y: 1
x: 2, y: 2
x: 3, y: 3
x: 1, y: 2
x: 2, y: 3
x: 3, y: 4
您会看到一个索引超出范围,一次迭代正在检查4个正方形,这超出了要求,并且没有足够的检查来覆盖两个对角线。
您似乎正在尝试从每个索引开始,这些索引可以“拟合”所需大小的对角线,然后向下移动到右侧,以检查对角线是否不匹配。让我们手动枚举支票:
1... .1.. .... .... ...1 ..1. .... ....
.2.. ..2. 1... .1.. ..2. .2.. ...1 ..1.
..3. ...3 .2.. ..2. .3.. 3... ..2. .2..
.... .... ..3. ...3 .... .... .3.. 3...
这是三个嵌套循环:行,列和对角线:
$sizeY - $requiredMarks
。 $sizeX - $requiredMarks
,检查从左到右的对角线。 $sizeX - $requiredMarks + 1
到$sizeX
运行,检查从右到左对角线。$requiredMarks
。 索引到单元格如下:行:($row + $diag)
,列:($col + $diag * $xDirection)
。 $xDirection
乘数(1
或-1
)使对角线检查功能可以向任一方向(向左或向右)移动。
这是执行此操作的代码:
private function checkDiagonals(int $mark) : bool {
$required = $this->getRequiredMarks();
for ($row = 0; $row <= $this->getSizeY() - $required; $row++) {
for ($col = 0; $col <= $this->getSizeX() - $required; $col++) {
if ($this->checkDiagonal($mark, $row, $col, 1)) {
return true;
}
}
for ($col = $this->getSizeX() - $required + 1; $col < $this->getSizeX(); $col++) {
if ($this->checkDiagonal($mark, $row, $col, -1)) {
return true;
}
}
}
return false;
}
private function checkDiagonal(int $mark, int $row, int $col, int $xDir) : bool {
for ($i = 0; $i < $this->getRequiredMarks(); $i++) {
if ($this->getMark($col + $i * $xDir, $row++) !== $mark) {
return false;
}
}
return true;
}
这些是它检查的对角线:
x: 0, y: 0
x: 1, y: 1
x: 2, y: 2
x: 1, y: 0
x: 2, y: 1
x: 3, y: 2
x: 2, y: 0
x: 1, y: 1
x: 0, y: 2
x: 3, y: 0
x: 2, y: 1
x: 1, y: 2
x: 0, y: 1
x: 1, y: 2
x: 2, y: 3
x: 1, y: 1
x: 2, y: 2
x: 3, y: 3
x: 2, y: 1
x: 1, y: 2
x: 0, y: 3
x: 3, y: 1
x: 2, y: 2
x: 1, y: 3
这并不是非常有效,因为它涉及多次访问广场,但至少应该可以使您操作。如果速度很重要,请考虑使用bitboards,它可以通过一些操作检查整个电路板,但需要相当多地调整您的方法。一种更简单的重构方法是跟踪玩家的最后举动,并仅检查从该方块可能获得的胜利。
这里是repl进行测试。