def test_is_valid_move_true_when_can_move_up(): game = Pylos() fill_layer(game, 0) assert_equal(game.render(), "0101/0101/0101/0101#.../.../...#../..#.") assert game.is_valid_move((0, 0, 0))
def test_is_valid_move_false_when_own_ball_but_no_valid_targets(): game = Pylos() play_turn(game, None, (0, 0, 0)) play_turn(game, None, (0, 1, 0)) assert not game.is_valid_move((0, 0, 0))
def test_is_valid_move_false_when_blocked(): game = Pylos() fill_layer(game, 0) fill_layer(game, 1) assert_equal(game.current_player, 1) assert not game.is_valid_move((0, 3, 3))
def test_is_valid_move_true_when_source_is_part_of_support(): game = Pylos() fill_layer(game, 0) fill_layer(game, 1) assert_equal(game.render(), "0101/0101/0101/0101#010/101/010#../..#.") assert not game.is_valid_move((0, 1, 0))
def test_render(): game = Pylos() assert game.render() == "..../..../..../....#.../.../...#../..#." play_turn(game, None, (0, 0, 0)) assert game.render() == "0.../..../..../....#.../.../...#../..#."
def test_is_valid_move_false_when_source_is_current_player_ball_and_no_possible_higher_targets( ): game = Pylos() play_turn(game, None, (0, 0, 0), None, None) play_turn(game, None, (0, 0, 1), None, None) assert not game.is_valid_move((0, 0, 0))
def test_layer0_valid_move(): game = Pylos() play_turn(game, None, (0, 1, 2), None, None) assert_equal(game.render(), "..../..0./..../....#.../.../...#../..#.") assert_equal(game.current_player, 1) assert_equal(game.reserve, [14, 15])
def test_fill_entire_board_winner_is_player_whose_ball_is_on_the_top(): game = Pylos() fill_layer(game, 0) fill_layer(game, 1) fill_layer(game, 2) fill_layer(game, 3) assert game.game_over() assert game.winner == 1
def test_move_valid_source_location_new_state(): game = Pylos() game.move(None) assert game.actions == Actions() assert game.phase == pylos.PHASE_TARGET_LOCATION assert game.render() == "..../..../..../....#.../.../...#../..#." assert game.reserve == [15, 15] assert game.current_player == 0
def __init__(self): self.pylos = Pylos() self.done = False self.reset() # each move consists of 31 possible values (0-29 meaning one of the board positions, # 30 meaning 'none' or 'reserve' depending on the current phase) # note that for the 'target' phase, 30 is never a valid value self.action_space = spaces.Discrete(31)
def test_winner_correct_when_board_full(): game = Pylos() assert_equal(game.get_winner(), None) fill_layer(game, 0) fill_layer(game, 1) fill_layer(game, 2) fill_layer(game, 3) assert_equal(game.get_winner(), 1)
def test_valid_turn(): game = Pylos() play_turn(game, None, (0, 0, 0), None, None) assert game.actions == Actions() assert game.phase == pylos.PHASE_SOURCE_LOCATION assert game.render() == "0.../..../..../....#.../.../...#../..#." assert game.reserve == [14, 15] assert game.current_player == 1 assert_equal(game.winner, None)
def test_is_valid_move_true_when_source_is_current_player_ball_and_possible_higher_targets( ): game = Pylos() play_turn(game, None, (0, 0, 0), None, None) play_turn(game, None, (0, 2, 2), None, None) play_turn(game, None, (0, 2, 3), None, None) play_turn(game, None, (0, 3, 2), None, None) play_turn(game, None, (0, 3, 3), None, None) play_turn(game, None, (0, 0, 3), None, None) assert game.is_valid_move((0, 0, 0))
def test_layer1_invalid_move_insufficient_support(): game = Pylos() play_turn(game, None, (0, 1, 1)) play_turn(game, None, (0, 1, 2)) play_turn(game, None, (0, 2, 2)) play_turn(game, None, (1, 1, 1))
def setup_moveup_scenario(): game = Pylos() fill_layer(game, 0) play_turn(game, None, (1, 0, 1)) play_turn(game, None, (1, 0, 2)) play_turn(game, None, (1, 1, 1)) play_turn(game, None, (1, 1, 2)) play_turn(game, None, (1, 1, 0)) play_turn(game, None, (1, 2, 2)) assert game.render() == "0101/0101/0101/0101#.01/001/..1#../..#." return game
def test_invalid_single_retraction_no_square(): game = Pylos() play_turn(game, None, (0, 0, 0)) play_turn(game, None, (0, 3, 0)) play_turn(game, None, (0, 1, 0)) play_turn(game, None, (0, 3, 1)) play_turn(game, None, (0, 1, 1), (0, 1, 0))
def test_invalid_retraction_retract_opponent_ball(): game = Pylos() play_turn(game, None, (0, 0, 0)) play_turn(game, None, (0, 3, 0)) play_turn(game, None, (0, 1, 0)) play_turn(game, None, (0, 3, 1)) play_turn(game, None, (0, 0, 1)) play_turn(game, None, (0, 3, 2)) play_turn(game, None, (0, 1, 1), (0, 3, 0))
def test_layer1_valid_move(): game = Pylos() play_turn(game, None, (0, 1, 1)) play_turn(game, None, (0, 1, 2)) play_turn(game, None, (0, 2, 2)) play_turn(game, None, (0, 2, 1)) play_turn(game, None, (1, 1, 1)) assert game.layers[0] == [[None, None, None, None], [None, 0, 1, None], [None, 1, 0, None], [None, None, None, None]] assert game.layers[1] == [[None, None, None], [None, 0, None], [None, None, None]]
def test_valid_single_retraction(): game = Pylos() play_turn(game, None, (0, 0, 0)) play_turn(game, None, (0, 3, 0)) play_turn(game, None, (0, 1, 0)) play_turn(game, None, (0, 3, 1)) play_turn(game, None, (0, 0, 1)) play_turn(game, None, (0, 3, 2)) assert_equal(game.layers[0], [[0, 0, None, None], [0, None, None, None], [None, None, None, None], [1, 1, 1, None]]) play_turn(game, None, (0, 1, 1), (0, 1, 0)) assert_equal(game.layers[0], [[0, 0, None, None], [None, 0, None, None], [None, None, None, None], [1, 1, 1, None]])
def test_winner_correct_when_current_player_no_balls(): game = Pylos() assert game.get_winner() is None play_turn(game, None, (0, 0, 0)) play_turn(game, None, (0, 3, 0)) play_turn(game, None, (0, 0, 1)) play_turn(game, None, (0, 3, 1)) play_turn(game, None, (0, 1, 0)) play_turn(game, None, (0, 3, 2)) play_turn(game, None, (0, 1, 1), (0, 1, 0), (0, 1, 1)) play_turn(game, None, (0, 3, 3)) play_turn(game, None, (0, 0, 2)) play_turn(game, None, (0, 0, 3)) assert_equal(game.render(), "0001/..../..../1111#.../.../...#../..#.") # fill rows 2 and 3 for row in range(1, 3): play_turn(game, None, (0, row, 0)) play_turn(game, None, (0, row, 1)) play_turn(game, None, (0, row, 2)) play_turn(game, None, (0, row, 3)) assert_equal(game.reserve, [8, 6]) assert_equal(game.render(), "0001/0101/0101/1111#.../.../...#../..#.") # fill next layer fill_layer(game, 1) fill_layer(game, 2) assert_equal(game.reserve, [1, 0]) assert_equal(game.current_player, 1) assert_equal(game.render(), "0001/0101/0101/1111#010/101/010#10/10#.") assert_equal(game.determine_winner(), 0) assert_equal(game.get_winner(), 0)
def test_valid_upmove(): game = Pylos() play_turn(game, None, (0, 0, 0)) assert_equal(game.render(), "0.../..../..../....#.../.../...#../..#.") play_turn(game, None, (0, 3, 3)) assert_equal(game.render(), "0.../..../..../...1#.../.../...#../..#.") play_turn(game, None, (0, 0, 1)) play_turn(game, None, (0, 1, 0)) play_turn(game, None, (0, 1, 1)) assert_equal(game.render(), "00../10../..../...1#.../.../...#../..#.") play_turn(game, (0, 3, 3), (1, 0, 0)) assert_equal(game.render(), "00../10../..../....#1../.../...#../..#.")
def test_is_valid_move_false_when_source_is_other_player_ball(): game = Pylos() play_turn(game, None, (0, 0, 0), None, None) assert not game.is_valid_move((0, 0, 0))
def test_is_valid_move_false_when_source_is_empty(): game = Pylos() assert not game.is_valid_move((0, 0, 0))
def test_invalid_move_target_without_support(): game = Pylos() play_turn(game, None, (1, 0, 0))
def test_layer0_invalid_move_already_occupied(): game = Pylos() play_turn(game, None, (0, 1, 2)) play_turn(game, None, (0, 1, 2))
class PylosEnv(gym.Env): done: bool metadata = {} def __init__(self): self.pylos = Pylos() self.done = False self.reset() # each move consists of 31 possible values (0-29 meaning one of the board positions, # 30 meaning 'none' or 'reserve' depending on the current phase) # note that for the 'target' phase, 30 is never a valid value self.action_space = spaces.Discrete(31) def state_from_pylos(self): player_onehot = np.array( [1 - self.pylos.current_player, self.pylos.current_player]) # game phase phase_onehot = np.array([ 1 if self.pylos.phase == pylos.PHASE_SOURCE_LOCATION else 0, 1 if self.pylos.phase == pylos.PHASE_TARGET_LOCATION else 0, 1 if self.pylos.phase == pylos.PHASE_RETRACT1 else 0, 1 if self.pylos.phase == pylos.PHASE_RETRACT2 else 0 ]) # board (3 nodes per board position) board_vector = np.array([[ 1 if ball_owner is None else 0 for ball_owner in self.flat_board() ], [1 if ball_owner == 0 else 0 for ball_owner in self.flat_board()], [ 1 if ball_owner == 1 else 0 for ball_owner in self.flat_board() ]]).flatten('F') return np.concatenate([player_onehot, phase_onehot, board_vector]) def flat_board(self): result = [] for l in self.pylos.layers: for r in l: result += r return result def reset(self): self.done = False self.pylos = Pylos() return self.state_from_pylos() def step(self, action): if self.done: print("WARNING: step called after done") return self.state_from_pylos(), 0, self.done, { 'warning_step_after_done': True } location = map_action_to_location(action) if not self.pylos.is_valid_move(location): return self.create_invalid_move_step_response(location) player = self.pylos.current_player reserve_before = self.pylos.reserve[player] self.pylos.move(location) reserve_after = self.pylos.reserve[player] winner = self.pylos.get_winner() reward = 1 reward += reserve_after - reserve_before if winner is not None: reward += 10 self.done = True return self.state_from_pylos(), reward, self.done, { 'phase': self.pylos.phase } def render(self): if self.pylos.winner is not None: print(" *** Winner *** ", self.pylos.winner) print("Player: ", self.pylos.current_player) print("Phase: ", PHASE_NAMES[self.pylos.phase]) print("\n".join([self._render_row(r) for r in range(4)])) def _render_row(self, row): layers = self.pylos.render().split("#") split_layers = [layer.split("/") for layer in layers] return " ".join(([ layer[row] if row < len(layer) else " " * (4 - row) for layer in split_layers ])) def create_invalid_move_step_response(self, location): return self.state_from_pylos(), -10, self.done, {'invalid': location}
def test_is_valid_move_false_when_source_is_opponent_ball(): game = Pylos() fill_layer(game, 0) assert not game.is_valid_move((0, 3, 3))
def reset(self): self.done = False self.pylos = Pylos() return self.state_from_pylos()
def test_is_valid_move_true_when_source_is_reserve(): game = Pylos() assert game.is_valid_move(None)