Python 极小极大为一个白痴解释

Python 极小极大为一个白痴解释,python,minimax,tic-tac-toe,Python,Minimax,Tic Tac Toe,我已经浪费了一整天的时间试图用minimax算法来制作一个无与伦比的Tictatcoe人工智能。一路上我错过了一些东西 我不是在这里寻找代码,只是为了更好地解释我的错误所在 这是我当前的代码(出于某种原因,minimax方法总是返回0): 正如你已经知道的,Minimax的思想是深入搜索最佳值,假设对手总是使用最差值(对我们来说最差,对他们来说也是最好的) 这个想法是,你将尝试给每个位置一个值。你输的位置是消极的(我们不希望这样),你赢的位置是积极的。你假设你总是会尝试最高价值的位置,但你也假设

我已经浪费了一整天的时间试图用minimax算法来制作一个无与伦比的Tictatcoe人工智能。一路上我错过了一些东西

我不是在这里寻找代码,只是为了更好地解释我的错误所在

这是我当前的代码(出于某种原因,minimax方法总是返回0):
正如你已经知道的,Minimax的思想是深入搜索最佳值,假设对手总是使用最差值(对我们来说最差,对他们来说也是最好的)

这个想法是,你将尝试给每个位置一个值。你输的位置是消极的(我们不希望这样),你赢的位置是积极的。你假设你总是会尝试最高价值的位置,但你也假设对手总是瞄准最低价值的位置,这对我们来说是最坏的结果,对他们来说是最好的(他们赢了,我们输了)。因此,你设身处地为他们着想,尽可能地发挥自己,并假设他们会这么做。
因此,如果你发现你可能有两个动作,一个让他们选择输赢,一个导致平局,你认为如果你让他们这么做,他们会选择让他们赢的动作。所以最好是抽签

现在来看一个更“算法”的观点

想象一下,除了两个可能的位置外,您的网格几乎已满。
考虑一下当你玩第一个游戏时会发生什么:
对手将与另一方比赛。这是他们唯一可能的行动,所以我们不必考虑他们的其他举动。查看结果,关联结果值(+∞ 如果获胜,平局为0-∞ 如果丢失:对于tic tac toe,您可以将其表示为+10和-1)。
现在考虑一下当你玩第二个游戏时会发生什么:
(这里也是一样,对手只有一个动作,看看结果位置,评估位置)

你需要在这两个动作中做出选择。这是我们的行动,所以我们想要最好的结果(这是minimax中的“max”)。选择一个更高的结果作为我们的“最佳”举措。这就是“从结尾开始的两个动作”的例子

现在,假设您没有2个移动,而是3个移动。
原理是一样的,你想给你的3个可能的动作中的每一个都指定一个值,这样你就可以选择最好的。
因此,您可以从三个动作中的一个开始考虑。
你现在处于上述情况,只有2个可能的动作,但轮到对手了。然后你开始考虑对手的一个可能动作,就像我们上面所做的那样。同样,您查看每一个可能的移动,并为它们找到结果值。这是对手的招式,所以我们假设他们将为他们打出“最好”的招式,对我们来说投票率最差的招式,所以这是一个值较小的招式(这是minimax中的“min”)。忽视另一个;假设他们会玩你发现对他们最有利的游戏。这是你的移动将产生的结果,因此这是你分配给你的三个移动中的第一个移动的值

现在你考虑一下其他可能的2个动作。你以同样的方式给他们一个值。从你的三个动作中,选择一个最大值

现在考虑一下4个动作会发生什么。对于你的4个招式中的每一个,你看对手的3个招式会发生什么,并且对于每一个招式,你假设他们会在剩下的2个招式中为你选择一个可能给你带来最坏结果的招式

你知道这是怎么回事。要从结尾开始计算一个移动n步,请查看n个可能移动中的每一步可能发生的情况,尝试给它们一个值,以便选择最佳移动。在这个过程中,你必须为在n-1位置的玩家找到最佳的移动方式:对手,并选择值较小的步骤。在评估n-1移动的过程中,你必须在可能的n-2移动之间进行选择,这将是我们的,并且假设我们将在这一步中发挥我们所能发挥的最好。等等

