Created
December 19, 2018 19:59
-
-
Save nmicht/b4d19fe770353fa5d30b1a107fb89fd7 to your computer and use it in GitHub Desktop.
A Tic-Tac-Toe game for any square board and any number of players
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class TicTacToe { | |
/** | |
* Board for the game | |
* @var array | |
*/ | |
private $board = []; | |
/** | |
* The size of the board | |
* @var integer | |
*/ | |
private $size = 0; | |
/** | |
* The size of the square board, (number of rows) | |
* @var integer | |
*/ | |
private $square = 0; | |
/** | |
* Tiles used on the game | |
* @var array | |
*/ | |
private $turnTiles = ['x','o']; | |
/** | |
* Strings for tiles winning lines, ex: 'xxx' | |
* @var array | |
*/ | |
private $tilesStr = []; | |
/** | |
* Colors used for each tile | |
* @var array | |
*/ | |
private $turnColors = [31, 32]; | |
/** | |
* Array with the Winning combinations | |
* @var array | |
*/ | |
private $winningLines = []; | |
/** | |
* Winning combinations ordered by position | |
* @var array | |
*/ | |
private $winningByPos = []; | |
/** | |
* Gamer turn, in case of two players: 0 first player, and 1 second player | |
* @var integer | |
*/ | |
private $turn = 0; | |
/** | |
* Initialize the default values for the game | |
* @param integer $size | |
* @param array $tiles | |
*/ | |
function __construct($size = 9, $tiles = ['x', 'o']){ | |
$this->size = $size; | |
$this->square = sqrt($size); | |
$this->board = range(1, $size); | |
$this->generateWinningLines(); | |
$this->turnTiles = $tiles; | |
foreach ($this->turnTiles as $tile) { | |
$this->tilesStr[] = str_repeat($tile, $this->square); | |
} | |
} | |
/** | |
* Generate the winning lines for a square board | |
* @return null | |
*/ | |
private function generateWinningLines() { | |
$lines = []; | |
// Horizontal and vertical | |
for($c = 0, $r = 0; $c < $this->square; $c++, $r+=$this->square) { | |
$lines[] = range($c, $this->size-1, $this->square); | |
$lines[] = range($r, ($c+1)*$this->square-1); | |
} | |
// Diagonal | |
$lines[] = range($this->square-1, $this->size-2, $this->square-1); | |
$lines[] = range(0, $this->size, $this->square+1); | |
$this->winningLines = $lines; | |
// Order winning lines by pos to improve performance on each turn | |
foreach($lines as $line) { | |
foreach ($line as $pos) { | |
$winningByPos[$pos][] = $line; | |
} | |
} | |
$this->winningByPos = $winningByPos; | |
} | |
/** | |
* Get the color according to the tile | |
* @param string $tile | |
* @return integer The color number | |
*/ | |
private function getColorByTile($tile) { | |
$color = 0; | |
$c = array_search($tile, $this->turnTiles); | |
if($c !== false) { | |
$color = $this->turnColors[$c]; | |
} | |
return $color; | |
} | |
/** | |
* Draw the board on the console | |
* @return null | |
*/ | |
private function drawBoard() { | |
echo "\n"; | |
foreach($this->board as $k => $place) { | |
$color = $this->getColorByTile($place); | |
echo "| \033[0;{$color}m{$place}\033[0m "; | |
if(($k+1) % $this->square == 0){ | |
echo "|\n"; | |
} | |
} | |
echo "\n"; | |
} | |
/** | |
* Validate if the given position is valid | |
* @param string $pos | |
* @return boolean true in case is valid | |
*/ | |
private function isValidPos($pos) { | |
if ($pos < 1 || $pos > $this->size) { | |
echo "The position is not valid\n"; | |
return false; | |
} | |
$pos = $pos-1; | |
if (in_array($this->board[$pos], $this->turnTiles)) { | |
echo "The position is already taked\n"; | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Ask for the position to put the new tile and validate if that gives the win | |
* @return boolean true if the game end by win | |
*/ | |
private function playTurn() { | |
do { | |
echo "Is the turn for \033[0;{$this->turnColors[$this->turn]}m{$this->turnTiles[$this->turn]}\033[0m \n"; | |
$place = readline("Select the position: "); | |
} while(!$this->isValidPos($place)); | |
$this->board[$place-1] = $this->turnTiles[$this->turn]; | |
$isWin = $this->checkWin($place); | |
return $isWin; | |
} | |
/** | |
* Validate if the last movement gives the win | |
* @param integer $pos The position of the last movement | |
* @return boolean True if a line is formed to win | |
*/ | |
private function checkWin($pos) { | |
$pos = $pos-1; | |
foreach($this->winningByPos[$pos] as $line) { | |
$t = implode($this->getBoardLine($line)); | |
if(in_array($t, $this->tilesStr)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* Get a board line, used to get the lines from winning | |
* @param array $line Array with the positions for the line | |
* @return array | |
*/ | |
private function getBoardLine($line) { | |
foreach($line as $k) { | |
$l[] = $this->board[$k]; | |
} | |
return $l; | |
} | |
/** | |
* Display message for winner | |
* @return null | |
*/ | |
private function showWinner() { | |
echo "Congratulations!! \033[0;{$this->turnColors[$this->turn]}m{$this->turnTiles[$this->turn]}\033[0m won!\n"; | |
} | |
/** | |
* Display message for draw | |
* @return null | |
*/ | |
private function showTie() { | |
echo "The game is a draw!\n"; | |
} | |
/** | |
* The main functionality for the tic tac toe game | |
* @return null | |
*/ | |
public function run() { | |
$this->turn = 0; | |
$round = 0; | |
$win = false; | |
$this->drawBoard(); | |
do { | |
$win = $this->playTurn(); | |
$this->drawBoard(); | |
if($win) { | |
$this->showWinner(); | |
break; | |
} | |
$this->turn = ($this->turn+1)%count($this->turnTiles); | |
$round++; | |
} while (!$win && $round < $this->size); | |
if ($round >= $this->size){ | |
$this->showTie(); | |
} | |
} | |
} | |
$game = new TicTacToe(); | |
$game->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment