Представляя меню несколько раз с Tkinter - PullRequest
0 голосов
/ 18 февраля 2020

Я пытаюсь создать простую игру «Connect 4». В этой игре я бы хотел, чтобы меню с радиокнопками отображалось пользователю на каждом шагу, чтобы он / она мог выбрать следующий ход.
Я пытался поместить меню в какое-то время "l" 1008 *, но он просто не выполняет никаких действий. Я не знаю, как это исправить, потому что я точно не понимаю, как работает tkinter. Я видел несколько других вопросов, касающихся этой темы, но я все еще не могу понять, как это исправить.

Ниже приведен код, а также два класса, которые я использовал вместе с ним. Меню, которое я пытаюсь представить в al oop, находится в 'present_columns_choice', которое используется в al oop в 'handle_two_humans'

Буду признателен за любую помощь. Спасибо!

from game import *
from tkinter import *

HUMAN = 0
COMPUTER = 1
GRID_SIZE = 50
NUM_ROWS = 6
NUM_COLUMNS = 7
COLOR_A = "red"
COLOR_B = "blue"


class GameGUI:

def __init__(self, root):
    self.__root = root
    self.__player_A = None
    self.__player_B = None

def assign_player(self, player, identity):
    if player == "player A":
        self.__player_A = identity
    elif player == "player B":
        self.__player_B = identity
    print("Player A is:  " + str(self.__player_A))
    print("Player B is:  " + str(self.__player_B))

def present_player_choice(self):
    self.ask_choose_player("player A")
    self.ask_choose_player("player B")
    Button(self.__root, text="OK", command=quit).pack(anchor=W)

def ask_choose_player(self, player):
    Label(self.__root, text="Who would you like to play " + player + "?").pack(anchor=W)
    var = IntVar()
    Radiobutton(self.__root, text="human", variable=var,
                command=lambda:self.assign_player(player, HUMAN), value=1).pack(anchor=W)
    Radiobutton(self.__root, text="computer", variable=var,
                command=lambda:self.assign_player(player, COMPUTER), value=2).pack(anchor=W)

def get_playerA(self):
    return self.__player_A

def get_playerB(self):
    return self.__player_B

def handle_two_humans(self):

    game = Game()
    canvas_width = GRID_SIZE*NUM_COLUMNS
    canvas_height = GRID_SIZE*NUM_ROWS
    canvas = Canvas(self.__root, width=canvas_width, height=canvas_height)
    canvas.pack()
    for row_ind in range(NUM_ROWS):
        for column_ind in range(NUM_COLUMNS):
            canvas.create_rectangle(column_ind*GRID_SIZE, row_ind*GRID_SIZE,
                                    (column_ind+1)*GRID_SIZE, (row_ind+1)*GRID_SIZE)
    while not IS_GAME_WON:
        self.present_columns_choice(game, canvas)

def add_disc(self, game, column, canvas):

    current_player = game.get_current_player()
    if current_player == PLAYER_A:
        self.fill_square(game, canvas, COLOR_A, column)
    elif current_player == PLAYER_B:
        self.fill_square(game, canvas, COLOR_B, column)
    game.make_move(column)

def present_columns_choice(self, game, canvas):
    columns = game.get_board().get_available_columns()
    var = IntVar()
    Label(self.__root, text="The following columns are still available.  "
                            "Where would you like to place your disc?").pack(anchor=W)
    for ind, column in enumerate(columns):
        shown_column = column+1
        Radiobutton(self.__root, text=shown_column, padx=20, variable=var, value=ind,
                    command=lambda column=column: self.add_disc(game, column, canvas)).pack(anchor=W)
    Button(self.__root, text="OK", command=quit).pack(anchor=W)

def fill_square(self, game, canvas, color, column):
    """  Fills square of column chosen by player.  """
    row = game.get_board().get_available_row(column)
    canvas.create_rectangle(column*GRID_SIZE, row*GRID_SIZE, (column+1)*GRID_SIZE,
                            (row+1)*GRID_SIZE, fill = color)

