I'm making a Tic-Tac-Toe program. I plan to use minimax with it. I made a tree with space for all possible game sequences and I'm looking for a way to populate it. I currently have this type:
typedef struct name
{
char grid [3] [3];
struct name * child [9];
} node;
and I'm looking for a way to fill grid just like it's shown here. How would I go filling the grid to make sure that all possible combinations are there? My plan is to have the game recognize every move player can take and then decide what steps to take in order to win (I still need to figure out the decision part, but I'm holding that until I can fill the grids in tree).
Sounds like a prime candidate for recursion to me...
Encode the positions in base 3. Make a unused grid 0
; a grid with "X" a 1
; and a grid with "O" a 2
. So the absolute maximum number of complete grids you can have is 3^9 = 19683 (some of those are invalid tic-tac-toe representations)
So let's say you are dealing with '020112021'. Its children are:
020112021 /* father */ (4759 base 10)
=========
020112022 father + 1 (1 base 3)
0201120x1 /* invalid */ father + 3 (10 base 3)
020112121 father + 9 (100 base 3)
02011x021 /* invalid */ father + 27 (1000 base 3)
020122021 father + 81 (10000 base 3)
020212021 father + 243
021112021 father + 729
0x0112021 /* invalid */ father + 2187
120112021 father + 6561 (100000000 base 3)
I think you can figure a way to go on from here.
Pseudocode because recursion is hard to make into a list:
function descend(X_or_O, board)
for square in board
If square isn't empty: continue
new_board = Fill in square with X_or_O.
Check for a winner (if yes, return)
newer_board = descend(opposite of X_or_O, new_board)
tack newer_board onto the tree.
clear out square
You should be able to do that with a couple for
loops and if
statements.
EDIT: Just in case the above link breaks, it's a reference to a description of the Tinkertoy Computer from Scientific American October 1989, also compiled and published with other SA amusement articles by the same author as The Tinkertoy Computer and Other Machinations. The guys (and gals) who built this machine were clever enough to avoid any alpha-beta search as well as compress the board into something that could easily be computed. By Tinkertoys.
Here you have a working solution. It is implemented in Python but I think it may help you.
The game tree is build recursively by the build_tree
function, and is implemented as a list of lists.
The build_tree
function takes a board (a node of the tree) and the piece that has the turn to play as input parameters, and builds all the possible new boards resulting of applying the piece to the board. Then, for each of these new boards, it calls the build_tree
function again, but this time changing the piece that has the turn to play. When the board is terminal (no empty squares) the recursion ends.
This is the resulting tree for a 1x3 board:
[(0, 0, 0), [[('x', 0, 0), [[('x', 'o', 0), [('x', 'o', 'x')]], [('x', 0, 'o'), [('x', 'x', 'o')]]]], [(0, 'x', 0), [[('o', 'x', 0), [('o', 'x', 'x')]], [(0, 'x', 'o'), [('x', 'x', 'o')]]]], [(0, 0, 'x'), [[('o', 0, 'x'), [('o', 'x', 'x')]], [(0, 'o', 'x'), [('x', 'o', 'x')]]]]]]
Empty squares are denoted by '0'.
For the tic tac toe game, please change blank_board = (0,0,0)
to blank_board = (0,0,0,0,0,0,0,0,0)
in order to have a 3x3 board.
def change_piece(piece):
if piece == 'x': return 'o'
return 'x'
def is_terminal(board):
"""Check if there are any empty square in the board"""
for square in board:
if square == 0:
return False
return True
def build_tree(node, piece):
"""Build the game tree recursively.
The tree is implemented as a list of lists.
"""
child_nodes = []
for index, value in enumerate(node):
if value == 0:
new_node = list(node)
new_node[index] = piece
new_node = tuple(new_node)
if not is_terminal(new_node):
child_nodes.append(build_tree(new_node,change_piece(piece)))
else:
child_nodes.append(new_node)
if child_nodes:
return [node,child_nodes]
return
if __name__ == "__main__":
blank_board = (0,0,0)
game_tree = build_tree(blank_board,'x')
print(game_tree)
This is a classic case for recursion:
typedef struct name
{
char grid [3] [3];
struct name * child [9];
} node;
node * new_grid(node *parent) {
node *n = calloc(1, sizeof(node));
if (parent)
memcpy(n->grid, parent->grid, sizeof(grid));
}
// is n a winner based on the move just made at x,y?
int winner(const node *n, int x, int y) {
return (n->grid[x][0] == n->grid[x][1] == n->grid[x][2]) ||
(n->grid[0][y] == n->grid[1][y] == n->grid[2][y]) ||
((x == y) && (n->grid[0][0] == n->grid[1][1] == n->grid[2][2])) ||
((2-x == y) && (n->grid[0][2] == n->grid[1][1] == n->grid[2][0]));
}
void fill(node *n, char c) {
int x, y, children;
node *child;
for (x = 0; x < 3; x++) {
for (y = 0; y < 3; y++) {
if (n->grid[x][y])
continue;
child = n->child[children++] = new_grid(n);
child->grid[x][y] = c;
// recurse unless this is a winning play
if (!winner(child, x, y))
fill(child, c == 'x' ? 'y' : 'x');
}
}
}
int main(void) {
node *start = new_grid(0);
fill(start);
}
来源:https://stackoverflow.com/questions/4090507/tic-tac-toe-how-to-populate-decision-tree