I'd recommend using the call stack as either a stack data structure, or more simply to create a recursive function.
Recursion naturally lends itself to an exhaustive search over the possible moves, pruning any branches that lead to forced loses. Formally this is called a Minimax search (to find the next move). https://www.cs.cornell.edu/courses/cs312/2002sp/lectures/rec21.htm
If you find a winning move, you return that fact, and the caller knows the board position it was considering lets the opponent force a win. You want your function to avoid letting the opponent force a win, and play moves that can lead to a forced win for itself if there is one. e.g. Tic Tac Toe recursive algorithm.
I'd recommend representing the board state with a fixed-size data structure. Your good options are
a 9-byte array of characters like space, 'X', and 'O' (padded to 12 so you can copy it with 3x lw
). i.e. in C, struct board { alignas(4) char b[9]; };
. You make copies of this whole struct.
2x bitmaps (one for X and one for O), for what's called a "bitboard". (In chess engines, a 64-bit bitmaps has one bit for every square on the board. In 3x3 tic-tac-toe, you only need 9 bits of a register for a full board). Then you could come up with bit-hacks to test for 3-in-a-row, like b & 0b111 == 0b111
(with andi
and beq
) to check for the top row being all one marker.
You can find open spaces left on the board by doing X|O
and the looking for bits that are still zero.
a single bitboard with 2 bits per position (18 bits total, larger than a MIPS immediate). It might be easier to look for 2 in a row with an empty 3rd position this way.