def test_shorthand(self): test_layout = (""" ############ #a . . y# #b x# ############ """) num_rounds = 5 teams = [ stepping_player('>v<^-', '-----'), stepping_player('<^>v-', '-----') ] state = setup_game(teams, layout_dict=parse_layout(test_layout), max_rounds=5) player0_expected_positions = [(1, 1), (2, 1), (2, 2), (1, 2), (1, 1), (1, 1)] player1_expected_positions = [(10, 2), (9, 2), (9, 1), (10, 1), (10, 2), (10, 2)] assert state['bots'][0] == player0_expected_positions[0] assert state['bots'][1] == player1_expected_positions[0] for i in range(1, num_rounds + 1): for step in range(4): state = play_turn(state) assert state['bots'][0] == player0_expected_positions[i] assert state['bots'][1] == player1_expected_positions[i]
def test_equal_positions(): layout_str = """ ######## #0### # # . ...# ######## ######## #1### # # . ...# ######## ######## #E### # # . ...# ######## ######## #E### # # . ...# ######## """ layout = parse_layout(layout_str, allow_enemy_chars=True) assert layout['bots'] == [(1, 1), (1, 1)] assert layout['enemy'] == [(1, 1), (1, 1)] setup_test_game(layout=layout_str)
def test_initial_position(_n_test): """ Test that out test team receives the correct initial positions.""" layout_name, layout_string = get_random_layout() l = parse_layout(layout_string) initial_pos = initial_positions(l['walls']) def move(bot, state): if bot.is_blue and bot.turn == 0: assert bot._initial_position == initial_pos[0] assert bot.other._initial_position == initial_pos[2] assert bot.enemy[0]._initial_position == initial_pos[1] assert bot.enemy[1]._initial_position == initial_pos[3] if bot.is_blue and bot.turn == 1: assert bot._initial_position == initial_pos[2] assert bot.other._initial_position == initial_pos[0] assert bot.enemy[0]._initial_position == initial_pos[1] assert bot.enemy[1]._initial_position == initial_pos[3] if not bot.is_blue and bot.turn == 0: assert bot._initial_position == initial_pos[1] assert bot.other._initial_position == initial_pos[3] assert bot.enemy[0]._initial_position == initial_pos[0] assert bot.enemy[1]._initial_position == initial_pos[2] if not bot.is_blue and bot.turn == 1: assert bot._initial_position == initial_pos[3] assert bot.other._initial_position == initial_pos[1] assert bot.enemy[0]._initial_position == initial_pos[0] assert bot.enemy[1]._initial_position == initial_pos[2] return randomBot(bot, state) state = run_game([move, move], max_rounds=3, layout_dict=l) # assertions might have been caught in run_game # check that all is good assert state['fatal_errors'] == [[], []]
def test_uniform_noise_4_bots_no_noise_manhattan(): test_layout = (""" ################## # #. . # . 2 # # ##### #####3# # 0 . # . .#1# ################## """) parsed = parse_layout(test_layout) expected_0 = [(1, 1), (3, 1), (4, 1), (5, 1), (6, 1), (1, 2), (1, 3), (2, 3), (3, 3), (4, 3), (5, 3), (6, 3), (7, 3), (7, 2)] position_bucket_0 = collections.defaultdict(int) expected_2 = [(13, 1)] position_bucket_2 = {(13, 1): 0} for i in range(200): noised = gf.noiser(walls=parsed['walls'], bot_position=parsed['bots'][1], enemy_positions=parsed['bots'][0::2]) assert noised['is_noisy'] == [True, False] position_bucket_0[noised['enemy_positions'][0]] += 1 position_bucket_2[noised['enemy_positions'][1]] += 1 assert 200 == sum(position_bucket_0.values()) assert 200 == sum(position_bucket_2.values()) # Since this is a randomized algorithm we need to be a bit lenient with # our tests. We check that each position was selected at least once. assert set(position_bucket_0.keys()) == set(expected_0) assert set(position_bucket_2.keys()) == set(expected_2)
def test_uniform_noise_manhattan(noise_radius, expected, test_layout=None): # Test how bot 1 observes bot 0 if not test_layout: test_layout = (""" ################## # #. . # . # # ##### ##### # # 0 . # . .#1# ################## """) parsed = parse_layout(test_layout) position_bucket = collections.defaultdict(int) for i in range(200): noised = gf.noiser(walls=parsed['walls'], bot_position=parsed['bots'][1], enemy_positions=[parsed['bots'][0]], noise_radius=noise_radius) if noise_radius == 0: assert noised['is_noisy'] == [False] else: assert noised['is_noisy'] == [True] noised_pos = noised['enemy_positions'][0] position_bucket[noised_pos] += 1 assert 200 == sum(position_bucket.values()) # Since this is a randomized algorithm we need to be a bit lenient with # our tests. We check that each position was selected at least once. assert set(position_bucket.keys()) == set(expected)
def test_cascade_kill_rescue_1(): """ Checks that killing occurs only for the bot whose turn it is or for any bot that this bot moves onto. If a bot respawns on an enemy, it will only be killed when it is its own or the enemy’s turn (and neither of them moves). If bot moves before it is the enemy’s turn. Bot is rescued. """ cascade = [ """ ######## #30.. 2# #1 # ######## """, """ ######## #0 .. 2# #1 # ######## ######## # .. 3# # # ######## """, """ ######## #0 ..23# #1 # ######## """ ] def move(bot, state): if bot.is_blue and bot.turn == 0 and bot.round == 1: return (1, 1) if bot.is_blue and bot.turn == 1 and bot.round == 1: return (5, 1) return bot.position layouts = [layout.parse_layout(l) for l in cascade] state = setup_game([move, move], max_rounds=5, layout_dict=layout.parse_layout(cascade[0])) assert state['bots'] == layouts[0]['bots'] state = game.play_turn(state) # Bot 0 moves, kills 3. Bot 2 and 3 are on same spot assert state['bots'] == layouts[1]['bots'] state = game.play_turn(state) # Bot 1 stands. Bot 2 and 3 are on same spot assert state['bots'] == layouts[1]['bots'] state = game.play_turn(state) # Bot 2 moves. Rescues itself assert state['bots'] == layouts[2]['bots']
def test_cascade_kill_2(): """ Checks that killing occurs only for the bot whose turn it is or for any bot that this bot moves onto. If a bot respawns on an enemy, it will only be killed when it is its own or the enemy’s turn (and neither of them moves). """ cascade = [ (""" ######## #ya.. b# #x # ######## """, {}), (""" ######## #a .. b# #x # ######## """, { 'y': (6, 1) }), (""" ######## #a .. y# #x # ######## """, { 'b': (1, 2) }), (""" ######## #a .. y# #b x# ######## """, {}), ] def move(bot, state): if bot.is_blue and bot.turn == 0 and bot.round == 1: return (1, 1) return bot.position layouts = [layout.parse_layout(l, bots=b) for l, b in cascade] state = setup_game([move, move], max_rounds=5, layout_dict=layouts[0]) assert state['bots'] == layouts[0]['bots'] state = game.play_turn( state) # Bot 0 moves, kills 3. Bot 2 and 3 are on same spot assert state['bots'] == layouts[1]['bots'] state = game.play_turn(state) # Bot 1 stands. Bot 2 and 3 are on same spot assert state['bots'] == layouts[1]['bots'] state = game.play_turn( state) # Bot 2 stands, gets killed. Bot 1 and 2 are on same spot assert state['bots'] == layouts[2]['bots'] state = game.play_turn(state) # Bot 3 stands. Bot 1 and 2 are on same spot assert state['bots'] == layouts[2]['bots'] state = game.play_turn(state) # Bot 0 stands. Bot 1 and 2 are on same spot assert state['bots'] == layouts[2]['bots'] state = game.play_turn(state) # Bot 1 stands, kills 2. assert state['bots'] == layouts[3]['bots']
def test_multiple_enemies_killing(): """ Check that you can kill multiple enemies at once. """ l0 = """ ######## # .. # # bxa # ######## """ l1 = """ ######## # .. # # xay # ######## """ # dummy bots stopping = lambda bot, s: (bot.position, s) parsed_l0 = layout.parse_layout(l0, bots={'y': (3, 2)}) for bot in (0, 2): game_state = setup_game([stopping, stopping], layout_dict=parsed_l0) game_state['turn'] = bot # get position of bots x (and y) kill_position = game_state['bots'][1] assert kill_position == game_state['bots'][3] new_state = apply_move(game_state, kill_position) # team 0 scores twice assert new_state['score'] == [10, 0] # bots 1 and 3 are back to origin assert new_state['bots'][1::2] == [(6, 2), (6, 1)] parsed_l1 = layout.parse_layout(l1, bots={'b': (4, 2)}) for bot in (1, 3): game_state = setup_game([stopping, stopping], layout_dict=parsed_l1) game_state['turn'] = bot # get position of bots 0 (and 2) kill_position = game_state['bots'][0] assert kill_position == game_state['bots'][2] new_state = apply_move(game_state, kill_position) # team 1 scores twice assert new_state['score'] == [0, 10] # bots 0 and 2 are back to origin assert new_state['bots'][0::2] == [(1, 1), (1, 2)]
def test_initial_positions_same_in_layout(layout_name): """Check initial positions are the same as what the layout says for all layouts""" l = layout.get_layout_by_name(layout_name=layout_name) parsed_l = layout.parse_layout(l) exp = parsed_l["bots"] walls = parsed_l["walls"] out = initial_positions(walls) assert out == exp
def test_initial_positions_same_in_layout_random(layout_t): """Check initial positions are the same as what the layout says for 30 random layouts""" layout_name, layout_string = layout_t # get_random_layout returns a tuple of name and string parsed_l = layout.parse_layout(layout_string) exp = parsed_l["bots"] walls = parsed_l["walls"] out = initial_positions(walls) assert out == exp
def test_get_legal_positions_basic(): """Check that the output of legal moves contains all legal moves for one example layout""" l = layout.get_layout_by_name(layout_name="small_100") parsed_l = layout.parse_layout(l) legal_positions = get_legal_positions(parsed_l["walls"], parsed_l["shape"], parsed_l["bots"][0]) exp = [(1, 4), (1, 6), (1, 5)] assert legal_positions == exp
def test_uniform_noise_manhattan_graphical(noise_radius, test_layout): # Test how bot 1 observes bot 0 # the expected locations are where the food is placed parsed = parse_layout(test_layout) expected = parsed['food'] + [parsed['bots'][0]] test_uniform_noise_manhattan(noise_radius, expected, test_layout=test_layout)
def test_suicide(): """ Check that suicide works. """ l0 = """ ######## # .. # #ybxa # ######## """ l1 = """ ######## # .. # # xayb# ######## """ # dummy bots stopping = lambda bot, s: (bot.position, s) parsed_l0 = layout.parse_layout(l0) for bot in (1, 3): game_state = setup_game([stopping, stopping], layout_dict=parsed_l0) game_state['turn'] = bot # get position of bot 2 suicide_position = game_state['bots'][2] new_state = apply_move(game_state, suicide_position) # team 0 scores assert new_state['score'] == [5, 0] # # bots 1 and 3 are back to origin if bot == 1: assert new_state['bots'][1::2] == [(6, 2), (1, 2)] elif bot == 3: assert new_state['bots'][1::2] == [(3, 2), (6, 1)] parsed_l1 = layout.parse_layout(l1) for bot in (0, 2): game_state = setup_game([stopping, stopping], layout_dict=parsed_l1) game_state['turn'] = bot # get position of bot 3 suicide_position = game_state['bots'][3] new_state = apply_move(game_state, suicide_position) # team 0 scores assert new_state['score'] == [0, 5]
def test_get_legal_positions_random(layout_t, bot_idx): """Check that the output of legal moves returns only moves that are 1 field away and not inside a wall""" layout_name, layout_string = layout_t # get_random_layout returns a tuple of name and string parsed_l = layout.parse_layout(layout_string) bot = parsed_l["bots"][bot_idx] legal_positions = get_legal_positions(parsed_l["walls"], bot) for move in legal_positions: assert move not in parsed_l["walls"] assert abs((move[0] - bot[0])+(move[1] - bot[1])) <= 1
def test_stepping_players(self): test_layout = (""" ############ #a . . x# #b y# ############ """) movements_0 = [east, east] movements_1 = [west, west] teams = [ stepping_player(movements_0, movements_0), stepping_player(movements_1, movements_1) ] state = setup_game(teams, layout_dict=parse_layout(test_layout), max_rounds=2) assert state['bots'] == [(1, 1), (10, 1), (1, 2), (10, 2)] state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=2) assert state['bots'] == [(3, 1), (8, 1), (3, 2), (8, 2)]
def test_too_many_registered_teams(self): test_layout_4 = ( """ ################## #a#. . # . # #b##### #####x# # . # . .#y# ################## """) team_1 = stopping_player with pytest.raises(ValueError): setup_game([team_1] * 3, layout_dict=parse_layout(test_layout_4), max_rounds=300)
def test_speaking_player(): test_layout = (""" ############ #ab#. .#yx# ############ """) teams = [speaking_player, random_player] state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=1) assert state["say"][0].startswith("Going") assert state["say"][1] == ""
def test_demo_players(self): test_layout = (""" ################ #b y# # # # # # a x # # # # # # # #. .# ################ """) teams = [random_player, random_player] state = setup_game(teams, layout_dict=parse_layout(test_layout), max_rounds=20, seed=20) assert state['bots'][0] == (4, 4) assert state['bots'][1] == (4 + 7, 4) state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=20, seed=20) pos_left_bot = state['bots'][0] pos_right_bot = state['bots'][1] # running again to test seed: state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=20, seed=20) assert state['bots'][0] == pos_left_bot assert state['bots'][1] == pos_right_bot # running again with other seed: state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=20, seed=200) # most probably, either the left bot or the right bot or both are at # a different position assert not (state['bots'][0] == pos_left_bot and state['bots'][1] == pos_right_bot)
def test_too_many_moves(self): test_layout = (""" ############ #a . . x# #b y# ############ """) movements_0 = [east, east] movements_1 = [west, west] teams = [ stepping_player(movements_0, movements_0), stepping_player(movements_1, movements_1) ] state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=2) assert state['fatal_errors'] == [[], []] state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=3) assert len(state['fatal_errors'][0])
def test_minimal_game(): def move(b, s): return b.position layout_name, layout_string = layout.get_random_layout() l = layout.parse_layout(layout_string) final_state = run_game([move, move], max_rounds=20, layout_dict=l) assert final_state['gameover'] is True assert final_state['score'] == [0, 0] assert final_state['round'] == 20
def test_invalid_setup_game_closes_players(): layout_name, layout_string = layout.get_random_layout() l = layout.parse_layout(layout_string) # setup a remote demo game with "0" and "1" but bad max rounds state = setup_game(["0", "1"], layout_dict=l, max_rounds=0, allow_exceptions=True) assert state["gameover"] # Check that both processes have exited assert state["teams"][0].proc[0].wait(timeout=3) == 0 assert state["teams"][1].proc[0].wait(timeout=3) == 0
def test_too_few_registered_teams(self): test_layout_4 = ( """ ################## #0#. . # . # #2##### #####1# # . # . .#3# ################## """) team_1 = stopping_player with pytest.raises(ValueError): setup_game([team_1], layout_dict=parse_layout(test_layout_4), max_rounds=300)
def test_minimal_remote_game(): def move(b, s): return b.position layout_name, layout_string = layout.get_random_layout() l = layout.parse_layout(layout_string) final_state = run_game(["test/demo01_stopping.py", move], max_rounds=20, layout_dict=l) final_state = run_game(["test/demo01_stopping.py", 'test/demo02_random.py'], max_rounds=20, layout_dict=l) assert final_state['gameover'] is True assert final_state['score'] == [0, 0] assert final_state['round'] == 20
def setup_random_basic_gamestate(*, round=1, turn=0): """helper function for testing play turn""" l = layout.get_layout_by_name("small_100") parsed_l = layout.parse_layout(l) stopping = lambda bot, s: (bot.position, s) game_state = setup_game([stopping, stopping], layout_dict=parsed_l) game_state['round'] = round game_state['turn'] = turn return game_state
def test_non_existing_file(): # TODO: Change error message to be more meaningful layout_name, layout_string = layout.get_random_layout() l = layout.parse_layout(layout_string) res = run_game(["blah", "nothing"], max_rounds=1, layout_dict=l) assert res['fatal_errors'][0][0] == { 'description': '("Could not load blah: No module named \'blah\'", \'ModuleNotFoundError\')', 'round': None, 'turn': 0, 'type': 'PlayerDisconnected' }
def test_demo_players(self): test_layout = (""" ############ #a#. .# x# ###. .#### #b#. .# y# ############ """) teams = [nq_random_player, nq_random_player] state = run_game(teams, layout_dict=parse_layout(test_layout), max_rounds=1) assert state['bots'][0] == (1, 1) assert state['bots'][1] == (9, 1)
def test_cascade_kill(): cascade = [ (""" ######## #x ..ya# # b# ######## """, {}), (""" ######## #a .. y# # b# ######## """, { 'x': (1, 1) }), (""" ######## #a .. y# # x# ######## """, { 'b': (6, 2) }), (""" ######## #a .. y# #b x# ######## """, {}), ] def move(bot, state): if not bot.is_blue and bot.turn == 1 and bot.round == 1: return (6, 1) return bot.position layouts = [layout.parse_layout(l, bots=b) for l, b in cascade] state = setup_game([move, move], max_rounds=5, layout_dict=layouts[0]) assert state['bots'] == layouts[0]['bots'] state = game.play_turn(state) # Bot 0 stands assert state['bots'] == layouts[0]['bots'] state = game.play_turn(state) # Bot 1 stands state = game.play_turn(state) # Bot 2 stands state = game.play_turn( state) # Bot 3 moves, kills 0. Bot 0 and 1 are on same spot assert state['bots'] == layouts[1]['bots'] state = game.play_turn( state) # Bot 0 stands, kills 1. Bot 1 and 2 are on same spot assert state['bots'] == layouts[2]['bots'] state = game.play_turn(state) # Bot 1 stands, kills 2. assert state['bots'] == layouts[3]['bots']
def test_remote_errors(tmp_path): # TODO: Change error messages to be more meaningful # we change to the tmp dir, to make our paths simpler syntax_error = dedent(""" def move(b, state) return b.position """) import_error = dedent(""" import does_not_exist def move(b, state): return b.position """) layout_name, layout_string = layout.get_random_layout() l = layout.parse_layout(layout_string) with temp_wd(tmp_path): s_py = Path("s.py") s_py.write_text(syntax_error) i_py = Path("i.py") i_py.write_text(import_error) res = run_game([str(s_py), str(i_py)], layout_dict=l, max_rounds=20) # Error messages have changed in Python 3.10. We can only do approximate maching assert "SyntaxError" in res['fatal_errors'][0][0].pop('description') assert res['fatal_errors'][0][0] == { 'round': None, 'turn': 0, 'type': 'PlayerDisconnected' } # Both teams fail during setup: DRAW assert res['whowins'] == 2 res = run_game(["0", str(i_py)], layout_dict=l, max_rounds=20) # Error messages have changed in Python 3.10. We can only do approximate maching assert "ModuleNotFoundError" in res['fatal_errors'][1][0].pop( 'description') assert res['fatal_errors'][1][0] == { 'round': None, 'turn': 1, 'type': 'PlayerDisconnected' } assert res['whowins'] == 0 res = run_game([str(i_py), "1"], layout_dict=l, max_rounds=20) # Error messages have changed in Python 3.10. We can only do approximate maching assert "ModuleNotFoundError" in res['fatal_errors'][0][0].pop( 'description') assert res['fatal_errors'][0][0] == { 'round': None, 'turn': 0, 'type': 'PlayerDisconnected' } assert res['whowins'] == 1
def test_initial_positions_basic(): """Checks basic example for initial positions""" simple_layout = """ ######## #a ##b # #x y # ######## """ parsed = layout.parse_layout(simple_layout) out = initial_positions(parsed['walls'], parsed['shape']) exp = [(1, 1), (6, 2), (1, 2), (6, 1)] assert len(out) == 4 assert out == exp
def test_initial_positions_basic(): """Checks basic example for initial positions""" simple_layout = """ ######## # ### # # # ######## """ walls = layout.parse_layout(simple_layout)['walls'] out = initial_positions(walls) exp = [(1, 1), (6, 2), (1, 2), (6, 1)] assert len(out) == 4 assert out == exp