【问题标题】:My minimax algorithm for Tic Tac Toe in Python is showing maximum recursion error我在 Python 中的井字游戏的极小极大算法显示最大递归错误
【发布时间】:2019-12-29 19:10:01
【问题描述】:

我正在尝试自己在 python 中为井字游戏编写 minimax 算法的代码,我已经编写了代码,但是每当调用该函数时,它都会显示“比较的最大递归深度”错误。我被困在这部分。当我尝试调试它时,它也无济于事。

import sys

marked=['','','','','','','','','']
markingSignal=[False,False,False,False,False,False,False,False,False]


def printTable():
    print("\t%s|\t%s|\t%s\n------------------------\n\t%s|\t%s|\t%s\n------------------------\n\t%s|\t%s|\t%s\n"%(marked[0],marked[1],marked[2],marked[3],marked[4],marked[5],marked[6],marked[7],marked[8]))

def winning(m,player):
    i=0
    x=0
    while x<3:
        if m[i]==player and m[i+1]==player and m[i+2]==player:
            return True
        x=x+1
        i=i+3    
    x=0
    i=0
    while x<3:
        if m[2]==player and m[4]==player and m[6]==player:
            return True
        x=x+1
        i=i+3  
    x=0
    i=0
    if m[0]==player and m[4]==player and m[8]==player:
        return True
    if m[2]==player and m[4]==player and m[6]==player:
        return True
    return False         


def minimax(table,marktab,points,pos=0):
    copyTab=table
    copymark=marktab
    remaining=0
    for x in table:
        if x==False:
            remaining=remaining+1
    if remaining==0:
        return points,pos
    scores=[None]*remaining
    positions=[None]*remaining
    z=0
    maximum=0
    bestpos=0
    previous=88
    x=0
    while x<9:
        if table[x]==False:
            if points%2==0:
                copyTab[x]==True
                copymark[x]=='O'
                result=winning(copymark,'O')
                previous=x
                if result:
                    return points ,x
            else:
                copyTab[x]==True
                copymark[x]=='X'    
            scores[z],positions[z]=minimax(copyTab,copymark,points+1,previous)
            z=z+1
            copyTab[x]==False
            copymark[x]==''
        x=x+1
    for x in range(0,len(scores)):
        if x==0:
            maximum=scores[x]
            bestpos=positions[x]
        if scores[x]<maximum:
            maximum=scores[x]
            bestpos=positions[x]
    return maximum, bestpos        



def takeInput(player):
    filled=False
    while filled==False:
        print("Enter Your Choice 1-9")
        x=int(input())
        if x>9:
            print("Invalid Choice")
            continue
        if markingSignal[x-1]:
            print("This slot is already filled")
            continue
        filled=True    
    marked[x-1]=player
    markingSignal[x-1]=True


def main():

    sys.setrecursionlimit(5000)
    print(sys.getrecursionlimit())
    printTable()
    count=0
    player='X'
    while count<9:

        if count%2==0:
            player='X'
            takeInput(player)
        else:
            player='O'  
            p,choice=minimax(markingSignal,marked,0)  
            marked[choice]=player
            markingSignal[choice]=True         
        printTable()
        result=winning(marked,player)
        if result:
            print("\n%s WON !!!\n"%(player))
            break
        count=count+1


main()  

在此代码中,用户输入部分工作正常,但计算机输入或极大极小算法部分不工作,并显示递归错误

【问题讨论】:

  • minimax的目的是什么?
  • 乍一看,我猜copyTab=tablecopymark=marktab 可能是问题所在——它们通过引用而不是复制来传递相同的列表。要制作副本,请改为使用copyTab = table[:]
  • stackoverflow.com/a/9697367/567595 以及那里的链接和其他答案可能有助于在 Python 中分配变量。
  • 我看了你的代码有一段时间了,错误太多了,无法总结。记住 Stuart 的评论,但抄袭、双人桌……这毫无意义。你为什么不去 Wikipedia 看看 minimax 是如何实现的?这比调试代码中的许多误解要好。
  • 两个月前我才开始学习python。我花了几个小时写这篇文章,现在从互联网上复制算法对我来说会令人心碎

