Last active
August 31, 2015 02:03
-
-
Save flyingL123/15dc3c829e98a46f58ac to your computer and use it in GitHub Desktop.
Tic Tac Toe
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 Game { | |
/** | |
* Array to hold the state of the cells in the game. | |
*/ | |
private $cells; | |
/** | |
* Integer representing the square size | |
* of the game. For example, 3, for a 3x3 game. | |
*/ | |
private $size; | |
/** | |
* The currently active player. | |
*/ | |
private $active; | |
/** | |
* Total number of moves made. | |
*/ | |
private $moves; | |
/** | |
* The letter value X or O of the computer player. | |
*/ | |
private $compPlayer; | |
/** | |
* The character to place in an empty cell. | |
*/ | |
const EMPTY_CELL = ' '; | |
/** | |
* Create a new Tic Tac Toe game of a certain size. | |
*/ | |
public function __construct($size = 3) { | |
$this->size = $size; | |
echo "To make a move, enter the row,column of the cell you want to mark.".PHP_EOL; | |
$this->newGame(); | |
} | |
private function newGame() { | |
$this->active = 'X'; | |
$this->compPlayer = 'O'; | |
$this->moves = 0; | |
$this->cells = array_fill(1, $this->size, array_fill(1, $this->size, self::EMPTY_CELL)); | |
$this->printBoard(); | |
} | |
public function play() { | |
$this->nextStep(); | |
} | |
private function nextStep() { | |
if (!$this->isOver()) { | |
if ($this->active != $this->compPlayer) { | |
$coords = $this->promptForMove(); | |
if ($coords) { | |
$valid = $this->validateMove($coords); | |
if ($valid) { | |
$this->makeMove($coords); | |
} | |
} | |
$this->nextStep(); | |
} | |
else { | |
$this->computerPlay(); | |
} | |
} | |
else { | |
$this->endGame(); | |
} | |
} | |
private function computerPlay() { | |
echo "Computer player is playing".PHP_EOL; | |
$x = rand(1, $this->size); | |
$y = rand(1, $this->size); | |
$coords = [rand(1, $this->size), rand(1, $this->size)]; | |
$valid = $this->validateMove($coords); | |
if ($valid) { | |
echo "Computer moved".PHP_EOL; | |
$this->makeMove($coords); | |
$this->nextStep(); | |
} | |
else { | |
$this->computerPlay(); | |
} | |
} | |
/** | |
* Make a move | |
*/ | |
private function makeMove($coords) { | |
$this->updateCell($coords); | |
$this->moves++; | |
$this->printBoard(); | |
$this->switchPlayer(); | |
} | |
/** | |
* End the game. | |
*/ | |
private function endGame() { | |
echo "The game is over".PHP_EOL; | |
$winner = $this->getWinner(); | |
if ($winner) { | |
echo "The winner is {$winner}".PHP_EOL; | |
} | |
else { | |
echo "The game ended in a tie".PHP_EOL; | |
} | |
$input = readline("Start a new game? y/n: "); | |
if ($input == 'y') { | |
$this->newGame(); | |
$this->play(); | |
} | |
} | |
/** | |
* Prompt for a move. | |
*/ | |
private function promptForMove() { | |
$input = readline("{$this->active}, please enter your move: "); | |
$coords = $this->coordsFromInput($input); | |
if ($coords) { | |
return $coords; | |
} | |
else { | |
echo "Invalid input, please try again".PHP_EOL; | |
return false; | |
} | |
} | |
/** | |
* Get [x,y] coordinates from user input | |
*/ | |
private function coordsFromInput($input) { | |
$input = explode(',', $input); | |
if (count($input) !== 2) { | |
return false; | |
} | |
$x = trim($input[0]); | |
$y = trim($input[1]); | |
if (ctype_digit($x) && ctype_digit($y)) { | |
return [$x, $y]; | |
} | |
return false; | |
} | |
/** | |
* Determine if a move is valid. | |
*/ | |
private function validateMove($coords) { | |
$x = $coords[0]; | |
$y = $coords[1]; | |
if ($x < 1 || $x > $this->size || $y < 1 || $y > $this->size) { | |
echo "Please try again. The cell does not exist.".PHP_EOL; | |
return false; | |
} | |
if ($this->cells[$x][$y] !== self::EMPTY_CELL) { | |
echo "Please try again. The cell is already full.".PHP_EOL; | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Update a cell. | |
*/ | |
private function updateCell($coords) { | |
$this->cells[$coords[0]][$coords[1]] = $this->active; | |
} | |
private function isOver() { | |
if ($this->moves == $this->size * $this->size || $this->getWinner()) { | |
return true; | |
} | |
} | |
private function getWinner() { | |
$cells = $this->cells; | |
// check for winner by row | |
foreach ($cells as $x => $row) { | |
$val = $row[1]; | |
foreach ($row as $y => $cell) { | |
if ($cell == self::EMPTY_CELL || $val != $cell) { | |
break; | |
} | |
if ($y == $this->size) { | |
return $val; | |
} | |
} | |
} | |
// check for winner by column | |
$cols = []; | |
foreach ($cells as $i => $row) { | |
$cols[$i] = array_column($cells, $i); | |
} | |
foreach ($cols as $y => $col) { | |
$val = $col[0]; | |
foreach ($col as $x => $cell) { | |
if ($cell == self::EMPTY_CELL || $val != $cell) { | |
break; | |
} | |
// need size - 1 here because column | |
// arrays are indexed from 0 by array_column | |
if ($x == $this->size - 1) { | |
return $val; | |
} | |
} | |
} | |
// check for winner on diagonal: top-left to bottom-right | |
$val = $cells[1][1]; | |
if ($val != self::EMPTY_CELL) { | |
for ($i = 1; $i <= $this->size; $i++) { | |
if ($cells[$i][$i] != $val) { | |
break; | |
} | |
if ($i == $this->size) { | |
return $val; | |
} | |
} | |
} | |
//check for winner on diagonal: top-right to bottom-left | |
$val = $cells[$this->size][1]; | |
if ($val != self::EMPTY_CELL) { | |
for ($x = $this->size, $y = 1; $x >= 1; $x--, $y++) { | |
if ($cells[$x][$y] != $val) { | |
break; | |
} | |
if ($x == 1) { | |
return $val; | |
} | |
} | |
} | |
return false; | |
} | |
public function switchPlayer() { | |
if ($this->active == 'X') { | |
$this->active = 'O'; | |
} | |
else { | |
$this->active = 'X'; | |
} | |
} | |
public function printBoard() { | |
$rowStrings = []; | |
foreach ($this->cells as $row) { | |
$rowStrings[] = implode('|', $row).PHP_EOL; | |
} | |
$str = ''; | |
for ($i = 0; $i < (2 * $this->size - 1); $i++) { | |
$str .= '-'; | |
} | |
echo PHP_EOL.implode($str.PHP_EOL, $rowStrings).PHP_EOL; | |
} | |
} | |
$game = new Game; | |
$game->play(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment