【问题标题】:Python board game with tkinter使用 tkinter 的 Python 棋盘游戏
【发布时间】:2013-07-02 23:53:33
【问题描述】:

我正在尝试创建一个 python 游戏,玩家将单击棋盘以填充其颜色,直到完成,谁拥有更多填充的框就获胜。 如果您单击一个框并且任何相邻的框都被其他玩家颜色填充,它会将颜色更改为您的颜色,我找到了这个板代码但我无法让它填充相邻的框。

import Tkinter as tk

board = [ [None]*10 for _ in range(10) ]

counter = 0

root = tk.Tk()

def on_click(i,j,event):
    global counter
    color = "green" if counter%2 else "red"
    event.widget.config(bg=color)
    board[i][j] = color
    counter += 1


for i,row in enumerate(board):
    for j,column in enumerate(row):
        L = tk.Label(root,text='    ',bg='grey')
        L.grid(row=i,column=j,padx='3',pady='3')
        L.bind('<Button-1>',lambda e i=i,j=j: on_click(i,j,e))

root.mainloop()

问题:我怎样才能使当玩家点击一个框时,已经充满敌人颜色的相邻框也变成红色/绿色?另外,我如何计算具有某种颜色的填充框的数量以确定谁赢了?感谢您的帮助。

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    如果您将标签存储在board 中,则可以使用

    访问其背景颜色
    board[i][j]['bg']
    

    你可以改变背景颜色

    board[i][j].config(bg=...)
    

    甚至

    board[i][j]['bg'] = ...
    

    既然要访问board上某个点的邻居,自然使用for-loops如:

    for ii in range(i - 1, i + 2):
        for jj in range(j - 1, j + 2):
    

    或者,等效但嵌套较少使用itertools.product

    import itertools as IT
    for ii, jj in IT.product(range(i - 1, i + 2), range(j - 1, j + 2)):
    

    现在您可以使用board[ii][jj] 访问邻居,请记住iijj 可能是越界索引。我们可以使用if-statement 来处理越界索引:

    if ii<0 or ii>=rows or jj<0 or jj>=cols: continue
    

    利用上面的两个想法, 您可以使用collections.Counter 计算红色、绿色和灰色方块的数量:

    import collections
    collections.Counter(
            board[i][j]['bg'] for i, j in IT.product(range(rows), range(cols)))
    

    import Tkinter as tk
    import itertools as IT
    import collections
    
    cols, rows = 3, 3
    board = [[None] * cols for _ in range(rows)]    
    other = {'green': 'red', 'red': 'green'}
    
    player = 'red'   
    
    def on_click(event, i, j):
        global player
        board[i][j]['bg'] = player
        for ii, jj in IT.product(range(i - 1, i + 2), range(j - 1, j + 2)):
            if ii<0 or ii>=rows or jj<0 or jj>=cols: continue
            neighbor = board[ii][jj]
            if neighbor['bg'] != 'grey' and (ii, jj) != (i, j):
                neighbor['bg'] = other[neighbor['bg']]
        check_for_winner()
        player = other[player]
    
    def check_for_winner():
        s = score()
        if s['red'] + s['green'] == cols*rows:
            # every box filled
            winner = max(s, key=s.get)
            print('Winner is: {}'.format(winner))
            root.after(1, flash_winner, winner, 'blue')
    
    def score():
        return collections.Counter(
            board[i][j]['bg'] for i, j in IT.product(range(rows), range(cols)))
    
    def flash_winner(winner, altcolor):
        for i, j in IT.product(range(rows), range(cols)):
            if board[i][j]['bg'] == winner:
                board[i][j]['bg'] = altcolor
        root.after(250, flash_winner, altcolor, winner)
    
    root = tk.Tk()
    for i, j in IT.product(range(rows), range(cols)):
        board[i][j] = L = tk.Label(root, text='    ', bg='grey')
        L.grid(row=i, column=j, padx=3, pady=3)
        L.bind('<Button-1>', lambda e, i=i, j=j: on_click(e, i, j))
    
    root.mainloop()
    

    【讨论】:

    • 非常感谢!现在我得研究一些你以前理解的东西。
    【解决方案2】:

    这需要一段时间!这是我的版本:

    import Tkinter as tk
    import TkMessageBox as messagebox
    
    board = [ [None]*10 for _ in range(10) ]
    
    counter = 0
    root = tk.Tk()
    
    def check_board():
        freespaces = 0
        redspaces = 0
        greenspaces = 0
        for i,row in enumerate(board):
            for j,column in enumerate(row):
                if board[i][j] == "red":
                    redspaces += 1
                elif board[i][j] == "green":
                    greenspaces += 1
                elif board[i][j] == None:
                    freespaces += 1
    
        if freespaces == 0:
            if greenspaces > redspaces:
                winner = "green"
            elif greenspaces < redspaces:
                winner = "red"
            else:
                winner = "draw"
    
            if winner != "draw":
                messagebox.showinfo("Game Over!",winner+" wins!")
            else:
                messagebox.showinfo("Game Over!","The game was a draw!")
    
    
    
    
    def on_click(i,j,event):
        global counter
        if counter < 100:
            if board[i][j] == None:
                color = "green" if counter%2 else "red"
                enemycolor = "red" if counter%2 else "green"
                event.widget.config(bg=color)
                board[i][j] = color
                for k in range(-1,2):
                    for l in range(-1,2):
                        try:
                            if board[i+k][j+l] == enemycolor:
                                board[i+k][j+l] = color
                        except IndexError:
                            pass
                counter += 1
                global gameframe
                gameframe.destroy()
                redraw()
                root.wm_title(enemycolor+"'s turn")
            else:
                messagebox.showinfo("Alert","This square is already occupied!")
            check_board()
    
    
    def redraw():
        global gameframe
        gameframe = tk.Frame(root)
        gameframe.pack()
    
        for i,row in enumerate(board):
    
            for j,column in enumerate(row):
                name = str(i)+str(j)
                L = tk.Label(gameframe,text='    ',bg= "grey" if board[i][j] == None else board[i][j])
                L.grid(row=i,column=j,padx='3',pady='3')
                L.bind('<Button-1>',lambda e,i=i,j=j:on_click(i,j,e))
    
    
    redraw()
    root.mainloop()
    

    我每次都重新绘制整个板,因为没有存储对小部件的引用。我看不到在创建每个小部件后访问它们的方法,因为它们都被称为“L”,所以我检查板的颜色值并根据它们是否着色来创建小部件。检查是通过查看正方形周围 3x3 网格中的颜色来完成的。

    我添加了一个检查方块的功能,然后检测它们是否都已满,您应该能够通过研究代码来弄清楚发生了什么,如果您有任何问题,请告诉我。我添加的一个不错的方法是根据轮到谁来更改标题栏!

    编辑:要添加通知当前玩家颜色的标签,请将以下内容添加到重绘函数的末尾!

    global counter
    whosturn = "Green" if counter%2 else "Red"
    turnLbl = tk.Label(gameframe,text=color+"'s Turn")
    turnLbl.grid(row=11,column = 0,columnspan = 10)
    

    【讨论】:

    • 感谢您的帮助!我有个问题。是否可以将关于哪个玩家轮到它的警告作为标签而不是在窗口中放在棋盘顶部,或者它需要对棋盘重新编码并创建一个类?我对董事会的创建方式感到困惑。
    • 是的!更新的答案,将最后一部分添加到重绘函数中。理想情况下,您应该从头开始重新创建整个程序,并在课堂上正确编码以了解更多信息。使用 self 传递变量要好得多。和 Tk vars 而不是使用凌乱的全局变量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多