【问题标题】:Recursively Generate Every Tic-Tac-Toe game in Python在 Python 中递归生成每个井字游戏
【发布时间】:2019-04-25 12:44:30
【问题描述】:

我正在做一个项目,我在该项目中生成所有可能的井字游戏数组。作为概念证明,我正在编写代码以用 9 个子数组填充数组。每个子数组将有两个值,第一个是 0 或 1(分别代表 x 和 o),第二个是从 1 到 9(表示放置时间)。 我想得到的数组示例如下所示:

[[0, 0], [1, 1], [0, 2], [1, 3], [0, 4], [1, 5], [0, 6], [1, 7], [0, 8]]

我已经编写了代码,使用了 9 个 for 循环,每个循环都嵌套在上面的循环中,这给了我想要的结果(每个可能的数组,每个数组都是唯一的)。但我正在尝试编写代码,使用递归,并避免编写大量嵌套循环。

当我运行下面的代码时,它只能生成上面的数组,不能创建其他组合。我的代码如下:

print("running...")

allGames = []
checkCurrentGame = [5, 5, 5, 5, 5, 5, 5, 5, 5]
stepsDown = 0

def cleanGame(move, currentGame):
    for j in range(9):
        if (currentGame[j][1] >= move):
            currentGame[j] = [5, 0]

def completeMove(moveNumber, currentGame):
    global stepsDown
    stepsDown = stepsDown + 1
    for i in range(9):
        cleanGame(moveNumber, currentGame)
        if (currentGame[i][0] == 5):
            currentGame[i][0] = i % 2
            currentGame[i][1] = moveNumber
            allGames.append(currentGame)
            break
    if (stepsDown < 9):
        generateGame(currentGame)

def generateGame(currentGame):
    for i in range(9):
        completeMove(i, currentGame)

generateGame([[5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0]])

for x in range(len(allGames)):
    print(allGames[x])

【问题讨论】:

    标签: python arrays recursion


    【解决方案1】:

    我建议你在 youtube 上观看 this video。教授很好地教了递归。我认为它会为您带来更多好处,然后获得工作代码。他以数独为例,但这两款游戏都只是二维数组。

    如果您观看了视频,您将知道如何修改此示例以更好地满足您的需求。 伪代码:

    def place(who, when, where, table):
        if (None, None) not in table: # the table is full
            print(table)
            return
    
        if table[where] not (None, None): # this cell is already marked
            return
    
        table[where] (who, when) # can place our mark on the cell
        # make the recursive calls
        for i in range(9):
            place(who.opponent, when+1, i, table)
    
    for cell in range(9):
        empty = [(None, None) for _ in range(9)]
        place(firstplayer, 0, cell, empty)
    

    【讨论】:

      【解决方案2】:

      这很有趣。这是一种玩游戏的方法,将每个可能的移动分支(或者无论如何,这就是意图)。

      虽然我的偏好通常是连接并返回一个纯结果,以便提前退出​​和检查,但这种递归会累积在一个全局变量中。

      import copy
      import json
      import time
      
      s = set()
      rs = []
      
      def f():
        """
        111
        111000
        111000000
        1001001
        10010010
        100100100
        1010100
        100010001
        """
        wins = [0007,0070,0700,0111,0222,0444,0124,0421]
        def winner(board):
          # check xs or os individual board for a win
          for i in wins:
            if not (i ^ (board & i)):
              return True
          return False
      
        # xs board, os board, move-number, game
        def g(xs, os, move, result):
          # allow for early exit
          global rs
          if (len(rs) > 20000):
            return
      
          # base case, win or draw
          if winner(xs) or winner(os) or move == 9:
            #print "{0:b}".format(xs)
            #print "{0:b}".format(os)
            #print (result, xs, os)
            #print
      
            # confirm we're not duplicating results
            enc = json.dumps(result)
            if enc in s:
              print "Duplicate!"
              print result
            s.add(enc)
      
            # accumulate results
            rs.append((result, xs, os))
            return
      
          board = xs | os
      
          for i in xrange(9):
            # candidate move
            m = 1 << i
      
            # valid move
            if not (m & board):
              _result = copy.deepcopy(result)
      
              # 'O' plays on odd numbered moves
              if move & 1:
                _result[i] = [1, move]
                g(xs, os | m, move + 1, _result)
      
              # 'X' plays on even numbered moves
              else:
                _result[i] = [0, move]
                g(xs | m, os, move + 1, _result)
      
        # start the recursion
        g(0, 0, 0, [[-1, -1]] * 9)
      
      start_time = time.time()
      f()
      print("--- %s seconds ---" % (time.time() - start_time))
      

      输出:

      """
      rs[2002]
      
      => ([[0, 0], [1, 1], [-1, -1],
         [-1, -1], [1, 5], [0, 2],
           [0, 4], [1, 3], [-1, -1]], 97, 146)
      
      x--
      ---
      ---
      
      xo-
      ---
      ---
      
      xo-
      --x
      ---
      
      xo-
      --x
      -o-
      
      xo-
      --x
      xo-
      
      xo-
      -ox
      xo-
      """
      

      【讨论】:

        【解决方案3】:

        如果我正确理解您的问题,这应该可以,但这不是递归 -

        import itertools
        [zip(p, range(0, 9)) for p in itertools.product([0, 1], repeat=9)]
        

        代码首先生成一个棋盘(9个0或1)-

        itertools.product([0, 1], repeat=9)
        

        然后将索引数据添加到它。

        我建议看看itertools

        【讨论】:

        • 这不好,因为它包含许多无效条目。例如,第一个条目是[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8)]
        • 另外,它只包括按顺序填充单元格的游戏。所以它错过了很多有效的游戏,例如第一个玩家从中间开始。
        • 它包含所有可能的条目 - len([zip(p, range(0, 9)) for p in itertools.product([0, 1], repeat=9)]) = 512= 2^9.
        • 但 512 并不是所有可能的条目。您需要考虑首先填充哪个单元格,第二个,......最后一个。这意味着系数为 9!。这是一个有效游戏的表示:[(0,8), (1,7), (0,6), (1,5), (0,4), (1,3), (0,2), (1,0), (0,0)](请注意,0 和 1 在填充单元格中交替出现)。您能在您的 512 列表中找到它吗?它不存在......此外,您没有解决您的代码会产生错误条目的事实。
        • 看来您可能误解了这个问题。 2^9 将为我们提供 9 个 0 和 1 的所有组合。井字游戏是其中一个小得多的子集,并且 OP 的游戏表示还在每个单元格的数据中包含一个数字,用于“放置 [移动] 的时间”。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-19
        • 2014-04-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-18
        相关资源
        最近更新 更多