2

我正在尝试创建一个 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()

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

4

2 回答 2

2

如果将标签存储在 中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],记住它ii或者jj可能是一个越界索引。我们可以使用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()
于 2013-07-03T01:22:28.167 回答
1

这花了一段时间!这是我的版本:

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)
于 2013-07-03T01:54:43.993 回答