241 lines
4.9 KiB
PHP
241 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace App\Game;
|
|
|
|
class GameBoardState
|
|
{
|
|
/**
|
|
* Cell states. (unset is default state)
|
|
*/
|
|
const STATE_PRESSED = 0;
|
|
const STATE_WIN = 1;
|
|
|
|
/**
|
|
* Width of the board
|
|
*/
|
|
protected int $width;
|
|
|
|
/**
|
|
* Height of the board
|
|
*/
|
|
protected int $height;
|
|
|
|
/**
|
|
* State of the cells.
|
|
*/
|
|
protected array $state = [];
|
|
|
|
/**
|
|
* If the board is in a winning state or not.
|
|
*/
|
|
protected int $winning = 0;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct($width, $height)
|
|
{
|
|
$this->width = $width;
|
|
$this->height = $height;
|
|
}
|
|
|
|
/**
|
|
* Set the state of a cell.
|
|
*/
|
|
public function set(int $pos, bool $pressed = true) : self
|
|
{
|
|
if ($this->hasGameEnded()) {
|
|
return $this;
|
|
}
|
|
|
|
if ($pressed) {
|
|
$this->state[$pos] = self::STATE_PRESSED;
|
|
} else {
|
|
unset($this->state[$pos]);
|
|
}
|
|
|
|
$this->update();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Toggle pressed state of a cell.
|
|
*/
|
|
public function toggle(int $pos) : self
|
|
{
|
|
return $this->set($pos, !$this->isPressed($pos));
|
|
}
|
|
|
|
/**
|
|
* Check if a cell is pressed or not.
|
|
*/
|
|
public function isPressed(int $pos) : bool
|
|
{
|
|
return isset($this->state[$pos]);
|
|
}
|
|
|
|
/**
|
|
* Check if a cell is part of a winning row/column.
|
|
*/
|
|
public function isWin(int $pos) : bool
|
|
{
|
|
return isset($this->state[$pos]) && $this->state[$pos] == self::STATE_WIN;
|
|
}
|
|
|
|
/**
|
|
* Get the sate of the cells.
|
|
*/
|
|
public function getState() : array
|
|
{
|
|
return $this->state;
|
|
}
|
|
|
|
/**
|
|
* Get the size of the board.
|
|
*/
|
|
public function getSize() : int
|
|
{
|
|
return $this->width * $this->height;
|
|
}
|
|
|
|
/**
|
|
* Clear the board.
|
|
*/
|
|
public function clear() : self
|
|
{
|
|
$this->state = [];
|
|
$this->winning = 0;
|
|
return $this;
|
|
}
|
|
|
|
// TODO: Rename
|
|
public function getNumWinRows() : int
|
|
{
|
|
return $this->winning;
|
|
}
|
|
|
|
/**
|
|
* Check if the board only has winning cards
|
|
*/
|
|
public function hasGameEnded() : bool
|
|
{
|
|
if (count($this->state) < $this->width * $this->height) {
|
|
return false;
|
|
}
|
|
|
|
foreach($this->state as $pos) {
|
|
if ($pos !== self::STATE_WIN) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the board is full and all positions is in a given state.
|
|
*/
|
|
public function isFull($state) : bool
|
|
{
|
|
// Board not filled, impossible to have all positions set.
|
|
if (count($this->state) < $this->width * $this->height) {
|
|
return false;
|
|
}
|
|
|
|
foreach($this->state as $pos) {
|
|
if ($pos !== $state) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
protected function update()
|
|
{
|
|
foreach($this->state as $pos => $_) {
|
|
$this->state[$pos] = self::STATE_PRESSED;
|
|
}
|
|
|
|
$patterns = $this->checkWinningState();
|
|
foreach($patterns as $cards) {
|
|
foreach($cards as $pos) {
|
|
$this->state[$pos] = self::STATE_WIN;
|
|
}
|
|
}
|
|
$this->winning = count($patterns);
|
|
}
|
|
|
|
/**
|
|
* Check if the board is in a winning state.
|
|
*/
|
|
protected function checkWinningState() : array
|
|
{
|
|
$win = [];
|
|
|
|
// Rows
|
|
for($y=0; $y < $this->height; $y++) {
|
|
$c = $this->checkRow($y);
|
|
if (count($c)) {
|
|
$win[] = $c;
|
|
}
|
|
}
|
|
|
|
// Columns
|
|
for($x=0; $x < $this->width; $x++) {
|
|
$c = $this->checkCol($x);
|
|
if (count($c)) {
|
|
$win[] = $c;
|
|
}
|
|
}
|
|
|
|
return $win;
|
|
}
|
|
|
|
/**
|
|
* Check if a row is in a winning state (all cells pressed)
|
|
*/
|
|
protected function checkRow($y) : array
|
|
{
|
|
$cards = [];
|
|
|
|
$y = $y * $this->width;
|
|
for($x = 0; $x < $this->width; $x++) {
|
|
$pos = $x + $y;
|
|
if (!$this->isPressed($pos)) {
|
|
return [];
|
|
}
|
|
/*
|
|
if ($this->state[$pos] == self::STATE_WIN) {
|
|
continue;
|
|
} */
|
|
$cards[] = $pos;
|
|
}
|
|
|
|
return $cards;
|
|
}
|
|
|
|
/**
|
|
* Check if a column is in a winning state (all cells pressed)
|
|
*/
|
|
protected function checkCol($x) : array
|
|
{
|
|
$cards = [];
|
|
|
|
for($y = 0; $y < $this->height; $y++) {
|
|
$pos = $x + ($y * $this->width);
|
|
if (!$this->isPressed($pos)) {
|
|
return [];
|
|
}
|
|
/*
|
|
if ($this->state[$pos] == self::STATE_WIN) {
|
|
continue;
|
|
} */
|
|
$cards[] = $pos;
|
|
}
|
|
|
|
return $cards;
|
|
}
|
|
}
|