从单击按钮获取Tic Tac Toe(在Python中使用Tkinter)

时间:2016-08-09 18:17:59

标签: python tkinter

我试图使用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"按0NaN为空方格。

使用with_GUI=True,我得到一系列未标记的按钮,这些按钮将获得标签" X"单击时将坐标打印到命令行(见下文)。

enter image description here enter image description here

但是,我正在努力了解如何从make_move类中的GUI函数返回到get_move类中的HumanPlayer函数。 make_move要求tk.Button的实例作为输入,如何让最近点击的Button可用于此课程?

1 个答案:

答案 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

由于TigerhawkT3的评论,

编辑1 编辑的代码