def test_same_from_and_to_coords_raises_exception(): game = Draughts({ '66': Counter(Color.BLACK), }) with pytest.raises(IllegalMoveError, match=game.SAME_SQUARE): game.move(Coords(x=6, y=6), Coords(x=6, y=6))
def test_draughts_setup_correctly(): game = Draughts() expected_board_setup = set([(Color.WHITE, Coords(x=0, y=0)), (Color.WHITE, Coords(x=2, y=0)), (Color.WHITE, Coords(x=4, y=0)), (Color.WHITE, Coords(x=6, y=0)), (Color.WHITE, Coords(x=1, y=1)), (Color.WHITE, Coords(x=3, y=1)), (Color.WHITE, Coords(x=5, y=1)), (Color.WHITE, Coords(x=7, y=1)), (Color.WHITE, Coords(x=0, y=2)), (Color.WHITE, Coords(x=2, y=2)), (Color.WHITE, Coords(x=4, y=2)), (Color.WHITE, Coords(x=6, y=2)), (Color.BLACK, Coords(x=1, y=5)), (Color.BLACK, Coords(x=3, y=5)), (Color.BLACK, Coords(x=5, y=5)), (Color.BLACK, Coords(x=7, y=5)), (Color.BLACK, Coords(x=0, y=6)), (Color.BLACK, Coords(x=2, y=6)), (Color.BLACK, Coords(x=4, y=6)), (Color.BLACK, Coords(x=6, y=6)), (Color.BLACK, Coords(x=1, y=7)), (Color.BLACK, Coords(x=3, y=7)), (Color.BLACK, Coords(x=5, y=7)), (Color.BLACK, Coords(x=7, y=7))]) actual_board_setup = set((counter.color, counter.coords) for row in game.board for counter in row if counter) assert actual_board_setup == expected_board_setup
def test_move_raises_error_if_capture_possible_for_piece(): game = Draughts({ '66': Counter(Color.BLACK), '55': Counter(Color.WHITE), }) with pytest.raises(IllegalMoveError, match=game.CAPTURE_POSSIBLE): game.move(Coords(x=6, y=6), Coords(x=7, y=5))
def test_illegal_capture_raises_exception(): game = Draughts({ '00': Counter(Color.WHITE), }) game.playing_color = Color.WHITE with pytest.raises(IllegalMoveError, match=game.ILLEGAL_CAPTURE): game.move(Coords(x=0, y=0), Coords(x=2, y=2))
def test_draughts_piece_can_move(): game = Draughts() game.playing_color = Color.WHITE assert game.board[5][3] is None game.move(Coords(x=4, y=2), Coords(x=5, y=3)) assert game.board[4][2] is None assert game.board[5][3] == Counter(Color.WHITE)
def test_draughts_piece_can_capture(): game = Draughts({ '00': Counter(Color.WHITE), '11': Counter(Color.BLACK), }) game.playing_color = Color.WHITE game.move(Coords(x=0, y=0), Coords(x=2, y=2)) assert game.board[1][1] is None assert game.board[2][2] == Counter(Color.WHITE)
def test_move_raises_error_if_capture_possible_for_crowned_piece(): game = Draughts({ '77': Counter(Color.BLACK), '55': Counter(Color.WHITE), '64': Counter(Color.BLACK) }) game.board[6][4].crowned = True with pytest.raises(IllegalMoveError, match=game.CAPTURE_POSSIBLE): game.move(Coords(x=7, y=7), Coords(x=6, y=6))
def test_crowned_black_piece_can_capture_backwards(): game = Draughts({ '55': Counter(Color.BLACK), '66': Counter(Color.WHITE), }) game.board[5][5].crowned = True game.move(Coords(x=5, y=5), Coords(x=7, y=7)) assert game.board[6][6] is None assert game.board[7][7] == Counter(Color.BLACK)
def test_crowned_white_piece_can_capture_backwards(): game = Draughts({ '22': Counter(Color.WHITE), '11': Counter(Color.BLACK), }) game.playing_color = Color.WHITE game.board[2][2].crowned = True game.move(Coords(x=2, y=2), Coords(x=0, y=0)) assert game.board[1][1] is None assert game.board[0][0] == Counter(Color.WHITE)
def test_black_can_capture_two_pieces_in_multiple_direction(): game = Draughts({ '66': Counter(Color.BLACK), '55': Counter(Color.WHITE), '53': Counter(Color.WHITE), }) game.move(Coords(x=6, y=6), Coords(x=6, y=2)) assert game.board[5][5] is None assert game.board[5][3] is None assert game.board[6][2] == Counter(Color.BLACK)
def test_crowned_piece_can_take_two_pieces_and_return_to_same_y_index(): game = Draughts({ '66': Counter(Color.BLACK), '55': Counter(Color.WHITE), '35': Counter(Color.WHITE), }) game.board[6][6].crowned = True game.move(Coords(x=6, y=6), Coords(x=2, y=6)) assert game.board[5][5] is None assert game.board[3][5] is None assert game.board[2][6] == Counter(Color.BLACK)
def test_can_capture_two_pieces_in_multiple_direction(): game = Draughts({ '00': Counter(Color.WHITE), '11': Counter(Color.BLACK), '13': Counter(Color.BLACK), }) game.playing_color = Color.WHITE game.move(Coords(x=0, y=0), Coords(x=0, y=4)) assert game.board[1][1] is None assert game.board[1][3] is None assert game.board[0][4] == Counter(Color.WHITE)
def test_can_capture_two_pieces_in_straight_line(): game = Draughts({ '00': Counter(Color.WHITE), '11': Counter(Color.BLACK), '33': Counter(Color.BLACK), }) game.playing_color = Color.WHITE game.move(Coords(x=0, y=0), Coords(x=4, y=4)) assert game.board[1][1] is None assert game.board[3][3] is None assert game.board[4][4] == Counter(Color.WHITE)
def test_black_can_capture_three_pieces_in_multiple_directions_s_shape(): game = Draughts({ '66': Counter(Color.BLACK), '55': Counter(Color.WHITE), '53': Counter(Color.WHITE), '51': Counter(Color.WHITE), }) game.move(Coords(x=6, y=6), Coords(x=4, y=0)) assert game.board[5][5] is None assert game.board[5][3] is None assert game.board[5][1] is None assert game.board[4][0] == Counter(Color.BLACK)
def test_can_capture_three_pieces_in_straight_line(): game = Draughts({ '77': Counter(Color.BLACK), '66': Counter(Color.WHITE), '44': Counter(Color.WHITE), '22': Counter(Color.WHITE), }) game.move(Coords(x=7, y=7), Coords(x=1, y=1)) assert game.board[6][6] is None assert game.board[4][4] is None assert game.board[2][2] is None assert game.board[1][1] == Counter(Color.BLACK)
def test_one_piece_capture_forced_to_make_two_move_capture_if_possible(): game = Draughts({ '66': Counter(Color.BLACK), '55': Counter(Color.WHITE), '33': Counter(Color.WHITE), }) game.move(Coords(x=6, y=6), Coords(x=4, y=4)) assert game.board[6][6] is None assert game.board[5][5] is None assert game.board[4][4] is None assert game.board[3][3] is None assert game.board[2][2] == Counter(Color.BLACK)
def test_white_can_capture_three_pieces_in_multiple_directions_l_shape(): game = Draughts({ '60': Counter(Color.WHITE), '51': Counter(Color.BLACK), '33': Counter(Color.BLACK), '35': Counter(Color.BLACK), }) game.playing_color = Color.WHITE game.move(Coords(x=6, y=0), Coords(x=4, y=6)) assert game.board[5][1] is None assert game.board[3][3] is None assert game.board[3][5] is None assert game.board[4][6] == Counter(Color.WHITE)
def test_two_extra_captures_forced_if_possible(): game = Draughts({ '66': Counter(Color.BLACK), '55': Counter(Color.WHITE), '33': Counter(Color.WHITE), '11': Counter(Color.WHITE), }) game.move(Coords(x=6, y=6), Coords(x=4, y=4)) assert game.board[6][6] is None assert game.board[5][5] is None assert game.board[3][3] is None assert game.board[1][1] is None assert game.board[0][0] == Counter(Color.BLACK)
def test_two_piece_capture_force_to_make_three_move_capture_if_possible(): game = Draughts({ '66': Counter(Color.BLACK), '55': Counter(Color.WHITE), '33': Counter(Color.WHITE), '11': Counter(Color.WHITE), }) game.move(Coords(x=6, y=6), Coords(x=2, y=2)) assert game.board[6][6] is None assert game.board[5][5] is None assert game.board[3][3] is None assert game.board[2][2] is None assert game.board[1][1] is None assert game.board[0][0] == Counter(Color.BLACK)
def test_counters_can_be_crowned(): game = Draughts({ '16': Counter(Color.WHITE), '11': Counter(Color.BLACK), }) black_piece = game.board[1][1] white_piece = game.board[1][6] assert str(black_piece) == '\u26C2' assert not black_piece.crowned game.move(Coords(x=1, y=1), Coords(x=0, y=0)) assert black_piece.crowned assert str(black_piece) == '\u26C3' assert str(white_piece) == '\u26C0' assert not white_piece.crowned game.move(Coords(x=1, y=6), Coords(x=0, y=7)) assert white_piece.crowned assert str(white_piece) == '\u26C1'
def test_no_counter_at_from_coords_raises_exception(): game = Draughts({}) # empty board with pytest.raises(IllegalMoveError, match=game.NO_PIECE): game.move(Coords(x=6, y=6), Coords(x=5, y=5))
def test_illegal_move_raises_exception(): game = Draughts() game.playing_color = Color.WHITE with pytest.raises(IllegalMoveError, match=game.ILLEGAL_MOVE): game.move(Coords(x=4, y=2), Coords(x=4, y=3))
def draughts(): return play_game(Draughts())
class TerminalGame(): RED = '\033[91m' BLUE = '\033[94m' END = '\033[0m' NO_MESSAGE = '\n\n' INVALID_ARG_ERROR = f'''{RED} Invalid Args provided.....\n Usage: python {sys.argv[0]} GAME_TYPE\n Game type options: (C)hess, (D)raughts, (O)thello {END}''' QUIT_MSG = f'\n{RED}Got too much for you, did it?!?{END}\n' GAME_OPTIONS = {'C': Chess(), 'D': Draughts(), 'O': Othello()} X_COORD_MAP = { 'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7 } Y_COORD_MAP = { '1': 0, '2': 1, '3': 2, '4': 3, '5': 4, '6': 5, '7': 6, '8': 7 } def __init__(self): self.game = self._parse_args_to_fetch_game() self.display_message = self.NO_MESSAGE self.player = None def play(self): """Main game loop. Continue until winner declared.""" while not self.game.winner: self.player = self.game.playing_color.value self._display_board_to_terminal() self._make_move() self._display_board_and_winner() def _parse_args_to_fetch_game(self): try: game_type = sys.argv[1].title()[0] return self.GAME_OPTIONS[game_type] except (KeyError, IndexError): print(self.INVALID_ARG_ERROR) sys.exit() def _display_board_and_winner(self): self.display_message = f'\n{self.BLUE}{self.player} wins!!! Thanks for playing.....{self.END}\n' self._display_board_to_terminal() def _make_move(self): try: move_coords = input( f'{self.player} to move. Choose wisely.....\n --> ') self.display_message = self.NO_MESSAGE if len(move_coords) > 2: move_coords = self._split_and_map_coords(move_coords) self.game.move(*move_coords) else: place_piece_coords = self._map_coords(move_coords) self.game.move(to_coords=place_piece_coords) except IllegalMoveError as error: self.display_message = f'\n{self.RED}{error.message}{self.END}\n' def _split_and_map_coords(self, move_coords): try: from_coords, to_coords = move_coords.split() return self._map_coords(from_coords), self._map_coords(to_coords) except ValueError: raise IllegalMoveError(self.game.input_error_msg) def _map_coords(self, input_coords): try: input_x, input_y = input_coords x_coord = self.X_COORD_MAP[str(input_x).lower()] y_coord = self.Y_COORD_MAP[str(input_y)] return f'{x_coord}{y_coord}' except (ValueError, KeyError): raise IllegalMoveError(self.game.input_error_msg) def _align_board_for_display(self): transposed_board = [list(row) for row in zip(*self.game.board)] return list(reversed(transposed_board)) def _display_board_to_terminal(self): self._clear_screen() board = self._align_board_for_display() board.append(self.game.x_axis()) print( tabulate(board, tablefmt="fancy_grid", showindex=self.game.y_axis())) print(self.display_message) def _clear_screen(self): print('\n' * 50)