if __name__ == '__main__':

root = Tk()
gui = GameGUI(root)
gui.handle_two_humans()
mainloop()



Here's the Game class, which was used here:


from board import *

PLAYER_A = 1
PLAYER_B = 2
INITIAL_ROW = 0
INITIAL_COLUMN = 0
FINAL_ROW = 5
FINAL_COLUMN = 6
ILLEGAL_LOCATION_MSG = "Illegal location."
ILLEGAL_MOVE_MSG = "Illegal move."
IS_GAME_WON = False
WINNER = None

class Game:

def __init__(self):
    self.__current_player = PLAYER_A
    self.__board = Board()
    self.__is_game_won = False
    self.__winner = None

def make_move(self, column):
    """  Makes move and updates board and current player, if column is a valid choice and game is ongoing.  """
    possible_winner = self.__current_player
    if self.__board.is_col_illegal(column) or self.__is_game_won:
        raise Exception(ILLEGAL_MOVE_MSG)
    self.do_move(column)
    if self.__board.is_win(column):
        self.__is_game_won = True
        self.__winner = possible_winner

def do_move(self, column):
    """  Actual implementation of the move.  """
    if self.__current_player == PLAYER_A:
        self.__board.update_board(column, PLAYER_A)
        self.__current_player = PLAYER_B
    elif self.__current_player == PLAYER_B:
        self.__board.update_board(column, PLAYER_B)
        self.__current_player = PLAYER_A

def get_winner(self):
   """  Returns winner, or None if there is none.  """
   return self.__winner

def get_player_at(self, row, col):
    """  Returns the player whose disc is at the given position in the game.  """
    if row < INITIAL_ROW or row > FINAL_ROW or col < INITIAL_COLUMN or col > FINAL_COLUMN:
        raise Exception(ILLEGAL_LOCATION_MSG)
    return self.__board.get_board()[row][col]

def get_current_player(self):
    """  Returns current_player.  """
    return self.__current_player

def get_board(self):
    """  Returns board."""
    return self.__board


And here's Board, which is used in Game:


NUM_ROWS = 6
NUM_COLUMNS = 7
INITIAL_VALUE = None
WIN_COUNT = 4
FIRST_ROW = 5
LAST_ROW = 0


class Board:

def __init__(self):
    self.__playboard = []
    self.__available_rows_list = NUM_COLUMNS*[NUM_ROWS-1]
    initial_row = NUM_COLUMNS * [INITIAL_VALUE]
    for i in range(NUM_ROWS):
        self.__playboard.append(initial_row.copy())

def get_available_columns(self):
    """  Returns all columns that still have empty space in them.  """
    available_columns = []
    for col in range(len(self.__available_rows_list)):
        if self.__available_rows_list[col] >= 0:
            available_columns.append(col)
    return available_columns

def get_playboard(self):
    """  Returns board.  """
    return self.__playboard

def update_board(self, col, val):
    """  Updates current status of board.  """
    row = self.__available_rows_list[col]
    self.__playboard[row][col] = val
    self.__update_row(col)

def __update_row(self, col):
    """  Updates available_row_list.  """
    self.__available_rows_list[col] = self.__available_rows_list[col] - 1


def __is_col_available(self, col):
    """  Checks if given col has empty spaces left on the playboard.  """
    if self.__available_rows_list[col] < 0:
        return False
    return True

def __is_col_exist(self, col):
    """  Checks if given column is within the capacity of the playboard.  """
    if col < 0 or col >= NUM_COLUMNS:
        return False
    return True

def is_col_illegal(self, col):
    """  Checks if given column is an illegal option.  """
    if not self.__is_col_available(col) or not self.__is_col_exist(col):
        return True
    return False

def print_playboard(self):
    for row in self.__playboard:
        print(row)

def is_win(self, col):
    """  Checks if current state of board resulted in a win.  """
    row = self.__available_rows_list[col]+1
    if self.__check_vertical_win(row, col) or self.__check_horizontal_win(row, col) or \
            self.__check_decreasing_diagonal_win(row, col) or self.__check_increasing_diagonal_win(row, col):
                return True
    return False

