The internal game board is represented by a 3x3 matrix where digit “0” represents empty space. When user place mark “X”, digit “0” in matrix is replaced by digit “1” and digit “2” when CPU place mark “O”. Game is completed when all the spaces are marked unless any player wins before all the spaces are occupied. To keep track of game state we use state variable “step” and to keep track of which player turn is it state variable “turn” is used. If turn value is 0 it’s player turn and if it is 1 it’s CPU turn. Below is the code snippet for initialization.
To show the external representation of game board. We loop through the rows and columns of the matrix and if particular position is marked as digit “0” is replaced by empty space. As stated above, player mark is represented by digit “1” so, if that particular position is equal to digit “1” in board, it is replaced by mark “X” same case for CPU mark as well mark “O” is placed if that position is occupied by digit “2”.
We know that our internal game board representation is of 3x3 matrix and index starts from 0. So, to make input position convenient we assume index will start from 1 so that we can give marking position in the range [1,1] and [3,3] instead of [0,0] and [2,2] for start and last position in a matrix. But we update the internal game state of 3x3 matrix by substracting 1 from the provided position. We validate if the provided range is valid (i.e. in between [1,1] and [3,3]).
To read position to place user mark we used the following function. We should given input position in the form of [num, num] and in valid range as stated above. If the range is not valid the program will prompt again for the input position until we provide valid empty position. The input is splitted into two digit to represent row and column position of matrix.
Below is the code snippet to implement function to place player mark in the game board. The function will take a mark and coordinate (optional) parameters. The coordinate parameter is optional because only position is given by the player where as CPU will take position which will be suitable in order to win or draw the game by CPU. If coordinate is present, it is player move and if not it is CPU move. After either player played their move, the step is incremented by 1 and the turn is toggled. At the same time, after a player move, the win condition is checked.
The player move function will take coordinate as parameter and as mentioned above we decrement each (x,y) position by 1 and that position in matrix is replaced by 1(which is a user mark in the internal representation matrix).
The CPU move is a bit more complicated. If you have read the article about the tic tac toe game stategy, the optimal position is always has been a center position because by choosing a center position it will block the opponent chance to win from both diagonals as well as from central column and central row. So, the CPU will choose the center position if available. If central position is occupied, the CPU will look for the position to win. If it finds the position to win, it will place mark in that position. But if it unable to find wining position, it will place mark so as to prevent the opponent from winning. If the above contitions doesn’t meet it will place mark in any valid available position.
The below code snippet is for a function for CPU to check the next winning position. If the position is empty (represented by 0) the CPU will place mark and check for win condition. If it can win at this position it will again make this position as empty and return this position as coordinate. If it is unable to find win position, it will retrun None instead.
The function to check the win condition will take mark as a parameter. If either condition (i.e. any diagonal or any row or any column) that have same mark is the required condition to win the game. To check row or column win condition, we use rhe function check_row_column with optional parameter “row”. If row value is true that means it will check condition for row and if false it will check for column win condition.
To check diagonal we have check_diagonal_win with mark and reverse_game_board (optional). To use code resuability, we use same function to check the both diagnoal with optimal parameter reverse_game_board which is actually a reversed board of our game board so as to use same code for checking both diagonals.
Below is the code snippet to check row and column win condition. The idea is a bit tricky, initilizing the win_row_column variable to [0,0,0] is to run loop through the rows and columns of game matrix and check the values and increment these values by 1 if value at the particular row or column equals to the mark value.
Lets take an example, suppose we are checking the row win condition. The win_row_column = [0,0,0] initially, will denote the three rows of the board and if a particular rows have satisfied the win condition (i.e. a particular row contains same values) we increment value by 1 if a particular index position value equals to mark value.
Suppose the game state of the board is as follow
As we can see row win condition is satisfied in the second row. If we run loop through rows and columns for mark O (i.e. CPU mark and internal representation value of 2). First row doesn’t contain any CPU mark so first index value of win_row_column variable will be as it is (i.e. 0). When the loop continues for the second row and first column the internal value is 2 as it is represented by mark O so 1 is added to the 2nd index value of win_row_column (0+1=1). Again for the second column, the mark is again O so again value 1 is added in the same row index value of win_row_column (1+1=2). Same case for third column of second row of matrix. Where as for the third row it is only 1 because there is only one O mark in that row.
So, the final win_row_column variable value is equal to [0,3,1]. Now when we loop through it, if any value is equal to 3 means the win condition for row is satisfied so we returned value True otherwise we returned value False. Similar case of column win as well.
To check diagonal we implement the following function. The optional parameter board is used to indicate if the contidion to be checked for the game board or for the reversed game board because if we reversed the board we can check the second diagonal with the same code which is used to check for the first diagonal. As we know for diagonal, the index value for row and column is same so if the diagonal position contain the value which is equal to the mark value we add 1 to the diag variable and if diag variable count is 3 the diagonal win condition is said to be satisfied and we return the True and False if value not equal to 3.
Below is the code snipet to implement the function to start the game. We run a while loop for condition “step value less than 9”. At first, the external representation of game board is shown then called a function to read position from user to place a mark “X” in the game board. We validate the position, if not valid user will be prompted again to enter correct valid position. Player will mark its position and check if he will win at this position otherwise its CPU turn to place mark. If either player wins or draw the final game board is displayed with the message of the result and final game board and then program will exit.