我试图使用Tkinter使用简单的GUI编写Tic Tac Toe程序。这是迄今为止的结果:
import numpy as np
import matplotlib.pyplot as plt
import Tkinter as tk
class Game:
def __init__(self, player1, player2, with_GUI=False):
self.player1 = player1
self.player2 = player2
self.current_player = player1
self.board = Board()
self.with_GUI = with_GUI
if self.with_GUI:
master = tk.Tk()
self.GUI = GUI(master)
self.board.GUI = self.GUI
self.player1.GUI = self.GUI
self.player2.GUI = self.GUI
def play(self):
self.board.render()
while not self.board.over():
self.play_turn()
self.declare_outcome()
def play_turn(self):
move = self.current_player.get_move()
mark = self.current_player.mark
self.board.place_mark(move, mark)
self.switch_players()
self.board.render()
def switch_players(self):
if self.current_player == self.player1:
self.current_player = self.player2
else:
self.current_player = self.player1
def declare_outcome(self):
if self.board.winner() == 1:
print "Player 1 wins!"
elif self.board.winner() == 0:
print "Player 2 wins!"
else:
print "Cat's game."
class Board:
def __init__(self, grid=np.ones((3,3))*np.nan, GUI=None):
self.grid = grid
self.GUI = GUI
def winner(self):
rows = [self.grid[i,:] for i in range(3)]
cols = [self.grid[:,j] for j in range(3)]
diag = [np.array([self.grid[i,i] for i in range(3)])]
cross_diag = [np.array([self.grid[2-i,i] for i in range(3)])]
lanes = np.concatenate((rows, cols, diag, cross_diag)) # A "lane" is defined as a row, column, diagonal, or cross-diagonal
any_lane = lambda x: any([np.array_equal(lane, x) for lane in lanes]) # Returns true if any lane is equal to the input argument "x"
if any_lane(np.ones(3)):
return 1
elif any_lane(np.zeros(3)):
return 0
def over(self):
return (not np.any(np.isnan(self.grid))) or (self.winner() is not None)
def place_mark(self, pos, mark):
num = self.mark2num(mark)
self.grid[tuple(pos)] = num
def mark2num(self, mark):
if mark == "X":
return 1
elif mark == "O":
return 0
else:
print "The player's mark must be either 'X' or 'O'."
def render(self):
if self.GUI is None:
print self.grid
else:
pass
class HumanPlayer:
def __init__(self, mark, GUI=None):
self.mark = mark
self.GUI = GUI
def get_move(self, board=Board()):
if self.GUI is None:
move_string = input("Where would you like to move? (row, column) ")
move = tuple(move_string)
if not self.empty(move, board):
print "That square is already occupied.\n"
return self.get_move(board)
else:
return tuple(move_string)
else:
# return GUI.make_move()
pass
def empty(self, move, board):
return np.isnan(board.grid[move])
class GUI:
def __init__(self, master):
frame = tk.Frame(master)
frame.pack()
self.buttons = [[None for _ in range(3)] for _ in range(3)]
for i in range(3):
for j in range(3):
self.buttons[i][j] = tk.Button(frame, height=3, width=3, text="", command=lambda i=i, j=j: self.make_move(self.buttons[i][j]))
self.buttons[i][j].grid(row=i, column=j)
def make_move(self, button):
if button["text"] == "":
button.configure(text="X")
info = button.grid_info()
move = (info["row"], info["column"])
print move
return move
player1 = HumanPlayer(mark="X")
player2 = HumanPlayer(mark="O")
game = Game(player1, player2, with_GUI=True)
game.play()
该程序在没有GUI的情况下工作:如果我在倒数第二行中将with_GUI
设置为False
,它将使用一个非常简单的命令行界面,其中该板由3x3 Numpy数组表示,其中" X"标记由1
表示," O"按0
,NaN
为空方格。
使用with_GUI=True
,我得到一系列未标记的按钮,这些按钮将获得标签" X"单击时将坐标打印到命令行(见下文)。
但是,我正在努力了解如何从make_move
类中的GUI
函数返回到get_move
类中的HumanPlayer
函数。 make_move
要求tk.Button
的实例作为输入,如何让最近点击的Button
可用于此课程?
答案 0 :(得分:-1)
要获取Button本身,请不要使用lambda绑定。
使用默认绑定并使用事件对象。
使用此方法,您无需在创建时传递row,col。
import Tkinter as tk
class someclass(tk.Frame):
def __init__(self, *args, **kwargs):
# the init, etc...
btn = tk.Button(self, text='bla')
btn.configure(command=lambda button=btn: self.callback(button))
btn.grid()
def callback(self, button):
btn = button
# do stuff with the button
留下一个评论:
请不要导入您不使用的内容(在您的情况下为matplotlib.pyplot
)
编辑1 编辑的代码