diff --git a/board.py b/board.py index 12f5128..688b82f 100644 --- a/board.py +++ b/board.py @@ -33,28 +33,68 @@ class Board: # TODO: Allow not doing anything if and only if no alternatives @staticmethod def is_move_valid(board, player, face_value, move): - board = list(board) + def sign(a): + return (a > 0) - (a < 0) + from_idx = move[0] to_idx = move[1] - to_state = board[to_idx] + to_state = None from_state = board[from_idx] + delta = to_idx - from_idx + direction = sign(delta) + bearing_off = None - if from_idx == 26: - from_idx = 1 - face_value -= 1 - elif from_idx == 27: - from_idx = 24 - face_value -= 1 - - if not (1 <= from_idx <= 24 and - 1 <= to_idx <= 24): - return False - elif ( abs( from_idx - to_idx ) != face_value ): - return False - elif (from_state * player >= 1 and # Is moving player's own checker? - (to_state * player >= 0 or # Is 'to' empty or has player's own checkers? - to_state * -player == 1)): # Can opponent checker be hit? + if to_idx >= 1 and to_idx <= 24: + to_state = board[to_idx] + bearing_off = False + else: # Bearing off + to_state = 0 + bearing_off = True + + # print("_"*20) + # print("board:", board) + # print("to_idx:", to_idx, "board[to_idx]:", board[to_idx], "to_state:", to_state) + # print("+"*20) + + def is_forward_move(): + return direction == player + + def face_value_match_move_length(): + return abs(delta) == face_value + + def bear_in_if_checker_on_bar(): + if player == 1: + bar = 0 + else: + bar = 25 + + bar_state = board[bar] + + if bar_state != 0: + return from_idx == bar + else: + return True + + def checkers_at_from_idx(): + return sign(from_state) == player + + def no_block_at_to_idx(): + if -sign(to_state) == player: + return abs(to_state) == 1 + else: + return True + + def can_bear_off(): + # TODO return True + + + return all([ is_forward_move(), + face_value_match_move_length(), + bear_in_if_checker_on_bar(), + checkers_at_from_idx(), + no_block_at_to_idx(), + can_bear_off() if bearing_off else True]) @staticmethod def is_winner(board, player): @@ -69,6 +109,10 @@ class Board: # Iterate through each index and check if it's a possible move given the roll # If player is O, then check for idx + roll # If player is X, then check for idx - roll + + + # 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): diff --git a/test.py b/test.py new file mode 100644 index 0000000..6d8f6cf --- /dev/null +++ b/test.py @@ -0,0 +1,94 @@ +import unittest +from board import Board + +class TestIsMoveValid(unittest.TestCase): + + 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 + + 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) + + 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) + + 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 ] + self.assertEqual(Board.is_move_valid(board, 1, 2, (0, 2)), True) + + 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 ] + self.assertEqual(Board.is_move_valid(board, -1, 2, (25, 23)), True) + + + def test_force_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 ] + self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 3)), False) + self.assertEqual(Board.is_move_valid(board, -1, 3, (24, 21)), True) + + 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 ] + self.assertEqual(Board.is_move_valid(board, -1, 2, (24, 22)), False) + self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 3)), True) + + + 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 + + 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) + + def test_able_to_hit_opponent_checkers(self): + board = [ 0, + 2, 0, 0, -1, 0, -4, + 0, -3, 0, 0, 0, 5, + -5, 0, 0, 0, 3, 0, + 4, 0, 1, 0, 0, -2, + 0 ] + self.assertEqual(Board.is_move_valid(board, 1, 3, (1, 4)), True) + self.assertEqual(Board.is_move_valid(board, -1, 3, (24, 21)), True) + + def test_bear_off_simple(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 ] + self.assertEqual(Board.is_move_valid(board, 1, 2, (24, 26)), True) + self.assertEqual(Board.is_move_valid(board, 1, 1, (24, 25)), True) + + # TODO: More tests for bearing off are needed + +if __name__ == '__main__': + unittest.main()