def test_directions(): r""" Verify the four direction operations """ l = Location(1, 1) assert l.up() == l.relative(row_diff=-1) assert l.right() == l.relative(col_diff=1) assert l.down() == l.relative(row_diff=1) assert l.left() == l.relative(col_diff=-1)
def test_is_identical(): r""" Verify the \p piece_has_move method of the \p State class """ path = STATES_PATH / "state_move_verify.txt" state = State.importer(path, STD_BRD) orig, new = Location(0, 0), Location(1, 0) # Get the state information p_state = state.red.get_piece_at_loc(orig) m_state = state.red.get_move(p_state, new) # Build a move for comparison p = copy.deepcopy(state.red.get_piece_at_loc(orig)) m = Move(p, orig, new) assert m_state != m, "Equality fails since different references" assert Move.is_identical(m, m_state), "Verify the two moves are identical" assert Move.is_identical(m_state, m), "Verify the two moves are identical" # Color checks m._piece._color = Color.BLUE assert not Move.is_identical( m, m_state), "Verify if color does not match test fails" assert not Move.is_identical( m_state, m), "Verify if color does not match test fails" m._piece._color = Color.RED assert Move.is_identical(m, m_state), "Verify move correctly restored" # Rank checks prev_rank, m._piece._rank = m.piece.rank, Rank.flag() assert not Move.is_identical( m, m_state), "Verify if rank does not match test fails" assert not Move.is_identical(m_state, m), "Verify if rank does not match test fails" m._piece._rank = prev_rank assert Move.is_identical(m_state, m), "Verify move correctly restored" # New checks m._new = orig assert not Move.is_identical(m, m_state), "Verify new will cause a failure" assert not Move.is_identical(m_state, m), "Verify new will cause a failure" m._new = new assert Move.is_identical(m, m_state), "Verify move correctly restored" # Old checks m._orig = new assert not Move.is_identical(m, m_state), "Verify orig will cause a failure" assert not Move.is_identical(m_state, m), "Verify orig will cause a failure" m._orig = orig assert Move.is_identical(m, m_state), "Verify move correctly restored" attacked, m._attacked = m._attacked, [] # Just give it so not None assert not Move.is_identical( m, m_state), "Verify attacked will cause a failure" assert not Move.is_identical(m_state, m), "Verify attacked will cause a failure" m._attacked = attacked assert Move.is_identical(m, m_state), "Verify move correctly restored"
def _get_move(plyr: Player, l1, l2) -> Move: available_moves = plyr.move_set.avail values = list(available_moves.values()) v = [ v for v in values if v.orig == Location(*l1) and v.new == Location(*l2) ] assert v return v[0]
def test_verify_neighbor_movements(): r""" Verifies basic movement of a piece that ensures basic movement works """ l_orig = Location(1, 1) p = Piece(Color.RED, Rank.marshall(), l_orig) # Verify all the neighbor locations are valid for l_new in l_orig.neighbors(): Move(p, l_orig, l_new) # Verify with attacking other = Piece(Color.BLUE, Rank.bomb(), Location(0, 0)) for l_new in l_orig.neighbors(): other.loc = l_new Move(p, l_orig, l_new, other)
def _get_move_from_player(plyr: Player, _orig: Tuple[int, int], new: Tuple[int, int]) -> Move: r""" Get the move from (row1, col1) in \p l1 to (row2, col2) in \p l2. :param plyr: Player whose move will be extracted :param _orig: Original location to move from :param new: New location to move to :return: Move corresponding to the move pair """ available_moves = plyr.move_set.avail values = list(available_moves.values()) v = [v for v in values if v.orig == Location(*_orig) and v.new == Location(*new)] assert v return v[0]
def test_attack_color(): r""" Verify that attacks of different colors either do or do not raise errors """ # Verify when blue attacks red and red attacks blue, everything works loc = Location(0, 0) red = Piece(Color.RED, Rank.marshall(), loc) blue = Piece(Color.BLUE, Rank.marshall(), loc.down()) # Red attack blue Move(red, loc, blue.loc, blue) # Blue attack red Move(blue, blue.loc, red.loc, red) # Red attack red (exception) red2 = Piece(red.color, Rank.flag(), blue.loc) with pytest.raises(Exception): Move(red, red.loc, red2.loc, red2) # Blue attack blue (exception) blue2 = Piece(blue.color, Rank.flag(), red.loc) with pytest.raises(Exception): Move(blue, blue.loc, blue2.loc, blue2)
def test_scout_movements(): r""" Verify that only scouts can move multiple squares at once """ loc = Location(3, STD_BRD.num_cols // 2) # Just make sure color of the Scout has not effect (it may be a dumb test) for color in [Color.RED, Color.BLUE]: scout, miner = Piece(color, Rank.scout(), loc), Piece(color, Rank.miner(), loc) for p in [scout, miner]: for move_dist in [1, 2]: for i in range(2): if i == 0: new_loc = loc.relative(row_diff=move_dist) else: new_loc = loc.relative(col_diff=move_dist) if move_dist == 1 or p.rank == Rank.scout(): Move(p, loc, new_loc) else: with pytest.raises(Exception) as e_info: Move(p, p.loc, new_loc) assert substr_in_err("multiple", e_info)
def test_if_has_move(): r""" Verify the \p piece_has_move method of the \p State class """ path = STATES_PATH / "deep_q_verify.txt" state = State.importer(path, STD_BRD) # Marshall cannot move marshall_loc = Location(0, 0) p = state.get_player(Color.RED).get_piece_at_loc(marshall_loc) assert not state.piece_has_move(p) # Rank3 can move rank3_loc = Location(1, 0) p = state.get_player(Color.RED).get_piece_at_loc(rank3_loc) assert state.piece_has_move(p) # Bomb cannot move bomb_loc = Location(0, 4) p = state.get_player(Color.RED).get_piece_at_loc(bomb_loc) assert not state.piece_has_move(p) # Flag cannot move flag_loc = Location(0, 6) p = state.get_player(Color.RED).get_piece_at_loc(flag_loc) assert not state.piece_has_move(p) # verify pieces with known moves piece_col = (1, 2, 3, 5) for col in piece_col: flag_loc = Location(0, col) p = state.get_player(Color.RED).get_piece_at_loc(flag_loc) assert state.piece_has_move(p) flag_loc = Location(state.board.num_rows - 1, col) p = state.get_player(Color.BLUE).get_piece_at_loc(flag_loc) assert state.piece_has_move(p)
def test_diagonal_movement(): r""" Verify diagonal movements fail """ loc = Location(1, 1) move_list = [-1, 0, 1] p = Piece(Color.RED, Rank.spy(), loc) for row_move in move_list: for col_move in move_list: man_dist = abs(col_move) + abs(row_move) l_new = loc.relative(row_move, col_move) # Adjacent moves of (exactly) one are sanity checks and should pass if man_dist == 0: with pytest.raises(Exception): Move(p, p.loc, l_new) # Valid adjacent moves for sanity check elif man_dist == 1: Move(p, loc, l_new) # Diagonal moves should fail elif man_dist == 2: with pytest.raises(Exception) as e_info: Move(p, p.loc, l_new) assert substr_in_err("diagonal", e_info)
def test_edge_lists_board_corner(): r""" Verify basic information for two edge lists """ corners = [ Location(0, 0), Location(0, num_brd_cols - 1), Location(num_brd_rows - 1, 0), Location(num_brd_rows - 1, num_brd_cols - 1) ] for c in corners: el = brd.to_edge_lists(c) num_empty = sum([1 for x in el if len(x) == 0]) assert num_empty == 2, "Corner to edge list does not have two empty lists" # Verify length of the lists num_squares = sum([len(x) for x in el]) expected_count = num_brd_rows + num_brd_cols - 2 # Subtract two to loc itself in each dir assert num_squares == expected_count num_in_row = len(el.right) + len(el.left) assert num_in_row == num_brd_cols - 1 num_in_col = len(el.up) + len(el.down) assert num_in_col == num_brd_rows - 1
def test_immovable_pieces(): r""" Verify that an immovable piece raises an error """ moveable = set(range(Rank.MIN(), Rank.MAX() + 1)) | {Rank.SPY} immovable = {Rank.BOMB, Rank.FLAG} combined = moveable | immovable # Sanity check rank sets assert len(moveable) > 0 and len( immovable) > 0, "Both rank sets should be non-empty" assert moveable & immovable == set(), "Rank sets not disjoint" assert len(combined) == len(Rank.get_all()), "One or more ranks missing" loc = Location(0, 0) # As a sanity make sure color is irrelvant for color in [Color.RED, Color.BLUE]: # Verify each rank is correctly marked movable or immovable for r in combined: p = Piece(color, Rank(r), loc) new_loc = loc.down() if r in moveable: Move(p, loc, new_loc) if r in immovable: with pytest.raises(Exception): Move(p, p.loc, new_loc)
def test_printer_piece_movement(): r""" Verify piece movement as part of the \p Printer """ brd = build_test_board(5, 5) p = Printer(brd, {Piece(Color.RED, Rank(1), Location(2, 1))}, {Piece(Color.BLUE, Rank(2), Location(3, 2))}, Printer.Visibility.RED) assert p._is_loc_empty(Location(0, 0)) assert not p._is_loc_empty(Location(3, 2)) # p.move_piece(Location(3, 2), Location(2, 4)) # assert not p._is_loc_empty(Location(2, 4)) # assert p._is_loc_empty(Location(3, 2)) # # p.delete_piece(Location(2, 4)) # assert p._is_loc_empty(Location(2, 4)) p.delete_piece(Location(3, 2)) assert p._is_loc_empty(Location(3, 2))
def test_neighbors(): r""" Verify the neighbors set contains each direction """ l = Location(1, 1) for direct in [l.up(), l.right(), l.down(), l.left()]: assert direct in l.neighbors()
def test_outside_board_moves(): r""" Verify that the program raises errors when moves are illegal given the board""" l_orig = Location(0, 0) # Test top and left boundaries p = Piece(Color.RED, Rank.marshall(), l_orig) for l_new in [l_orig.up(), l_orig.left()]: with pytest.raises(Exception) as e_info: Move(p, l_orig, l_new) assert e_info.type == ValueError # Test right and bottom boundaries l_orig = Location(STD_BRD.num_rows - 1, STD_BRD.num_cols - 1) p = Piece(Color.RED, Rank.marshall(), l_orig) for l_new in [l_orig.right(), l_orig.down()]: with pytest.raises(Exception) as e_info: Move(p, l_orig, l_new) assert e_info.type == ValueError blocked_loc = Location(4, 2) # Verify a blocked location cannot be used as original location p = Piece(Color.RED, Rank.marshall(), blocked_loc) with pytest.raises(Exception) as e_info: Move(p, blocked_loc, blocked_loc.up()) assert e_info.type == ValueError # Verify a blocked location cannot be used as new location p = Piece(Color.RED, Rank.marshall(), blocked_loc.up()) with pytest.raises(Exception) as e_info: Move(p, blocked_loc.up(), blocked_loc) assert e_info.type == ValueError
def test_state_basic_moves(): r""" Verify the basic movement mechanics work without issue """ path = STATES_PATH / "state_move_verify.txt" assert path.exists(), "Move verify file does not exist" state = State.importer(path, STD_BRD) # Verify initial state matches expectations _verify_num_pieces_and_move_set_size(state, 7, 7, 4 + 3, 4 + 3) move_stack = MoveStack() # Define a series of moves. Entries in each tuple are: # 0: Original piece location # 1: Piece new location # 2: Number of red pieces # 3: Number of blue pieces # 4: Size of the red move set # 5: Size of the blue move set move_list = [((0, 1), (1, 1), 7, 7, 12, 7), ((9, 1), (8, 1), 7, 7, 12, 12), ((1, 1), (2, 1), 7, 7, 12, 12), ((8, 1), (7, 1), 7, 7, 12, 12), ((2, 1), (3, 1), 7, 7, 12, 12), ((7, 1), (6, 1), 7, 7, 12, 12), ((3, 1), (4, 1), 7, 7, 11, 12), # One less due to blocked by (4, 2) ((6, 1), (5, 1), 7, 7, 11, 11), # One less due to blocked by (5, 2) ((4, 1), (5, 1), 6, 6, 8, 8), # Both lost piece in battle ((9, 3), (6, 3), 6, 6, 8, 18), # Move blue scout ((0, 3), (3, 3), 6, 6, 18, 18), # Move red scout ((6, 3), (6, 5), 6, 6, 18, 23), # Move blue scout ((3, 3), (3, 5), 6, 6, 20, 20), # Move red scout ((6, 5), (6, 4), 6, 6, 23, 23), # Move blue scout ((3, 5), (9, 5), 6, 5, 16, 22), # Red scout attack blue spy ((6, 4), (0, 4), 6, 4, 16, 5) # Blue scout attack red bomb ] printer_out = [] for orig, new, num_red_p, num_blue_p, num_red_mv, num_blue_mv in move_list: orig, new = Location(orig[0], orig[1]), Location(new[0], new[1]) p = state.next_player.get_piece_at_loc(orig) assert p is not None attacked = state.get_other_player(state.next_player).get_piece_at_loc(new) move_stack.push(Move(p, orig, new, attacked)) assert state.update(move_stack.top()) assert state._printer._is_loc_empty(orig) _verify_num_pieces_and_move_set_size(state, num_red_p, num_blue_p, num_red_mv, num_blue_mv) printer_out.append(state.write_board()) # Try to move red bomb then the red flag for orig in [Location(0, 4), Location(0, 6)]: p = state.next_player.get_piece_at_loc(orig) assert p is not None for new in [orig.left(), orig.right]: attacked = state.get_other_player(state.next_player).get_piece_at_loc(new) with pytest.raises(Exception): Move(p, orig, new, attacked) # Verify Undo for i in range(2, len(move_list) + 1): _, _, num_red_p, num_blue_p, num_red_mv, num_blue_mv = move_list[-i] state.undo() assert state.write_board() == printer_out[-i], "Printer mismatch after do/undo" _verify_num_pieces_and_move_set_size(state, num_red_p, num_blue_p, num_red_mv, num_blue_mv)
~~~~~~~~~~~~~~~~ Test functions for the Stratego \p Board class. :copyright: (c) 2019 by Zayd Hammoudeh. :license: MIT, see LICENSE for more details. """ from stratego.board import Board from stratego.location import Location from testing_utils import build_test_board # Create a dummy board for use in comparison in the movements num_brd_rows = num_brd_cols = 10 blocked_loc = Location(5, 5) brd = build_test_board(num_brd_rows, num_brd_cols, {blocked_loc}) # type: Board def test_build_test_board(): r""" Test the function \p build_test_board """ assert brd.num_rows == num_brd_rows assert brd.num_cols == num_brd_cols assert {blocked_loc} == brd.blocked def test_edge_lists_board_corner(): r""" Verify basic information for two edge lists """ corners = [