From ab0c799407fc6992f9b3024aef16fd277edd9591 Mon Sep 17 00:00:00 2001 From: AngelVI13 Date: Wed, 1 May 2019 15:14:19 +0300 Subject: [PATCH] - Changed handling of mouse press. Now it is monitored in mainloop on MOUSE_UP and not during drawing of subcells - Added highlighting of grid when a result is present - Added handling of game over and automatic restart afterwards. - Moved some magic numbers to constants - Removed highlighting of cell on mouse hover --- gui/colors.py | 2 ++ gui/constants.py | 20 ++++++++-------- gui/gui_app.py | 61 ++++++++++++++++++++++++++++++++++++++++-------- gui/gui_board.py | 26 ++++++++++----------- 4 files changed, 75 insertions(+), 34 deletions(-) diff --git a/gui/colors.py b/gui/colors.py index 44c6aeb..567b647 100644 --- a/gui/colors.py +++ b/gui/colors.py @@ -7,6 +7,8 @@ RED_HIGHLIGHT = (255, 219, 212) GREEN = (0, 200, 0) BLUE = (59, 214, 255) +BLUE_HIGHLIGHT = (164, 217, 221) +GREY = (170, 191, 193) YELLOW = (0, 250, 250) BRIGHT_RED = (255, 0, 0) diff --git a/gui/constants.py b/gui/constants.py index 42bf702..2740f08 100644 --- a/gui/constants.py +++ b/gui/constants.py @@ -2,6 +2,7 @@ from itertools import count from gui.colors import * +from board.constants import PLAYER_X, PLAYER_O, DRAW DISPLAY_WIDTH = 600 @@ -40,48 +41,47 @@ # offset for main grid from main window OFFSET_X, OFFSET_Y = (DISPLAY_WIDTH - BOARD_WIDTH) / 2, (DISPLAY_HEIGHT - BOARD_HEIGHT) / 2 -HIGHLIGHT_COLOR = GREEN +GRID_RESULT_COLORS = { + PLAYER_X: RED_HIGHLIGHT, + PLAYER_O: BLUE_HIGHLIGHT, + DRAW: GREY +} + BORDER_COLOR = BLACK BOX_COLOR = WHITE # In these parameters the values for x & y will the added to the current position computed for each grid cell MAIN_GRID_DRAW_PARAMETERS = [ {'border': Grid.TOP_LEFT, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X, 'y': OFFSET_Y, 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, {'border': Grid.TOP_MIDDLE, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X + BOARD_WIDTH / 3, 'y': OFFSET_Y, 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, {'border': Grid.TOP_RIGHT, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X + BOARD_WIDTH * (2 / 3), 'y': OFFSET_Y, 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, # middle row {'border': Grid.MIDDLE_LEFT, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X, 'y': OFFSET_Y + BOARD_HEIGHT / 3, 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, {'border': Grid.MIDDLE_MIDDLE, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X + BOARD_WIDTH / 3, 'y': OFFSET_Y + BOARD_HEIGHT / 3, 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, {'border': Grid.MIDDLE_RIGHT, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X + BOARD_WIDTH * (2 / 3), 'y': OFFSET_Y + BOARD_HEIGHT / 3, 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, # bottom row {'border': Grid.BOTTOM_LEFT, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X, 'y': OFFSET_Y + BOARD_HEIGHT * (2 / 3), 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, {'border': Grid.BOTTOM_MIDDLE, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X + BOARD_WIDTH / 3, 'y': OFFSET_Y + BOARD_HEIGHT * (2 / 3), 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, {'border': Grid.BOTTOM_RIGHT, 'border_colour': BORDER_COLOR, 'box_colour': BOX_COLOR, - 'highlight_colour': HIGHLIGHT_COLOR, 'x': OFFSET_X + BOARD_WIDTH * (2 / 3), 'y': OFFSET_Y + BOARD_HEIGHT * (2 / 3), 'w': MAIN_BOX_WIDTH, 'h': MAIN_BOX_HEIGHT}, ] + +FRAMES_PER_SECOND = 60 +PAUSE_BEFORE_GAME_RESTART = 4 # seconds diff --git a/gui/gui_app.py b/gui/gui_app.py index 01e3ff5..5cc312e 100644 --- a/gui/gui_app.py +++ b/gui/gui_app.py @@ -1,3 +1,4 @@ +import time from itertools import cycle, chain from gui.gui_board import * @@ -7,13 +8,16 @@ class Gui(GuiBoard): def __init__(self): super(Gui, self).__init__() self.board = UltimateBoard() - self.on_cell_clicked = self.subcell_clicked self.allowed_cells = set() + self.grids_with_result = set() # all grids that have a result on them - def subcell_clicked(self, x, y, w, h, board, move): - # do not provide player just yet, only do so when we are sure we can click the cell - cell = Cell(x, y, w, h, player=None, board_idx=board, cell_idx=move) + def restart_game(self): + self.board = UltimateBoard() + self.allowed_cells = set() + self.grids_with_result = set() + def subcell_clicked(self, cell): + # do not provide player just yet, only do so when we are sure we can click the cell if cell in self.clicked_cells: # if cell already clicked -> do nothing return @@ -22,11 +26,18 @@ def subcell_clicked(self, x, y, w, h, board, move): cell.player = self.board.playerJustMoved * -1 self.clicked_cells.add(cell) + + move, board = cell.cell_idx, cell.board_idx self.board.make_move(move, board) - print(board, move) - print(self.board) self.allowed_cells = self.find_allowed_cells() + result = self.board.pos[board].get_result() + if result is not None: + for grid in self.all_grids: + if grid.board_idx == board: + grid.player = result # add the player who won to the grid that he won + self.grids_with_result.add(grid) + def find_allowed_cells(self): allowed_cells = set() # clear currently allowed moves @@ -40,8 +51,20 @@ def find_allowed_cells(self): def draw_allowed_moves(self, color): for cell in self.allowed_cells: - pygame.draw.rect(self.gameDisplay, color, - (cell.pos_x, cell.pos_y, cell.width, cell.height)) + pygame.draw.rect(self.gameDisplay, color, (cell.pos_x, cell.pos_y, cell.width, cell.height)) + + def click_cell_under_mouse(self, pos): + mouse_x, mouse_y = pos + for cell in self.all_cells: + if cell.pos_x < mouse_x < cell.pos_x + cell.width and cell.pos_y < mouse_y < cell.pos_y + cell.height: + self.subcell_clicked(cell) + break # don't bother finishing up the loop -> break on match + + def draw_results(self): # todo this is the same as draw_all moves? parameterize + for grid in self.grids_with_result: + # print(grid.player, GRID_RESULT_COLORS[grid.player]) + pygame.draw.rect(self.gameDisplay, GRID_RESULT_COLORS[grid.player], + (grid.pos_x, grid.pos_y, grid.width, grid.height)) def game_loop(self): # set up an endless cycle of B values (rgB) for highlighting moves @@ -53,6 +76,9 @@ def game_loop(self): # print(event) if event.type == pygame.QUIT: self.quit_game() + elif event.type == pygame.MOUSEBUTTONUP: + pos = pygame.mouse.get_pos() + self.click_cell_under_mouse(pos) brightness = next(brightness_iter) highlight = (255, 255, brightness) # yellow highlight used to accent available moves @@ -60,14 +86,29 @@ def game_loop(self): self.gameDisplay.fill(WHITE) self.draw_main_grid() - # todo check for game end here as well. Mind not have allowed cells because game is over + # todo check for game end here as well. Might not have allowed cells because game is over if not self.allowed_cells: self.allowed_cells = self.find_allowed_cells() self.draw_clicked_cells() + self.draw_results() self.draw_allowed_moves(highlight) pygame.display.update() - self.clock.tick(60) + self.clock.tick(FRAMES_PER_SECOND) + + # todo factor out repetitive code below + if self.board.get_result(player_jm=PLAYER_X) == WIN: + self.message_display(text='X won!') + time.sleep(PAUSE_BEFORE_GAME_RESTART) + self.restart_game() + elif self.board.get_result(player_jm=PLAYER_O) == WIN: + self.message_display(text='O won!') + time.sleep(PAUSE_BEFORE_GAME_RESTART) + self.restart_game() + elif self.board.get_result(player_jm=PLAYER_O) == DRAW: + self.message_display(text='Tie!') + time.sleep(PAUSE_BEFORE_GAME_RESTART) + self.restart_game() if __name__ == '__main__': diff --git a/gui/gui_board.py b/gui/gui_board.py index be66104..db19b80 100644 --- a/gui/gui_board.py +++ b/gui/gui_board.py @@ -42,7 +42,7 @@ class GuiBoard: clicked_cells = set() # a set of all clicked cells all_cells = set() # a set of all created cells - on_cell_clicked = None # callback method for action on cell clicked + all_grids = set() # a set of all subgrids def __init__(self): pygame.init() @@ -89,13 +89,11 @@ def quit_game(): pygame.quit() quit() - def draw_subcell(self, border, border_colour, box_colour, highlight_colour, x, y, w, h, grid_idx, action=None): + def draw_subcell(self, border, border_colour, box_colour, x, y, w, h, grid_idx): # Draw bounding box of cell mod_x, mod_y, mod_w, mod_h = BORDERS[border] pygame.draw.rect(self.gameDisplay, border_colour, (x, y, w, h)) - mouse = pygame.mouse.get_pos() - click = pygame.mouse.get_pressed() inner_x, inner_y, inner_w, inner_h = x + mod_x, y + mod_y, w + mod_w, h + mod_h # keep track of all cells on the board @@ -104,15 +102,11 @@ def draw_subcell(self, border, border_colour, box_colour, highlight_colour, x, y if cell not in self.all_cells: self.all_cells.add(cell) - if x + w > mouse[0] > x and y + h > mouse[1] > y: - pygame.draw.rect(self.gameDisplay, highlight_colour, (inner_x, inner_y, inner_w, inner_h)) + # draw inner box of cell (main content) + pygame.draw.rect(self.gameDisplay, box_colour, (inner_x, inner_y, inner_w, inner_h)) - if click[0] == 1 and action is not None: - action(inner_x, inner_y, inner_w, inner_h, grid_idx, border) - else: - pygame.draw.rect(self.gameDisplay, box_colour, (inner_x, inner_y, inner_w, inner_h)) - - def draw_sub_grid(self, border, border_colour, box_colour, highlight_colour, x, y, w, h): + def draw_sub_grid(self, border, border_colour, box_colour, x, y, w, h): + # todo maybe turn this into a method to clean up the logic # draw bounding box of subgrid mod_x, mod_y, mod_w, mod_h = BORDERS[border] pygame.draw.rect(self.gameDisplay, border_colour, (x, y, w, h)) @@ -121,6 +115,11 @@ def draw_sub_grid(self, border, border_colour, box_colour, highlight_colour, x, w, h = w + mod_w, h + mod_h pygame.draw.rect(self.gameDisplay, box_colour, (x, y, w, h)) + # keep track of all subgrids. NOTE: here 'border' is the index of the subgrid on the main grid. + grid = Cell(pos_x=x, pos_y=y, width=w, height=h, player=None, board_idx=border, cell_idx=None) + if grid not in self.all_grids: + self.all_grids.add(grid) + # calculate inner box for subgrid cell_size = min(w, h) x, y = x + 2 * SUB_GRID_PADDING, y + 2 * SUB_GRID_PADDING @@ -148,8 +147,7 @@ def draw_sub_grid(self, border, border_colour, box_colour, highlight_colour, x, for position in positions: # here border is the index of which grid all of the cells are part of self.draw_subcell(**position, border_colour=border_colour, box_colour=box_colour, w=cell_width_, - h=cell_height_, highlight_colour=highlight_colour, grid_idx=border, - action=self.on_cell_clicked) + h=cell_height_, grid_idx=border) def draw_main_grid(self): for parameters in MAIN_GRID_DRAW_PARAMETERS: