Skip to content

Instantly share code, notes, and snippets.

@vpArth
Created August 24, 2018 09:34
Show Gist options
  • Save vpArth/d8f2775f2cfdde9ebb5cb1a24ac3b345 to your computer and use it in GitHub Desktop.
Save vpArth/d8f2775f2cfdde9ebb5cb1a24ac3b345 to your computer and use it in GitHub Desktop.
TicTacToe implementation (variable map size, win condition, players set)
<?php
class TicTacToeGame
{
/** @var int */
private $width;
/** @var int */
private $height;
/** @var int */
private $winCond;
/** @var array */
private $players;
private $currentPlayer = 0;
private $turnsLeft;
private $map = [];
private $winner = null;
private $isOver = false;
public function __construct($width = 3, $height = 3, $winCond = 3, $players = ['X', 'O'])
{
if ($winCond > min($width, $height)) throw new InvalidArgumentException('$win can not be greater than size');
$this->width = $width;
$this->height = $height;
$this->winCond = $winCond;
$this->players = $players;
$this->start();
}
public function turn($i, $j)
{
if ($i < 0 or $i >= $this->width) throw new InvalidArgumentException('Invalid $i arg');
if ($j < 0 or $j >= $this->height) throw new InvalidArgumentException('Invalid $j arg');
if (!is_null($this->map[$j][$i])) throw new InvalidArgumentException('Field is busy!');;
$this->map[$j][$i] = $this->currentPlayer;
$this->turnsLeft--;
if ($this->checkWin($i, $j)) {
$this->winner = $this->currentPlayer;
$this->isOver = true;
} elseif ($this->turnsLeft <= 0) {
// Draw
$this->isOver = true;
} else {
$this->nextPlayer();
}
}
private function nextPlayer()
{
$this->currentPlayer = ($this->currentPlayer + 1) % count($this->players);
}
public function getPlayer($player = null)
{
return $this->players[$player ?? $this->currentPlayer];
}
private function checkWin($i, $j)
{
$player = $this->map[$j][$i];
// '—' direction
$count = 1;
for ($ci = $i - 1; $this->map[$j][$ci] === $player; $ci--) $count++;
for ($ci = $i + 1; $this->map[$j][$ci] === $player; $ci++) $count++;
if ($count >= $this->winCond) return true;
// '|' direction
$count = 1;
for ($cj = $j - 1; $this->map[$cj][$i] === $player; $cj--) $count++;
for ($cj = $j + 1; $this->map[$cj][$i] === $player; $cj++) $count++;
if ($count >= $this->winCond) return true;
// '\' direction
$count = 1;
for ($ci = $i - 1, $cj = $j - 1; $this->map[$cj][$ci] === $player; $ci--, $cj--) $count++;
for ($ci = $i + 1, $cj = $j + 1; $this->map[$cj][$ci] === $player; $ci++, $cj++) $count++;
if ($count >= $this->winCond) return true;
// '/' direction
$count = 1;
for ($ci = $i - 1, $cj = $j + 1; $this->map[$cj][$ci] === $player; $ci--, $cj++) $count++;
for ($ci = $i + 1, $cj = $j - 1; $this->map[$cj][$ci] === $player; $ci++, $cj--) $count++;
if ($count >= $this->winCond) return true;
return false;
}
public function render()
{
echo '<table border="1">';
foreach ($this->map as $j => $row) {
echo '<tr>';
foreach ($row as $i => $cell) {
echo "<td width='30' onclick='location=\"?i={$i}&j={$j}\"'>";
echo is_null($cell) ? '&nbsp;' : $this->players[$cell];
echo "</td>";
}
echo '</tr>';
}
echo '</table>';
echo '<hr>';
?>
<form action="">
<input type="text" name="width" title="Width" placeholder="width" value="<?= $this->width ?>">
<input type="text" name="height" title="Height" placeholder="height" value="<?= $this->height ?>">
<input type="text" name="winCond" title="WinCond" placeholder="winCond" value="<?= $this->winCond ?>">
<input type="text" name="players" title="Players" placeholder="players"
value="<?= implode(',', $this->players) ?>">
<input type="submit" name="reset">
</form>
<?php
echo 'Turn of ' . $this->getPlayer();
}
public function getWinner()
{
return $this->winner;
}
public function isOver()
{
return $this->isOver;
}
public function start()
{
$this->isOver = false;
$this->winner = null;
$this->currentPlayer = 0;
$this->turnsLeft = $this->width * $this->height;
for ($j = 0; $j < $this->height; $j++) {
$this->map[$j] = [];
for ($i = 0; $i < $this->width; $i++) {
$this->map[$j][$i] = null;
}
}
}
}
session_start();
if (empty($_SESSION['game']) || array_key_exists('reset', $_GET)) {
$width = $_GET['width'] ?? 3;
$height = $_GET['height'] ?? 3;
$cond = $_GET['winCond'] ?? 3;
$players = isset($_GET['players']) ? explode(',', $_GET['players']) : ['X', 'O'];
// start game
$game = new TicTacToeGame($width, $height, $cond, $players);
$_SESSION['game'] = $game;
echo 'Game started<br>';
} else {
/** @var TicTacToeGame $game */
$game = &$_SESSION['game'];
if ($game->isOver()) {
$game->start();
}
if (!isset($_GET['i'], $_GET['j'])) {
echo 'Pass coords: i, j!<br>';
} else {
// turn
try {
$game->turn($_GET['i'], $_GET['j']);
if ($game->isOver()) {
echo "Game is over!<br/>";
$winner = $game->getWinner();
if (is_null($winner)) {
echo "Draw!<br/>";
} else {
echo $game->getPlayer($winner) . " Win!<br/>";
}
}
} catch (InvalidArgumentException $e) {
echo $e->getMessage(), '<br/>';
}
}
}
$game->render();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment