[PHP-Gtk] Tic-Tac-Toe: Contemplation to Compilation Posted by: John in PHP on
In one of my previous blogs I showed you how to create a PHP-Gtk window you might use for a Tic-Tac-Toe application. In this blog I intend on showing you the logic needed to finish the application. Lets start off with the code we created in the last blog:
 
  1. if (!class_exists('gtk')) {
  2.     die("Please load the php-gtk2 module in your php.inirn");
  3. }
  4.  
  5. $wnd = new GtkWindow();
  6. $wnd->set_title('Tic-Tac-Toe');
  7. $wnd->set_size_request(200, 200);
  8. $wnd->connect_simple('destroy', array('gtk', 'main_quit'));
  9.  
  10. //create 9 buttons
  11. for($i = 0; $i < 9; $i++) {
  12.     $b[$i] = new GtkButton('');
  13.     $b[$i]->connect_simple('clicked', 'actionClicked', $b[$i], $clicks);
  14. }
  15. //Create a GtkTable to place the 9 buttons on
  16. $tbl = new GtkTable(3, 3);
  17. //place the 9 buttons onto a table
  18. for($i = 0; $i < 9; $i++) {
  19.     $tbl->attach($b[$i], ($i%3), ($i%3)+1, floor($i/3), floor($i/3)+1);
  20. }
  21.  
  22. $wnd->add($tbl);
  23. $wnd->show_all();
  24. Gtk::main();
  25. ?>
The only line of code where I left you hanging without much information was this line:
 
  1. $b[$i]->connect_simple('clicked', 'actionClicked', $b[$i], $clicks);
For this application to work, we need to define an actionClicked function which will be called every time a button is clicked. I am going to first create a global variable called $clicks. This will keep track of the amount of clicks. The reason behind this is simple, if no one has won the game before $clicks reaches nine, then the game resulted in a tie. This accounts for 50% of our winning logic. Moreover, $clicks helps us determine whose “turn” it is. If $clicks is an even number it is Player A's turn, if $clicks is odd then it is Player B's turn. By using the mod operator ( % ) we can easily print X or O to the grid. If $click % 2 is 0, then it is X's turn. We will then print X to the tile that was clicked and disable it so it cannot be clicked again. Lastly, we will call our to be defined checkWin function which will " drumb roll " check for a winner. If you have been coding this function in your mind has I have been describing it, it should look similar to this:
 
  1. $clicks = 0;
  2.  
  3. function actionClicked($button) {    
  4.     global $clicks;
  5.  
  6.     ($clicks % 2 == 0) ? $lbl = "X" : $lbl = "O";
  7.  
  8.     $button->set_label($lbl);
  9.     $button->set_sensitive(false);
  10.     checkWin();
  11. }
The set_label function will set either X or O as the buttons label (depending on whose turn it is). The set_sensitive method will deactivate the button so it cannot be pressed again. Now that we take care of all the X's and O's lets figure out who won.

In a three by three Tic-Tac-Toe game, there are eight possible win strategies. Three vertical, three horizontal, and two diagonal. If you think of the buttons on our GtkTable as having an index (the upper left being zero and the bottom right being eight) the win combinations can be represented using an array of three indexes (in a row). For example, if there are three X's on the top row, X would be a winner. The button indexes that caused him to be a winner are 0, 1 and 2. Thus we can assume if the buttons at index 0, 1, and 2 have the same label, the player with that label is the winner. All eight win combinations can be represented in an array:
 
  1. $combinations = array(
  2.         0 => array(0, 1, 2),
  3.         1 => array(3, 4, 5),
  4.         2 => array(6, 7, 8),
  5.  
  6.         3 => array(0, 3, 6),
  7.         4 => array(1, 4, 7),
  8.         5 => array(2, 5, 8),
  9.    
  10.         6 => array(0, 4, 8),
  11.         7 => array(2, 4, 6));
The algorithm to check for a winner might seem a little complicated at first sight but in essence it is very simple. It loops over all eight possible win combinations and checks if each of the three indexes associated with that combination has the same label. If that is the case, set the boolean variable $win to true.
 
  1. for($i = 0; $i < 8; $i++)
  2.     {
  3.         if( ($b[$combinations[$i][0]]->get_label() == $b[$combinations[$i][1]]->get_label()) &&
  4.             ($b[$combinations[$i][1]]->get_label() == $b[$combinations[$i][2]]->get_label()) &&
  5.             ($b[$combinations[$i][0]]->get_label() != "")){
  6.            
  7.             $win = true;
  8.         }
  9.     }
Finally if there is a winner $win will be true, if $win is false and $clicks is nine, the game has resulted in a tie:
 
  1. if($win) {
  2.         echo "We have a winner!";
  3.     }
  4.     else if(!$win && $clicks == 9) {
  5.         echo "We have a tie!";
  6.     }
The complete code for a fully functional two player Tic-Tac-Toe game is below:
 
  1. if (!class_exists('gtk')) {
  2.     die("Please load the php-gtk2 module in your php.inirn");
  3. }
  4.  
  5. $clicks = 0;
  6.  
  7. function actionClicked($button) {    
  8.     global $clicks;
  9.  
  10.     ($clicks % 2 == 0) ? $lbl = "X" : $lbl = "O";
  11.  
  12.     $button->set_label($lbl);
  13.     $button->set_sensitive(false);
  14.     checkWin();
  15. }
  16.  
  17. function checkWin()
  18. {
  19.     global $b, $clicks;
  20.  
  21.     $win = false;
  22.     $combinations = array(
  23.         0 => array(0, 1, 2),
  24.         1 => array(3, 4, 5),
  25.         2 => array(6, 7, 8),
  26.  
  27.         3 => array(0, 3, 6),
  28.         4 => array(1, 4, 7),
  29.         5 => array(2, 5, 8),
  30.    
  31.         6 => array(0, 4, 8),
  32.         7 => array(2, 4, 6));
  33.  
  34.     for($i = 0; $i < 8; $i++)
  35.     {
  36.         if( ($b[$combinations[$i][0]]->get_label() == $b[$combinations[$i][1]]->get_label()) &&
  37.             ($b[$combinations[$i][1]]->get_label() == $b[$combinations[$i][2]]->get_label()) &&
  38.             ($b[$combinations[$i][0]]->get_label() != "")){
  39.            
  40.             $win = true;
  41.         }
  42.     }
  43.  
  44.     if($win) {
  45.         echo "We have a winner!";
  46.     }
  47.     else if(!$win && $clicks == 9) {
  48.         echo "We have a tie!";
  49.     }
  50.  
  51.  
  52.  
  53. }
  54.  
  55. $wnd = new GtkWindow();
  56. $wnd->set_title('Tic-Tac-Toe');
  57. $wnd->set_size_request(200, 200);
  58. $wnd->connect_simple('destroy', array('gtk', 'main_quit'));
  59.  
  60. //create 9 buttons
  61. for($i = 0; $i < 9; $i++) {
  62.     $b[$i] = new GtkButton('');
  63.     $b[$i]->connect_simple('clicked', 'actionClicked', $b[$i], $clicks);
  64. }
  65. //Create a GtkTable to place the 9 buttons on
  66. $tbl = new GtkTable(3, 3);
  67. //place the 9 buttons onto a table
  68. for($i = 0; $i < 9; $i++) {
  69.     $tbl->attach($b[$i], ($i%3), ($i%3)+1, floor($i/3), floor($i/3)+1);
  70. }
  71.  
  72. $wnd->add($tbl);
  73. $wnd->show_all();
  74. Gtk::main();
  75. ?>