0

Python を使用して単純な Tic Tac Toe ゲームを構築しようとしていますが、最後の視覚効果を除いてほぼ完成しています。

クリックしたときにキャンバスに表示される 9 つの四角形のいずれかを変更できるようにする必要があります。スタック上の多くの例を検索しました...まあ...どれも機能しませんでしたカーソルをマップするために辞書を使用します:

self.map = {(0, 0): 0, (0, 1): 1, (0, 2): 2, (1, 0): 3, (1, 1): 4, (1, 2) : 5, (2, 0): 6, (2, 1): 7,(2, 2): 8}

左上の最初の四角は 0 です これは機能しないようです: id= self.canvas.create_rectangle() はこれでは機能しません。self.canvas.itemconfig(id,fill='bule')

残りではなく、塗りつぶしまたはクリックされた四角形にアクセスする効果的な方法を探しています。ありがとう

私のコードはすべてここに添付されています:

import tkinter
import random


    class Game(object):
        """
        Enter the class docstring here
        """
        block_size = 100
        def __init__(self, parent):
            parent.title('Tic Tac Toe')
            self.parent = parent

            self.initialize_game()

        def initialize_game(self):
            # These are the initializations that need to happen
            # at the beginning and after restarts
            self.board = [None, None, None, None, None, None, None, None, None]  # game board as a instance variable
            self.map = {(0, 0): 0, (0, 1): 1, (0, 2): 2, (1, 0): 3, (1, 1): 4, (1, 2): 5, (2, 0): 6, (2, 1): 7,
                        (2, 2): 8}  # map to self.board
            self.top_frame = tkinter.Frame(self.parent)
            self.top_frame.pack(side=tkinter.TOP)

            # add restart button on top frame
            restart_button = tkinter.Button(self.top_frame, text='Restart', width=20,
                                            command=self.restart)
            restart_button.pack()  # register restart_button with geometry manager

            # create bottom frame for group the label below
            self.bottom_frame=tkinter.Frame(self.parent)
            self.bottom_frame.pack(side=tkinter.BOTTOM)

            # create label for displaying game result text
            self.my_lbl=tkinter.Label(self.bottom_frame,text=None)
            self.my_lbl.pack()

            # create a canvas to draw our board on the top frame
            self.canvas = tkinter.Canvas(self.top_frame,
                                         width=self.block_size * 3,
                                         height=self.block_size * 3)
            # draw 3x3 visible blocks on the canvas
            for ro in range(3):
                for col in range(3):
                    tag=self.canvas.create_rectangle(self.block_size * col,
                                                 self.block_size * ro,
                                                 self.block_size * (col + 1),
                                                 self.block_size * (ro + 1))
            # bind entire canvas with left click  handler (play function)
            self.canvas.bind("<Button-1>", self.play)
            self.canvas.pack()                  # register canvas with a geometry manager

        def board_full(self):
            if None not in self.board:
                return True  # true for full
            else:
                return False  # false for not full


        def possible_moves(self):
            """return: list of possible moves"""
            possible_moves = []  # list for possible moves
            for i in range(0, 9):
                if self.board[i] is None:  # if cell un-taken
                    possible_moves.append(i)  # append the cell number to list
                else:
                    pass  # cell taken, don't append
            return possible_moves  # return list of possible moves

        def pc_move(self):
            m = True
            while m:
                pc_move = random.randint(0, 8)  # random generate a number from 0 to 8
                if pc_move in self.possible_moves():  # if the number is a possible move
                    self.board[pc_move] = 'O'  # do it
                    m = False  # exit loop
                else:  # not a possible movie
                    continue  # re-do
            return self

        def draw_out(self):
            """to be deleted"""
            print(self.board[0:3])
            print(self.board[3:6])
            print(self.board[6:9])

        def play(self, event):  # This method is invoked when the user clicks on a square.
            """
            when the player clicks on a un-taken square, this method first translate cursor into cell number,
            then update game board and check game result based on condition
            :parameter: click
            :return: updated game object
            """
            print('clicked', event.y, event.x)  # to be deleted
            # after the click: part 1     human play first
            my_move = self.map[(event.y // self.block_size, event.x // self.block_size)]  # map cursor
            if self.board[my_move] is None:  # check if cell is empty
                self.board[my_move] = 'X'  # if cell empty mark X for my play,  PC use O
                #self.canvas.itemconfigure(fill='blue')
                # self.draw_out()              # delete this line later
            else:  # if the cell taken, do nothing until click on empty square
                return None
            #check game result and board full:
            if (self.board_full()is True) or(
                        self.check_game()is not None):
                self.canvas.unbind("<Button-1>")    # when win, lost, tie occur,disable handler
                print(self.check_game())             # DEBUGGING DELETE
            else:
                pass
            # part 2: while not filled, PC make one move right after my move:
            self.possible_moves()  # check possible moves for PC
            self.pc_move()  # pc make move
            self.draw_out()  # DELETE LATER
            # part3: check game result and board full
            if (self.board_full()is True) or(
                        self.check_game()is not None):
                self.canvas.unbind("<Button-1>")    # when win, lost, tie occur,disable handler
                print(self.check_game())             # DEBUGGING DELETE
            else:
                pass
            return self  # when board is filled, return

        def check_game(self):
            """
            Check if the game is won or lost or a tie
            Return:  win, lose, tie, none 
            """
            result=None
            if (self.board[0] == self.board[1] == self.board[2] == 'X') or (
                                self.board[3] == self.board[4] == self.board[5] == 'X') or (
                                self.board[6] == self.board[7] == self.board[8] == 'X') or (
                                self.board[0] == self.board[3] == self.board[6] == 'X') or (
                                self.board[1] == self.board[4] == self.board[7] == 'X') or (
                                self.board[2] == self.board[5] == self.board[8] == 'X') or (
                                self.board[0] == self.board[4] == self.board[8] == 'X') or (
                                self.board[2] == self.board[4] == self.board[6] == 'X'):
                result = 'You win!'  # player win
                self.my_lbl.config(text=result)
            elif (self.board[0] == self.board[1] == self.board[2] == 'O') or (
                                self.board[3] == self.board[4] == self.board[5] == 'O') or (
                                self.board[6] == self.board[7] == self.board[8] == 'O') or (
                                self.board[0] == self.board[3] == self.board[6] == 'O') or (
                                self.board[1] == self.board[4] == self.board[7] == 'O') or (
                                self.board[2] == self.board[5] == self.board[8] == 'O') or (
                                self.board[0] == self.board[4] == self.board[8] == 'O') or (
                                self.board[2] == self.board[4] == self.board[6] == 'O'):
                result = 'You lost!'  # player lose
                self.my_lbl.config(text=result)
            else:
                if self.board_full()is True:
                    result = "It's a tie!"  # tie
                    self.my_lbl.config(text=result)
                else:
                    pass
            return result


        def restart(self):
            """ Reinitialize the game and board after restart button is pressed """
            self.top_frame.destroy()
            self.bottom_frame.destroy()
            self.initialize_game()


    def main():
        root = tkinter.Tk()  # Instantiate a root window
        my_game = Game(root)  # Instantiate a Game object
        root.mainloop()  # Enter the main event loop


    if __name__ == '__main__':
        main()
4

1 に答える 1

1

この問題を解決するには、少なくとも 2 つの方法があります。

まず、 を使用find_closestして、カーソルに最も近いオブジェクトを見つけることができます。このソリューションでは、2 つの点に留意する必要があります。まず、ウィンドウ座標 (イベント オブジェクトが持つ座標) ではなく、キャンバス座標を指定する必要があります。次に、長方形に塗りつぶしの色を指​​定するか、それらが重ならないようにする必要があります。find_closest色付きのピクセルを探すので、塗りつぶしの色がない場合は境界線が見つかるまで検索します。境界線が重なっている場合 (これはあなたの場合です)、間違った正方形を見つける可能性があります (つまり、一番右の正方形の左端が中央の正方形の右端と重なり、重なり順により最も近いと見なされます)。

したがって、長方形に塗りつぶしの色 (例: self.canvas.create_rectangle(..., fill='white')) を指定すると、次のようにカーソルの下の正方形を見つけることができます。

def play(self, event):
    ...
    cx = self.canvas.canvasx(event.x)
    cy = self.canvas.canvasy(event.y)
    cid = self.canvas.find_closest(cx,cy)[0]
    self.canvas.itemconfigure(cid, fill="blue")
    ...

他にできることは、各正方形を で個別にバインドし、バインドtag_bindで数値を関数に渡すことです。繰り返しますが、長方形には塗りつぶしの色が必要ですが、理由は異なります。クリックは不透明な領域でのみ登録されるため、塗りつぶしの色がないと、クリックが登録されるのは境界線をクリックしたときだけです。

def initialize_game(self):
    ...
    tag=self.canvas.create_rectangle(self.block_size * col,
                                     self.block_size * ro,
                                     self.block_size * (col + 1),
                                     self.block_size * (ro + 1), fill="white")
    self.canvas.tag_bind(tag, "<1>", lambda event, tag=tag: self.play(event, tag))
    ...

...
def play(self, event, tag):
    ...
    self.canvas.itemconfigure(tag, fill="blue")
    ...
于 2014-12-03T22:55:50.657 に答える