这就是为什么这个算法本质上是递归的。无论n是什么,在步骤n中,您评估n-1中所有可能的步骤。冲洗并重复


对于今天的tic-tac-toe来说,机器的功能强大到足以从游戏一开始就计算出所有可能的结果,因为它们只有几百个。当你想在一个更复杂的游戏中实现它时,你将不得不在某个时候停止计算,因为这将花费太长的时间。因此,对于一个复杂的游戏,您还必须编写代码来决定是继续寻找所有可能的下一步动作,还是尝试现在就给位置赋值并尽早返回。这意味着您还必须计算一个非最终位置的值-例如,对于国际象棋,您需要考虑每个对手在棋盘上有多少材料,在没有配偶的情况下立即检查的可能性,您控制的牌数以及所有,这使它变得不平凡。

步骤1:构建游戏树

从当前棋盘开始,生成对手可以做出的所有可能的移动。 然后,为每一个目标生成所有可能的移动。 对于Tic-Tac-Toe,只需继续,直到没有人可以玩为止。在其他游戏中,您通常会在给定的时间或深度后停止

这看起来像一棵树,你自己画在一张纸上,上面是当前的棋盘,所有对手移动一层以下,所有你可能的移动回应一层以下等等

步骤2:为树底部的所有板打分

对于像“井字游戏”这样的简单游戏,如果你输了,就得0分,50平,100胜

步骤3:将分数向上传播到树上

这就是最小最大值发挥作用的地方。一个以前未评分的棋盘的分数取决于它的孩子和谁来玩。假设你和你的对手总是在给定的状态下选择最好的移动。对对手来说,最好的一步就是给你最差分数的那一步。同样,你最好的m
from copy import deepcopy


class Square(object):
    def __init__(self, player=None):
        self.player = player
    
    @property
    def empty(self):
        return self.player is None


class Board(object):
    winning_combos = (
        [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8],
        [0, 4, 8], [2, 4, 6],
    )
    
    def __init__(self, squares={}):
        self.squares = squares
        for i in range(9):
            if self.squares.get(i) is None:
                self.squares[i] = Square()
    
    @property
    def available_moves(self):
        return [k for k, v in self.squares.iteritems() if v.empty]
    
    @property
    def complete(self):
        for combo in self.winning_combos:
            combo_available = True
            for pos in combo:
                if not pos in self.available_moves:
                    combo_available = False
            if combo_available:
                return self.winner is not None
        return True
    
    @property
    def player_won(self):
        return self.winner == 'X'
    
    @property
    def computer_won(self):
        return self.winner == 'O'
    
    @property
    def tied(self):
        return self.complete == True and self.winner is None
    
    @property
    def winner(self):
        for player in ('X', 'O'):
            positions = self.get_squares(player)
            for combo in self.winning_combos:
                win = True
                for pos in combo:
                    if pos not in positions:
                        win = False
                if win:
                    return player
        return None
    
    @property
    def heuristic(self):
        if self.player_won:
            return -1
        elif self.tied:
            return 0
        elif self.computer_won:
            return 1
    
    def get_squares(self, player):
        return [k for k,v in self.squares.iteritems() if v.player == player]
    
    def make_move(self, position, player):
        self.squares[position] = Square(player)
    
    def minimax(self, node, player):
        if node.complete:
            return node.heuristic
        a = -1e10000
        for move in node.available_moves:
            child = deepcopy(node)
            child.make_move(move, player)
            a = max([a, -self.minimax(child, get_enemy(player))])
        return a


def get_enemy(player):
    if player == 'X':
        return 'O'
    return 'X'
>> oWinning = {
 1: Square('X'),
 3: Square('O'), 4: Square('X'),
 6: Square('O'), 8: Square('X'),
}
>> nb = Board(oWinning)
>> nb.complete
True
>> nb.tied
True
def available_combos(self, player):
    return self.available_moves + self.get_squares(player)

@property
def complete(self):
    for player in ('X', 'O'):
        for combo in self.winning_combos:
            combo_available = True
            for pos in combo:
                if not pos in self.available_combos(player):
                   combo_available = False
            if combo_available:
                return self.winner is not None
    return True
>>> nb.minimax(nb, 'O')
-1
>>> nb.minimax(nb, 'X')
1