Minimax算法产生错误的针脚结果

时间:2019-06-01 16:25:03

标签: python artificial-intelligence tic-tac-toe minimax

我正在练习我的算法,并希望基于python中的minimax算法实现井字游戏AI(以下代码):

import itertools

import more_itertools
import numpy as np


class Position:
    win_positions = np.array(([1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 5, 9],
                              [1, 4, 7], [2, 5, 8], [3, 6, 9], [3, 5, 7]))

    def __init__(self, position=None):

        """If a position is given ensure 0 is in the first index, 
        for a dictionary lookup later for
            an empty board otherwise use the position
        """
        self.position = np.array([0]) if position is None else \
            np.array(position) if position[0] == 0 else np.append([0], np.array(
                position))

        self.turn = itertools.cycle(
            ['x', 'o']) if self.position.size % 2 else itertools.cycle(
            ['o', 'x'])
        self.p = more_itertools.peekable(self.turn)

    def add(self, value):
        next(self.turn)
        self.position = np.append(self.position, value)
        return self.win()

    def win(self):
        x = np.array(self.position[1::2])
        o = np.array(self.position[2::2])

        """
        takes the difference of a players moves and the winning move sets,
        an array resulting from this with a size of 0 indicates the player 
        contains
        a winning arrangement in their moves
        """

        if any([False if np.setdiff1d(position, x).size else True for position
                in Position.win_positions]):
            return 10
        elif any([False if np.setdiff1d(position, o).size else True for position
                  in Position.win_positions]):
            return -10
        elif self.valid_moves().size == 0:
            return 0
        else:
            return None

    def whose_turn(self, player=None):
        return self.p.peek() if player is None else self.p.peek() == player

    def valid_moves(self):
        return np.setdiff1d(np.arange(1, 10), self.position)

    def clone(self, next_pos=None):
        """returns a new position object with one of two options, either a copy,
            of the current position or a copy with a move added"""
        return Position(
            np.append(self.position, next_pos)) if next_pos else Position(
            np.copy(self.position))

    def mini_max(self, depth=0):

        best_move = -1
        """return the win value of the board position
            draws are 0, 'o' wins are -10 and 'x' wins are
            10. A board with valid moves available returns none for 
            this call """
        if self.win() is not None:
            return self.win(), None

        minimax = float('-inf') if self.whose_turn('x') else float('inf')
        for move in self.valid_moves():

            val = self.clone(move).mini_max()[0]
            if minimax < val and self.whose_turn(
                    'x') or minimax > val and self.whose_turn('o'):
                minimax = val
                best_move = move

        return minimax + (-1 if self.whose_turn('x') else 1), best_move

    def print_board(self):
        blank = np.chararray(shape=(9,), unicode=True)
        for i in range(1, self.position.size):
            blank[self.position[i] - 1] = 'X' if i % 2 else 'O'
        string = ("{:^3s}█{:^3s}█{:^3s}\n"
                  "▄▄▄█▄▄▄█▄▄▄\n"
                  "{:^3s}█{:^3s}█{:^3s}\n"
                  "▀▀▀█▀▀▀█▀▀▀\n"
                  "{:^3s}█{:^3s}█{:^3s}\n".format(*blank))
        print(string)

    def __str__(self):
        return "".join(map(str, self.position))


if __name__ == '__main__':
    # test winning conditions
    assert Position([1, 4, 2, 5, 3]).win() == 10
    assert Position([1, 4, 2, 5, 7, 6]).win() == -10
    assert Position([1, 4, 2, 5, 7, 8, 6, 5, 9, 3]).win() == 0

    # testing turns
    assert Position([1, 2, 3]).whose_turn() == 'o'
    assert Position([1, 5, 6, 4]).whose_turn() == 'x'
    assert Position([5, 6, 3, 2]).whose_turn('x')
    assert Position([5, 6, 3, 2, 1]).whose_turn('o')

    # valid moves
    assert all(
        Position([1, 2, 3, 4, 5, 6]).valid_moves() == np.array([7, 8, 9]))
    assert all(
        Position([9, 5, 3, 8]).valid_moves() == np.array([1, 2, 4, 6, 7]))

    # test minimax correct moves
    assert Position([1, 4, 2, 5]).mini_max()[1] == 3
    assert Position([1, 3, 4, 5]).mini_max()[1] == 7
    assert Position([1, 2, 5]).mini_max()[1] == 9  # error here

我已经尝试过多次重写代码,但是我的测试在最佳移动方面一直失败。 o玩家似乎忽略了x玩家的获胜位置。我有点茫然。我已经根据Wikipedia上的伪代码对其进行了检查,但似乎没有任何明显的错误

0 个答案:

没有答案