I've written a game of tic-tac-toe in Java, and my current method of determining the end of the game accounts for the following possible scenarios for the game being over:
- The board is full, and no winner has yet been declared: Game is a draw.
- Cross has won.
- Circle has won.
Unfortunately, to do so, it reads through a predefined set of these scenarios from a table. This isn't necessarily bad considering that there are only 9 spaces on a board, and thus the table is somewhat small, but is there a better algorithmic way of determining if the game is over? The determination of whether someone has won or not is the meat of the problem, since checking if 9 spaces are full is trivial.
The table method might be the solution, but if not, what is? Also, what if the board were not size n=9
? What if it were a much larger board, say n=16
, n=25
, and so on, causing the number of consecutively placed items to win to be x=4
, x=5
, etc? A general algorithm to use for all n = { 9, 16, 25, 36 ... }
?
Another option: generate your table with code. Up to symmetry, there are only three ways to win: edge row, middle row, or diagonal. Take those three and spin them around every way possible:
These symmetries can have more uses in your game-playing code: if you get to a board you've already seen a rotated version of, you can just take the cached value or cached best move from that one (and unrotate it back). This is usually much faster than evaluating the game subtree.
(Flipping left and right can help the same way; it wasn't needed here because the set of rotations of the winning patterns is mirror-symmetric.)
This is similar to Osama ALASSIRY's answer, but it trades constant-space and linear-time for linear-space and constant-time. That is, there's no looping after initialization.
Initialize a pair
(0,0)
for each row, each column, and the two diagonals (diagonal & anti-diagonal). These pairs represent the accumulated(sum,sum)
of the pieces in the corresponding row, column, or diagonal, whereWhen a player places a piece, update the corresponding row pair, column pair, and diagonal pairs (if on the diagonals). If any newly updated row, column, or diagonal pair equals either
(n,0)
or(0,n)
then either A or B won, respectively.Asymptotic analysis:
For the memory use, you use
4*(n+1)
integers.Exercise: Can you see how to test for a draw in O(1) time per-move? If so, you can end the game early on a draw.
I just wrote this for my C programming class.
I am posting it because none of the other examples here will work with any size rectangular grid, and any number N-in-a-row consecutive marks to win.
You'll find my algorithm, such as it is, in the
checkWinner()
function. It doesn't use magic numbers or anything fancy to check for a winner, it simply uses four for loops - The code is well commented so I'll let it speak for itself I guess.I was asked the same question in one of my interviews. My thoughts: Initialize the matrix with 0. Keep 3 arrays 1)sum_row (size n) 2) sum_column (size n) 3) diagonal (size 2)
For each move by (X) decrement the box value by 1 and for each move by (0) increment it by 1. At any point if the row/column/diagonal which has been modified in current move has sum either -3 or +3 means somebody has won the game. For a draw we can use above approach to keep the moveCount variable.
Do you think I am missing something ?
Edit: Same can be used for nxn matrix. Sum should be even +3 or -3.
Here is my solution using an 2-dimensional array:
You know a winning move can only happen after X or O has made their most recent move, so you can only search row/column with optional diag that are contained in that move to limit your search space when trying to determine a winning board. Also since there are a fixed number of moves in a draw tic-tac-toe game once the last move is made if it wasn't a winning move it's by default a draw game.
edit: this code is for an n by n board with n in a row to win (3x3 board requries 3 in a row, etc)
edit: added code to check anti diag, I couldn't figure out a non loop way to determine if the point was on the anti diag so thats why that step is missing