需要帮助找到决定井字游戏转弯的解决方案

need help finding solution to deciding turns for tic-tac-toe

我正在处理 tic tac toe 游戏。 我无法弄清楚如何让 XO swap 模糊地模仿交换转弯我看到有人像下面的代码那样做了但出于某种原因,我要么得到一个错误或者当我将它连接到我预先存在的代码时它就不起作用了。

我应该详细说明我计划(尝试)使用 Minimax 算法让这个游戏无法获胜

print("Begin")

global top_left, top_middle, top_right
global middle_left, center, middle_right
global bottem_left, bottem_middle, bottem_right

#these are the variables used to check if someone has put their move their already
#0 = empty
#1 = Circle
#2  = X

top_left = 0
top_middle = 0
top_right = 0

middle_left = 0
center = 0
middle_right = 0

bottem_left = 0
bottem_middle = 0
bottem_right = 0

#code for changing turns
turn = 1
def turn_changer():
    global turn
    if turn == 1:
        turn = 2
    else:
        turn = 1

#board setup
def setup():
    size(600,600)

#this hurt my brain trying to fully understand
#lines dividing board
def draw():
    for y in range(3):
        for x in range(3):
            rect(200*x,200*y,200,200)
    #hope this is not what geomtry is like

    #top left ellipse
    if top_left == 1:
        ellipse(100,100,150,150)

    #top left X
    elif top_left == 2:
        line(0,0,200,200)
        line(200,0,0,200)

    #top middle ellipse
    if top_middle == 1:
        ellipse(300,100,150,150)

    #top middle  X
    elif top_middle == 2:
        line(200,0,400,200)
        line(400,0,200,200)

    #top right ellipse
    if top_right == 1:
        ellipse(500,100,150,150)

    #top right X
    elif top_right == 2:
        line(400,0,600,200)
        line(600,0,400,200)

    #middle left ellipse
    if middle_left == 1:
        ellipse(100,300,150,150)

    #middle left X
    elif middle_left == 2:
        line(0,200,200,400)
        line(200,200,0,400)

    #middle ellipse
    if center == 1:
        ellipse(300,300,150,150)

    #middle X
    elif center == 2:
        line(200,200,400,400)
        line(400,200,200,400)

    #middle right ellipse
    if middle_right == 1:
        ellipse(500,300,150,150)

    #middle right X
    elif middle_right == 2:
        line(400,200,600,400)
        line(600,200,400,400)

    #bottem left ellipse
    if bottem_left == 1:
        ellipse(100,500,150,150)

    #bottem left  X
    elif bottem_left == 2:
        line(0,400,200,600)
        line(200,400,0,600)

    #bottem middle ellipse
    if bottem_middle == 1:
        ellipse(300,500,150,150)

    #bottem middle X
    elif bottem_middle == 2:
        line(200,400,400,600)
        line(400,400,200,600)

    #bottem right ellipse
    if bottem_right == 1:
        ellipse (500,500,150,150)

    #bottem right Xw
    elif bottem_right == 2:
        line(400,400,600,600)
        line(600,400,400,600)


#dectects the quardnates where the mouse clicked and prints them
def mousePressed():
    println( (mouseX, mouseY) )

    #top left square hitbox
    if (mouseX > 0 and mouseX < 200) and (mouseY > 0 and mouseY < 200):
        top_left =+ turn
        turn_changer()
        print("top left")



    #top middle square hitbox 
    elif (mouseX > 200 and mouseX < 400) and (mouseY > 0 and mouseY < 200): 
        top_middle = turn      
        turn_changer()
        print(turn)
        print("top middle")


    #top right square hitbox  
    elif (mouseX > 400 and mouseX < 600) and (mouseY > 0 and mouseY < 200):  
        top_right = turn
        turn_changer()
        print("top right")

    #middle left square hitbox
    elif (mouseX > 0  and mouseX < 200) and (mouseY > 200 and mouseY < 400):  
        middle_left = turn
        turn_changer()
        print("middle left")

    #center square hitbox
    elif (mouseX > 200 and mouseX < 400) and (mouseY > 200 and mouseY < 400):  
        center = turn
        turn_changer()
        print("middle")  

    #middle right square hitbox
    elif (mouseX > 400 and mouseX < 600) and (mouseY > 200 and mouseY < 400):  
        middle_right = turn
        turn_changer()
        print("middle right") 

    #bottem left square hitbox
    elif (mouseX > 0 and mouseX < 200) and (mouseY > 400 and mouseY < 600):  
        bottem_left = turn
        turn_changer()
        print("bottem left")

    #bottem middle square hitbox
    elif (mouseX > 200 and mouseX < 400) and (mouseY > 400 and mouseY < 600):  
        bottem_middle = turn
        turn_changer()
        print("bottem middle")

    #bottem right square hitbox
    elif (mouseX > 400 and mouseX < 600) and (mouseY > 400 and mouseY < 600):  
        bottem_right = turn
        turn_changer()
        print("bottem right")

