【问题标题】:Python - Determine Tic-Tac-Toe WinnerPython - 确定井字游戏赢家
【发布时间】:2017-02-16 18:59:00
【问题描述】:

我正在尝试编写一个代码来确定井字游戏的获胜者。 (这是大学作业)

为此,我编写了以下函数:

这段代码只检查水平线,其余的我没有添加。我觉得这需要一些硬编码。

def iswinner(board, decorator):
    win = True
    for row in range(len(board)):
        for col in range(len(board)):
            if board[row][col] == decorator:
                win = True
            else:
                win = False
                break

其中“board”是一个大小为 n^2 的二维数组,“decorator”是“X”或“O”值

我希望完成的是函数循环遍历二维数组的行。然后循环遍历每一行中的值。如果该元素与“装饰器”匹配,则它继续并检查下一个,但如果不匹配,则它从第一个循环中断并进入下一行。它会这样做,直到在同一行中找到 n 个元素。然后它会给出一个布尔值 True 否则 False。

代码似乎没有这样做,即使我检查了下面的“板”,它也给了我一个“真”的输出

check_list = [['O', 'X', 'X'],
              ['O', 'X', 'O'],
              ['O', 'X', 'X']]

非常感谢!

最好, 赛义德

【问题讨论】:

  • 如果部分代码看不懂,请告诉我
  • 什么时候将win 设置为False?它总是True。而且逻辑听起来是错误的……缩进是错误的,你不返回win!这开始在您的代码中累积。
  • @Jean-FrançoisFabre 我更新了代码,不知何故该行丢失了
  • @Jean-FrançoisFabre 很抱歉出现缩进,现在已修复
  • 装饰器是什么?您不需要连续 3 次来赢得井字游戏吗?根据一个单元格的价值,您如何获胜?

标签: python arrays loops boolean break


【解决方案1】:

您可以只为每一行制作一组,并检查其长度。如果它只包含一个元素,那么游戏就赢了。

def returnWinner(board):
    for row in board:
        if len(set(row)) == 1:
            return row[0]
    return -1

如果有整行“O”,则返回“O”,如果有一行“X”,则返回“X”,否则返回-1。

下面是一个完整的井字棋检查器的代码,应该不难理解,但请不要犹豫:

import numpy as np

def checkRows(board):
    for row in board:
        if len(set(row)) == 1:
            return row[0]
    return 0

def checkDiagonals(board):
    if len(set([board[i][i] for i in range(len(board))])) == 1:
        return board[0][0]
    if len(set([board[i][len(board)-i-1] for i in range(len(board))])) == 1:
        return board[0][len(board)-1]
    return 0

def checkWin(board):
    #transposition to check rows, then columns
    for newBoard in [board, np.transpose(board)]:
        result = checkRows(newBoard)
        if result:
            return result
    return checkDiagonals(board)


a = [['X', 'A', 'X'],
     ['A', 'X', 'A'],
     ['A', 'X', 'A']]

print(checkWin(a))

请注意,无论您选择在井字游戏中放置什么符号(“O”和“X”与“bloop”和“!”一样好),这都有效,并且对于任何大小的网格,只要是正方形。

【讨论】:

  • 你能解释一下“set”部分是做什么的吗?
  • 它将数组转换为集合。一个集合是一个unordered collection of unique elements。因此set(['X', 'O', 'X']) 只包含'X', 'O',而set(['X', 'X', 'X']) 只包含'X'。如果集合只包含一个元素,那么就只有一种装饰器。
【解决方案2】:

执行此操作的一种方法是创建所有可能的索引组合的集合(生成器函数会更好)以检查是否获胜。然后循环遍历这些索引组合并检查它们是否都包含相同的值,如果是,那就是胜利。

