Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A Tic-Tac-Toe game for any square board and any number of players
<?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