当按下按钮时,检查转数是否等于 1 或 2,然后告诉程序显示一个十字或一个圆,而不是创建一个函数来更改该值

希望对您有所帮助!

将变量 'turn' 设置为全局变量(在 'turn_changer' 函数之外/确保此代码仅 运行 一次,而不是每次游戏循环时)。 运行 您的游戏逻辑,玩家(X 或 O)由 'turn' 的值决定。 在玩家做出他们的移动呼叫 'turn_changer' 之后,转弯发生变化。 重复。

使用您提供的代码,我添加了这个:

def game():
    print (turn)
    turn_changer()
    print (turn)
    turn_changer()
    print (turn)

if __name__ == "__main__":
    game()

并且 turn_changer() 函数似乎可以正常工作

有很多方法可以做到这一点...

  1. 你可以翻转一个变量(有点像你刚才展示的那样)。由于只有两名玩家,我可能会翻转一个布尔值:myBoolean = !myBoolean 每回合后。
  2. 您可以计算圈数并使用 modulo 运算符。然后你们都知道现在是第几回合以及当前游戏的总回合数。假设您有一个全局 TurnCount 变量。如果您执行 TurnCount % 2,结果将是 0 或 1,这是确定轮到哪一回合的可靠方法。这个运算符非常有用,无论如何你都应该记住它!
  3. 如果玩家 1 和 2 除了他们的 X 和 O 在游戏玩法上没有区别,你可以在角色 'X' 和角色 'O' 之间翻转,而不是像 1 和 2在你的代码片段中。此变量可用于直观地显示将放置哪个符号或直接放置一个符号或另一个。简单高效。

所以这主要取决于您编写代码的方式。如果您正在做 "simpel hot seat" 玩家轮流使用同一只鼠标点击,则方法 3 非常好。如果你想在游戏结束后显示 "statistics",方法 2 会很好用。方法 1 也可以,但它更粗糙,即使它很简单。如果您需要量身定制的建议,可以添加更多代码。

玩得开心!

我尊重你在自学,所以我花了一些时间学习 python 的基础知识,给你一些思考。我还不是 python 爱好者,所以我可能在某个地方做了一些误导操作(所以如果比我更好的编码员正在阅读这篇文章并发现一些糟糕的事情,请告诉我),但我相信这大部分都是好东西。

我用 class 因为我倾向于用 OOP 思考(过一会儿你也会这样)。我看到的不是带有 X 和 O 的网格,而是这样的游戏:

  1. 一个游戏就是一个对象。
  2. A游戏管理:

    一个网格(也是一个对象)。

    轮到谁了(轮到AI时,AI应该怎么玩)。

    游戏结束时。

  3. 一格管理:

    9 个案例(也是对象)。

  4. 一个案例管理:

    它自己绘制(所以...它是坐标和其他东西)。

    如果上面有 X 或 O。

    如果被点击

我完全意识到,当你开始编程时,对象是学习曲线中的一个巨大障碍,但我在这里坚持是因为我在你的代码中看到了很多硬编码,而且这种东西会当你扩大你的项目时给你带来问题。

硬编码,例如检查单击了哪个案例,本质上并不是坏事,但它使一切变得更加困难。这是你有时学到的东西的一部分 "the hard way",所以这是我的建议:当你忍者编码一些东西时(快速编写的短代码片段,不会成为更大的东西的一部分),它不是很好,但它确实工作。在任何其他情况下,它必须是出于某种特定需要而成为一种良好做法,即使在大多数情况下也可以避免这种情况。

这是基于我刚刚编写的注释代码。我没有做整个井字游戏,只是在玩家或 player/AI 之间切换回合的部分(我在那里放了一个布尔值,让你在人类对手和 AI 对手之间切换).缺少的主要是 AI 逻辑(我放了一个临时的逻辑,它会选择它找到的第一个案例)和胜利条件。

布尔值当前处于 "player vs player" 模式。改成True让AI接管O端

# Player 1 (X) is human and play first
# Player 2 (O) is cpu
# You can change this boolean to play hotseat with a human if you want:
_AIPlayer = False

