I have plans on making an online Tic Tac Toe Game. I am working on coding as it is a work in progress, but this is what I have so far. My final steps are to create a Game object that will deal with actually administrating the game. Right now it is set up so that whenever a valid move is made, the player is switched. When I have some time I might actually publish a game using this class. Some notes, instead of traversing though the board to check if a player has won it stores score as a hexadecimal number. If the score contains a digit that is equal to the board size, the game has been won. For example, a game with 8 rows and columns would have a 18 digit hexadecimal number to keep track of score. Any time a player puts a spot in the first row, the leftmost bit has 1 added to it. the leftmost bit is equal to 8, a player has won. The first four bits keep track of rows the next four keep track of columns and the last two keep track of diagonals.

Someone once asked why I use three if statements in the checkWinner() function. In this particular case it doesn’t matter whether I use if or if else since each if statement is returning a value, so once a true statement is found, subsequent ifs won’t be evaluated. This was done as a matter of efficiency as it makes the file size slightly smaller for the php file.


PHP

define( 'CROSS', 'X' );
define( 'NOUGHT', 'O' );
define( 'NOTHING', 'ø' );
/**
* Space class for each space of the board
* Each space contains either X O or is empty ( ø )
*/
class Space
{
    private $content;
    private $location;
    private $numberOfBits; // Keeps track of how many bits we need for player score
    /**
    * Constructors for Space object
    * @param $row the X value of the space we are setting up
    * @param $column the Y value of the space we are setting up
    * @param $boardSize how big the board is. need to help figure out bits for location
    */
    public function __construct( $row, $column, $boardSize = 3 )
    {
        $this->clear();
        // Calculate the number of bits needed and convert x and y values to a hex number the represents location
        $this->numberOfBits = ( $boardSize * 2 ) + 2;
        $location           = dechex( pow( 16, ( $this->numberOfBits - $row ) ) + pow( 16, ( ( $this->numberOfBits / 2 ) + 1 - $column ) ) );
        // Check to see if the row is on one of the diagonals and set left/right diagnoal bit appropiately
        if ( $row == $column ) {
            $location += dechex( 16 );
        }
        if ( $row == ( abs( $column - $boardSize ) + 1 ) ) {
            $location += dechex( 1 );
        }
        // Add leading 0s to location string
        $this->location = str_pad( $location, $this->numberOfBits, '0', STR_PAD_LEFT );
    }
    /**
    * Empties out the cell
    */
    public function clear( )
    {
        $this->content = NOTHING;
    }
    /**
    * Changes content to either and X or O or empty
    */
    public function setContent( $crossNought )
    {
        switch ( $crossNought ) {
            case CROSS:
            case NOUGHT:
                $this->content = $crossNought;
                break;
            default:
                $this->content = NOTHING;
        }
    }
    /**
    * Return the location of the space
    */
    public function getLocation( )
    {
        return $this->location;
    }
    /**
    * Returns weither it is Cross, Nought, or empty
    */
    public function getPlay( )
    {
        return $this->content;
    }
}
/**
* Board class, handles the tic tac toe games.
* X player has the first play, default board size is 3
*/
class Board
{
    private $boardSize; // The size of the game we are creating. Between 3 and 10
    private $board; // Array to hold cell locations
    private $player; // Keeps track of player
    private $xscore = 0x0; // Keeps track of CROSS score
    private $oscore = 0x0; // Keeps track of NOUGHT score
    private $moves; // Keeps tracks of how many moves have been played
    /**
    * Constructor
    * @param $boardSize (optional) the size of the board we are creating. If the board size is greater than 10 or less than 2 it creates a default board of 3 squares.
    */
    public function __construct( $boardSize = 3 )
    {
        $this->moves = 0;
        if ( $boardSize < 2 || $boardSize > 10 ) {
            $boardSize = 3;
        }
        $this->boardSize = $boardSize;
        // Populate each space with empty cell
        for ( $i = 1; $i boardSize; $i++ ) {
            for ( $j = 1; $j boardSize; $j++ ) {
                $this->board[$i][$j] = new Space( $i, $j, $this->boardSize );
            }
        }
        $this->player = CROSS;
    }
    /**
    * Converts gameboard to String
    * @param
    */
    public function __toString( )
    {
        $gameBoard = "";
        for ( $i = 1; $i <= $this->boardSize; $i++ ) {
            for ( $j = 1; $j <= $this->boardSize; $j++ ) {
                $gameBoard .= $this->board[$i][$j]->getPlay();
            }
            $gameBoard .= "\n";
        }
        return $gameBoard;
    }
    /**
    * Populates space with piece then switches players.
    * Will return false if the row and column doesn't exist
    * @param $x row to place piece
    * @param $y column to place piece
    * @throws "Space is already occupied." error
    * @return true if the move was made sucessfully
    */
    public function makeMove( $x, $y )
    {
        if ( $x < 0 || $x > $this->boardSize || $y < 0 || $y > $this->boardSize || $this->board[$x][$y]->getPlay() != NOTHING ) {
            throw new Exception( 'Space is already occupied.' );
        }
        $this->moves++;
        $this->board[$x][$y]->setContent( $this->player );
        switch ( $this->player ) {
            case NOUGHT:
                $this->oscore += $this->board[$x][$y]->getLocation();
                $this->player = CROSS;
                break;
            case CROSS:
                $this->xscore += $this->board[$x][$y]->getLocation();
                $this->player = NOUGHT;
                break;
        }
        return true;
    }
    /**
    * Checks to see if there is a winner
    * @return CROSS NOUGHT or NOTHING
    */
    public function checkWinner( )
    {
        if ( strpos( $this->xscore, (String) $this->boardSize ) !== false ) {
            return CROSS;
        }
        if ( strpos( $this->oscore, (String) $this->boardSize ) !== false ) {
            return NOUGHT;
        }
        if ( $this->moves == $this->boardSize * $this->boardSize ) {
            return NOTHING;
        }
        return false;
    }
}