标签: python algorithm tic-tac-toe minimax


【解决方案1】:

所以,在你的代码中

scores[z],positions[z]=minimax(copyTab,copymark,points+1,previous)

这是进入一个永无止境的循环。它一遍又一遍地突破……之前的值总是在 88 和 0 之间。那个递归函数必须在某个点返回(在调用递归函数之前你只有一个 return 是一个获胜位置。在第一个动作之后你不能有获胜的位置,因此递归永远不会结束)。

在 minimax 函数中考虑到这一点,您不会复制值,只是通过引用传递:

copyTab=table.copy()
copymark=marktab.copy()

另外,你没有增加 X 值,因为在递归函数中,板没有更新也没有测试。

因此您需要分配值: 复制标签[x]=真 复制标记[x]='O' 并且不使用 double 等于 == 只会返回一个布尔值。

所以该功能现在按预期工作:

def minimax(table,marktab,points,pos=0):
    copyTab=table.copy()
    copymark=marktab.copy()
    remaining=0
    for x in table:
        if x==False:
            remaining=remaining+1
    if remaining==0:
        return points,pos
    scores=[None]*remaining
    positions=[None]*remaining
    z=0
    maximum=0
    bestpos=0
    previous=88
    x=0
    while x<9:
        if table[x]==False:
            if points%2==0:
                copyTab[x]=True
                copymark[x]='O'
                result=winning(copymark,'O')
                previous=x
                if result:
                    return points ,x
            else:
                copyTab[x]=True
                copymark[x]='X' 
            scores[z],positions[z]=minimax(copyTab,copymark,points+1,previous)
            z=z+1
            copyTab[x]=False
            copymark[x]=''
        x=x+1
    for x in range(0,len(scores)):
        if x==0:
            maximum=scores[x]
            bestpos=positions[x]
        if scores[x]<maximum:
            maximum=scores[x]
            bestpos=positions[x]
    return maximum, bestpos

【讨论】:

  • “那个递归函数必须在某个点返回。”:我看到代码中至少有两个地方有return,而没有更深入地递归。 “你没有增加 X 值”:代码中有一行写着x = x + 1
  • @trincot,我用这些观点改变了我的答案,并解释了它们。他只有在获胜的情况下才会回来。而 X 值只有在调用递归函数后才会增加。因此,启动递归的函数将永远不会进入下一个任务。对于这个 porpuses,它可以添加一个新参数以在一定的递归深度后返回
  • 这不能解释。在第一步之后,它必须更深地递归,所以这是正常的,并且还有一个平局测试,它也返回。第二点也不清楚。 x 在递归调用之前不能增加,因为递归调用有自己的 x 局部变量。
  • 它自己的局部变量总是初始化为0。是的,它需要在开始时更深入。虽然需要改变才能知道他正在测试船上的那个地方。
  • 哦,哦,他不是在分配!!! copyTab[x] == True 不是 copyTab[x] = True
【解决方案2】:

另一个答案想提供帮助,但实际上您不需要这些副本。您正在应用的是一个 do-undo 模式,因此您创建一个步骤,检查结果并撤消该步骤。这可以在不复制表的情况下完成,但也必须在从循环内部返回之前完成。此外,=== 的错误当然需要解决

def minimax(table,marktab,points,pos=0):
    #copyTab=table                             # copyTab eliminated
    #copymark=marktab                          # copymark eliminated
    remaining=0
    for x in table:                            # note that this...
        if x==False:
            remaining=remaining+1
    if remaining==0:
        return points,pos
    scores=[None]*remaining
    positions=[None]*remaining
    z=0
    maximum=0
    bestpos=0
    previous=88
    x=0
    while x<9:
        if table[x]==False:                    # ... and this line were referring to table anyway
            if points%2==0:
                table[x]=True                  # now it is table and =
                marktab[x]='O'                 # marktab and =
                result=winning(marktab,'O')
                previous=x
                if result:
                    table[x]=False             # this ...
                    marktab[x]=''              # ... and this undo steps were missing
                    return points ,x
            else:
                table[x]=True                  # table and =
                marktab[x]='X'                 # marktab and =
            scores[z],positions[z]=minimax(table,marktab,points+1,previous) # table and marktab
            z=z+1
            table[x]=False                     # table and =
            marktab[x]=''                      # marktab and =
        x=x+1
    for x in range(0,len(scores)):
        if x==0:
            maximum=scores[x]
            bestpos=positions[x]
        if scores[x]<maximum:
            maximum=scores[x]
            bestpos=positions[x]
    return maximum, bestpos        

