class Section(Dialog): def __init__(self, master, title: str, gameplay: FourInARowGameplay): Dialog.__init__(self, master, bg_color=BLUE) self.master = master self.gameplay = gameplay arrow = pygame.transform.flip(RESOURCES.IMG["arrow"], True, False) self.button_back = ImageButton(self, img=arrow, width=50, callback=self.stop, active_offset=(0, 5), highlight_color=YELLOW) self.title = Text(title) def place_objects(self) -> None: self.button_back.move(left=self.frame.left + 5, top=self.frame.top + 5) self.title.move(centerx=self.frame.centerx, top=self.frame.top + 10) def on_start_loop(self) -> None: self.frame.size = self.frame.width, self.master.buttons.height self.frame.move(right=0, top=self.master.buttons.top) self.frame.animate_move(self, speed=50, at_every_frame=self.place_objects, left=10, top=self.frame.top) def on_quit(self) -> None: self.frame.animate_move(self, speed=50, at_every_frame=self.place_objects, right=0, top=self.frame.top)
def __init__(self, master): Window.__init__(self, master=master, bg_music=master.bg_music) self.bg = RectangleShape(*self.size, (0, 0, 0, 170)) self.frame = RectangleShape(0.50 * self.width, 0.50 * self.height, GREEN, outline=3) self.text = Text("Are you sure you want\nto buy this car ?", (RESOURCES.FONT["algerian"], 60), justify=Text.T_CENTER) self.button_yes = Button(self, "Yes", self.text.font, bg=GREEN, hover_bg=GREEN_LIGHT, active_bg=GREEN_DARK, hover_sound=RESOURCES.SFX["select"], on_click_sound=RESOURCES.SFX["validate"], highlight_color=YELLOW, callback=self.buy) self.button_red_cross = ImageButton( self, img=RESOURCES.IMG["red_cross"], active_img=RESOURCES.IMG["red_cross_hover"], hover_sound=RESOURCES.SFX["select"], on_click_sound=RESOURCES.SFX["back"], callback=self.stop, highlight_color=YELLOW) self.buyed = False self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"]))
def __init__(self, player_id: int): Window.__init__(self, bg_color=(0, 200, 255), bg_music=RESOURCES.MUSIC["setup"]) self.gameplay = Gameplay(player_id) self.count_down = CountDown(self, 60, "Time left: {seconds}", font=(None, 70), color=WHITE) self.start_count_down = lambda: self.count_down.start( at_end=self.timeout) if self.client_socket.connected() else None params_for_all_buttons = { "bg": GREEN, "hover_bg": GREEN_LIGHT, "active_bg": GREEN_DARK, "highlight_color": YELLOW } self.button_back = ImageButton(self, RESOURCES.IMG["arrow_blue"], **params_for_all_buttons, rotate=180, size=50, callback=self.stop) self.navy_grid = DrawableListVertical(offset=0, bg_color=(0, 157, 255)) for i in range(NB_LINES_BOXES): box_line = DrawableListHorizontal(offset=0) for j in range(NB_COLUMNS_BOXES): box_line.add(BoxSetup(self, size=BOX_SIZE, pos=(i, j))) self.navy_grid.add(box_line) self.ships_list = DrawableListVertical(offset=70, justify="left") for ship_name, ship_infos in SHIPS.items(): ship_line = DrawableListHorizontal(offset=ship_infos["offset"]) for _ in range(ship_infos["nb"]): ship_line.add(ShipSetup(self, ship_name, ship_infos["size"])) self.ships_list.add(ship_line) option_size = 50 self.button_restart = Button.withImageOnly( self, Image(RESOURCES.IMG["reload_blue"], size=option_size), callback=self.reinit_all_ships, **params_for_all_buttons) self.button_random = Button.withImageOnly(self, Image( RESOURCES.IMG["random"], size=option_size), callback=self.shuffle, **params_for_all_buttons) self.button_play = Button(self, "Play", font=(None, 40), callback=self.play, **params_for_all_buttons)
def __init__(self, master, title: str, gameplay: FourInARowGameplay): Dialog.__init__(self, master, bg_color=BLUE) self.master = master self.gameplay = gameplay arrow = pygame.transform.flip(RESOURCES.IMG["arrow"], True, False) self.button_back = ImageButton(self, img=arrow, width=50, callback=self.stop, active_offset=(0, 5), highlight_color=YELLOW) self.title = Text(title)
def __init__(self): Window.__init__(self, bg_color=(0, 200, 255)) self.player_id = 0 self.button_back = ImageButton(self, RESOURCES.IMG["arrow_blue"], rotate=180, size=50, callback=self.stop, highlight_color=YELLOW) self.player_grid = PlayerNavy(self, self.client_socket) self.opposite_grid = OppositeNavy(self, self.client_socket) self.ai = AI() self.turn_checker = TurnArrow() self.restart = False self.bind_key(pygame.K_ESCAPE, lambda event: self.stop()) self.text_finish = Text("Finish !!!", font=(None, 120), color=WHITE) self.window_finish = FinishWindow(self) self.game_finished = False
def __init__(self, master): Window.__init__(self, bg_color=master.bg_color, bg_music=master.bg_music) params_for_button = { "highlight_color": YELLOW, "hover_sound": RESOURCES.SFX["select"], "on_click_sound": RESOURCES.SFX["back"] } self.master = master self.button_back = ImageButton(self, img=RESOURCES.IMG["blue_arrow"], **params_for_button, callback=self.stop) self.objects.add(master.text_highscore, master.text_money) self.text_title = Text("ENVIRONMENT", (RESOURCES.FONT["algerian"], 90), GREEN_DARK, shadow=True, shadow_x=2, shadow_y=2) self.environment = ButtonListHorizontal(offset=15) self.texts = DrawableList() for name, color in ENVIRONMENT.items(): b = Button(self, img=Image(RESOURCES.IMG[name], max_width=180, max_height=180), compound="center", outline=3, callback=lambda env=name: self.play(env), bg=color, hover_bg=change_saturation(color, -20), active_bg=change_brightness(color, -20), hover_sound=RESOURCES.SFX["select"], on_click_sound=RESOURCES.SFX["validate"], highlight_color=YELLOW) b.set_size(200) self.texts.add( Text(name.upper(), (RESOURCES.FONT["algerian"], 50), GREEN_DARK, shadow=True, shadow_x=2, shadow_y=2)) self.environment.add(b) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"]))
class ConfirmPayement(Window): def __init__(self, master): Window.__init__(self, master=master, bg_music=master.bg_music) self.bg = RectangleShape(*self.size, (0, 0, 0, 170)) self.frame = RectangleShape(0.50 * self.width, 0.50 * self.height, GREEN, outline=3) self.text = Text("Are you sure you want\nto buy this car ?", (RESOURCES.FONT["algerian"], 60), justify=Text.T_CENTER) self.button_yes = Button(self, "Yes", self.text.font, bg=GREEN, hover_bg=GREEN_LIGHT, active_bg=GREEN_DARK, hover_sound=RESOURCES.SFX["select"], on_click_sound=RESOURCES.SFX["validate"], highlight_color=YELLOW, callback=self.buy) self.button_red_cross = ImageButton( self, img=RESOURCES.IMG["red_cross"], active_img=RESOURCES.IMG["red_cross_hover"], hover_sound=RESOURCES.SFX["select"], on_click_sound=RESOURCES.SFX["back"], callback=self.stop, highlight_color=YELLOW) self.buyed = False self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"])) def place_objects(self): self.text.center = self.frame.center = self.center self.button_red_cross.move(left=self.frame.left + 5, top=self.frame.top + 5) self.button_yes.move(bottom=self.frame.bottom - 10, centerx=self.frame.centerx) def set_grid(self): self.button_red_cross.set_obj_on_side(on_bottom=self.button_yes) self.button_yes.set_obj_on_side(on_top=self.button_red_cross) def buy(self): self.buyed = True self.stop()
def __init__(self, master: Window): Window.__init__(self, bg_color=BACKGROUND_COLOR) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop()) self.master = master self.logo = Image(RESOURCES.IMG["logo"]) arrow = pygame.transform.flip(RESOURCES.IMG["arrow"], True, False) self.button_back = ImageButton(self, img=arrow, width=100, callback=self.stop, active_offset=(0, 5), highlight_color=YELLOW) self.grid = FourInARowGrid(self, self.width / 2, self.height * 0.75) self.__player_turn = 0 self.player_who_start_first = 0 self.player = 0 self.__turn = str() self.__turn_dict = dict() self.__score_player = self.__score_enemy = 0 self.__highlight_line_window_callback = None self.enemy = str() self.ai = FourInARowAI() self.text_score = Text() self.text_player_turn = Text() self.left_options = ButtonListVertical(offset=50) self.left_options.add( Button(self, "Restart", theme="option", callback=self.restart), Button(self, "Quit game", theme="option", callback=self.quit_game)) self.text_winner = Text() self.text_drawn_match = Text("Drawn match.") self.token_players = Grid(self) for row in range(2): self.token_players.place(CircleShape(20, PLAYER_TOKEN_COLOR[row + 1], outline=2, outline_color=WHITE), row, column=1, padx=5, pady=5, justify="left") self.enemy_quit_dialog = EnemyQuitGame(self)
def __init__(self): Window.__init__(self, bg_color=(0, 200, 255)) self.gameplay = Gameplay() self.enemy_quit_window = EnemyQuitGame(self) self.transition = GameSetupTransition() self.count_down = CountDown(self, 60, "Time left: {seconds}", font=(None, 70), color=WHITE) self.start_count_down = lambda: self.count_down.start( at_end=self.timeout) if self.client_socket.connected() else None self.button_back = ImageButton(self, RESOURCES.IMG["arrow_blue"], rotate=180, size=50, callback=self.stop) self.navy_grid = Grid(self, bg_color=(0, 157, 255)) self.__boxes_dict = {(i, j): BoxSetup(self, size=BOX_SIZE, pos=(i, j)) for i in range(NB_LINES_BOXES) for j in range(NB_COLUMNS_BOXES)} self.__boxes_list = list(self.__boxes_dict.values()) self.navy_grid.place_multiple(self.__boxes_dict) self.ships_list = DrawableListVertical(offset=70, justify="left") for ship_name, ship_infos in SHIPS.items(): ship_line = DrawableListHorizontal(offset=ship_infos["offset"]) for _ in range(ship_infos["nb"]): ship_line.add(ShipSetup(self, ship_name, ship_infos["size"])) self.ships_list.add(ship_line) option_size = 50 self.button_restart = Button(self, img=Image(RESOURCES.IMG["reload_blue"], size=option_size), callback=self.reinit_all_ships) self.button_random = Button(self, img=Image(RESOURCES.IMG["random"], size=option_size), callback=self.shuffle) self.button_play = Button(self, "Play", font=(None, 40), callback=self.play)
class Options(Window): def __init__(self, master: Window): Window.__init__(self, master=master, bg_music=master.bg_music) self.frame = RectangleShape(0.60 * self.width, 0.60 * self.height, GREEN, outline=3) self.title = Text("Options", font=(RESOURCES.FONT["algerian"], 70)) self.options_font = (RESOURCES.FONT["algerian"], 40) self.case_font = (RESOURCES.FONT["algerian"], 30) self.control_font = ("calibri", 20) params_for_all_scales = { "width": 0.45 * self.frame.w, "color": TRANSPARENT, "scale_color": GREEN_DARK, "from_": 0, "to": 100, "outline": 3, } params_for_all_buttons = { "highlight_color": YELLOW, "hover_sound": RESOURCES.SFX["select"], "disabled_sound": RESOURCES.SFX["block"] } params_for_option_buttons = { "on_click_sound": RESOURCES.SFX["validate"] } params_for_buttons = { "bg": GRAY_DARK, "fg": WHITE, "hover_bg": GRAY, "active_bg": BLACK } params_for_reset_button = { "bg": RED, "fg": WHITE, "hover_bg": RED_LIGHT, "active_bg": RED_DARK, } self.button_back = ImageButton(self, img=RESOURCES.IMG["blue_arrow"], on_click_sound=RESOURCES.SFX["back"], callback=self.stop, **params_for_all_buttons) self.button_change_page = Button(self, ">>", font=self.case_font, callback=self.change_page, **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.nb_pages = 2 self.page = 1 ## PAGE 1 ## valid_img = Image(RESOURCES.IMG["green_valid"]) self.text_music = Text("Music:", self.options_font) self.cb_music = CheckBox(self, 30, 30, TRANSPARENT, image=valid_img, value=self.get_music_state(), callback=self.set_music_state, **params_for_all_buttons, **params_for_option_buttons) self.scale_music = Scale( self, **params_for_all_scales, **params_for_all_buttons, height=self.cb_music.height, default=Window.music_volume() * 100, callback=lambda value, percent: Window.set_music_volume(percent)) self.text_sound = Text("SFX:", self.options_font) self.cb_sound = CheckBox(self, 30, 30, TRANSPARENT, image=valid_img, value=self.get_sound_state(), callback=self.set_sound_state, **params_for_all_buttons, **params_for_option_buttons) self.scale_sound = Scale( self, **params_for_all_scales, **params_for_all_buttons, height=self.cb_sound.height, default=Window.sound_volume() * 100, callback=lambda value, percent: Window.set_sound_volume(percent)) self.text_fps = Text("FPS:", self.options_font) self.cb_show_fps = CheckBox(self, 30, 30, TRANSPARENT, image=valid_img, value=Window.fps_is_shown(), callback=self.show_fps, **params_for_all_buttons, **params_for_option_buttons) self.button_reset = Button(self, "Reset Save", font=(RESOURCES.FONT["algerian"], 30), callback=SAVE.reset, state=Button.DISABLED, **params_for_all_buttons, **params_for_option_buttons, **params_for_reset_button) ## PAGE 2 ## self.text_acceleration = Text("Accélérer:", self.options_font) self.button_auto_acceleration = Button( self, font=self.case_font, callback=lambda: SAVE.update(auto_acceleration=not SAVE[ "auto_acceleration"]), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.button_acceleration = Button( self, font=self.control_font, callback=lambda: self.choose_key("speed_up"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.text_brake = Text("Freiner:", self.options_font) self.button_brake = Button(self, font=self.control_font, callback=lambda: self.choose_key("brake"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.text_move_up = Text("Aller en haut:", self.options_font) self.button_move_up = Button(self, font=self.control_font, callback=lambda: self.choose_key("up"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.text_move_down = Text("Aller en bas:", self.options_font) self.button_move_down = Button( self, font=self.control_font, callback=lambda: self.choose_key("down"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"])) def on_quit(self): SAVE.dump() def change_page(self): self.page = (self.page % self.nb_pages) + 1 def update(self): self.hide_all(without=[ self.frame, self.title, self.button_back, self.button_change_page ]) if self.page == 1: self.text_music.show() self.text_sound.show() for checkbox, scale in [(self.cb_music, self.scale_music), (self.cb_sound, self.scale_sound)]: checkbox.show() if checkbox.value is True: scale.show() checkbox.set_obj_on_side(on_right=scale) else: checkbox.set_obj_on_side(on_right=self.button_change_page) self.text_fps.show() self.cb_show_fps.show() self.button_reset.show() self.button_back.set_obj_on_side(on_bottom=self.cb_music, on_right=self.cb_music) self.button_change_page.set_obj_on_side(on_top=self.cb_show_fps, on_left=self.button_reset) elif self.page == 2: self.text_acceleration.show() self.button_auto_acceleration.text = "Automatique" if SAVE[ "auto_acceleration"] else "Manuel" self.button_auto_acceleration.show() control_text_format = "Key: {key}\nJoystick: {joy}" if not SAVE["auto_acceleration"]: self.button_acceleration.text = control_text_format.format( **SAVE["controls"]["speed_up"]) self.button_acceleration.move( left=self.button_auto_acceleration.right + 10, centery=self.button_auto_acceleration.centery) self.button_acceleration.show() self.button_auto_acceleration.set_obj_on_side( on_right=self.button_acceleration) else: self.button_auto_acceleration.set_obj_on_side( on_right=self.button_back) fields = [ (self.text_brake, self.button_brake, "brake"), (self.text_move_up, self.button_move_up, "up"), (self.text_move_down, self.button_move_down, "down"), ] for text, button, action in fields: text.show() button.text = control_text_format.format( **SAVE["controls"][action]) button.show() self.button_back.set_obj_on_side( on_bottom=self.button_auto_acceleration, on_right=self.button_auto_acceleration) self.button_change_page.set_obj_on_side( on_top=self.button_move_down, on_left=self.button_move_down) def place_objects(self): self.frame.move(center=self.center) self.title.move(top=self.frame.top + 10, centerx=self.frame.centerx) self.button_back.move(top=self.frame.top + 5, left=self.frame.left + 5) self.button_change_page.move(bottom=self.frame.bottom - 5, right=self.frame.right - 5) ## PAGE 1 ## self.text_music.move(left=self.frame.left + 10, top=self.title.bottom + 10) self.text_sound.move(right=self.text_music.right, top=self.text_music.bottom + 5) self.cb_music.move(left=self.text_music.right + 10, centery=self.text_music.centery) self.cb_sound.move(left=self.text_music.right + 10, centery=self.text_sound.centery) self.scale_music.move(centerx=self.frame.centerx, centery=self.cb_music.centery) self.scale_music.show_value(Scale.S_RIGHT, font=self.case_font) self.scale_sound.move(centerx=self.frame.centerx, centery=self.cb_sound.centery) self.scale_sound.show_value(Scale.S_RIGHT, font=self.case_font) self.text_fps.move(right=self.text_music.right, top=self.text_sound.bottom + 50) self.cb_show_fps.move(left=self.text_fps.right + 10, centery=self.text_fps.centery) self.button_reset.move(bottom=self.frame.bottom - 5, left=self.frame.left + 5) ## PAGE 2 ## self.text_acceleration.move(left=self.frame.left + 10, top=self.title.bottom + 50) self.button_auto_acceleration.move( left=self.text_acceleration.right + 10, centery=self.text_acceleration.centery) self.text_brake.move(left=self.text_acceleration.left, top=self.text_acceleration.bottom + 50) self.button_brake.move(left=self.text_brake.right + 10, centery=self.text_brake.centery) self.text_move_up.move(left=self.text_acceleration.left, top=self.text_brake.bottom + 50) self.button_move_up.move(left=self.text_move_up.right + 10, centery=self.text_move_up.centery) self.text_move_down.move(left=self.text_acceleration.left, top=self.text_move_up.bottom + 50) self.button_move_down.move(left=self.text_move_down.right + 10, centery=self.text_move_down.centery) def set_grid(self): ## PAGE 1 ## self.cb_music.set_obj_on_side(on_top=self.button_back, on_left=self.button_back, on_bottom=self.cb_sound) self.scale_music.set_obj_on_side(on_top=self.button_back, on_left=self.cb_music, on_bottom=self.scale_sound, on_right=self.button_change_page) self.cb_sound.set_obj_on_side(on_top=self.cb_music, on_left=self.button_back, on_bottom=self.cb_show_fps) self.scale_sound.set_obj_on_side(on_top=self.scale_music, on_left=self.cb_sound, on_bottom=self.cb_show_fps, on_right=self.button_change_page) self.cb_show_fps.set_obj_on_side(on_left=self.button_back, on_top=self.cb_sound, on_bottom=self.button_reset, on_right=self.button_change_page) self.button_reset.set_obj_on_side(on_left=self.button_back, on_top=self.cb_show_fps, on_right=self.button_change_page) ## PAGE 2 ## self.button_auto_acceleration.set_obj_on_side( on_top=self.button_back, on_left=self.button_back, on_bottom=self.button_brake) self.button_acceleration.set_obj_on_side( on_top=self.button_back, on_left=self.button_auto_acceleration, on_bottom=self.button_brake, on_right=self.button_change_page) self.button_brake.set_obj_on_side(on_top=self.button_auto_acceleration, on_left=self.button_back, on_bottom=self.button_move_up, on_right=self.button_change_page) self.button_move_up.set_obj_on_side(on_top=self.button_brake, on_left=self.button_back, on_bottom=self.button_move_down, on_right=self.button_change_page) self.button_move_down.set_obj_on_side( on_top=self.button_move_up, on_left=self.button_back, on_bottom=self.button_change_page, on_right=self.button_change_page) def choose_key(self, action: str): AssignmentPrompt(self, action).mainloop()
class FourInARowGameplay(Window): def __init__(self, master: Window): Window.__init__(self, bg_color=BACKGROUND_COLOR) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop()) self.master = master self.logo = Image(RESOURCES.IMG["logo"]) arrow = pygame.transform.flip(RESOURCES.IMG["arrow"], True, False) self.button_back = ImageButton(self, img=arrow, width=100, callback=self.stop, active_offset=(0, 5), highlight_color=YELLOW) self.grid = FourInARowGrid(self, self.width / 2, self.height * 0.75) self.__player_turn = 0 self.player_who_start_first = 0 self.player = 0 self.__turn = str() self.__turn_dict = dict() self.__score_player = self.__score_enemy = 0 self.__highlight_line_window_callback = None self.enemy = str() self.ai = FourInARowAI() self.text_score = Text() self.text_player_turn = Text() self.left_options = ButtonListVertical(offset=50) self.left_options.add( Button(self, "Restart", theme="option", callback=self.restart), Button(self, "Quit game", theme="option", callback=self.quit_game)) self.text_winner = Text() self.text_drawn_match = Text("Drawn match.") self.token_players = Grid(self) for row in range(2): self.token_players.place(CircleShape(20, PLAYER_TOKEN_COLOR[row + 1], outline=2, outline_color=WHITE), row, column=1, padx=5, pady=5, justify="left") self.enemy_quit_dialog = EnemyQuitGame(self) def start(self, enemy: str, player=1, player_name=None, enemy_name=None, ai_level=None) -> None: self.player = player self.enemy = enemy if enemy == LAN_PLAYER: player_name = str(player_name) self.client_socket.send("name", player_name) result = self.client_socket.wait_for("name") if result == self.client_socket.QUIT_MESSAGE: return enemy_name = str(self.client_socket.get(result)) self.__turn_dict = { 1: { 1: player_name, 2: enemy_name }[player], 2: { 1: enemy_name, 2: player_name }[player] } else: player_name = str(player_name) if player_name is not None else "P1" enemy_name = str(enemy_name) if enemy_name is not None else "P2" self.__turn_dict = { 1: "You" if self.enemy == AI else player_name, 2: "AI" if self.enemy == AI else enemy_name } if self.enemy == AI: self.ai.level = ai_level for row, name in enumerate(self.__turn_dict.values()): self.token_players.place(Text(name + ":"), row, column=0, padx=5, pady=5, justify="right") self.mainloop() def on_start_loop(self) -> None: self.grid[0].focus_set() self.score_player = self.score_enemy = 0 self.player_who_start_first = 0 self.text_winner.hide() self.restart(init=True) def on_quit(self) -> None: self.stop_connection() def quit_game(self) -> None: self.stop() self.master.stop() def restart(self, init=False) -> None: if not init: self.client_socket.send("restart") self.grid.reset() self.remove_window_callback(self.__highlight_line_window_callback) if self.player_who_start_first == 0: if self.enemy == AI: self.player_turn = 1 elif self.enemy == LOCAL_PLAYER or (self.enemy == LAN_PLAYER and self.player == 1): self.player_turn = random.randint(1, 2) if self.enemy == LAN_PLAYER: self.client_socket.send("player_turn", int(self.player_turn)) elif self.enemy == LAN_PLAYER and self.player == 2: result = self.client_socket.wait_for("player_turn") if result == self.client_socket.QUIT_MESSAGE: self.stop() return self.player_turn = self.client_socket.get(result) elif self.text_winner.is_shown(): self.player_turn = (self.player_who_start_first % 2) + 1 else: self.player_turn = self.player_who_start_first self.player_who_start_first = self.player_turn self.text_winner.hide() self.text_drawn_match.hide() def place_objects(self) -> None: self.logo.move(centerx=self.centerx, top=10) self.grid.midbottom = self.midbottom self.text_score.move(left=10, top=self.grid.top) self.text_player_turn.move(left=self.text_score.left, top=self.text_score.bottom + 50) self.left_options.move(centerx=(self.left + self.grid.left) // 2, top=self.text_player_turn.bottom + 50) self.text_winner.center = ((self.grid.right + self.right) // 2, self.grid.centery) self.token_players.move(centerx=self.text_winner.centerx, top=self.grid.top) self.text_drawn_match.center = self.text_winner.center def set_grid(self) -> None: self.grid.set_obj_on_side(on_top=self.button_back, on_left=self.left_options[0]) self.button_back.set_obj_on_side(on_bottom=self.left_options[0], on_right=self.grid[0]) self.left_options.set_obj_on_side(on_top=self.button_back, on_right=self.grid[0]) @property def player_turn(self) -> int: return self.__player_turn @player_turn.setter def player_turn(self, player: int) -> None: self.__player_turn = player self.__turn = turn = self.__turn_dict[player] self.text_player_turn.message = f"Player turn:\n{turn}" self.place_objects() if self.enemy != LOCAL_PLAYER: for column in filter(lambda column: not column.full(), self.grid.columns): column.set_enabled(self.player_turn == self.player) if self.enemy == AI and self.player_turn == 2: self.after(500, self.play, self.ai.play(self.grid.map)) @property def score_player(self) -> int: return self.__score_player @score_player.setter def score_player(self, value: int) -> None: self.__score_player = value self.__update_text_score() @property def score_enemy(self) -> int: return self.__score_enemy @score_enemy.setter def score_enemy(self, value: int) -> None: self.__score_enemy = value self.__update_text_score() def __update_text_score(self) -> None: self.text_score.message = "Score:\n{you}: {score_1}\n{enemy}: {score_2}".format( you=self.__turn_dict[1], enemy=self.__turn_dict[2], score_1=self.score_player, score_2=self.score_enemy) self.place_objects() def update(self) -> None: if self.enemy == LAN_PLAYER: if self.client_socket.recv("column"): self.play(self.client_socket.get("column")) if self.client_socket.recv("restart"): self.restart() if self.client_socket.recv(self.client_socket.QUIT_MESSAGE): self.enemy_quit_dialog.text.message = "{}\nhas left the game".format( self.__turn_dict[(self.player % 2) + 1]) self.enemy_quit_dialog.mainloop() def play(self, column: int) -> None: self.block_only_event([ pygame.KEYDOWN, pygame.KEYUP, pygame.JOYBUTTONDOWN, pygame.JOYBUTTONUP, pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP ]) if self.enemy == LAN_PLAYER and self.player_turn == self.player: self.client_socket.send("column", column) self.grid.play(self.player_turn, column) line = self.check_victory() if line: for c in self.grid.columns: c.disable() self.left_options[0].focus_set() self.update_winner() self.highlight_line(line) elif self.grid.full(): self.left_options[0].focus_set() self.text_drawn_match.show() else: self.player_turn = (self.player_turn % 2) + 1 if self.enemy == LOCAL_PLAYER: self.draw_and_refresh() pygame.time.wait(500) self.clear_all_events() self.allow_all_events() def check_victory(self) -> list[tuple[int, int]]: grid = self.grid.map grid_pos_getter = [ lambda row, col, index: (row, col + index), # Check row (->) lambda row, col, index: (row, col - index), # Check row (<-) lambda row, col, index: (row + index, col), # Check column lambda row, col, index: (row + index, col - index), # Check diagonal (/) lambda row, col, index: (row + index, col + index), # Check diagonal (\) ] all_box_pos = list() box_pos = list() for row, column in filter(lambda pos: grid[pos] != 0, grid): for grid_pos in grid_pos_getter: index = 0 box_pos.clear() while grid.get(grid_pos(row, column, index), -1) == grid[row, column]: box_pos.append(grid_pos(row, column, index)) index += 1 if len(box_pos) >= 4: all_box_pos.extend( filter(lambda pos: pos not in all_box_pos, box_pos)) return all_box_pos def highlight_line(self, line: list[tuple[int, int]], highlight=True): for row, col in line: box = self.grid.columns[col].boxes[row] if highlight: box.circle.color = GREEN else: box.value = box.value self.__highlight_line_window_callback = self.after( 500, self.highlight_line, line=line, highlight=not highlight) def update_winner(self): if self.player_turn == 1: self.score_player += 1 else: self.score_enemy += 1 winner = self.__turn self.text_winner.message = f"Winner:\n{winner}" self.text_winner.show()
def __init__(self): Window.__init__(self, bg_color=GRAY, bg_music=RESOURCES.MUSIC["garage"]) params_for_all_buttons = { "bg": GREEN, "hover_bg": GREEN_LIGHT, "active_bg": GREEN_DARK, "highlight_color": YELLOW, "hover_sound": RESOURCES.SFX["select"], } params_for_button_except_back = { "on_click_sound": RESOURCES.SFX["validate"], "disabled_sound": RESOURCES.SFX["block"], "disabled_bg": GRAY_LIGHT, } params_for_button_except_back.update(params_for_all_buttons) params_for_car_viewer = { k: params_for_button_except_back[k] for k in ["hover_sound", "on_click_sound"] } self.button_back = ImageButton(self, RESOURCES.IMG["blue_arrow"], **params_for_all_buttons, on_click_sound=RESOURCES.SFX["back"], callback=self.stop) self.car_viewer = CarViewer(self, SAVE["car"], **params_for_car_viewer) size_progress_bar = (300, 30) self.speed_bar = ProgressBar(*size_progress_bar, TRANSPARENT, GREEN) self.maniability_bar = ProgressBar(*size_progress_bar, TRANSPARENT, GREEN) self.braking_bar = ProgressBar(*size_progress_bar, TRANSPARENT, GREEN) self.left_arrow = ImageButton( self, img=RESOURCES.IMG["left_arrow"], active_img=RESOURCES.IMG["left_arrow_hover"], **params_for_button_except_back, callback=self.car_viewer.decrease_id) self.right_arrow = ImageButton( self, img=RESOURCES.IMG["right_arrow"], active_img=RESOURCES.IMG["right_arrow_hover"], **params_for_button_except_back, callback=self.car_viewer.increase_id) for arrow in [self.left_arrow, self.right_arrow]: arrow.take_focus(False) self.button_price = Button(self, font=(RESOURCES.FONT["algerian"], 40), img=Image(RESOURCES.IMG["piece"], size=40), compound="right", callback=self.buy_car, **params_for_button_except_back) self.button_play = Button(self, "Play", font=(RESOURCES.FONT["algerian"], 70), callback=self.play, **params_for_button_except_back) self.text_money = Text(format_number(SAVE["money"]), (RESOURCES.FONT["algerian"], 50), YELLOW, img=Image(RESOURCES.IMG["piece"], height=40), compound="right") self.text_highscore = Text( "Highscore: {}".format(format_number(SAVE["highscore"])), (RESOURCES.FONT["algerian"], 50), YELLOW) self.padlock = Image(RESOURCES.IMG["padlock"]) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"]))
class Garage(Window): def __init__(self): Window.__init__(self, bg_color=GRAY, bg_music=RESOURCES.MUSIC["garage"]) params_for_all_buttons = { "bg": GREEN, "hover_bg": GREEN_LIGHT, "active_bg": GREEN_DARK, "highlight_color": YELLOW, "hover_sound": RESOURCES.SFX["select"], } params_for_button_except_back = { "on_click_sound": RESOURCES.SFX["validate"], "disabled_sound": RESOURCES.SFX["block"], "disabled_bg": GRAY_LIGHT, } params_for_button_except_back.update(params_for_all_buttons) params_for_car_viewer = { k: params_for_button_except_back[k] for k in ["hover_sound", "on_click_sound"] } self.button_back = ImageButton(self, RESOURCES.IMG["blue_arrow"], **params_for_all_buttons, on_click_sound=RESOURCES.SFX["back"], callback=self.stop) self.car_viewer = CarViewer(self, SAVE["car"], **params_for_car_viewer) size_progress_bar = (300, 30) self.speed_bar = ProgressBar(*size_progress_bar, TRANSPARENT, GREEN) self.maniability_bar = ProgressBar(*size_progress_bar, TRANSPARENT, GREEN) self.braking_bar = ProgressBar(*size_progress_bar, TRANSPARENT, GREEN) self.left_arrow = ImageButton( self, img=RESOURCES.IMG["left_arrow"], active_img=RESOURCES.IMG["left_arrow_hover"], **params_for_button_except_back, callback=self.car_viewer.decrease_id) self.right_arrow = ImageButton( self, img=RESOURCES.IMG["right_arrow"], active_img=RESOURCES.IMG["right_arrow_hover"], **params_for_button_except_back, callback=self.car_viewer.increase_id) for arrow in [self.left_arrow, self.right_arrow]: arrow.take_focus(False) self.button_price = Button(self, font=(RESOURCES.FONT["algerian"], 40), img=Image(RESOURCES.IMG["piece"], size=40), compound="right", callback=self.buy_car, **params_for_button_except_back) self.button_play = Button(self, "Play", font=(RESOURCES.FONT["algerian"], 70), callback=self.play, **params_for_button_except_back) self.text_money = Text(format_number(SAVE["money"]), (RESOURCES.FONT["algerian"], 50), YELLOW, img=Image(RESOURCES.IMG["piece"], height=40), compound="right") self.text_highscore = Text( "Highscore: {}".format(format_number(SAVE["highscore"])), (RESOURCES.FONT["algerian"], 50), YELLOW) self.padlock = Image(RESOURCES.IMG["padlock"]) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"])) def update(self): self.left_arrow.set_visibility(self.car_viewer.id > 1) self.right_arrow.set_visibility( self.car_viewer.id < len(self.car_viewer)) if not SAVE["owned_cars"][self.car_viewer.id]: self.padlock.show() price = self.car_viewer["price"] if isinstance(price, int): self.button_price.show() self.button_price.text = format_number(price) self.button_price.state = Button.NORMAL if SAVE[ "money"] >= price else Button.DISABLED else: self.button_price.hide() self.button_play.state = Button.DISABLED else: self.padlock.hide() self.button_price.hide() self.button_play.state = Button.NORMAL SAVE["car"] = self.car_viewer.id max_s = self.car_viewer.max_speed min_a = self.car_viewer.min_acceleration max_m = self.car_viewer.max_maniability min_b = self.car_viewer.min_braking s = self.car_viewer["max_speed"] a = self.car_viewer["acceleration"] m = self.car_viewer["maniability"] b = self.car_viewer["braking"] self.speed_bar.percent = (s + min_a) / (max_s + a) self.maniability_bar.percent = m / max_m self.braking_bar.percent = min_b / b def place_objects(self): self.button_back.topleft = (5, 5) self.car_viewer.move(center=self.center) self.padlock.center = self.car_viewer.center self.braking_bar.move(bottom=self.car_viewer.top - 40, centerx=self.car_viewer.centerx + 100) self.maniability_bar.move(bottom=self.braking_bar.top - 10, centerx=self.car_viewer.centerx + 100) self.speed_bar.move(bottom=self.maniability_bar.top - 10, centerx=self.car_viewer.centerx + 100) self.speed_bar.show_label("Speed/Acc.", ProgressBar.S_LEFT, font=(RESOURCES.FONT["algerian"], 40)) self.maniability_bar.show_label("Maniability", ProgressBar.S_LEFT, font=(RESOURCES.FONT["algerian"], 40)) self.braking_bar.show_label("Braking", ProgressBar.S_LEFT, font=(RESOURCES.FONT["algerian"], 40)) self.left_arrow.move(left=self.left + 50, centery=self.centery) self.right_arrow.move(right=self.right - 50, centery=self.centery) self.button_price.move(centerx=self.centerx, top=self.car_viewer.bottom + 25) self.button_play.move(bottom=self.bottom - 50, right=self.right - 10) self.text_money.move(top=5, right=self.right - 10) self.text_highscore.move(bottom=self.bottom - 50, left=5) def set_grid(self): self.button_back.set_obj_on_side(on_bottom=self.car_viewer) self.car_viewer.set_obj_on_side(on_top=self.button_back, on_bottom=self.button_price) self.button_price.set_obj_on_side(on_top=self.car_viewer, on_bottom=self.button_play) self.button_play.set_obj_on_side(on_top=self.button_price) def buy_car(self): confirm_window = ConfirmPayement(self) confirm_window.mainloop() if confirm_window.buyed: SAVE["money"] -= self.car_viewer["price"] SAVE["owned_cars"][self.car_viewer.id] = True self.text_money.message = format_number(SAVE["money"]) if Clickable.MODE != Clickable.MODE_MOUSE: self.car_viewer.focus_set() def play(self): environment_chooser = EnvironmentChooser(self) environment_chooser.mainloop() self.text_money.message = format_number(SAVE["money"]) self.text_highscore.message = "Highscore: {}".format( format_number(SAVE["highscore"]))
class EnvironmentChooser(Window): def __init__(self, master): Window.__init__(self, bg_color=master.bg_color, bg_music=master.bg_music) params_for_button = { "highlight_color": YELLOW, "hover_sound": RESOURCES.SFX["select"], "on_click_sound": RESOURCES.SFX["back"] } self.master = master self.button_back = ImageButton(self, img=RESOURCES.IMG["blue_arrow"], **params_for_button, callback=self.stop) self.objects.add(master.text_highscore, master.text_money) self.text_title = Text("ENVIRONMENT", (RESOURCES.FONT["algerian"], 90), GREEN_DARK, shadow=True, shadow_x=2, shadow_y=2) self.environment = ButtonListHorizontal(offset=15) self.texts = DrawableList() for name, color in ENVIRONMENT.items(): b = Button(self, img=Image(RESOURCES.IMG[name], max_width=180, max_height=180), compound="center", outline=3, callback=lambda env=name: self.play(env), bg=color, hover_bg=change_saturation(color, -20), active_bg=change_brightness(color, -20), hover_sound=RESOURCES.SFX["select"], on_click_sound=RESOURCES.SFX["validate"], highlight_color=YELLOW) b.set_size(200) self.texts.add( Text(name.upper(), (RESOURCES.FONT["algerian"], 50), GREEN_DARK, shadow=True, shadow_x=2, shadow_y=2)) self.environment.add(b) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"])) def place_objects(self): self.button_back.topleft = (5, 5) self.environment.move(center=self.center) self.text_title.move(centerx=self.environment.centerx, bottom=self.environment.top - 10) for text, button in zip(self.texts, self.environment): text.move(top=button.bottom + 5, centerx=button.centerx) def set_grid(self): self.environment.set_obj_on_side(on_left=self.button_back, on_top=self.button_back) self.button_back.set_obj_on_side(on_bottom=self.environment[0], on_right=self.environment[0]) self.environment[0].focus_set() def play(self, env: str): gameplay = Gameplay(self.master.car_viewer.id, env) gameplay.mainloop() if not gameplay.go_to_garage: self.master.stop() else: self.master.car_viewer.focus_set() self.stop()
def __init__(self, master: Window): Window.__init__(self, master=master, bg_music=master.bg_music) self.frame = RectangleShape(0.60 * self.width, 0.60 * self.height, GREEN, outline=3) self.title = Text("Options", font=(RESOURCES.FONT["algerian"], 70)) self.options_font = (RESOURCES.FONT["algerian"], 40) self.case_font = (RESOURCES.FONT["algerian"], 30) self.control_font = ("calibri", 20) params_for_all_scales = { "width": 0.45 * self.frame.w, "color": TRANSPARENT, "scale_color": GREEN_DARK, "from_": 0, "to": 100, "outline": 3, } params_for_all_buttons = { "highlight_color": YELLOW, "hover_sound": RESOURCES.SFX["select"], "disabled_sound": RESOURCES.SFX["block"] } params_for_option_buttons = { "on_click_sound": RESOURCES.SFX["validate"] } params_for_buttons = { "bg": GRAY_DARK, "fg": WHITE, "hover_bg": GRAY, "active_bg": BLACK } params_for_reset_button = { "bg": RED, "fg": WHITE, "hover_bg": RED_LIGHT, "active_bg": RED_DARK, } self.button_back = ImageButton(self, img=RESOURCES.IMG["blue_arrow"], on_click_sound=RESOURCES.SFX["back"], callback=self.stop, **params_for_all_buttons) self.button_change_page = Button(self, ">>", font=self.case_font, callback=self.change_page, **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.nb_pages = 2 self.page = 1 ## PAGE 1 ## valid_img = Image(RESOURCES.IMG["green_valid"]) self.text_music = Text("Music:", self.options_font) self.cb_music = CheckBox(self, 30, 30, TRANSPARENT, image=valid_img, value=self.get_music_state(), callback=self.set_music_state, **params_for_all_buttons, **params_for_option_buttons) self.scale_music = Scale( self, **params_for_all_scales, **params_for_all_buttons, height=self.cb_music.height, default=Window.music_volume() * 100, callback=lambda value, percent: Window.set_music_volume(percent)) self.text_sound = Text("SFX:", self.options_font) self.cb_sound = CheckBox(self, 30, 30, TRANSPARENT, image=valid_img, value=self.get_sound_state(), callback=self.set_sound_state, **params_for_all_buttons, **params_for_option_buttons) self.scale_sound = Scale( self, **params_for_all_scales, **params_for_all_buttons, height=self.cb_sound.height, default=Window.sound_volume() * 100, callback=lambda value, percent: Window.set_sound_volume(percent)) self.text_fps = Text("FPS:", self.options_font) self.cb_show_fps = CheckBox(self, 30, 30, TRANSPARENT, image=valid_img, value=Window.fps_is_shown(), callback=self.show_fps, **params_for_all_buttons, **params_for_option_buttons) self.button_reset = Button(self, "Reset Save", font=(RESOURCES.FONT["algerian"], 30), callback=SAVE.reset, state=Button.DISABLED, **params_for_all_buttons, **params_for_option_buttons, **params_for_reset_button) ## PAGE 2 ## self.text_acceleration = Text("Accélérer:", self.options_font) self.button_auto_acceleration = Button( self, font=self.case_font, callback=lambda: SAVE.update(auto_acceleration=not SAVE[ "auto_acceleration"]), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.button_acceleration = Button( self, font=self.control_font, callback=lambda: self.choose_key("speed_up"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.text_brake = Text("Freiner:", self.options_font) self.button_brake = Button(self, font=self.control_font, callback=lambda: self.choose_key("brake"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.text_move_up = Text("Aller en haut:", self.options_font) self.button_move_up = Button(self, font=self.control_font, callback=lambda: self.choose_key("up"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.text_move_down = Text("Aller en bas:", self.options_font) self.button_move_down = Button( self, font=self.control_font, callback=lambda: self.choose_key("down"), **params_for_all_buttons, **params_for_option_buttons, **params_for_buttons) self.bind_key(pygame.K_ESCAPE, lambda event: self.stop(sound=RESOURCES.SFX["back"])) self.bind_joystick( 0, "B", lambda event: self.stop(sound=RESOURCES.SFX["back"]))
class Gameplay(Window): def __init__(self, player: int): Window.__init__(self, bg_color=(0, 200, 255), bg_music=RESOURCES.MUSIC["gameplay"]) self.player_id = player self.button_back = ImageButton(self, RESOURCES.IMG["arrow_blue"], rotate=180, size=50, callback=self.stop, highlight_color=YELLOW) self.player_grid = PlayerNavy(self, self.client_socket) self.opposite_grid = OppositeNavy(self, self.client_socket) self.ai = AI() self.turn_checker = TurnArrow() self.restart = False self.bind_key(pygame.K_ESCAPE, lambda event: self.stop()) self.text_finish = Text("Finish !!!", font=(None, 120), color=WHITE) self.game_finished = False def start(self, navy_setup: Sequence[Dict[str, Any]], ai_setup=None) -> None: self.player_grid.load_setup(navy_setup) self.opposite_grid.ai_setup = ai_setup or list() self.turn_checker.turn = self.get_default_turn() self.game_finished = self.restart = False self.ai.reset() self.mainloop() def on_quit(self) -> None: self.player_grid.reset() self.opposite_grid.reset() self.objects.set_focus(None) def update(self): self.text_finish.set_visibility(self.game_finished) if self.game_finished: return if self.player_grid.destroyed(): self.opposite_grid.set_box_clickable(False) self.highlight_ships( self.opposite_grid.show_all_non_destroyed_ships()) self.after(3000, lambda victory=False: self.finish(victory)) self.game_finished = True elif self.opposite_grid.destroyed(): self.opposite_grid.set_box_clickable(False) self.player_grid.send_non_destroyed_ships() self.after(3000, lambda victory=True: self.finish(victory)) self.game_finished = True elif self.client_socket.connected(): if self.client_socket.recv("attack"): self.hit_a_box(self.player_grid, json.loads(self.client_socket.get("attack"))) elif self.client_socket.recv("quit"): self.finish(None) def get_default_turn(self) -> bool: if not self.client_socket.connected(): return True if self.player_id == 1: my_turn = random.choice([True, False]) self.client_socket.send("turn", not my_turn) return my_turn result = self.client_socket.wait_for("turn") if result == "quit": return False return bool(self.client_socket.get("turn")) def finish(self, victory: bool): FinishWindow(self, victory).mainloop() self.stop() def highlight_ships(self, ships: Sequence[Ship]): for ship in ships: for box in ship.boxes_covered: box.hover = not box.hover self.after(500, lambda: self.highlight_ships(ships)) def place_objects(self): self.button_back.move(x=20, y=20) self.text_finish.move(y=20, centerx=self.centerx) self.player_grid.move(x=20, centery=self.centery) self.opposite_grid.move(right=self.right - 20, centery=self.centery) self.turn_checker.resize_all_sprites(width=self.opposite_grid.left - self.player_grid.right - 150) self.turn_checker.move(center=self.center) def hit_a_box(self, navy: Navy, box: Union[Tuple[int, int], Box]): if isinstance(box, (list, tuple)): box = navy.get_box(*box) hitted = navy.box_hit(box) if navy.destroyed(): return turn = hitted if navy == self.opposite_grid else not hitted if turn is False: self.opposite_grid.set_box_clickable(False) wait_time = 1000 if turn != self.turn_checker.turn else 0 self.after(wait_time, lambda: self.set_turn(turn)) def set_turn(self, turn: bool): self.turn_checker.turn = turn if self.turn_checker.turn is True: self.opposite_grid.set_box_clickable(True) if self.turn_checker.turn is False and not self.client_socket.connected( ) and not self.player_grid.destroyed(): self.after( 1000, lambda: self.hit_a_box( self.player_grid, self.ai.play(self.player_grid.map)))
class NavySetup(Window): def __init__(self): Window.__init__(self, bg_color=(0, 200, 255)) self.gameplay = Gameplay() self.enemy_quit_window = EnemyQuitGame(self) self.transition = GameSetupTransition() self.count_down = CountDown(self, 60, "Time left: {seconds}", font=(None, 70), color=WHITE) self.start_count_down = lambda: self.count_down.start( at_end=self.timeout) if self.client_socket.connected() else None self.button_back = ImageButton(self, RESOURCES.IMG["arrow_blue"], rotate=180, size=50, callback=self.stop) self.navy_grid = Grid(self, bg_color=(0, 157, 255)) self.__boxes_dict = {(i, j): BoxSetup(self, size=BOX_SIZE, pos=(i, j)) for i in range(NB_LINES_BOXES) for j in range(NB_COLUMNS_BOXES)} self.__boxes_list = list(self.__boxes_dict.values()) self.navy_grid.place_multiple(self.__boxes_dict) self.ships_list = DrawableListVertical(offset=70, justify="left") for ship_name, ship_infos in SHIPS.items(): ship_line = DrawableListHorizontal(offset=ship_infos["offset"]) for _ in range(ship_infos["nb"]): ship_line.add(ShipSetup(self, ship_name, ship_infos["size"])) self.ships_list.add(ship_line) option_size = 50 self.button_restart = Button(self, img=Image(RESOURCES.IMG["reload_blue"], size=option_size), callback=self.reinit_all_ships) self.button_random = Button(self, img=Image(RESOURCES.IMG["random"], size=option_size), callback=self.shuffle) self.button_play = Button(self, "Play", font=(None, 40), callback=self.play) @property def ships(self) -> Sequence[ShipSetup]: return self.ships_list.drawable @property def boxes(self) -> Sequence[BoxSetup]: return self.__boxes_list def start(self, player_id: int) -> None: self.gameplay.player_id = player_id self.mainloop(transition=self.transition) def on_start_loop(self) -> None: self.start_count_down() def on_quit(self) -> None: self.reinit_all_ships() def place_objects(self) -> None: self.button_back.move(x=20, y=20) self.count_down.move(top=20, right=self.right - 20) self.navy_grid.move(x=20, centery=self.centery) self.ships_list.move(left=self.navy_grid.right + 100, top=self.navy_grid.top + 30) self.button_restart.move(left=self.navy_grid.right + 20, bottom=self.navy_grid.bottom) self.button_random.move(left=self.button_restart.right + 20, bottom=self.navy_grid.bottom) self.button_play.move(right=self.right - 20, bottom=self.bottom - 20) for ship in self.ships: ship.default_center = ship.center def update(self): self.button_play.state = Button.NORMAL if all( ship.on_map for ship in self.ships) else Button.DISABLED if self.client_socket.recv("quit"): self.count_down.stop() self.enemy_quit_window.mainloop() self.stop() def create_setup(self) -> Sequence[dict[str, dict[str, Any]]]: setup = list() for ship in self.ships: setup.append({ "name": ship.name, "orient": ship.orient, "boxes": [box.pos for box in ship.boxes_covered] }) return setup def timeout(self): for ship in filter(lambda ship: not ship.on_map, self.ships): self.set_random_position_for_ship(ship) self.play() def play(self): if not all(ship.on_map for ship in self.ships): return if not self.client_socket.connected(): ai_navy_setup = NavySetup() ai_navy_setup.shuffle() ai_setup = ai_navy_setup.create_setup() else: ai_setup = None self.count_down.stop() self.client_socket.send("ready") WaitEnemy(self).mainloop() self.gameplay.start(self.create_setup(), ai_setup=ai_setup) self.reinit_all_ships() if self.gameplay.restart: self.start_count_down() else: self.stop() def reinit_all_ships(self): for ship in self.ships: ship.center = ship.default_center ship.orient = ShipSetup.HORIZONTAL ship.clear() def get_box(self, line: int, column: float) -> BoxSetup: return self.__boxes_dict.get((line, column)) def remove_boxes_highlight(self): for box in self.boxes: box.hover = False box.state = Button.NORMAL def get_valid_highlighted_boxes(self) -> Sequence[BoxSetup]: return list( filter(lambda box: box.hover and box.state == Button.NORMAL, self.boxes)) def highlight_boxes(self, ship: ShipSetup) -> None: boxes = list() for box in self.boxes: self.highlight_one_box(ship, box, len(boxes)) if box.hover is True: boxes.append(box) if len(boxes) != ship.ship_size or any(not self.valid_box(ship, box) for box in boxes): for box in boxes: box.state = Button.DISABLED def highlight_one_box(self, ship: ShipSetup, box: BoxSetup, nb_boxes_covered: int) -> None: box.hover = False box.state = Button.NORMAL if nb_boxes_covered == ship.ship_size: return if ship.orient == ShipSetup.HORIZONTAL: if (box.top <= ship.centery <= box.bottom) is False: return if ship.left > box.centerx or ship.right < box.centerx: return else: if (box.left <= ship.centerx <= box.right) is False: return if ship.top > box.centery or ship.bottom < box.centery: return box.hover = True def valid_box(self, ship: ShipSetup, box: BoxSetup) -> bool: line, column = box.pos offsets = [(-1, -1), (0, -1), (1, -1), (-1, 0), (0, 0), (1, 0), (-1, 1), (0, 1), (1, 1)] for u, v in offsets: box = self.get_box(line + u, column + v) if box is None: continue if box.ship is not None and box.ship != ship: return False return True def shuffle(self) -> None: self.reinit_all_ships() for ship in self.ships: self.set_random_position_for_ship(ship) def set_random_position_for_ship(self, ship: ShipSetup) -> None: ship.orient = random.choice([ShipSetup.HORIZONTAL, ShipSetup.VERTICAL]) first_box = random.choice(self.get_available_boxes(ship)) boxes = [first_box] for i in range(1, ship.ship_size): u = first_box.pos[ 0] + i if ship.orient == ShipSetup.VERTICAL else first_box.pos[ 0] v = first_box.pos[ 1] + i if ship.orient == ShipSetup.HORIZONTAL else first_box.pos[ 1] boxes.append(self.get_box(u, v)) ship.place_ship_on_map(boxes) def get_available_boxes(self, ship: ShipSetup): available_boxes = list() for box in self.boxes: if not self.valid_box(ship, box): continue line, column = box.pos valid = True for i in range(1, ship.ship_size): u = line + i if ship.orient == ShipSetup.VERTICAL else line v = column + i if ship.orient == ShipSetup.HORIZONTAL else column b = self.get_box(u, v) if b is None or (not self.valid_box(ship, b)): valid = False break if valid: available_boxes.append(box) return available_boxes