class TUI(GameObserver, CursesMenuControllerObserver, GameControllerObserver): def __init__(self, curses_module=curses, logger=logger): super().__init__() self.curses_module = curses_module self.logger = logger self.row_count = None self.column_count = None self.board = None self.minefield_identifier = None self.most_recent_turn = None self.most_recent_board_snapshot = None self.create_minefield = None self.reveal_cell = None self.toggle_flag = None self.cursor_row = 0 self.cursor_column = 0 self.presentation_count = 0 self.FOREGROUND_COLOR_KEY = 1 self.BACKGROUND_COLOR_KEY = 2 self.screen_controller = None self.game_controller = None self.menu_options = None self.present_create_board_options = None #### # # GameObserver # def game_did_present_start_menu(self, menu_options: MenuOptions, present_create_board_options: Callable[ [PresentCreateBoardOptionsUseCaseObserver], None]): self.logger.info("Presenting start menu…") self.menu_options = menu_options self.present_create_board_options = present_create_board_options self.curses_module.wrapper(self._start) def game_did_present_create_board_options(self, minimum_row_count: int, minimum_column_count: int, default_row_count: int, default_column_count: int, create_board: Callable[[int, int, CreateBoardUseCaseObserver], None]): row_count = minimum_row_count column_count = minimum_column_count message = "At some point we will show a board options menu here, for now, just choose size {} x {}…" self.logger.info(message.format(row_count, column_count)) self.logging_controller.refresh() create_board(row_count=row_count, column_count=column_count) def game_did_create_board(self, board: Board, board_snapshot: [[CellType]], create_minefield: Callable[[int, int, CreateMinefieldUseCaseObserver], None]): self.create_minefield = create_minefield self.game_controller.prepare_to_create_minefield(board=board, board_snapshot=board_snapshot, observer=self) def game_did_create_minefield(self, minefield_identifier: UUID, first_turn: Turn, board_snapshot: [[CellType]], reveal_cell: Callable[[int, int, TakeTurnUseCaseObserver], None], toggle_flag: Callable[[int, int, TakeTurnUseCaseObserver], None]): self.logger.info("First cell revealed, mines placed, game on…") self.logging_controller.refresh() self.minefield_identifier = minefield_identifier self.most_recent_turn = first_turn self.reveal_cell = reveal_cell self.toggle_flag = toggle_flag self.game_controller.update_and_refresh(board_snapshot=board_snapshot) def game_did_take_turn(self, turn: Turn, game_state: GameState, board_snapshot: [[CellType]], reveal_cell: Callable[[int, int, TakeTurnUseCaseObserver], None], toggle_flag: Callable[[int, int, TakeTurnUseCaseObserver], None]): if game_state == GameState.Won: message = "You won! Congratulations." elif game_state == GameState.Lost: message = "Ruh-roh, looks like you stepped on a mine" else: message = "Took turn to {} on x: {}, y: {}".format(turn.action.name, turn.coordinate.row_index, turn.coordinate.column_index) self.logger.info(message) self.logging_controller.refresh() self.most_recent_turn = turn self.reveal_cell = reveal_cell self.toggle_flag = toggle_flag self.game_controller.update_and_refresh(board_snapshot=board_snapshot) #### # # GameControllerObserver # def did_signal_intent_to_toggle_flag(self, controller, row_index, column_index): if self.toggle_flag: self.toggle_flag(column_index=column_index, row_index=row_index) def did_signal_intent_to_reveal_cell(self, controller, row_index, column_index): if self.reveal_cell: self.reveal_cell(column_index=column_index, row_index=row_index) elif self.create_minefield: self.create_minefield(column_index=column_index, row_index=row_index) def did_signal_intent_to_pause_game(self, controller): self.screen_controller.refresh() def did_update_cursor_location(self, controller): controller.refresh() #### # # CursesMenuControllerObserver # def did_signal_intent_to_close_menu(self, controller): pass def did_select_menu_option_at_index(self, controller, menu_option_index: int): menu_option = self.menu_options.options[menu_option_index] if menu_option == StartMenuOptionType.StartNewGame: self.logger.info("Starting new game…") self.present_create_board_options() return True elif menu_option == StartMenuOptionType.Quit: self.logger.info("Quiting…") exit(1) def will_redisplay_updated_menu_options(self, controller): self._prepare_for_doupdate() def did_redisplay_updated_menu_options(self, controller): self.curses_module.doupdate() #### # # Private # def _start(self, screen): self.curses_module.noecho() self.curses_module.cbreak() self.curses_module.curs_set(0) if self.curses_module.has_colors(): self.curses_module.start_color() screen_height, screen_width = screen.getmaxyx() start_menu_height = 20 start_menu_width = 25 start_menu_origin_y = 1 start_menu_origin_x = 0 start_menu_window = self.curses_module.newwin(start_menu_height, start_menu_width, start_menu_origin_y, start_menu_origin_x) start_menu_window.keypad(1) data_source = StartMenuDataSource(menu_options=self.menu_options) start_menu_controller = CursesMenuController(keypress_listener=start_menu_window.getch, window=start_menu_window, data_source=data_source, box=True) game_height = start_menu_height game_width = screen_width - start_menu_width game_origin_y = start_menu_origin_y game_origin_x = start_menu_origin_x + start_menu_width game_window = self.curses_module.newwin(game_height, game_width, game_origin_y, game_origin_x) game_window.keypad(1) self.curses_module.init_pair(self.FOREGROUND_COLOR_KEY, self.curses_module.COLOR_BLACK, self.curses_module.COLOR_RED) self.curses_module.init_pair(self.BACKGROUND_COLOR_KEY, self.curses_module.COLOR_WHITE, self.curses_module.COLOR_BLACK) highlighted_color_pair = self.curses_module.color_pair(self.FOREGROUND_COLOR_KEY) nonhighlighted_color_pair = self.curses_module.color_pair(self.BACKGROUND_COLOR_KEY) self.game_controller = GameController(window=game_window, keypress_listener=game_window.getch, highlighted_color_pair=highlighted_color_pair, nonhighlighted_color_pair=nonhighlighted_color_pair, logger=self.logger, box=True) logging_window_height = 6 logging_window_width = screen_width logging_window = self.curses_module.newwin(logging_window_height, logging_window_width, start_menu_height + 1, 0) self.logging_controller = LineListController(window=logging_window, lines="", box=True) curses_logging_handler = CursesLoggingHandler(controller=self.logging_controller) self.logger.addHandler(curses_logging_handler) screen_controller_children = [start_menu_controller, self.game_controller, self.logging_controller] self.screen_controller = TitledController(title="MINESWEEPER", window=screen, child_controllers=screen_controller_children) self._prepare_for_doupdate() self.curses_module.doupdate() start_menu_controller.wait_and_listen_for_keypress(observer=self) def _prepare_for_doupdate(self): self.screen_controller.refresh()
def _start(self, screen): self.curses_module.noecho() self.curses_module.cbreak() self.curses_module.curs_set(0) if self.curses_module.has_colors(): self.curses_module.start_color() screen_height, screen_width = screen.getmaxyx() start_menu_height = 20 start_menu_width = 25 start_menu_origin_y = 1 start_menu_origin_x = 0 start_menu_window = self.curses_module.newwin(start_menu_height, start_menu_width, start_menu_origin_y, start_menu_origin_x) start_menu_window.keypad(1) data_source = StartMenuDataSource(menu_options=self.menu_options) start_menu_controller = CursesMenuController(keypress_listener=start_menu_window.getch, window=start_menu_window, data_source=data_source, box=True) game_height = start_menu_height game_width = screen_width - start_menu_width game_origin_y = start_menu_origin_y game_origin_x = start_menu_origin_x + start_menu_width game_window = self.curses_module.newwin(game_height, game_width, game_origin_y, game_origin_x) game_window.keypad(1) self.curses_module.init_pair(self.FOREGROUND_COLOR_KEY, self.curses_module.COLOR_BLACK, self.curses_module.COLOR_RED) self.curses_module.init_pair(self.BACKGROUND_COLOR_KEY, self.curses_module.COLOR_WHITE, self.curses_module.COLOR_BLACK) highlighted_color_pair = self.curses_module.color_pair(self.FOREGROUND_COLOR_KEY) nonhighlighted_color_pair = self.curses_module.color_pair(self.BACKGROUND_COLOR_KEY) self.game_controller = GameController(window=game_window, keypress_listener=game_window.getch, highlighted_color_pair=highlighted_color_pair, nonhighlighted_color_pair=nonhighlighted_color_pair, logger=self.logger, box=True) logging_window_height = 6 logging_window_width = screen_width logging_window = self.curses_module.newwin(logging_window_height, logging_window_width, start_menu_height + 1, 0) self.logging_controller = LineListController(window=logging_window, lines="", box=True) curses_logging_handler = CursesLoggingHandler(controller=self.logging_controller) self.logger.addHandler(curses_logging_handler) screen_controller_children = [start_menu_controller, self.game_controller, self.logging_controller] self.screen_controller = TitledController(title="MINESWEEPER", window=screen, child_controllers=screen_controller_children) self._prepare_for_doupdate() self.curses_module.doupdate() start_menu_controller.wait_and_listen_for_keypress(observer=self)