def it_can_undo_last_move_or_pull(self, reverse_mover): src = index_1d(4, 2, reverse_mover.board.width) dest = index_1d(3, 2, reverse_mover.board.width) assert reverse_mover.move(Direction.LEFT) reverse_mover.undo() assert reverse_mover.state.pusher_position(DEFAULT_PIECE_ID) == src assert reverse_mover.board[src].has_pusher assert not reverse_mover.board[dest].has_pusher reverse_mover.pulls_boxes = True box_src = index_1d(4, 1, reverse_mover.board.width) box_dest = index_1d(4, 2, reverse_mover.board.width) pusher_src = box_dest pusher_dest = index_1d(4, 3, reverse_mover.board.width) reverse_mover.move(Direction.DOWN) reverse_mover.undo() assert reverse_mover.state.box_position(DEFAULT_PIECE_ID) == box_src assert reverse_mover.state.pusher_position(DEFAULT_PIECE_ID) == pusher_src assert reverse_mover.board[box_src].has_box assert reverse_mover.board[pusher_src].has_pusher assert not reverse_mover.board[box_dest].has_box assert not reverse_mover.board[pusher_dest].has_pusher
def it_moves_pusher_in_requested_direction(self, forward_mover): src = index_1d(4, 2, forward_mover.board.width) dest = index_1d(3, 2, forward_mover.board.width) assert forward_mover.move(Direction.LEFT) assert forward_mover.state.pusher_position(DEFAULT_PIECE_ID) == dest assert not forward_mover.board[src].has_pusher assert forward_mover.board[dest].has_pusher
def it_can_undo_last_jump(self, reverse_mover): src = index_1d(4, 2, reverse_mover.board.width) dest = index_1d(1, 1, reverse_mover.board.width) reverse_mover.jump(dest) reverse_mover.undo() assert reverse_mover.state.pusher_position(DEFAULT_PIECE_ID) == src assert reverse_mover.board[src].has_pusher == True assert reverse_mover.board[dest].has_pusher == False
def it_skips_explicitly_excluded_positions(self): root = index_1d(5, 1, 7) assert self.board_graph.reachables( root, excluded_positions=[root] ) == [index_1d(4, 1, 7)] root = index_1d(5, 1, 7) assert self.board_graph.reachables( root, excluded_positions=[index_1d(4, 1, 7)] ) == [root]
def it_can_undo_last_move_or_push(self, forward_mover): src = index_1d(4, 2, forward_mover.board.width) dest = index_1d(3, 2, forward_mover.board.width) forward_mover.move(Direction.LEFT) forward_mover.undo() assert forward_mover.state.pusher_position(DEFAULT_PIECE_ID) == src assert forward_mover.board[src].has_pusher == True assert forward_mover.board[dest].has_pusher == False
def it_can_exclude_some_positions(self): expected = [index_1d(5, 1, 7), index_1d(4, 1, 7), index_1d(3, 1, 7), index_1d(3, 2, 7)] excluded = [index_1d(3, 3, 7), index_1d(2, 3, 7), index_1d(1, 3, 7), index_1d(1, 2, 7), index_1d(1, 1, 7)] assert ( self.variant_board.positions_reachable_by_pusher( pusher_position=index_1d(5, 1, 7), excluded_positions=excluded ) == expected )
def it_returns_list_of_positions_reachable_by_pusher_movement_only(self): expected = [ index_1d(5, 1, 7), index_1d(4, 1, 7), index_1d(3, 1, 7), index_1d(3, 2, 7), index_1d(3, 3, 7), index_1d(2, 3, 7), index_1d(1, 3, 7), index_1d(1, 2, 7), index_1d(1, 1, 7), ] assert self.variant_board.positions_reachable_by_pusher(pusher_position=index_1d(5, 1, 7)) == expected
def test_returns_sequence_of_positions_defining_shortest_path_for_pusher_jump(self, variant_board): start_position = index_1d(11, 8, variant_board.width) end_position = index_1d(8, 5, variant_board.width) expected = variant_board.position_path_to_direction_path( variant_board.find_jump_path(start_position, end_position) ) assert expected["path"] == [ Direction.UP, Direction.UP, Direction.UP, Direction.LEFT, Direction.LEFT, Direction.LEFT, ]
def it_doesnt_require_that_start_position_actually_contain_pusher(self): expected = [ index_1d(4, 1, 7), index_1d(3, 1, 7), index_1d(3, 2, 7), index_1d(3, 3, 7), index_1d(2, 3, 7), index_1d(1, 3, 7), index_1d(1, 2, 7), index_1d(1, 1, 7), ] assert self.variant_board.positions_reachable_by_pusher(pusher_position=index_1d(4, 1, 7)) == expected
def it_doesnt_pull_boxes_if_flag_is_not_set(self, reverse_mover): reverse_mover.pulls_boxes = False box_src = index_1d(4, 1, reverse_mover.board.width) pusher_src = index_1d(4, 2, reverse_mover.board.width) pusher_dest = index_1d(4, 3, reverse_mover.board.width) assert reverse_mover.move(Direction.DOWN) assert reverse_mover.state.box_position( DEFAULT_PIECE_ID ) == box_src assert reverse_mover.state.pusher_position( DEFAULT_PIECE_ID ) == pusher_dest assert reverse_mover.board[box_src].has_box assert not reverse_mover.board[pusher_src].has_box assert not reverse_mover.board[pusher_src].has_pusher assert reverse_mover.board[pusher_dest].has_pusher
def it_memoizes_last_jump_into_movement_history(self, reverse_mover): expected = [ AtomicMove(Direction.UP, False), AtomicMove(Direction.LEFT, False), AtomicMove(Direction.LEFT, False), AtomicMove(Direction.LEFT, False), ] for am in expected: am.is_jump = True reverse_mover.jump(index_1d(1, 1, reverse_mover.board.width)) assert reverse_mover.last_performed_moves == expected
def it_pulls_box_behind_pusher(self, reverse_mover): reverse_mover.pulls_boxes = True box_src = index_1d(4, 1, reverse_mover.board.width) box_dest = index_1d(4, 2, reverse_mover.board.width) pusher_src = box_dest pusher_dest = index_1d(4, 3, reverse_mover.board.width) assert reverse_mover.move(Direction.DOWN) assert reverse_mover.state.box_position( DEFAULT_PIECE_ID ) == box_dest assert reverse_mover.state.pusher_position( DEFAULT_PIECE_ID ) == pusher_dest assert not reverse_mover.board[box_src].has_box assert not reverse_mover.board[pusher_src].has_pusher assert reverse_mover.board[box_dest].has_box assert reverse_mover.board[pusher_dest].has_pusher
def it_pushes_box_in_front_of_pusher(self, forward_mover): assert forward_mover.move(Direction.DOWN) assert forward_mover.move(Direction.RIGHT) box_src = index_1d(5, 2, forward_mover.board.width) box_dest = index_1d(5, 1, forward_mover.board.width) pusher_src = index_1d(5, 3, forward_mover.board.width) pusher_dest = box_src assert forward_mover.move(Direction.UP) assert forward_mover.state.box_position( DEFAULT_PIECE_ID + 1 ) == box_dest assert forward_mover.state.pusher_position( DEFAULT_PIECE_ID ) == pusher_dest assert not forward_mover.board[box_src].has_box assert not forward_mover.board[pusher_src].has_pusher assert forward_mover.board[box_dest].has_box assert forward_mover.board[pusher_dest].has_pusher
def boxes_positions(board_str_width): return { DEFAULT_PIECE_ID: index_1d(5, 2, board_str_width), DEFAULT_PIECE_ID + 1: index_1d(7, 3, board_str_width), DEFAULT_PIECE_ID + 2: index_1d(5, 4, board_str_width), DEFAULT_PIECE_ID + 3: index_1d(7, 4, board_str_width), DEFAULT_PIECE_ID + 4: index_1d(2, 7, board_str_width), DEFAULT_PIECE_ID + 5: index_1d(5, 7, board_str_width), }
def goals_positions(board_str_width): return { DEFAULT_PIECE_ID: index_1d(16, 6, board_str_width), DEFAULT_PIECE_ID + 1: index_1d(17, 6, board_str_width), DEFAULT_PIECE_ID + 2: index_1d(16, 7, board_str_width), DEFAULT_PIECE_ID + 3: index_1d(17, 7, board_str_width), DEFAULT_PIECE_ID + 4: index_1d(16, 8, board_str_width), DEFAULT_PIECE_ID + 5: index_1d(17, 8, board_str_width), }
def test_silently_stops_search_on_illegal_direction(self, variant_board): direction_path = [Direction.DOWN, Direction.NORTH_WEST] start_position = index_1d(11, 8, variant_board.width) assert variant_board.path_destination(start_position, direction_path) == index_1d( 11, 9, variant_board.width )
def test_silently_stops_search_on_first_of_board_position(self, variant_board): direction_path = [Direction.DOWN, Direction.DOWN, Direction.DOWN] start_position = index_1d(11, 8, variant_board.width) assert variant_board.path_destination(start_position, direction_path) == index_1d( 11, 10, variant_board.width )
def test_calculates_destination_position_from_source_and_direction_path(self, variant_board): direction_path = [Direction.UP, Direction.RIGHT] start_position = index_1d(11, 8, variant_board.width) assert variant_board.path_destination(start_position, direction_path) == index_1d( 12, 7, variant_board.width )
def test_can_exclude_some_positions(self): assert self.variant_board.normalized_pusher_position( pusher_position=index_1d(4, 1, 7), excluded_positions=[index_1d(1, 1, 7)] ) == index_1d(3, 1, 7)
def test_doesnt_require_that_start_position_actually_contain_pusher(self): assert self.variant_board.normalized_pusher_position(pusher_position=index_1d(4, 1, 7)) == index_1d(1, 1, 7)
def test_returns_top_left_position_of_pusher_in_his_reachable_area(self): assert self.variant_board.normalized_pusher_position(pusher_position=index_1d(5, 1, 7)) == index_1d(1, 1, 7)
def it_refuses_to_jump_off_the_board(self, reverse_mover): with pytest.raises(IndexError): reverse_mover.jump(index_1d(42, 42, reverse_mover.board.width))
def it_calculates_all_positions_reachable_from_root(self): root = index_1d(5, 1, 7) assert self.board_graph.reachables(root ) == [root, index_1d(4, 1, 7)]
def pushers_positions(board_str_width): return { DEFAULT_PIECE_ID: index_1d(7, 1, board_str_width), DEFAULT_PIECE_ID + 1: index_1d(11, 8, board_str_width), }
def it_calculates_1D_index_from_2D_coordinates(self): assert index_1d(0, 0, 5) == 0 assert index_1d(4, 5, 5) == 29 assert index_1d(3, 3, 5) == 18
def invalid_goal_position(): return index_1d(17, 8, 42)
def invalid_pusher_position(): return index_1d(11, 8, 42)
def normalized_pushers_positions(board_str_width): return { DEFAULT_PIECE_ID: index_1d(5, 1, board_str_width), DEFAULT_PIECE_ID + 1: index_1d(8, 4, board_str_width), }
def test_returns_empty_sequence_if_movement_is_blocked(self, variant_board): assert variant_board.find_move_path(index_1d(11, 8, variant_board.width), 0) == []
def invalid_box_position(): return index_1d(5, 7, 42)