然后对手很高兴地输了,就像其他修复一样。

旁白

  • 标记和标记信号可以使用复制,所以marked = ['']*9markingSignal = [False]*9
  • %-format 期望右侧有一个元组,因此您可以简单地写成% tuple(marked) 而不是长的% (marked[0],...)
  • 在去掉 copyTabcopymark 之后,tablemarktab 真的不需要作为参数传递
  • markingSignal 并不是真的需要,检查 table[x]=='' 可以判断一个字段是空闲还是被占用

这解决了递归问题,但对算法没有帮助。在Wikipedia 上查看伪代码的样子:

function minimax(node, depth, maximizingPlayer) is
    if depth = 0 or node is a terminal node then
        return the heuristic value of node
    if maximizingPlayer then
        value := −∞
        for each child of node do
            value := max(value, minimax(child, depth − 1, FALSE))
        return value
    else (* minimizing player *)
        value := +∞
        for each child of node do
            value := min(value, minimax(child, depth − 1, TRUE))
        return value

在您的代码中只有一个最大值,我们称之为max(scores)。您还需要在某处使用min(scores),具体取决于目前考虑的玩家,或者您可以应用min(scores) 可以作为找到max(-scores) 的常用“技巧”,但这种“翻转”不是也出现在代码中。

正如你所说你想自己修复它,我只提供包含建议的简化的缩短版本,但其他方面完好无损(所以它会毫不犹豫地丢失):

import sys

marked=[''] * 9

def printTable():
    print("\t%s|\t%s|\t%s\n------------------------\n\t%s|\t%s|\t%s\n------------------------\n\t%s|\t%s|\t%s\n"%tuple(marked))

def winning(player):
    i=0
    x=0
    while x<3:
        if marked[i]==player and marked[i+1]==player and marked[i+2]==player:
            return True
        x=x+1
        i=i+3    
    x=0
    i=0
    while x<3:
        if marked[2]==player and marked[4]==player and marked[6]==player:
            return True
        x=x+1
        i=i+3  
    x=0
    i=0
    if marked[0]==player and marked[4]==player and marked[8]==player:
        return True
    if marked[2]==player and marked[4]==player and marked[6]==player:
        return True
    return False         

def minimax(points,pos=0):
    remaining=0
    for x in marked:
        if x=='':
            remaining=remaining+1
    if remaining==0:
        return points,pos
    scores=[None]*remaining
    positions=[None]*remaining
    z=0
    maximum=0
    bestpos=0
    previous=88
    x=0
    while x<9:
        if marked[x]=='':
            if points%2==0:
                marked[x]='O'
                result=winning('O')
                previous=x
                if result:
                    marked[x]=''
                    return points ,x
            else:
                marked[x]='X'    
            scores[z],positions[z]=minimax(points+1,previous)
            z=z+1
            marked[x]=''
        x=x+1
    for x in range(0,len(scores)):
        if x==0:
            maximum=scores[x]
            bestpos=positions[x]
        if scores[x]<maximum:
            maximum=scores[x]
            bestpos=positions[x]
    return maximum, bestpos        

def takeInput(player):
    filled=False
    while filled==False:
        print("Enter Your Choice 1-9")
        x=int(input())
        if x>9:
            print("Invalid Choice")
            continue
        if marked[x-1]!='':
            print("This slot is already filled")
            continue
        filled=True    
    marked[x-1]=player

def main():
    printTable()
    count=0
    player='X'
    while count<9:
        if count%2==0:
            player='X'
            takeInput(player)
        else:
            player='O'  
            p,choice=minimax(0)  
            marked[choice]=player
        printTable()
        result=winning(player)
        if result:
            print("\n%s WON !!!\n"%(player))
            break
        count=count+1

main()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多