Tic-Tac-Toe: How to populate decision tree?

本小妞迷上赌 提交于 2019-12-06 08:20:00

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.

Child's play.

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);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!