http://www.chrispearson.org/pages/programming/php/oxo/buildingPHPoxo.asp
08h07
Friday, 21. November 2008

PHP CODING

PRESENTATION

The noughts and crosses application combines a HTML page framework and server-side PHP code.

The application is realised on the user's browser using a banner graphic; a game board which is a HTML table containing 40x40 pixel images; a HTML form with a button to invoke a new game and, finally, some HTML text which reports on the game's current status: a HTML non-breaking space while a game is in process and, when the game is over, You win!, I win! or Looks like a draw is displayed. Much as shown by the screen shot on the right.

The game board consists a table with border = 0 and each cell has associated CSS styling to create the blue game-board grid. The top-left cell has the CSS class topleft, <td class="topleft">, and the style is

<style type="text/css">

.topleft {
border-right-width: 3px;
border-bottom-width: 3px;
border-right-style: solid;
border-bottom-style: solid;
border-right-color: #0000FF;
border-bottom-color: #0000FF;
}

. . .

 

The noughts and crosses game board: It's a draw!

No dimensions are specified for the table or its constituent cells so they collapse around the graphics.

CODE

The full PHP page is zipped up along with the graphics in oxo.zip. There are plenty of comments in the source code. There is no client-side scripting - no JavaScript.

SETTING UP THE GAME BOARD

When the page is first requested no parameters are passed along with the url - Once the game is in process two parameters are passed in the query string: Cell and txtBoard.

Once the variables are all initialised the first decision the code has to make is, do we need to set up for a new game? The code