def win_indexes(n):
    # Rows
    for r in range(n):
        yield [(r, c) for c in range(n)]
    # Columns
    for c in range(n):
        yield [(r, c) for r in range(n)]
    # Diagonal top left to bottom right
    yield [(i, i) for i in range(n)]
    # Diagonal top right to bottom left
    yield [(i, n - 1 - i) for i in range(n)


def is_winner(board, decorator):
    n = len(board)
    for indexes in win_indexes(n):
        if all(board[r][c] == decorator for r, c in indexes):
            return True
    return False

【讨论】:

  • 有没有办法将其概括为水平、垂直和对角线检查,或者这是不可能的?
  • 是的,这是通用的。它目前检查水平、垂直和对角线。如果您想添加更多获胜条件(例如 4 个角),您只需 yield 来自 win_indexes 生成器的附加索引。
  • 你能解释一下你的代码中的产量吗?我自己也在阅读。
  • 它是一个生成器。如果您不想使用生成器,而不是 yielding 每个索引列表,您可以将它们 append 到一个列表并返回整个列表。
  • 当我尝试运行代码时,它给了我“NameError: name 'n' is not defined”
【解决方案3】:
def check_winner(board,mark):
    return((board[1]==mark and board[2]== mark and board[3]==mark )or #for row1 

            (board[4]==mark and board[5]==mark and board[6]==mark )or #for row2

            (board[7]==mark and board[8]==mark and board[9]==mark )or #for row3

            (board[1]==mark and board[4]==mark and board[7]== mark )or#for Colm1 

            (board[2]==mark and board[5]==mark and board[8]==mark )or #for Colm 2

            (board[3]==mark and board[6]==mark and board[9]==mark )or #for colm 3

            (board[1]==mark and board[5]==mark and board[9]==mark )or #daignole 1

            (board[3]==mark and board[5]==mark and board[7]==mark )) #daignole 2

【讨论】:

    【解决方案4】:

    我刚刚想出了这段小代码:

    def eval_w(board):
    possible = []
    
    #rows
    for i in range(3):
        possible.append( board[i][0] + board[i][1] + board[i][2] )
    
    #columns
    for i in range(3):
        possible.append( board[0][i] + board[1][i] + board[2][i] )
    
    #diagonals
    possible.append( board[0][0] + board[1][1] + board[2][2] )
    possible.append( board[0][2] + board[1][1] + board[2][0] )
    
    if 3 in possible:
        ended = True
        return 1
    elif -3 in possible:
        ended = True
        return -1
    else:
        return 0
    

    我这里没看到类似的东西,所以我还是贴吧。

    【讨论】:

      【解决方案5】:

      您可以通过创建一个generatorlines() 来生成所有 8 行(3 行、3 列和 2 条对角线),然后检查是否有任何行仅由一个元素和该元素组成不是None

      一旦你有类似的东西

      board = [
          [ 'o', 'x', None], 
          [None, 'x', None], 
          [None, 'x',  'o']
      ]
      

      这样做:

      def _lines(board):
          yield from board  # the rows
          yield [board[i][i] for i in range(len(board))]  # one of the diagonals
      
      def lines(board):
          yield from _lines(board)
          # rotate the board 90 degrees to get the columns and the other diagonal
          yield from _lines(list(zip(*reversed(board))))
      
      def who_won(board):
          for line in lines(board):
              if len(set(line)) == 1 and line[0] is not None:
                  return line[0]
          return None  # if we got this far, there's no winner
      

      对于我上面给出的板,list(lines(board)) 将返回

      [['o', 'x', None], 
       [None, 'x', None], 
       [None, 'x', 'o'], 
       ['o', 'x', 'o'], 
       (None, None, 'o'), 
       ('x', 'x', 'x'), 
       ('o', None, None), 
       [None, 'x', None]]
      

      其中 3 个元素是元组而不是列表,因为 zip 返回元组。您可以使用列表推导来转换它们,请参阅 this question 了解如何执行此操作以及更多有关 zip(*reversed(some_list)) 功能的详细信息。

      然后只需通过转换为set 和那个唯一元素is not None 来检查每个元素是否只有一个唯一元素。

      这适用于任何尺寸的井字游戏,而不仅仅是 3×3。

      【讨论】:

        【解决方案6】:

        我有点晚了,但使用 numpy 会是更好的方法,因为它解决了遍历多个列表的麻烦。它基本上使 'x' 为 1 和 'o' 为 0,所以如果 w 将所有元素添加到一行或列中,我们得到 3 或 0,x 或 o 将获胜。如果是平局,我们得到 -1 结果数组

        import numpy as np
        def check(arr):
            if np.sum(arr)==3:
                return 'x'
            elif np.sum(arr)==0:
                return 'o'
            else: return -1
        if __name__=='__main__':
            a=np.array([['X','O','X'],['O','X','O'],['X','X','O']]) #input array
            b=np.where(a=='X',1,0)
            r1=check(b[:,:1])
            r2=check(b[:,1:2])
            r3=check(b[:,2:3])
            c1=check(b[:1,:])
            c2=check(b[1:2,:])
            c3=check(b[2:3,:])
            d1=check(np.diagonal(b))
            d2=check(np.diagonal(np.fliplr(b)))
            result=np.array([r1,r2,r3,c1,c2,c3,d1,d2])
            if 'x' in result:print('x won')
            elif 'o' in result:print('o won')
            else:print('draw again')
        

        【讨论】:

          【解决方案7】:

          一个细胞总共可以有3种状态

          1. 如果还没有填满,则为0(游戏有可能在5步内结束)
          2. 如果用'X'填充则为1
          3. -1 如果用'O'填充

          我将扩展@EfferLagan 的答案

          def checkRows(board):
          for row in board:
              if (len(set(row)) == 1) and (row[0] != 0):
                  return row[0]
          return 999
          
          def checkDiagonals(board):
              if (len(set([board[i][i] for i in range(len(board))])) == 1) and (board[0][0] != 0):
                  return board[0][0]
              if (len(set([board[i][len(board)-i-1] for i in range(len(board))])) == 1) and (board[0][0] !=0):
                  return board[0][len(board)-1]
              return 999
          
          def checkWin(board):
              #transposition to check rows, then columns
              for newBoard in [board, np.transpose(board)]:
                  result = checkRows(newBoard)
                  if result:
                      return result
              return checkDiagonals(board)
          
          randomInput=[
              [0,0,1],
              [-1,-1,1],
              [0,0,0]
          ]
          

          您有 3 个输出 1、-1 和 999(这意味着两者都没有获胜) checkWin(随机输入)

          【讨论】:

            【解决方案8】:
              def check_winer(board):
                #used to check the winner in row
                for row in range(0,3):
                    if board[row][0]==board[row][1]==board[row][2] and board[row][0] != 0:
                        if chance=='o':
                            print("x is winner")
                        elif chance=='x':
                            print("o is winner")
                #used to check thw winner in column
                for col in range(0,3):
                    if board[0][col]==board[1][col]==board[2][col] and board[0][col] != 0:
                        if chance=='o':
                            print("x is winner")
                        elif chance=='x':
                            print("o is winner")
                #used to check winner in one diagonal
                if board[0][0]==board[1][1]==board[2][2] and board[0][0] !=0:
                    if chance=='o':
                        print('x is winner')
                    elif chance=='x':
                        print("o is winner")
                #used to check winner in another diagonal
                if board[0][2]==board[1][1]==board[2][0] and board[0][2]!=0:
                    if chance=='o':
                        print("x is winner")
                    elif chance=='x':
                        print("o is winner")
            

            在tic-tac-toe中两次检查获胜者您可以使用此功能。 你必须先创建一个像这样的二维数组:

            board=[[0,0,0],
                  [0,0,0],
                  [0,0,0]]
            

            所以当轮到 x 时单击框并将数组中的值更改为相对于框的 1。 并在每次点击中将机会从 x 更改为 o 并从 o 更改为 x。 如果轮到 o,则将数组中的值相对于坐标更改为 2。 每次单击框以放置 x 或 o 时,都必须调用此函数。

            请确保在将机会从 x 更改为 o 或从 o 更改为 x 后调用此函数。 因为如果你不这样做,这个函数会给出错误的输出。

            例如,如果这是板:

            [[1,2,1],
             [2,1,0],
             [1,0,0]]
            

            那么这个函数会给出如下输出:

            o is the winner
            

            【讨论】:

            • 您需要完整记录此解决方案如何使用 OP 的当前代码。这是一个老问题,你的答案太抽象和复杂,因为向 OP 提问的开发人员不太可能理解你的贡献或从你的贡献中受益。看到 OP 不太可能做出回应,您必须付出更多努力来提供完整的解决方案,并让我们其他人相信您的解决方案优于其他解决方案。
            猜你喜欢
            • 2012-01-13
            • 2022-12-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-04-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多