def test_double_suicide(): # Test how a bot can be killed when it runs into two bots test_layout = """ ###### # bx # #. y.# ###### """ teams = [stepping_player('-', '-'), stepping_player('<', '-')] state = setup_game(teams, layout_dict=layout.parse_layout(test_layout, bots={'a': (2, 1)}), max_rounds=2) assert state['bots'] == [(2, 1), (3, 1), (2, 1), (3, 2)] assert state['food'] == [{(1, 2)}, {(4, 2)}] # play a two turns so that 1 moves state = play_turn(state) state = play_turn(state) # bot 1 has been reset assert state['bots'] == [(2, 1), (4, 2), (2, 1), (3, 2)] assert state['food'] == [{(1, 2)}, {(4, 2)}] assert state['gameover'] == False assert state['round'] == 1 assert state['turn'] == 1 # only a single KILL_POINT has been given assert state['score'] == [game.KILL_POINTS, 0]
def test_cascade_suicide(): cascade = [ """ ######## #1 ..03# # 2# ######## """, """ ######## #0 .. 3# # 2# ######## ######## #1 .. # # # ######## """, """ ######## #0 .. 3# # 1# ######## ######## # .. # # 2# ######## """, """ ######## #0 .. 3# #2 1# ######## """ ] def move(bot, state): if bot.is_blue and bot.turn == 0 and bot.round == 1: return (6, 1), state return bot.position, state 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 onto 3. Gets killed. Bot 0 and 1 are on same spot. assert state['bots'] == layouts[1]['bots'] state = game.play_turn( state) # Bot 1 moves, gets killed. Bot 1 and 2 are on same spot assert state['bots'] == layouts[2]['bots'] state = game.play_turn(state) # Bot 2 moves, gets killed. assert state['bots'] == layouts[3]['bots']
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), state if bot.is_blue and bot.turn == 1 and bot.round == 1: return (5, 1), state return bot.position, state 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_suicide(): cascade = [ (""" ######## #x ..ay# # b# ######## """, {}), (""" ######## #a .. y# # b# ######## """, { 'x': (1, 1) }), (""" ######## #a .. y# # x# ######## """, { 'b': (6, 2) }), (""" ######## #a .. y# #b x# ######## """, {}), ] def move(bot, state): if bot.is_blue and bot.turn == 0 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 moves onto 3. Gets killed. Bot 0 and 1 are on same spot. assert state['bots'] == layouts[1]['bots'] state = game.play_turn( state) # Bot 1 moves, gets killed. Bot 1 and 2 are on same spot assert state['bots'] == layouts[2]['bots'] state = game.play_turn(state) # Bot 2 moves, gets killed. assert state['bots'] == layouts[3]['bots']
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_requested_moves(move_request, expected_prev, expected_req, expected_success): # test the possible return values of gamestate['requested_moves'] test_layout = """ ###### #a#.y# #.bx # ###### """ def move(bot, state): return move_request teams = [move, stopping_player] state = setup_game(teams, layout_dict=layout.parse_layout(test_layout), max_rounds=2) assert state['requested_moves'] == [None, None, None, None] state = play_turn(state) assert state['requested_moves'][1:] == [None, None, None] assert state['requested_moves'][0] == { 'previous_position': (1, 1), 'requested_position': expected_req, 'success': expected_success }
def test_cascade_kill_rescue_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). If enemy moves before it is the bot’s turn. Bot is rescued. """ cascade = [ (""" ######## #y .. # #xa b# ######## """, {}), (""" ######## #y .. # #a x# ######## """, { 'b': (6, 2) }), (""" ######## #y .. # #a xb# ######## """, {}), ] def move(bot, state): if bot.is_blue and bot.turn == 0 and bot.round == 1: return (1, 2) if not bot.is_blue and bot.turn == 0 and bot.round == 1: return (5, 2) 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 1. Bot 1 and 2 are on same spot assert state['bots'] == layouts[1]['bots'] state = game.play_turn(state) # Bot 1 moves. Bot 2 is rescued. assert state['bots'] == layouts[2]['bots']
def test_play_turn_maxrounds(score): """Check that game quits at maxrounds and choses correct winner""" # this works for ties as well, because there are no points to be gained at init positions game_state = setup_random_basic_gamestate() game_state["round"] = 301 game_state["score"] = score[0] game_state_new = game.play_turn(game_state) assert game_state_new["gameover"] assert game_state_new["whowins"] == score[1]
def test_team_names(): test_layout = (""" ################## #a#. . # . # #b##### #####x# # . # . .#y# ################## """) def team_pattern(fn): # The pattern for a local team. return f'local-team ({fn})' def team_1(bot, state): assert bot.team_name == team_pattern('team_1') assert bot.other.team_name == team_pattern('team_1') assert bot.enemy[0].team_name == team_pattern('team_2') assert bot.enemy[1].team_name == team_pattern('team_2') return bot.position def team_2(bot, state): assert bot.team_name == team_pattern('team_2') assert bot.other.team_name == team_pattern('team_2') assert bot.enemy[0].team_name == team_pattern('team_1') assert bot.enemy[1].team_name == team_pattern('team_1') return bot.position state = setup_game([team_1, team_2], layout_dict=parse_layout(test_layout), max_rounds=3) assert state['team_names'] == [ team_pattern('team_1'), team_pattern('team_2') ] state = play_turn(state) # check that player did not fail assert state['errors'] == [{}, {}] assert state['fatal_errors'] == [[], []] state = play_turn(state) # check that player did not fail assert state['errors'] == [{}, {}] assert state['fatal_errors'] == [[], []]
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_cascade_kill(): cascade = [ """ ######## #1 ..30# # 2# ######## """, """ ######## #0 .. 3# # 2# ######## ######## #1 .. # # # ######## """, """ ######## #0 .. 3# # 1# ######## ######## # .. # # 2# ######## """, """ ######## #0 .. 3# #2 1# ######## """ ] def move(bot, state): if not bot.is_blue and bot.turn == 1 and bot.round == 1: return (6, 1), state return bot.position, state 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 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_bot_does_not_eat_own_food(): test_layout = """ ###### #a .y# #.bx # ###### """ teams = [stepping_player('v', '<'), stepping_player('^', '<')] state = setup_game(teams, layout_dict=layout.parse_layout(test_layout), max_rounds=2) assert state['bots'] == [(1, 1), (3, 2), (2, 2), (4, 1)] assert state['food'] == [{(1, 2)}, {(3, 1)}] for i in range(4): state = play_turn(state) assert state['bots'] == [(1, 2), (3, 1), (1, 2), (3, 1)] assert state['food'] == [{(1, 2)}, {(3, 1)}]
def test_manual_remote_game_closes_players(): layout_name, layout_string = layout.get_random_layout() l = layout.parse_layout(layout_string) # run a remote demo game with "0" and "1" state = setup_game(["0", "1"], layout_dict=l, max_rounds=10, allow_exceptions=True) assert not state["gameover"] while not state["gameover"]: # still running # still running assert state["teams"][0].proc[0].poll() is None assert state["teams"][1].proc[0].poll() is None state = play_turn(state) # 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_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_track_and_kill_count(): # for each team, we track whether they have been eaten at least once # and count the number of times they have been killed bot_states = { 0: [{ 'track': [], 'eaten': False, 'times_killed': 0, 'deaths': 0 }, { 'track': [], 'eaten': False, 'times_killed': 0, 'deaths': 0 }], 1: [{ 'track': [], 'eaten': False, 'times_killed': 0, 'deaths': 0 }, { 'track': [], 'eaten': False, 'times_killed': 0, 'deaths': 0 }] } def trackingBot(bot, state): turn = bot.turn other = bot.other # first move. get the state from the global cache if state == {}: team_idx = 0 if bot.is_blue else 1 state.update(enumerate(bot_states[team_idx])) if bot.round == 1 and turn == 0: assert bot.track[0] == bot.position if bot.was_killed: state[turn]['eaten'] = True # if bot.deaths has increased from our last known value, # we add a kill # this avoids adding two kills as the eaten attribute could # be True in two consecutive turns if state[turn]['deaths'] != bot.deaths: state[turn]['times_killed'] += 1 state[turn]['deaths'] = bot.deaths if other.was_killed: state[1 - turn]['eaten'] = True if state[1 - turn]['deaths'] != bot.other.deaths: state[1 - turn]['times_killed'] += 1 state[1 - turn]['deaths'] = bot.other.deaths if bot.was_killed or not state[turn]['track']: state[turn]['track'] = [bot.position] if other.was_killed or not state[1 - turn]['track']: state[1 - turn]['track'] = [other.position] else: state[1 - turn]['track'].append(other.position) # The assertion is that the first position in bot.track # is always the respawn position. # However, in our test case, this will only happen, once # a bot has been eaten. if state[turn]['eaten']: assert bot.track[0] == bot._initial_position assert bot.track == state[turn]['track'] # bot.round * 2 + 1 + turn assert bot.track[-1] == bot.position # just move randomly. hopefully, this means some bots will be killed return randomBot(bot, state) layout = """ ########## # ab .y x# # ######## #.##. .### ########## """ team = [trackingBot, trackingBot] state = setup_game(team, max_rounds=300, layout_dict=parse_layout(layout)) while not state['gameover']: # Check that our count is consistent with what the game thinks # for the current and previous bot, we have to subtract the deaths that have just respawned # as they have not been passed to the bot yet # therefore, we only update old_deaths for the current team if state['turn'] is not None: old_deaths[state['turn']] = state['deaths'][state['turn']] old_deaths = state['deaths'][:] state = play_turn(state) deaths = state['deaths'][:] team = state['turn'] % 2 # The current bot knows about its deaths, *unless* it made suicide, # so we have to subtract 1 if bot_was_killed is True suicide_correction = [0] * 4 suicide_correction[state['turn']] = 1 if state['bot_was_killed'][ state['turn']] else 0 # The other team knows about its deaths, *unless* one of the bots got eaten # just now, or the previous bot made suicide other_team_correction = [0] * 4 for idx in range(1 - team, 4, 2): if old_deaths[idx] != deaths[idx]: other_team_correction[idx] = 1 # suicide prev_idx = state['turn'] - 1 if old_deaths[prev_idx] == deaths[prev_idx] and state[ 'bot_was_killed'][prev_idx]: other_team_correction[prev_idx] = 1 assert bot_states[0][0]['times_killed'] == deaths[ 0] - suicide_correction[0] - other_team_correction[0] assert bot_states[1][0]['times_killed'] == deaths[ 1] - suicide_correction[1] - other_team_correction[1] assert bot_states[0][1]['times_killed'] == deaths[ 2] - suicide_correction[2] - other_team_correction[2] assert bot_states[1][1]['times_killed'] == deaths[ 3] - suicide_correction[3] - other_team_correction[3] # assertions might have been caught in run_game # check that all is good assert state['fatal_errors'] == [[], []] # check that the game has run at all assert state['round'] >= 1 # check that someone has been killed, or the whole test is not doing anything assert sum(state['deaths']) > 0 # check that each single bot has been eaten, or we are not testing the full range of possibilities assert all(state['deaths'])
def test_moving_through_maze(): test_start = """ ###### #a . # #.. x# #b y# ###### """ parsed = layout.parse_layout(test_start) teams = [ stepping_player('>-v>>>-', '-^^->->'), stepping_player('<<-<<<-', '-------') ] state = setup_game(teams, layout_dict=parsed, max_rounds=8) # play first round for i in range(4): state = game.play_turn(state) test_first_round = layout.parse_layout(""" ###### # a. # #..x # #b y# ###### """) assert test_first_round['bots'] == state['bots'] assert test_first_round['food'] == list(state['food'][0]) + list( state['food'][1]) assert state['score'] == [0, 0] for i in range(4): state = game.play_turn(state) test_second_round = layout.parse_layout(""" ###### # a. # #bx # # y# ###### """, bots={'b': (1, 2)}, food=[(1, 2)]) # b sitting on food assert test_second_round['bots'] == state['bots'] assert test_second_round['food'] == list(state['food'][0]) + list( state['food'][1]) assert state['score'] == [0, 1] for i in range(4): state = game.play_turn(state) test_third_round = layout.parse_layout(""" ###### #b . # #.a x# # y# ###### """) assert test_third_round['bots'] == state['bots'] assert test_third_round['food'] == list(state['food'][0]) + list( state['food'][1]) assert state['score'] == [game.KILL_POINTS, 1] for i in range(4): state = game.play_turn(state) test_fourth_round = layout.parse_layout(""" ###### #b . # #a x # # y# ###### """, bots={'a': (1, 2)}, food=[(1, 2)]) # a sitting on food assert test_fourth_round['bots'] == state['bots'] assert test_fourth_round['food'] == list(state['food'][0]) + list( state['food'][1]) assert state['score'] == [game.KILL_POINTS, game.KILL_POINTS + 1] for i in range(4): state = game.play_turn(state) test_fifth_round = layout.parse_layout(""" ###### # b. # #.a x# # y# ###### """) assert test_fifth_round['bots'] == state['bots'] assert test_fifth_round['food'] == list(state['food'][0]) + list( state['food'][1]) assert state['score'] == [game.KILL_POINTS * 2, game.KILL_POINTS + 1] for i in range(4): state = game.play_turn(state) test_sixth_round = layout.parse_layout(""" ###### # b. # #a x # # y# ###### """, bots={'a': (1, 2)}, food=[(1, 2)]) # a sitting on food assert test_sixth_round['bots'] == state['bots'] assert test_sixth_round['food'] == list(state['food'][0]) + list( state['food'][1]) assert state['score'] == [game.KILL_POINTS * 2, game.KILL_POINTS * 2 + 1] for i in range(3): # !! Only move three bots state = game.play_turn(state) test_seventh_round = layout.parse_layout(""" ###### # b # #a x # # y# ###### """, bots={'a': (1, 2)}, food=[(1, 2) ]) # a sitting on food assert test_seventh_round['bots'] == state['bots'] assert test_seventh_round['food'] == list(state['food'][0]) + list( state['food'][1]) assert state['score'] == [ game.KILL_POINTS * 2 + 1, game.KILL_POINTS * 2 + 1 ] assert state['gameover'] == True assert state['whowins'] == 2 with pytest.raises(ValueError): state = game.play_turn(state)
def test_random_seeds(self): test_layout = (""" ################ # # # # # # # a x # # b y # # # # # #. .# ################ """) def init_rng_players(): player_rngs = [] def rng_test(bot, state): player_rngs.append(bot.random) return bot.position, state team = [rng_test, rng_test] return team, player_rngs team0, player_rngs0 = init_rng_players() state = setup_game(team0, layout_dict=parse_layout(test_layout), max_rounds=5, seed=20) # play two steps play_turn(play_turn(state)) assert len(player_rngs0) == 2 # generate some random numbers for each player random_numbers0 = [rng.randint(0, 10000) for rng in player_rngs0] # teams should have generated a different number assert random_numbers0[0] != random_numbers0[1] team1, player_rngs1 = init_rng_players() state = setup_game(team1, layout_dict=parse_layout(test_layout), max_rounds=5, seed=20) # play two steps play_turn(play_turn(state)) assert len(player_rngs1) == 2 # generate some random numbers for each player random_numbers1 = [rng.randint(0, 10000) for rng in player_rngs1] # teams should have generated the same numbers as before assert random_numbers0 == random_numbers1 # now, use a different seed team2, player_rngs2 = init_rng_players() state = setup_game(team2, layout_dict=parse_layout(test_layout), max_rounds=5, seed=200) # play two steps play_turn(play_turn(state)) assert len(player_rngs2) == 2 # generate some random numbers for each player random_numbers2 = [rng.randint(0, 10000) for rng in player_rngs0] # teams should have generated different numbers than before assert random_numbers0 != random_numbers2