First we will use your tic-tac-toe.
- The minimax algorithm is best suited for games where players alternate, but can be adapted to games in which players can make several moves per turn. For simplicity, we will consider the first. In this case, you do not need to store “X to move” or “O to move” with each node, because it can only be determined by the parity of the depth of the node (whether it is an even number of steps, or an odd number of steps, above).
- Generating possible moves from each position requires that you know whose move (which can be defined as before) and the rules for legal movements from a specific position. For a simple game, such as tic-tac-toe, taking into account the position, it is enough to list all the states that consist of a copy of the current position plus a new part belonging to the current player, placed in turn on each empty square. For games like Othello, you also need to check each place to make sure it matches the rules, and update the end position according to the consequences of the rule (for Othello, turning a bunch of pieces upside down). In general, from each valid position that you are tracking, you list all the possible locations of the new part and check which of them are allowed by a set of rules.
- In general, you NEVER generate the entire tree, as the size of the game tree can easily exceed the capacity of the Earth’s storage. You always set the maximum iteration depth. The node terminal, then, is simply a node at maximum depth or node, of which there are no legal movements (for tic-tac-toe, a board with each square filled). Do not create terminal nodes before; they are generated naturally when building the game tree. Tic-tac-toe is simple enough that you can generate the entire game tree, but then do not try to use the tic-tac-toe code, for example. Othello
Looking at your pseudo code:
max(a, b) is any function that returns more than a or b . This is usually provided by a math library or similar.depth - the maximum depth to which you will search.- The heuristic value that you calculate is some numerical value that describes the value of the board. For a game like tic-tac-toe, which is simple enough for you to list the entire game tree, you can designate
1 for the position of the board that wins for the player performing the analysis, -1 for the position of the board that wins the other player and 0 for any unconvincing position. In general, you will have to prepare a heuristic yourself or use the well-accepted option. - You generate nodes on the fly during analysis based on their parent nodes. The root of the node is always the position from which you are doing the analysis.
If you have not worked with graphs or trees, I suggest you do this first; A primitive tree element, in particular, is important for this problem.
As a response to a comment in this thread asking for an example of determining whose move for a given node, I suggest this pseudo-Python:
who_started_first = None class TreeNode: def __init__(self, board_position = EMPTY_BOARD, depth = 0): self.board_position = board_position self.children = [] self.depth = depth def construct_children(self, max_depth):
Each node is able to track its absolute depth from the "root" node. When we try to determine how we should create positions on the board for the next step, we check whose move it is based on the ratio of our depth (result self.depth % 2 ) and our record of who moved first.
source share