# Game own a grid, count turns and do any other game-specific concepts
# One "game of tic-tac-toe" would equal one of this object
class Game:
    def __init__(self):
        self.Grid = Grid(self) # creating the grid we'll use
        self.TurnCount = 0 # first turn is turn number zero

    def Render(self):
        # when you draw the game, in fact it asks it's grid to draw itself
        self.Grid.Render()

    def Play(self):
        # if it's the CPU's turn, let him play, else the game will wait for the player before going forward
        # if there is no cpu player, the mouse can be used by player two
        # the difference is that the cpu will do it's turn as a consequence of the player's turn
        # and then add +1 to the turn count, while player 2 is exactly like player one but with O instead of X
        # the game will check X and O to see who win, not a player class (but it could have been designed that way if needed)
        if self.GetCurrentPlayer() == "O" and _AIPlayer:
            self.AITurn()

    def GetCurrentPlayer(self):
        # return which's player is currently playing
        if self.TurnCount % 2 == 0:
            return "X"
        else:
            return "O"

    def AITurn(self):
        # this is a dumb placeholder
        # your AI logic will be used here
        # for now it just put a O on the first available case
        print("AI turn")
        for c in self.Grid.Cases:
            if c.XO == "":
                c.XO = self.GetCurrentPlayer()
                break
        self.TurnCount += 1


# Grid class is the whole grid
class Grid:
    def __init__(self, game):
        # the grid knows the game. I could use the global variable instead, but I dislike
        # this kind of spaghetti. It would have worked, though.
        # It's usually best to make everything you can dynamic, i.e. not hardcoded. 
        # It's easier to maintain and fix bugs that way, and you can upscale more easily too
        # for an example, I could use this code to run several tic-tac-toe games in the
        # same window at the same time with only a few modifications
        self.Game = game
        self.Cases = []
        for i in range(3):
            for j in range(3):
                self.Cases.append(GridCase(i, j))

    def Render(self):
        # when you draw the grid, in fact it ask it's cases to draw themselves
        for c in self.Cases:
            c.Render()

    def CaseClicked(self, xPos, yPos):
        # this checks which case was clicked when it's a player
        # since we don't care about the case's coordinated, we ask them if they have been clicked instead
        for c in self.Cases:
            if c.Clicked(xPos, yPos, self.Game.GetCurrentPlayer()):
                self.Game.TurnCount += 1
                return


# GridCase is each instance of 1 case in the grid
class GridCase:    
    def __init__(self, gridX, gridY):
        # gridX and gridY are useful to know which case is part of which line
        self.gridX = gridX
        self.gridY = gridY
        # I hardcoded the case's width and height, but you could totally make them dynamic
        # and decide "on the fly" how big the grid will be. And it would still work.
        self.w = 200  # width
        self.h = 200  # height
        # these coordinates are in pixels, and are useful to draw the case and for hit detection
        self.x = self.w * gridX # x coordinate of the case
        self.y = self.h * gridY # y coordinate of the case
        # the "content" of the case
        self.XO = ""  # X or O as a character (it could be anything, I choose to stick to these)

    def Render(self):
        # the lines positions are dynamic: they'll be calculated from the case's perspective
        # every case top left corner is in fact: (self.x, self.y)
        rect(self.x, self.y, self.w, self.h)
        # if the case has content, it'll be drawn at the same time than the case
        if self.XO == "X":
            line(self.x , self.y, self.x+self.w, self.y+self.h)
            line(self.x, self.y+self.h, self.x+self.w, self.y)
        elif self.XO == "O":
            ellipse(self.x+(self.w/2),self.y+(self.h/2), self.w*0.75, self.h*0.75)

    def SetXO(self, XO):
        self.XO = XO

    def Clicked(self, xPos, yPos, car):
        # if the case is free and the click was inside it's boundaries, then attribute it to the current player
        # the return True to tell that a sign was just placed
        if self.XO == "" and xPos > self.x and xPos < self.x + self.w and yPos > self.y and yPos < self.y + self.h:
            self.XO = car
            return True
        return False


# globals
_game = Game()

def setup():
    size(600,600)

def draw():
    # background wipes the screen "clean" (here it paints it black)
    # then we can draw the current state of the grid
    # here we could do without but I wanted you to know about it
    background(0)
    # draw the grid, then let the players do their thing
    _game.Render()
    # here you should check for game end conditions (victory or draw)
    _game.Play()

def mouseClicked():
   # listeing to mouse clicks
   _game.Grid.CaseClicked(mouseX, mouseY)

您应该将此代码复制并粘贴到 Processing.py IDE 中并尝试一下。 Fiddle 转转并阅读评论。如果你尝试,你可以在这里学到很多东西。如果您有任何疑问,请在评论中用我的句柄提问,我会回来帮助您。

还有...玩得开心!