if (@($Cell == "") OR @($Cell > 8)) {

tests whether Cell is unassigned or set to a value of more than 8. The @ symbol directs PHP to suppress any errors while evaluating the function which follows it, in this instance there are two @, one each for the equality test and the test for more than eight. Without the @ to suppress error messages PHP would send an error notification to the users browser along the lines of

Noughts and crosses game board
Back to the top of this page

Warning: Undefined variable: Cell in C:\programming\php\oxo\TMPi02us9la92.php on line 250

Name-value pairs from the HTTP query string are available in PHP code without code to extract the values. In the case of the url string

http://www.chrispearson.org/pages/programming/php/oxo/default.php?Cell=8&txtBoard=EEEEEEEEE

the value of Cell is available in the PHP variable $Cell and of txtBoard in $txtBoard.

Once a game is in progress the value assigned to Cell represents to cell in the game-board matrix clicked by the user - The cell into which we'll place a nought. The nine cells are identified from zero through to eight, as shown below. When the New Game button is clicked the form's POST action returns a query string with Cell=9 indicating that a new game has been requested.

All references to individual cells are made using these index numbers. The other parameter passed in the query string, txtBoard, is a nine-character string representing the status of each cell - E representing an empty, available location with X and O indicating cells already taken by a player. When the game begins the string is initialised to EEEEEEEEE,

$txtBoard = "EEEEEEEEE";

With the new board set up in code the new board must also be set up for the browser display: In a loop for each of the cells from 0 to 8 an image is specified together with a short text message which will be placed into the images alt text attribute. The image is also used as a link back to the same page.

for ($i = 0; $i < 9; $i++) {
   $Image[] = "40x40.gif";
   $Message[] = "Click to place your O";
   $Link[] = "default.php?Cell=" . $i . "&amp;txtBoard=" . $txtBoard;

Cells identified on the noughts and crosses game board

The link contains the parameters Cell and txtBoard, allowing the page to process the user's move and to respond to it. Using the format $arrayname[] = x is a quick and dirty way to add elements to a PHP array. (There is an article on using arrays in PHP which covers assignment of arrays and other topics at array.asp.)

Each of these assignments is to an array, the code $Image[] = "40x40.gif" puts the string 40x40.gif into the next available element of the array $Image. The elements of these arrays are used later to write out the HTML to display the game board:

<tr>
<td class="topleft">
   <a href="<?=$Link[0]?>">
     
<img src="<?=$Image[0]?>" alt="<?=$Message[0]?>" width="40" height="40" border="0" />
   < /a>
< /td>
<td class="top">
   <a href="<?=$Link[1]?>">
      <img src="<?=$Image[1]?>" alt="<?=$Message[1]?>" width="40" height="40" border="0" />
   < /a>
< /td>
<td class="topright">
   <a href="<?=$Link[2]?>">
      <img src="<?=$Image[2]?>" alt="<?=$Message[2]?>" width="40" height="40" border="0" />
   < /a>
< /td>
</tr>

This is the code assigning images, alt text and links to the top row of the game board. This shows how PHP output can be quickly and effectively embedded into HTML. The syntax of <?=$Image[2]?> is <?=, meaning this is PHP, output the variable $Image[2], ?> end of PHP. (This is directly equivalent to <% =variable %> in VBScript) Like its counterpart in VBScript it can appear anywhere in HTML - inside quotes, even - and will be properly processed. (NOT, however, when it appears in HTML comments!)

PLAYING THE GAME

Since each "empty cell" image on the game board is a hyperlink, when the user clicks on an available cell the PHP page is requested with that cell's identity in the query string along with the current state of the game board.

There is no concept of state on the server side: Every request is processed as a stand-alone. Similarly, only the two data entities Cell and txtBoard are exchanged between browser and server with txtBoard being returned from server to browser. No cookies are set on the client.

When the client returns a move to the server the PHP page processes the move through some in-line code in the body of the HTML: This code calls a number of functions.

Back to the top of this page

 

PROCESSING A MOVE

Since only empty cells on the game board are rendered as hyperlinks (previously-filled squares have appropriate alt text and no link) only valid moves can be returned to the server.

Each link includes the url of the noughts and crosses page together with a string representing the current board and the identity of the cell the player has clicked.

Arrays of images, alt text and links are created and used to create HTML on the fly when the PHP page is served.

Noughts and crosses game board: Alt text shows user where moves may be made $Image[] = "40x40.gif";
$Message[] = "Click to place your O";
$Link[] = "default.php?Cell=" . $i . "&amp;txtBoard=" . $txtBoard;

The resulting HTML is similar to that shown here, on the right.

Try View=>Source on default.php to examine the HTML produced.

Once a move is received from the player a number of functions are called to process the move in accordance with the game-play rules, as described below.

Once the move has been processed - and any possible response has been made - the board is written as an HTML table containing blank, X and O graphics and the page re-served. Again, these steps are fully commented in the source code.

<td class="topright">
   <a href="default.php?Cell=0&amp;txtBoard=EEEEEEEEE">
      <img src="40x40.gif" alt="Click to place your O"
         width="40" height="40" border="0" />
   </a>
</td>

Back to the top of this page

THE GAME PLAY

When a move is received by the PHP page each step is carried out by a function.

Checkwin() determines whether there is a winning line - three symbols in a row; down, across or diagonally. This sets a flag which then suppresses any attempts to respond to that move. The code next loops through every possible move the server can make for X - every empty cell, that is. Rather than taking the first possible move available the program now tries to determine whether this is the best move to make.

The programs estimates which of the possible moves is best by calling BestMove(). In BestMove() every possible move for X is identified then every possible response by O is evaluated. The position after O has responded to each move is then evaluated by ScoreBoard(). ScoreBoard() takes each row of the board - verticals, horizontals and diagonals - and passes them to another function, ScoreRow().

ScoreRow() gives the passed row a score based on how many O's or X's there are in a row and how they are mixed, together with any empty cells on that row. The scores are set such that a win (three of ours in a row) will clearly be the best move to make and any move resulting in three-in-a-row for the opponent is the worst case. Changing the score for a given symbol combination can have a significant effect on how PHP plays the game.

Setting the scores in minimax and other outcome-scoring applications is all part of the fine tuning process. Small changes can have major impacts.

On the right here is the switch() used in the ScoreRow() function in the current version of the game application: A win for the opposition (the first case) scores minus 2500 while a win for us (about half way down) scores plus 2500.

These scores have been chosen to mask any of the other conditions since win or loose are both terminal cases.

In the code snippet opposite the switch variable, $Row, contains a nine character representation of the game board, $TheirSymbol is the opponent's symbol (O in this case) playing against $OurSymbol, X here.

The hard-coded E is the character representing an empty cell on the game board.

The board position - the nine-character string representing the board - is used as the key to an array element with the element's value being set to that board's score.

After all the moves are evaluated the program performs a sort by descending value on the array, the key of the first element in the sorted array contains the result of the highest scoring move.

The scores are sorted highest-scoring to lowest scoring using the code

arsort($Score);

Then the first value is popped from the array using each()


$BestRow = each($Score);

All that remains is to call CheckWin() again, this time to check whether X has won the game.

The formatted board is then re-served to the client.

(There is no check after X makes a move and before possible responses by O to see whether X has won - There is a small processing overhead but the programming is simpler.)

switch($Row) {
case $TheirSymbol . $TheirSymbol . $TheirSymbol:
$RowScore = -2500;
break;
case $TheirSymbol . $TheirSymbol . "E":
$RowScore = -600;
break;
case $TheirSymbol . "E" . $TheirSymbol:
$RowScore = -600;
break;
case "E" . $TheirSymbol . $TheirSymbol:
$RowScore = -600;
break;
case $TheirSymbol . "E" . "E":
$RowScore = -100;
break;
case "E" . $TheirSymbol . "E":
$RowScore = -100;
break;
case "E" . "E" . $TheirSymbol:
$RowScore = -100;
break;
case "E" . "E" . "E":
$RowScore = 0;
break;
case $OurSymbol . $OurSymbol . $OurSymbol:
$RowScore = 2500;
break;
case $OurSymbol . $OurSymbol . "E":
$RowScore =400;
break;
case $OurSymbol . "E" . $OurSymbol:
$RowScore = 400;
break;
case "E" . $OurSymbol . $OurSymbol:
$RowScore = 400;
break;
case $OurSymbol . "E" . "E":
$RowScore = 100;
break;
case "E" . $OurSymbol . "E":
$RowScore = 100;
break;
case "E" . "E" . $OurSymbol:
$RowScore = 100;
break;
case $OurSymbol . $TheirSymbol . $TheirSymbol:
$RowScore = 50;
break;
case $TheirSymbol . $OurSymbol . $TheirSymbol:
$RowScore = 50;
break;
case $TheirSymbol . $TheirSymbol . $OurSymbol:
$RowScore = 50;
break;
case $OurSymbol . $OurSymbol . $TheirSymbol:
$RowScore = 50;
break;
case $OurSymbol . $TheirSymbol . $OurSymbol:
$RowScore = 50;
break;
case $TheirSymbol . $OurSymbol . $OurSymbol:
$RowScore = 50;
break;
default:
// Should include XEO XOE OXE EOX OEX
$RowScore = 50;
break;
}

Back to the top of this page

As I mentioned in the introduction, the impetus for writing the noughts and crosses application was to test out some scoring methods for other what-if evaluation - I discovered a number of approaches in my research, from minimax (the same kind of scoring used here) through to learning heuristics where the program assimilates outcomes with their associated games, then determines the best game to play next time: All interesting, worthy of further investigation and waiting for a few hours on a rainy day. As far as it goes I think it exercises some useful PHP, too.

xxx,xxx

copyright ©2000 - 2008 Chris Pearson