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

别说谁变了你拦得住时间么 提交于 2019-12-22 21:45:51

问题


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).


回答1:


Sounds like a prime candidate for recursion to me...




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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)



回答6:


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

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