2

我还在学习python。我刚刚编写了这个方法来确定玩家是否已经赢得了井字游戏,给定的棋盘状态如下:'[['o','x','x'],['x','o','-'],['x','o','o']]'

def hasWon(board):
  players = ['x', 'o']
  for player in players:
    for row in board:
      if row.count(player) == 3:
        return player
    top, mid, low = board
    for i in range(3):
      if [ top[i],mid[i],low[i] ].count(player) == 3:
        return player
    if [top[0],mid[1],low[2]].count(player) == 3:
        return player
    if [top[2],mid[1],low[0]].count(player) == 3:
        return player
  return None

我突然想到,我多次检查 3 个字符的列表,并且可以将检查重构为自己的方法,如下所示:

def check(list, player):
  if list.count(player) == 3:
    return player

...但后来意识到真正要做的只是改变行,例如:

 if [ top[i],mid[i],low[i] ].count(player) == 3:
    return player

到:

  if check( [top[i],mid[i],low[i]], player ):
    return player

...坦率地说,这似乎并没有太大的改进。你有没有更好的重构方法?或者一般来说是一个更 Pythonic 的选项?我很想听听!

4

7 回答 7

5

我可能会使用

def check(somelist, player):
  return somelist.count(player) == 3

编辑:正如@Andrew 在评论中建议的那样(tx @Andrew!),您可以做得更好,例如:

def check(somelist, player):
  return somelist.count(player) == len(somelist)

无需对3-- 进行硬编码,这也提出了另一个不错的选择:

def check(somelist, player):
  return all(x==player for x in somelist)

它非常直接地读取“列表中的所有项目都相等player”。一般的观点是,通过重构为一个单独的方法,您可以使用该方法的实现——当然,这里的代码非常简单,因此优势同样适中,但在您迁移到更复杂的代码。

正如您已经注意到的那样,无论如何您只需要一个 bool,因此这允许一种更简单的方法——只需返回 bool 表达式而不是if对其进行操作。永远不要使用像您自己的标识符这样的内置名称,这一点很重要list——语言的“有吸引力的麻烦”......;-)。

我的意思是,Python 在其内置函数中使用了许多漂亮、有吸引力的名称,例如 list、bool、sum 等等,因此很容易发现自己不小心将这些名称中的一个用于自己的变量,而什么也没有糟糕的事情似乎发生了...直到您需要将元组转换为列表时,使用明显最好的解决方案,x = list(thetuple)...并最终花费我们的努力来理解和解决由于您已经用于list表示该名称的内置类型之外的任何其他内容。

所以,只要养成使用那些漂亮的内置名称的习惯,除了指示各自的内置名称之外,你会为自己节省很多未来的恶化!-)

回到您的代码,您可能会考虑不解包所提供的简洁board这是一个艰难的决定,因为您的代码可读性很强......但可能看起来有点冗长):

for i in range(3):
  if check([row[i] for row in board], player):
    return player
if check([row[i] for i, row in enumerate(board)], player):
    return player
if check([row[2-i] for i, row in enumerate(board)], player):
    return player

最后,我想我会坚持你的选择——如果有的话,更具可读性,只是稍微冗长一些——但我认为,了解替代方案很好——在这里,列表推导并enumerate生成列表被检查作为“手动编码”三种可能性的替代方案。

于 2010-04-28T14:18:04.463 回答
2

只需在board.

def get_lines(board):
  nums = range(3)
  for i in nums: 
    yield (board[i][j] for j in nums) #cols
  for j in nums: 
    yield (board[i][j] for i in nums) #rows
  yield (board[i][i] for i in nums) #diag \
  yield (board[i][2-i] for i in nums) #diag /

def get_winner(board): #a bit too indented
  for line in get_lines(board): #more expensive, so go through it only once
    for player in 'x', 'o':
      if line == player, player, player: #other way to check victory condition
        return player
  return None

显然这些真的应该是一个board类的方法:)

于 2010-04-28T14:48:04.627 回答
2

你可以使用一个更好的名字,而不是check那个并不能说明什么。经验法则是:如果您可以为代码的和平想一个好名字,那么即使它只是一行代码,将它移到单独的函数中也可能是有益的。allsame可能是这里的替代方案之一。

def winner(board):
    main_diag = [row[i] for i, row in enumerate(board)]
    aux_diag = [row[len(board) - i - 1] for i, row in enumerate(board)]   
    for triple in board + zip(*board) + [main_diag, aux_diag]: 
        if allsame(triple):         
           return triple[0]

def allsame(lst):    
    return all(x == lst[0] for x in lst)
于 2010-04-28T14:55:49.433 回答
1

就我个人而言,我认为你最好的可读性选择是冒泡函数来给你看板的 rows()、columns() 和 diags(),作为列表的列表。然后你可以遍历这些并统一检查。您甚至可以定义 allTriples(),它附加 rows()、columns() 和 diags() 的输出,以便您可以在一个简洁的循环中进行检查。我可能还会让板子成为一个对象,这样这些函数就可以成为对象方法。

于 2010-04-28T14:37:38.210 回答
1

现在来点完全不同的东西:

用九个元素的列表来代表董事会。每个元素可以是 -1 (X)、1 (O) 或 0(空):

WIN_LINES = (
    (0, 1, 2),
    (3, 4, 5),
    (6, 7, 8),
    (0, 3, 6),
    (1, 4, 7),
    (2, 5, 8),
    (2, 4, 6),
    (0, 4, 8),
    )

def test_for_win(board):
    for line in WIN_LINES:
        total = sum(board[point] for point in line)
        if abs(total) == 3:
            return total // 3
    return None

细化:

WIN_LINES = (
    0, 1, 2,
    3, 4, 5,
    6, 7, 8,
    0, 3, 6,
    1, 4, 7,
    2, 5, 8,
    2, 4, 6,
    0, 4, 8,
    )

def test_for_win(board):
    wpos = 0
    for _unused in xrange(8):
        total  = board[WIN_LINES[wpos]]; wpos += 1
        total += board[WIN_LINES[wpos]]; wpos += 1
        total += board[WIN_LINES[wpos]]; wpos += 1
        if total ==  3: return  1
        if total == -3: return -1
    return None
于 2010-04-28T22:45:47.463 回答
0

只是一个想法

def hasWon(board):
  players = ['x', 'o']
  for player in players:
    top, mid, low = board
    game = board + [[ top[i],mid[i],low[i]] for i in range(3)] + [top[0],mid[1],low[2]] +[top[2],mid[1],low[0]]
    if 3 in [l.count(player) for l in game] :
      return player
  return None
于 2010-04-28T14:30:28.470 回答
0

您的解决方案很好 - 正确、可读且易于理解。

不过,如果您想优化速度,我会使用一维数字数组,而不是字符串,并尝试尽可能少地查找每个数字。肯定会有一个看起来非常尴尬的解决方案,您只检查每个字段一次。我现在不想构建这个。:) 如果您想在探索可能移动的整个搜索树的同时实现一个与您对战的 AI,那么类似的事情可能很重要。在那里需要进行有效的赢/输检查。

于 2010-04-28T14:41:02.140 回答