From 77f5e74785334485e0ccb376d602818546081628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoffer=20M=C3=BCller=20Madsen?= Date: Thu, 15 Feb 2018 12:21:42 +0100 Subject: [PATCH] is_move_valid should be complete(-ish) and tested --- board.py | 63 +++++++++++++-------- test.py | 163 +++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 185 insertions(+), 41 deletions(-) diff --git a/board.py b/board.py index 688b82f..37e22b5 100644 --- a/board.py +++ b/board.py @@ -23,6 +23,14 @@ class Board: # bar. A fix of this is of course to check if the from_idx = bar and if so, # allow some extra stuff! + @staticmethod + def idxs_with_checkers_of_player(board, player): + idxs = [] + for idx, checker_count in enumerate(board): + if checker_count * player >= 1: + idxs.append(idx) + return idxs + # TODO: write tests # FIXME: make sure to disallow backwards movement # TODO: implement double roll feature (4 dice if dice are equal) @@ -43,7 +51,8 @@ class Board: delta = to_idx - from_idx direction = sign(delta) bearing_off = None - + + # FIXME: Use get instead of array-like indexing if to_idx >= 1 and to_idx <= 24: to_state = board[to_idx] bearing_off = False @@ -85,8 +94,24 @@ class Board: return True def can_bear_off(): + checker_idxs = Board.idxs_with_checkers_of_player(board, player) + def is_moving_backmost_checker(): + if player == 1: + return all([(idx >= from_idx) for idx in checker_idxs]) + else: + return all([(idx <= from_idx) for idx in checker_idxs]) + + def all_checkers_in_last_quadrant(): + if player == 1: + return all([(idx >= 19) for idx in checker_idxs]) + else: + return all([(idx >= 6) for idx in checker_idxs]) + + return all([ is_moving_backmost_checker(), + all_checkers_in_last_quadrant() ]) + # TODO - return True + # TODO: add switch here instead of wonky ternary in all return all([ is_forward_move(), @@ -94,15 +119,12 @@ class Board: bear_in_if_checker_on_bar(), checkers_at_from_idx(), no_block_at_to_idx(), - can_bear_off() if bearing_off else True]) + can_bear_off() if bearing_off else True ]) @staticmethod def is_winner(board, player): - for i in range(1,25): - if player * board[i] > 0: - return False - return True - + return Board.idxs_with_checkers_of_player(board, player) == [] + @staticmethod def calculate_legal_states(board, player, roll): # Find all pips with things on them belonging to the player @@ -114,25 +136,18 @@ class Board: # TODO: make sure that it is not possible to do nothing on first part of # turn and then do something with the second die print("Find legal moves: ",roll,"-"*20) - - def idxs_with_checkers_of_current_player(board): - idxs = [] - for idx, checker_count in enumerate(board): - if checker_count * player >= 1: - idxs.append(idx) - return idxs def calc_moves(board, face_value): - idxs_with_checkers = idxs_with_checkers_of_current_player(board) + idxs_with_checkers = Board.idxs_with_checkers_of_player(board, player) boards = [(do_move(board, player, - (idx, idx + (face_value * player))) if - is_move_valid(board, - player, - face_value, - (idx, idx + (face_value * player))) else None) - for idx - in idxs_with_checkers] + (idx, idx + (face_value * player))) + if is_move_valid(board, + player, + face_value, + (idx, idx + (face_value * player))) + else None) + for idx in idxs_with_checkers] # If no move can be made, make sure to include current board if is_move_valid(board, player, face_value, None): @@ -152,7 +167,7 @@ class Board: # Calculate boards resulting from first move boards = calc_moves(board, pair[0]) - for x in dice_permutations[1:None]: + for x in dice_permutations[1:]: # Calculate boards resulting from second move nested_boards = [calc_moves(board, x) for board in boards] boards = [board for boards in nested_boards for board in boards] diff --git a/test.py b/test.py index 6d8f6cf..d564533 100644 --- a/test.py +++ b/test.py @@ -2,21 +2,21 @@ import unittest from board import Board class TestIsMoveValid(unittest.TestCase): + def setUp(self): + self.initial = Board.initial_state + def test_simple_movement(self): - board = Board.initial_state - self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 3)), True) # White - self.assertEqual(Board.is_move_valid(board, -1, 3, (24, 21)), True) # Black + self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (1, 3)), True) # White + self.assertEqual(Board.is_move_valid(self.initial, -1, 3, (24, 21)), True) # Black def test_backwards_movement_invalid(self): - board = Board.initial_state - self.assertEqual(Board.is_move_valid(board, 1, 2, (12, 10)), False) - self.assertEqual(Board.is_move_valid(board, -1, 3, (8, 11)), False) + self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (12, 10)), False) + self.assertEqual(Board.is_move_valid(self.initial, -1, 3, (8, 11)), False) def test_face_value_match_move_length(self): - board = Board.initial_state - self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 3)), True) - self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 4)), False) + self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (1, 3)), True) + self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (1, 4)), False) def test_bear_in(self): board = [ 1, @@ -57,16 +57,14 @@ class TestIsMoveValid(unittest.TestCase): def test_owned_checker_at_from(self): - board = Board.initial_state - self.assertEqual(Board.is_move_valid(board, 1, 2, (3, 5)), False) # No checkers - self.assertEqual(Board.is_move_valid(board, -1, 2, (23, 21)), False) # No checkers - self.assertEqual(Board.is_move_valid(board, 1, 2, (8, 10)), False) # Opponent checkers - self.assertEqual(Board.is_move_valid(board, -1, 2, (1, 3)), False) # Opponent checkers + self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (3, 5)), False) # No checkers + self.assertEqual(Board.is_move_valid(self.initial, -1, 2, (23, 21)), False) # No checkers + self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (8, 10)), False) # Opponent checkers + self.assertEqual(Board.is_move_valid(self.initial, -1, 2, (1, 3)), False) # Opponent checkers def test_no_block_at_to(self): - board = Board.initial_state - self.assertEqual(Board.is_move_valid(board, 1, 5, (1, 6)), False) - self.assertEqual(Board.is_move_valid(board, -1, 5, (24, 19)), False) + self.assertEqual(Board.is_move_valid(self.initial, 1, 5, (1, 6)), False) + self.assertEqual(Board.is_move_valid(self.initial, -1, 5, (24, 19)), False) def test_able_to_hit_opponent_checkers(self): board = [ 0, @@ -88,7 +86,138 @@ class TestIsMoveValid(unittest.TestCase): self.assertEqual(Board.is_move_valid(board, 1, 2, (24, 26)), True) self.assertEqual(Board.is_move_valid(board, 1, 1, (24, 25)), True) + + def test_bear_off_with_higher_face_value(self): + board = [ 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 0, + 0 ] + self.assertEqual(Board.is_move_valid(board, 1, 5, (23, 28)), False) + self.assertEqual(Board.is_move_valid(board, 1, 5, (22, 27)), True) + + def test_bear_off_all_in_last_quadrant(self): + board = [ 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0 ] + self.assertEqual(Board.is_move_valid(board, 1, 1, (24, 25)), False) + board = [ 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0 ] + self.assertEqual(Board.is_move_valid(board, 1, 1, (24, 25)), False) + + board = [ 0, + -1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + -1 ] + self.assertEqual(Board.is_move_valid(board, -1, 1, (1, 0)), False) + board = [ 0, + -1, 0, 0, 0, 0, 0, + 0, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0 ] + self.assertEqual(Board.is_move_valid(board, -1, 1, (1, 0)), False) + # TODO: More tests for bearing off are needed + + +class TestIsWinner(unittest.TestCase): + def test_is_winner(self): + board = [ 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 3, 0, + 4, 0, 1, 0, 0, 0, + 0 ] + self.assertEqual(Board.is_winner(board, 1), False) + self.assertEqual(Board.is_winner(board, -1), True) + + board = [ 0, + 0, 0, 0, -1, 0, -4, + 0, -3, 0, 0, 0, 0, + -5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -2, + 0 ] + self.assertEqual(Board.is_winner(board, 1), True) + self.assertEqual(Board.is_winner(board, -1), False) + +class TestDoMove(unittest.TestCase): + def test_simple_move(self): + board = [ 0, + 2, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 3, 0, + 4, 0, 1, 0, 0, 0, + 0 ] + expected_board = [ 0, + 2, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 3, 0, + 4, 0, 1, 0, 0, 0, + 0 ] + self.assertEqual(Board.do_move(board, -1, (3, 2)), expected_board) + + def test_bear_in(self): + board = [ 1, + 1, 0, 0, 0, 0, -5, + 0, -3, 0, 0, 0, 5, + -5, 0, 0, 0, 3, 0, + 5, 0, 0, 0, 0, -2, + 0 ] + expected_board = [ 0, + 2, 0, 0, 0, 0, -5, + 0, -3, 0, 0, 0, 5, + -5, 0, 0, 0, 3, 0, + 5, 0, 0, 0, 0, -2, + 0 ] + self.assertEqual(Board.do_move(board, 1, (0, 1)), expected_board) + + board = [ 0, + 2, 0, 0, 0, 0, -5, + 0, -3, 0, 0, 0, 5, + -5, 0, 0, 0, 3, 0, + 5, 0, 0, 0, 0, -1, + -1 ] + expected_board = [ 0, + 2, 0, 0, 0, 0, -5, + 0, -3, 0, 0, 0, 5, + -5, 0, 0, 0, 3, 0, + 5, 0, 0, 0, 0, -2, + 0 ] + self.assertEqual(Board.do_move(board, -1, (25, 24)), expected_board) + + def test_bear_off(self): + board = [ 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0 ] + expected_board = [0] * 26 + self.assertEqual(Board.do_move(board, 1, (24, 30)), expected_board) + self.assertEqual(Board.do_move(board, 1, (24, 26)), expected_board) + self.assertEqual(Board.do_move(board, 1, (24, 25)), expected_board) + + board = [ 0, + -1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0 ] + expected_board = [0] * 26 + self.assertEqual(Board.do_move(board, -1, (1, 0)), expected_board) + self.assertEqual(Board.do_move(board, -1, (1, -1)), expected_board) + self.assertEqual(Board.do_move(board, -1, (1, -4)), expected_board) if __name__ == '__main__': unittest.main()