def __check_increasing_diagonal_win(self, original_row, original_col):
    """  Checks if player has won in the increasing diagonal direction.  """
    count = 1
    player = self.__playboard[original_row][original_col]
    col = original_col + 1
    row = original_row - 1
    while self.__is_col_exist(col) and row >= LAST_ROW and self.__playboard[row][col] == player:
        count = count + 1
        if count == WIN_COUNT:
            return True
        col = col + 1
        row = row - 1
    # Then: undergo same process, this time in the opposite direction.
    col = original_col - 1
    row = original_row + 1
    while self.__is_col_exist(col) and row <= FIRST_ROW and self.__playboard[row][col] == player:
        count = count + 1
        if count == WIN_COUNT:
            return True
        col = col - 1
        row = row + 1

def __check_decreasing_diagonal_win(self, original_row, original_col):
    """  Checks if player has won in the decreasing diagonal direction.  """
    count = 1
    player = self.__playboard[original_row][original_col]
    col = original_col + 1
    row = original_row + 1
    while self.__is_col_exist(col) and row <= FIRST_ROW and self.__playboard[row][col] == player:
        count = count + 1
        if count == WIN_COUNT:
            return True
        col = col + 1
        row = row + 1
    # Then: undergo same process, this time in the opposite direction.
    col = original_col - 1
    row = original_row - 1
    while self.__is_col_exist(col) and row >= LAST_ROW and self.__playboard[row][col] == player:
        count = count + 1
        if count == WIN_COUNT:
            return True
        col = col - 1
        row = row - 1

def __check_vertical_win(self, original_row, col):
    """  Checks if player has won in the horizontal direction.  """
    count = 1
    player = self.__playboard[original_row][col]
    row = original_row + 1
    while row <= FIRST_ROW and self.__playboard[row][col] == player:
        count = count + 1
        if count == WIN_COUNT:
            return True
        row = row + 1

def __check_horizontal_win(self, row, original_col):
    """  Checks if player has won in the horizontal direction.  """
    count = 1
    player = self.__playboard[row][original_col]
    col = original_col + 1
    while self.__is_col_exist(col) and self.__playboard[row][col] == player:
        count = count + 1
        if count == WIN_COUNT:
            return True
        col = col + 1
    # Then: undergo same process, this time in the opposite direction (the left).
    col = original_col - 1
    while self.__is_col_exist(col) and self.__playboard[row][col] == player:
        count = count + 1
        if count == WIN_COUNT:
            return True
        col = col - 1

def get_available_row(self, column):
    """  Returns the row which will be filled if you choose this column.  """
    return self.__available_rows_list[column]

1 Ответ

1 голос
/ 18 февраля 2020

В то время как l oop внутри handle_two_humans() заблокирует tkinter mainl oop.

Поскольку вы выполняете перемещение, если в меню выбрана радиокнопка, почему бы вам просто не сделать двигаться при нажатии на доску?

Создать функцию check_move() в GameGUI классе:

def check_move(self, game, column, canvas):
    if not game.get_winner():
        self.add_disc(game, column, canvas)

И изменить handle_two_humands(), как показано ниже:

def handle_two_humans(self):
    game = Game()
    canvas_width = GRID_SIZE*NUM_COLUMNS
    canvas_height = GRID_SIZE*NUM_ROWS
    canvas = Canvas(self.__root, width=canvas_width, height=canvas_height)
    canvas.pack()
    for row_ind in range(NUM_ROWS):
        for column_ind in range(NUM_COLUMNS):
            canvas.create_rectangle(column_ind*GRID_SIZE, row_ind*GRID_SIZE,
                                    (column_ind+1)*GRID_SIZE, (row_ind+1)*GRID_SIZE)
    # bind mouse click event to perform move
    # e.x//GRID_SIZE will get the clicked column
    canvas.bind('<Button-1>', lambda e: self.check_move(game, e.x//GRID_SIZE, canvas))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...