is_move_valid should be complete(-ish) and tested
This commit is contained in:
parent
dbd18b603f
commit
77f5e74785
63
board.py
63
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,
|
# bar. A fix of this is of course to check if the from_idx = bar and if so,
|
||||||
# allow some extra stuff!
|
# 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
|
# TODO: write tests
|
||||||
# FIXME: make sure to disallow backwards movement
|
# FIXME: make sure to disallow backwards movement
|
||||||
# TODO: implement double roll feature (4 dice if dice are equal)
|
# TODO: implement double roll feature (4 dice if dice are equal)
|
||||||
|
@ -43,7 +51,8 @@ class Board:
|
||||||
delta = to_idx - from_idx
|
delta = to_idx - from_idx
|
||||||
direction = sign(delta)
|
direction = sign(delta)
|
||||||
bearing_off = None
|
bearing_off = None
|
||||||
|
|
||||||
|
# FIXME: Use get instead of array-like indexing
|
||||||
if to_idx >= 1 and to_idx <= 24:
|
if to_idx >= 1 and to_idx <= 24:
|
||||||
to_state = board[to_idx]
|
to_state = board[to_idx]
|
||||||
bearing_off = False
|
bearing_off = False
|
||||||
|
@ -85,8 +94,24 @@ class Board:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def can_bear_off():
|
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
|
# TODO
|
||||||
return True
|
# TODO: add switch here instead of wonky ternary in all
|
||||||
|
|
||||||
|
|
||||||
return all([ is_forward_move(),
|
return all([ is_forward_move(),
|
||||||
|
@ -94,15 +119,12 @@ class Board:
|
||||||
bear_in_if_checker_on_bar(),
|
bear_in_if_checker_on_bar(),
|
||||||
checkers_at_from_idx(),
|
checkers_at_from_idx(),
|
||||||
no_block_at_to_idx(),
|
no_block_at_to_idx(),
|
||||||
can_bear_off() if bearing_off else True])
|
can_bear_off() if bearing_off else True ])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_winner(board, player):
|
def is_winner(board, player):
|
||||||
for i in range(1,25):
|
return Board.idxs_with_checkers_of_player(board, player) == []
|
||||||
if player * board[i] > 0:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def calculate_legal_states(board, player, roll):
|
def calculate_legal_states(board, player, roll):
|
||||||
# Find all pips with things on them belonging to the player
|
# 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
|
# TODO: make sure that it is not possible to do nothing on first part of
|
||||||
# turn and then do something with the second die
|
# turn and then do something with the second die
|
||||||
print("Find legal moves: ",roll,"-"*20)
|
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):
|
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,
|
boards = [(do_move(board,
|
||||||
player,
|
player,
|
||||||
(idx, idx + (face_value * player))) if
|
(idx, idx + (face_value * player)))
|
||||||
is_move_valid(board,
|
if is_move_valid(board,
|
||||||
player,
|
player,
|
||||||
face_value,
|
face_value,
|
||||||
(idx, idx + (face_value * player))) else None)
|
(idx, idx + (face_value * player)))
|
||||||
for idx
|
else None)
|
||||||
in idxs_with_checkers]
|
for idx in idxs_with_checkers]
|
||||||
|
|
||||||
# If no move can be made, make sure to include current board
|
# If no move can be made, make sure to include current board
|
||||||
if is_move_valid(board, player, face_value, None):
|
if is_move_valid(board, player, face_value, None):
|
||||||
|
@ -152,7 +167,7 @@ class Board:
|
||||||
# Calculate boards resulting from first move
|
# Calculate boards resulting from first move
|
||||||
boards = calc_moves(board, pair[0])
|
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
|
# Calculate boards resulting from second move
|
||||||
nested_boards = [calc_moves(board, x) for board in boards]
|
nested_boards = [calc_moves(board, x) for board in boards]
|
||||||
boards = [board for boards in nested_boards for board in boards]
|
boards = [board for boards in nested_boards for board in boards]
|
||||||
|
|
163
test.py
163
test.py
|
@ -2,21 +2,21 @@ import unittest
|
||||||
from board import Board
|
from board import Board
|
||||||
|
|
||||||
class TestIsMoveValid(unittest.TestCase):
|
class TestIsMoveValid(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.initial = Board.initial_state
|
||||||
|
|
||||||
|
|
||||||
def test_simple_movement(self):
|
def test_simple_movement(self):
|
||||||
board = Board.initial_state
|
self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (1, 3)), True) # White
|
||||||
self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 3)), True) # White
|
self.assertEqual(Board.is_move_valid(self.initial, -1, 3, (24, 21)), True) # Black
|
||||||
self.assertEqual(Board.is_move_valid(board, -1, 3, (24, 21)), True) # Black
|
|
||||||
|
|
||||||
def test_backwards_movement_invalid(self):
|
def test_backwards_movement_invalid(self):
|
||||||
board = Board.initial_state
|
self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (12, 10)), False)
|
||||||
self.assertEqual(Board.is_move_valid(board, 1, 2, (12, 10)), False)
|
self.assertEqual(Board.is_move_valid(self.initial, -1, 3, (8, 11)), False)
|
||||||
self.assertEqual(Board.is_move_valid(board, -1, 3, (8, 11)), False)
|
|
||||||
|
|
||||||
def test_face_value_match_move_length(self):
|
def test_face_value_match_move_length(self):
|
||||||
board = Board.initial_state
|
self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (1, 3)), True)
|
||||||
self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 3)), True)
|
self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (1, 4)), False)
|
||||||
self.assertEqual(Board.is_move_valid(board, 1, 2, (1, 4)), False)
|
|
||||||
|
|
||||||
def test_bear_in(self):
|
def test_bear_in(self):
|
||||||
board = [ 1,
|
board = [ 1,
|
||||||
|
@ -57,16 +57,14 @@ class TestIsMoveValid(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
def test_owned_checker_at_from(self):
|
def test_owned_checker_at_from(self):
|
||||||
board = Board.initial_state
|
self.assertEqual(Board.is_move_valid(self.initial, 1, 2, (3, 5)), False) # No checkers
|
||||||
self.assertEqual(Board.is_move_valid(board, 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(board, -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(board, 1, 2, (8, 10)), False) # Opponent checkers
|
self.assertEqual(Board.is_move_valid(self.initial, -1, 2, (1, 3)), False) # Opponent checkers
|
||||||
self.assertEqual(Board.is_move_valid(board, -1, 2, (1, 3)), False) # Opponent checkers
|
|
||||||
|
|
||||||
def test_no_block_at_to(self):
|
def test_no_block_at_to(self):
|
||||||
board = Board.initial_state
|
self.assertEqual(Board.is_move_valid(self.initial, 1, 5, (1, 6)), False)
|
||||||
self.assertEqual(Board.is_move_valid(board, 1, 5, (1, 6)), False)
|
self.assertEqual(Board.is_move_valid(self.initial, -1, 5, (24, 19)), False)
|
||||||
self.assertEqual(Board.is_move_valid(board, -1, 5, (24, 19)), False)
|
|
||||||
|
|
||||||
def test_able_to_hit_opponent_checkers(self):
|
def test_able_to_hit_opponent_checkers(self):
|
||||||
board = [ 0,
|
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, 2, (24, 26)), True)
|
||||||
self.assertEqual(Board.is_move_valid(board, 1, 1, (24, 25)), 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
|
# 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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user