Python - 确定井字游戏赢家

Posted

技术标签:

【中文标题】Python - 确定井字游戏赢家【英文标题】:Python - Determine Tic-Tac-Toe Winner 【发布时间】: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 次来赢得井字游戏吗?根据一个单元格的价值,您如何获胜? 【参考方案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】:

您可以通过创建一个generator、lines() 来生成所有 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种状态

    如果还没有填满,则为0(游戏有可能在5步内结束) 如果用'X'填充则为1 -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 不太可能做出回应,您必须付出更多努力来提供完整的解决方案,并让我们其他人相信您的解决方案优于其他解决方案。

以上是关于Python - 确定井字游戏赢家的主要内容,如果未能解决你的问题,请参考以下文章

java井字棋游戏 多人版哦

用React写井字棋游戏

关于三子棋(井字棋)小游戏

Python小游戏 井字棋(人机对战,玩家对战)

井字游戏最小最大算法 Python。计算机算法并不稳健

python基础游戏之井字棋(讲解)