TicTacToe Alpha Beta修剪RuntimeError:最大递归深度超过python与pygame

时间:2015-10-28 17:15:05

标签: python recursion pygame artificial-intelligence alpha-beta-pruning

所以,我正在编写一个非常简单的TicTacToe程序,该程序使用alpha-beta修剪来搜索针对播放器的下一步操作,但是在我想要运行它的任何时候遇到问题。我已经尝试了任何想到解决它的东西,甚至做了一行一行,相当于完美运行的逻辑,但是python没有用。

这是我的代码:

#encoding: UTF-8
#Andres de Lago Gomez
#A01371779

from random import randint
from utils import *


def getEnemy(who):
    return 1 if who==2 else 2


def getLetter(who):
    return "X" if who==1 else "O"


class TicTacToe:
    winningCombos =[[0, 1, 2], [3, 4, 5],
                    [6, 7, 8], [0, 3, 6],
                    [1, 4, 7], [2, 5, 8],
                    [0, 4, 8], [2, 4, 6]]
    board = [0 for i in range(9)]
    player = 1
    cpu = 2

    def __init__(self):
        pass

    def makeMove(self, who, pos):
        self.board[pos] = who

    def setCpu(self, who):
        self.player = who
        self.cpu = getEnemy(who)

    def availableMoves(self):
        return [i for i in range(9) if self.board[i]==0]

    def isOver(self):
        if 0 not in self.board:
            return True
        if self.Winner():
            return True
        return False

    def Winner(self):
        for i in [1,2]:
            positions = self.getSquares(i)
            for combo in self.winningCombos:
                win = True
                for pos in combo:
                    if pos not in positions:
                        win = False
                if win:
                    return i
        return False

    def getSquares(self, player):
        return [i for i in range(9) if self.board[i]==player]

    def alphaBeta(self, player, alpha, beta):
        if self.isOver():
            return self.evaluateNode()
        for move in self.availableMoves():
            self.makeMove(move, player)
            value = self.alphaBeta(getEnemy(player),alpha,beta)
            self.makeMove(move, 0)
            if player == self.cpu:
                if alpha < value:
                    alpha = value
                if alpha >=beta:
                    return beta
            else:
                if beta > value:
                    beta = value
                if beta <= alpha:
                    return alpha
        return alpha if player==self.cpu else beta

    def evaluateNode(self):
        tmp = self.Winner()
        if tmp==self.cpu:
            return 1
        elif tmp==self.player:
            return -1
        else:
            return 0

    def getNextMove(self, player):
        test = -5
        possibleMoves = []
        for move in self.availableMoves():
            self.makeMove(player, move)
            value = self.alphaBeta(getEnemy(player), -5, 5)
            self.makeMove(0, move)
            if value>test:
                test = value
                possibleMoves = [move]
            elif value == test:
                possibleMoves.append(move)
        return possibleMoves[randint(0, len(possibleMoves)-1)]

class Tile(pygame.sprite.Sprite):

    def __init__(self, rect, screen, player="X"):
        pygame.sprite.Sprite.__init__(self)
        self.rect = rect
        self.images = [load_png(get_path()+"\\data\\TicTacToe\\"+player+"Tile\\{0:0>2d}.png".format(x)) for x in range(21)]
        clock = pygame.time.Clock()
        for i in range(21):
            self.image = self.images[i]
            screen.blit(self.image, self.rect)
            pygame.display.update(self.rect)
            clock.tick(80)

class GUI:

    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode([100,100])
        self.dir = get_path()
        #get background
        self.background = pygame.image.load(self.dir+"\\data\\TicTacToe\\Background.png")
        self.background.convert()
        self.tiles = pygame.sprite.Group()
        #blit to screen
        self.screen = pygame.display.set_mode(self.background.get_size())
        self.screen.blit(self.background,(0,0))
        pygame.display.update()
        #create tiles
        self.places = [pygame.Rect((15+165*x, 110+165*y), (150,150) )for x in range(3) for y in range (3)]
        #create game
        self.board = TicTacToe()
        done = False
        goFirstI = load_png(get_path()+"\\data\\TicTacToe\\GoFirst.png")
        goFirstR = pygame.Rect((0,0), (400,200))
        goFirstR.center = self.places[4].center
        self.screen.blit(goFirstI,goFirstR)
        pygame.display.update(goFirstR)
        yesR = pygame.Rect((97,382),(115,45))
        noR = pygame.Rect((297,382),(115,45))
        while not done:
            for event in pygame.event.get():
                if event.type == pygame.MOUSEBUTTONUP:
                    if event.button == 1:
                        if yesR.collidepoint(event.pos):
                            done = True
                            self.board.setCpu(1)
                        elif noR.collidepoint(event.pos):
                            done = True
                            self.board.setCpu(2)
        self.screen.blit(self.background,(0,0))
        pygame.display.update(goFirstR)
        #start clock
        clock = pygame.time.Clock()
        done = False
        turn = 1
        bdown = 0
        while not done:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    done = True
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        pass    #pause menu?
                elif event.type == pygame.MOUSEBUTTONUP:
                    if event.button == 1:
                        bdown = event
            if turn == self.board.player:
                if bdown != 0:
                    index = 0
                    for rect in self.places:
                        if rect.collidepoint(bdown.pos):
                            moves = self.board.availableMoves()
                            if index in moves:
                                self.board.makeMove(turn, index)
                                self.tiles.add(Tile(rect, self.screen, getLetter(self.board.player)))
                                turn = getEnemy(turn)
                        index += 1
            else:
                pos = self.board.getNextMove(self.board.cpu)
                self.board.makeMove(turn, pos)
                self.tiles.add(Tile(self.tiles[pos], self.screen, getLetter(self.board.cpu)))

            clock.tick(60)

a = GUI()

我得到的错误是:RuntimeError: maximum recursion depth exceeded

我真的很想知道导致它的原因。

1 个答案:

答案 0 :(得分:1)

Python和Java以不同方式处理递归。 Python具有相当大的堆栈框架,因此在崩溃和抛出该错误之前,对递归的次数或代码可以调用多少次的硬性限制也是如此。在您的代码中,这是您自己调用的def alphaBeta(self, player, alpha, beta)方法:value = self.alphaBeta(getEnemy(player),alpha,beta)

在Java中,这有点不同,因为Java会进行更多优化。这意味着在获得Java Stack Overflow错误之前,您可以进行更多的重复调用。

您最好的选择是将代码从递归函数更改为迭代函数。 I.E.,更改你的alphaBeta方法,以便它不使用自身,而是使用for或while循环来做同样的事情