233 lines
4.6 KiB
PHP
233 lines
4.6 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(): void
|
|
{
|
|
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 [];
|
|
}
|
|
$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 [];
|
|
}
|
|
$cards[] = $pos;
|
|
}
|
|
|
|
return $cards;
|
|
}
|
|
}
|