Esempio n. 1
0
    def test_three_collision(self):
        state = GameState()
        loc1 = (10, 11)
        loc2 = (9, 10)
        loc3 = (11, 10)

        dest = (10, 10)

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)
        state.add_robot(loc3, 1)

        actions = {
            loc1: ['move', dest],
            loc2: ['move', dest],
            loc3: ['move', dest]
        }

        deltas = state.get_delta(actions)

        d_player0 = delta_for(deltas, loc1)
        d_player1_1 = delta_for(deltas, loc2)
        d_player1_2 = delta_for(deltas, loc3)

        self.assertEqual(d_player0.hp - d_player0.hp_end,
                         d_player1_1.damage_caused + d_player1_2.damage_caused)

        player1_hp_lose = d_player1_1.hp - d_player1_1.hp_end + \
            d_player1_2.hp - d_player1_2.hp_end

        self.assertEqual(player1_hp_lose, d_player0.damage_caused)
Esempio n. 2
0
    def test_three_collision(self):
        state = GameState()
        loc1 = (10, 11)
        loc2 = (9, 10)
        loc3 = (11, 10)

        dest = (10, 10)

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)
        state.add_robot(loc3, 1)

        actions = {
            loc1: ['move', dest],
            loc2: ['move', dest],
            loc3: ['move', dest]
        }

        deltas = state.get_delta(actions)

        d_player0 = delta_for(deltas, loc1)
        d_player1_1 = delta_for(deltas, loc2)
        d_player1_2 = delta_for(deltas, loc3)

        self.assertEqual(d_player0.hp - d_player0.hp_end,
                         d_player1_1.damage_caused + d_player1_2.damage_caused)

        player1_hp_lose = d_player1_1.hp - d_player1_1.hp_end + \
            d_player1_2.hp - d_player1_2.hp_end

        self.assertEqual(player1_hp_lose, d_player0.damage_caused)
Esempio n. 3
0
    def test_suicide(self):
        state = GameState()
        loc1 = (10, 11)
        dest1 = (10, 10)
        loc2 = (9, 10)

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)

        actions = {loc1: ['move', dest1], loc2: ['suicide']}
        deltas = state.get_delta(actions)

        d_guard = delta_for(deltas, loc1)
        d_suicide = delta_for(deltas, loc2)

        self.assertEqual(d_guard.hp - d_guard.hp_end, d_suicide.damage_caused)
Esempio n. 4
0
    def test_attack(self):
        state = GameState()
        loc1 = (10, 11)
        dest1 = (10, 10)
        loc2 = (9, 10)
        dest2 = dest1

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)

        actions = {loc1: ['move', dest1], loc2: ['attack', dest2]}
        deltas = state.get_delta(actions)

        d_move = delta_for(deltas, loc1)
        d_attack = delta_for(deltas, loc2)

        self.assertEqual(d_move.hp - d_move.hp_end, d_attack.damage_caused)
Esempio n. 5
0
    def test_simple_collision(self):
        state = GameState()
        loc1 = (10, 11)
        dest1 = (10, 10)
        loc2 = (9, 10)
        dest2 = dest1

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)

        actions = {loc1: ['move', dest1], loc2: ['move', dest2]}

        deltas = state.get_delta(actions)

        d_move1 = delta_for(deltas, loc1)
        d_move2 = delta_for(deltas, loc2)

        self.assertEqual(d_move1.hp - d_move1.hp_end, d_move2.damage_caused)
        self.assertEqual(d_move2.hp - d_move2.hp_end, d_move1.damage_caused)
Esempio n. 6
0
    def test_suicide(self):
        state = GameState()
        loc1 = (10, 11)
        dest1 = (10, 10)
        loc2 = (9, 10)

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)

        actions = {
            loc1: ['move', dest1],
            loc2: ['suicide']
        }
        deltas = state.get_delta(actions)

        d_guard = delta_for(deltas, loc1)
        d_suicide = delta_for(deltas, loc2)

        self.assertEqual(d_guard.hp - d_guard.hp_end, d_suicide.damage_caused)
Esempio n. 7
0
    def test_attack(self):
        state = GameState()
        loc1 = (10, 11)
        dest1 = (10, 10)
        loc2 = (9, 10)
        dest2 = dest1

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)

        actions = {
            loc1: ['move', dest1],
            loc2: ['attack', dest2]
        }
        deltas = state.get_delta(actions)

        d_move = delta_for(deltas, loc1)
        d_attack = delta_for(deltas, loc2)

        self.assertEqual(d_move.hp - d_move.hp_end, d_attack.damage_caused)
Esempio n. 8
0
    def test_collision_and_attack(self):
        state = GameState()
        loc1 = (10, 11)
        loc2 = (9, 10)
        loc3 = (11, 11)

        dest = (10, 10)
        attack_dest = loc1

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)
        state.add_robot(loc3, 1)

        actions = {
            loc1: ['move', dest],
            loc2: ['move', dest],
            loc3: ['attack', attack_dest]
        }

        deltas = state.get_delta(actions)

        d_player0 = delta_for(deltas, loc1)
        d_player1_move = delta_for(deltas, loc2)
        d_player1_attack = delta_for(deltas, loc3)

        self.assertEqual(d_player1_move.damage_caused,
                         settings.collision_damage)

        self.assertTrue(settings.attack_range[0]
                        <= d_player1_attack.damage_caused <=
                        settings.attack_range[1])

        self.assertEqual(d_player0.hp - d_player0.hp_end,
                         d_player1_move.damage_caused +
                         d_player1_attack.damage_caused)

        player1_hp_lose = d_player1_move.hp - d_player1_move.hp_end + \
            d_player1_attack.hp - d_player1_attack.hp_end

        self.assertEqual(player1_hp_lose, d_player0.damage_caused)
Esempio n. 9
0
    def test_collision_and_attack(self):
        state = GameState()
        loc1 = (10, 11)
        loc2 = (9, 10)
        loc3 = (11, 11)

        dest = (10, 10)
        attack_dest = loc1

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)
        state.add_robot(loc3, 1)

        actions = {
            loc1: ['move', dest],
            loc2: ['move', dest],
            loc3: ['attack', attack_dest]
        }

        deltas = state.get_delta(actions)

        d_player0 = delta_for(deltas, loc1)
        d_player1_move = delta_for(deltas, loc2)
        d_player1_attack = delta_for(deltas, loc3)

        self.assertEqual(d_player1_move.damage_caused,
                         settings.collision_damage)

        self.assertTrue(settings.attack_range[0] <=
                        d_player1_attack.damage_caused <=
                        settings.attack_range[1])

        self.assertEqual(d_player0.hp - d_player0.hp_end,
                         d_player1_move.damage_caused +
                         d_player1_attack.damage_caused)

        player1_hp_lose = d_player1_move.hp - d_player1_move.hp_end + \
            d_player1_attack.hp - d_player1_attack.hp_end

        self.assertEqual(player1_hp_lose, d_player0.damage_caused)
Esempio n. 10
0
    def test_simple_collision(self):
        state = GameState()
        loc1 = (10, 11)
        dest1 = (10, 10)
        loc2 = (9, 10)
        dest2 = dest1

        state.add_robot(loc1, 0)
        state.add_robot(loc2, 1)

        actions = {
            loc1: ['move', dest1],
            loc2: ['move', dest2]
        }

        deltas = state.get_delta(actions)

        d_move1 = delta_for(deltas, loc1)
        d_move2 = delta_for(deltas, loc2)

        self.assertEqual(d_move1.hp - d_move1.hp_end, d_move2.damage_caused)
        self.assertEqual(d_move2.hp - d_move2.hp_end, d_move1.damage_caused)
Esempio n. 11
0
File: game.py Progetto: ramk13/rgkit
class Game(object):
    def __init__(self, players, record_actions=False, record_history=False,
                 print_info=False, seed=None, quiet=0, delta_callback=None,
                 symmetric=True):
        self._players = players
        for i, player in enumerate(self._players):
            player.set_player_id(i)
        self._record_actions = record_actions
        self._record_history = record_history
        self._print_info = print_info
        if seed is None:
            seed = random.randint(0, settings.max_seed)
        self.seed = str(seed)
        self._random = random.Random(self.seed)
        self._quiet = quiet
        self._delta_callback = delta_callback
        self._state = GameState(use_start=True, seed=self.seed,
                                symmetric=symmetric)

        self._actions_on_turn = {}
        self._states = {}
        self.history = []  # TODO: make private

    # actions_on_turn = {loc: log_item}
    # log_item = {
    #     'name': action_name,
    #     'target': action_target or None,
    #     'loc': loc,
    #     'hp': hp,
    #     'player': player_id,
    #     'loc_end': loc_end,
    #     'hp_end': hp_end
    # }
    #
    # or dummy if turn == settings.max_turn
    def get_actions_on_turn(self, turn):
        assert self._record_actions
        return self._actions_on_turn[turn]

    def get_state(self, turn):
        return self._states[turn]

    def _save_actions_on_turn(self, actions_on_turn, turn):
        self._actions_on_turn[turn] = actions_on_turn

    def _save_state(self, state, turn):
        self._states[turn] = state

    def _get_robots_actions(self):
        if self._quiet >= 1:
            sys.stdout = NullDevice()
        if self._quiet >= 2:
            sys.stderr = NullDevice()

        actions = {}
        for player in self._players:
            seed = self._random.randint(0, settings.max_seed)
            actions.update(player.get_actions(self._state, seed))

        if self._quiet >= 1:
            sys.stdout = sys.__stdout__
        if self._quiet >= 2:
            sys.stderr = sys.__stderr__

        return actions

    def _make_history(self, actions):
        '''
        An aggregate of all bots and their actions this turn.

        Stores a list of each player's bots at the start of this turn and
        the actions they each performed this turn. Newly spawned bots have no
        actions.
        '''
        robots = []
        for loc, robot in self._state.robots.iteritems():
            robot_info = {
                'location': loc,
                'hp': robot.hp,
                'player_id': robot.player_id,
                'robot_id': robot.robot_id,
            }
            if loc in actions:
                robot_info['action'] = actions[loc]
            robots.append(robot_info)
        return robots

    def _calculate_actions_on_turn(self, delta, actions):
        actions_on_turn = {}

        for delta_info in delta:
            loc = delta_info.loc

            if loc in actions:
                name = actions[loc][0]
                if name in ['move', 'attack']:
                    target = actions[loc][1]
                else:
                    target = None
            else:
                name = 'spawn'
                target = None

            # note that a spawned bot may overwrite an existing bot
            actions_on_turn[loc] = {
                'name': name,
                'target': target,
                'loc': loc,
                'hp': delta_info.hp,
                'player': delta_info.player_id,
                'loc_end': delta_info.loc_end,
                'hp_end': delta_info.hp_end
            }

        return actions_on_turn

    def run_turn(self):
        if self._print_info:
            print (' running turn %d ' % (self._state.turn)).center(70, '-')

        actions = self._get_robots_actions()

        delta = self._state.get_delta(actions)

        if self._record_actions:
            actions_on_turn = self._calculate_actions_on_turn(delta, actions)
            self._save_actions_on_turn(actions_on_turn, self._state.turn)

        new_state = self._state.apply_delta(delta)

        if self._delta_callback is not None and self._state.turn > 1:
            self._delta_callback(delta, new_state)

        self._save_state(new_state, new_state.turn)

        if self._record_history:
            self.history.append(self._make_history(actions))

        self._state = new_state

    def run_all_turns(self):
        assert self._state.turn == 0

        if self._print_info:
            print ('Match seed: {0}'.format(self.seed))

        self._save_state(self._state, 0)

        while self._state.turn < settings.max_turns:
            self.run_turn()

        # create last turn's state for server history
        if self._record_history:
            self.history.append(self._make_history({}))

        # create dummy data for last turn
        # TODO: render should be cleverer
        actions_on_turn = {}

        for loc, robot in self._state.robots.iteritems():
            log_item = {
                'name': '',
                'target': None,
                'loc': loc,
                'hp': robot.hp,
                'player': robot.player_id,
                'loc_end': loc,
                'hp_end': robot.hp
            }

            actions_on_turn[loc] = log_item

        self._save_actions_on_turn(actions_on_turn, settings.max_turns)

    def get_scores(self):
        return self.get_state(settings.max_turns).get_scores()
Esempio n. 12
0
 def test_spawn(self):
     state = GameState()
     deltas = state.get_delta(actions={}, spawn=True)
     for d in deltas:
         self.assertTrue(hasattr(d, 'damage_caused'))
Esempio n. 13
0
class Game(object):
    def __init__(self,
                 players,
                 record_actions=False,
                 record_history=False,
                 print_info=False,
                 seed=None,
                 quiet=0,
                 delta_callback=None,
                 symmetric=True):
        self._players = players
        for i, player in enumerate(self._players):
            player.set_player_id(i)
        self._record_actions = record_actions
        self._record_history = record_history
        self._print_info = print_info
        if seed is None:
            seed = random.randint(0, settings.max_seed)
        self.seed = str(seed)
        self._random = random.Random(self.seed)
        self._quiet = quiet
        self._delta_callback = delta_callback
        self._state = GameState(use_start=True,
                                seed=self.seed,
                                symmetric=symmetric)

        self._actions_on_turn = {}
        self._states = {}
        self.history = []  # TODO: make private

    # actions_on_turn = {loc: log_item}
    # log_item = {
    #     'name': action_name,
    #     'target': action_target or None,
    #     'loc': loc,
    #     'hp': hp,
    #     'player': player_id,
    #     'loc_end': loc_end,
    #     'hp_end': hp_end
    # }
    #
    # or dummy if turn == settings.max_turn
    def get_actions_on_turn(self, turn):
        assert self._record_actions
        return self._actions_on_turn[turn]

    def get_state(self, turn):
        return self._states[turn]

    def _save_actions_on_turn(self, actions_on_turn, turn):
        self._actions_on_turn[turn] = actions_on_turn

    def _save_state(self, state, turn):
        self._states[turn] = state

    def _get_robots_responses(self):
        # TODO: honour quietness
        actions, outputs = {}, {}
        for player in self._players:
            seed = self._random.randint(0, settings.max_seed)
            responses = player.get_responses(self._state, seed)
            actions.update(responses[0])
            outputs.update(responses[1])

        return actions, outputs

    def _make_history(self, responses, record_output=False):
        # todo: rework this. We are getting data about the player
        #       from two sources: 1) from arguments, and 2) from
        #       class members. Either move *all* to 1) and make
        #       static or move all to 2).
        '''
        An aggregate of all bots and their actions this turn.

        Optionally records per-bot output.

        Stores a list of each player's bots at the start of this turn and
        the actions they each performed this turn. Newly spawned bots have no
        actions.
        '''
        actions, outputs = responses
        robots = []
        for loc, robot in self._state.robots.items():
            robot_info = {
                'location': loc,
                'hp': robot.hp,
                'player_id': robot.player_id,
                'robot_id': robot.robot_id,
            }
            if loc in actions:
                # since the state after the final turn does not contain any
                # actions, 'loc' is not always contained in 'actions'
                robot_info['action'] = actions[loc]
            if outputs and loc in outputs:
                robot_info['output'] = outputs[loc]
            robots.append(robot_info)
        return robots

    def _calculate_actions_on_turn(self, delta, actions):
        actions_on_turn = {}

        for delta_info in delta:
            loc = delta_info.loc

            if loc in actions:
                name = actions[loc][0]
                if name in ['move', 'attack']:
                    target = actions[loc][1]
                else:
                    target = None
            else:
                name = 'spawn'
                target = None

            # note that a spawned bot may overwrite an existing bot
            actions_on_turn[loc] = {
                'name': name,
                'target': target,
                'loc': loc,
                'hp': delta_info.hp,
                'player': delta_info.player_id,
                'loc_end': delta_info.loc_end,
                'hp_end': delta_info.hp_end
            }

        return actions_on_turn

    def run_turn(self, record_output=False):
        if self._print_info:
            print((' running turn %d ' % (self._state.turn)).center(70, '-'))

        # pr = cProfile.Profile()
        # pr.enable()

        responses = self._get_robots_responses()
        actions = responses[0]

        # pr.disable()
        # s = StringIO.StringIO()
        # sortby = 'cumulative'
        # ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
        # ps.print_stats()
        # print(s.getvalue())

        delta = self._state.get_delta(actions)

        if self._record_actions:
            actions_on_turn = self._calculate_actions_on_turn(delta, actions)
            self._save_actions_on_turn(actions_on_turn, self._state.turn)

        new_state = self._state.apply_delta(delta)

        if self._delta_callback is not None and self._state.turn > 1:
            self._delta_callback(delta, new_state)

        self._save_state(new_state, new_state.turn)

        if self._record_history:
            self.history.append(
                self._make_history(responses, record_output=record_output))

        self._state = new_state

    def run_all_turns(self):
        assert self._state.turn == 0

        if self._print_info:
            print(('Match seed: {0}'.format(self.seed)))

        self._save_state(self._state, 0)

        while self._state.turn < settings.max_turns:
            self.run_turn()

        # create last turn's state for server history
        if self._record_history:
            self.history.append(self._make_history(({}, {})))

        # create dummy data for last turn
        # TODO: render should be cleverer
        actions_on_turn = {}

        for loc, robot in self._state.robots.items():
            log_item = {
                'name': '',
                'target': None,
                'loc': loc,
                'hp': robot.hp,
                'player': robot.player_id,
                'loc_end': loc,
                'hp_end': robot.hp
            }

            actions_on_turn[loc] = log_item

        self._save_actions_on_turn(actions_on_turn, settings.max_turns)

    def get_scores(self):
        return self.get_state(settings.max_turns).get_scores()
Esempio n. 14
0
class Game(object):
    def __init__(self,
                 players,
                 record_actions=False,
                 record_history=False,
                 print_info=False,
                 seed=None,
                 quiet=0,
                 delta_callback=None,
                 symmetric=True):
        self._players = players
        for i, player in enumerate(self._players):
            player.set_player_id(i)
        self._record_actions = record_actions
        self._record_history = record_history
        self._print_info = print_info
        if seed is None:
            seed = random.randint(0, settings.max_seed)
        self.seed = str(seed)
        self._random = random.Random(self.seed)
        self._quiet = quiet
        self._delta_callback = delta_callback
        self._state = GameState(use_start=True,
                                seed=self.seed,
                                symmetric=symmetric)

        self._actions_on_turn = {}
        self._states = {}
        self.history = []  # TODO: make private

    # actions_on_turn = {loc: log_item}
    # log_item = {
    #     'name': action_name,
    #     'target': action_target or None,
    #     'loc': loc,
    #     'hp': hp,
    #     'player': player_id,
    #     'loc_end': loc_end,
    #     'hp_end': hp_end
    # }
    #
    # or dummy if turn == settings.max_turn
    def get_actions_on_turn(self, turn):
        assert self._record_actions
        return self._actions_on_turn[turn]

    def get_state(self, turn):
        return self._states[turn]

    def _save_actions_on_turn(self, actions_on_turn, turn):
        self._actions_on_turn[turn] = actions_on_turn

    def _save_state(self, state, turn):
        self._states[turn] = state

    def _get_robots_actions(self):
        if self._quiet >= 1:
            sys.stdout = NullDevice()
        if self._quiet >= 2:
            sys.stderr = NullDevice()

        actions = {}
        for player in self._players:
            seed = self._random.randint(0, settings.max_seed)
            actions.update(player.get_actions(self._state, seed))

        if self._quiet >= 1:
            sys.stdout = sys.__stdout__
        if self._quiet >= 2:
            sys.stderr = sys.__stderr__

        return actions

    def _make_history(self, actions):
        '''
        An aggregate of all bots and their actions this turn.

        Stores a list of each player's bots at the start of this turn and
        the actions they each performed this turn. Newly spawned bots have no
        actions.
        '''
        robots = []
        for loc, robot in self._state.robots.iteritems():
            robot_info = {
                'location': loc,
                'hp': robot.hp,
                'player_id': robot.player_id,
                'robot_id': robot.robot_id,
            }
            if loc in actions:
                robot_info['action'] = actions[loc]
            robots.append(robot_info)
        return robots

    def _calculate_actions_on_turn(self, delta, actions):
        actions_on_turn = {}

        for delta_info in delta:
            loc = delta_info.loc

            if loc in actions:
                name = actions[loc][0]
                if name in ['move', 'attack']:
                    target = actions[loc][1]
                else:
                    target = None
            else:
                name = 'spawn'
                target = None

            # note that a spawned bot may overwrite an existing bot
            actions_on_turn[loc] = {
                'name': name,
                'target': target,
                'loc': loc,
                'hp': delta_info.hp,
                'player': delta_info.player_id,
                'loc_end': delta_info.loc_end,
                'hp_end': delta_info.hp_end
            }

        return actions_on_turn

    def run_turn(self):
        if self._print_info:
            print(' running turn %d ' % (self._state.turn)).center(70, '-')

        actions = self._get_robots_actions()

        delta = self._state.get_delta(actions)

        if self._record_actions:
            actions_on_turn = self._calculate_actions_on_turn(delta, actions)
            self._save_actions_on_turn(actions_on_turn, self._state.turn)

        new_state = self._state.apply_delta(delta)

        if self._delta_callback is not None and self._state.turn > 1:
            self._delta_callback(delta, new_state)

        self._save_state(new_state, new_state.turn)

        if self._record_history:
            self.history.append(self._make_history(actions))

        self._state = new_state

    def run_all_turns(self):
        assert self._state.turn == 0

        if self._print_info:
            print('Match seed: {0}'.format(self.seed))

        self._save_state(self._state, 0)

        while self._state.turn < settings.max_turns:
            self.run_turn()

        # create last turn's state for server history
        if self._record_history:
            self.history.append(self._make_history({}))

        # create dummy data for last turn
        # TODO: render should be cleverer
        actions_on_turn = {}

        for loc, robot in self._state.robots.iteritems():
            log_item = {
                'name': '',
                'target': None,
                'loc': loc,
                'hp': robot.hp,
                'player': robot.player_id,
                'loc_end': loc,
                'hp_end': robot.hp
            }

            actions_on_turn[loc] = log_item

        self._save_actions_on_turn(actions_on_turn, settings.max_turns)

    def get_scores(self):
        return self.get_state(settings.max_turns).get_scores()
Esempio n. 15
0
 def test_spawn(self):
     state = GameState()
     deltas = state.get_delta(actions={}, spawn=True)
     for d in deltas:
         self.assertTrue(hasattr(d, 'damage_caused'))
Esempio n. 16
0
class Game(object):
    def __init__(self, players, record_actions=False, record_history=False,
                 print_info=False, seed=None, quiet=0, delta_callback=None,
                 symmetric=True):
        self._players = players
        for i, player in enumerate(self._players):
            player.set_player_id(i)
        self._record_actions = record_actions
        self._record_history = record_history
        self._print_info = print_info
        if seed is None:
            seed = random.randint(0, settings.max_seed)
        self.seed = str(seed)
        self._random = random.Random(self.seed)
        self._quiet = quiet
        self._delta_callback = delta_callback
        self._state = GameState(use_start=True, seed=self.seed,
                                symmetric=symmetric)

        self._actions_on_turn = {}
        self._states = {}
        self.history = []  # TODO: make private

    # actions_on_turn = {loc: log_item}
    # log_item = {
    #     'name': action_name,
    #     'target': action_target or None,
    #     'loc': loc,
    #     'hp': hp,
    #     'player': player_id,
    #     'loc_end': loc_end,
    #     'hp_end': hp_end
    # }
    #
    # or dummy if turn == settings.max_turn
    def get_actions_on_turn(self, turn):
        assert self._record_actions
        return self._actions_on_turn[turn]

    def get_state(self, turn):
        return self._states[turn]

    def _save_actions_on_turn(self, actions_on_turn, turn):
        self._actions_on_turn[turn] = actions_on_turn

    def _save_state(self, state, turn):
        self._states[turn] = state

    def _get_robots_responses(self):
        # TODO: honour quietness
        actions, outputs = {}, {}
        for player in self._players:
            seed = self._random.randint(0, settings.max_seed)
            responses = player.get_responses(self._state, seed)
            actions.update(responses[0])
            outputs.update(responses[1])

        return actions, outputs

    def _make_history(self, responses, record_output=False):
        # todo: rework this. We are getting data about the player
        #       from two sources: 1) from arguments, and 2) from
        #       class members. Either move *all* to 1) and make
        #       static or move all to 2).

        '''
        An aggregate of all bots and their actions this turn.

        Optionally records per-bot output.

        Stores a list of each player's bots at the start of this turn and
        the actions they each performed this turn. Newly spawned bots have no
        actions.
        '''
        actions, outputs = responses
        robots = []
        for loc, robot in self._state.robots.items():
            robot_info = {
                'location': loc,
                'hp': robot.hp,
                'player_id': robot.player_id,
                'robot_id': robot.robot_id,
            }
            if loc in actions:
                # since the state after the final turn does not contain any
                # actions, 'loc' is not always contained in 'actions'
                robot_info['action'] = actions[loc]
            if outputs and loc in outputs:
                robot_info['output'] = outputs[loc]
            robots.append(robot_info)
        return robots

    def _calculate_actions_on_turn(self, delta, actions):
        actions_on_turn = {}

        for delta_info in delta:
            loc = delta_info.loc

            if loc in actions:
                name = actions[loc][0]
                if name in ['move', 'attack']:
                    target = actions[loc][1]
                else:
                    target = None
            else:
                name = 'spawn'
                target = None

            # note that a spawned bot may overwrite an existing bot
            actions_on_turn[loc] = {
                'name': name,
                'target': target,
                'loc': loc,
                'hp': delta_info.hp,
                'player': delta_info.player_id,
                'loc_end': delta_info.loc_end,
                'hp_end': delta_info.hp_end
            }

        return actions_on_turn

    def run_turn(self, record_output=False):
        if self._print_info:
            print((' running turn %d ' % (self._state.turn)).center(70, '-'))

        responses = self._get_robots_responses()
        actions = responses[0]

        delta = self._state.get_delta(actions)

        if self._record_actions:
            actions_on_turn = self._calculate_actions_on_turn(delta, actions)
            self._save_actions_on_turn(actions_on_turn, self._state.turn)

        new_state = self._state.apply_delta(delta)

        if self._delta_callback is not None and self._state.turn > 1:
            self._delta_callback(delta, new_state)

        self._save_state(new_state, new_state.turn)

        if self._record_history:
            self.history.append(self._make_history(
                responses, record_output=record_output))

        self._state = new_state

    def run_all_turns(self):
        assert self._state.turn == 0

        if self._print_info:
            print(('Match seed: {0}'.format(self.seed)))

        self._save_state(self._state, 0)

        while self._state.turn < settings.max_turns:
            self.run_turn()

        # create last turn's state for server history
        if self._record_history:
            self.history.append(self._make_history(({}, {})))

        # create dummy data for last turn
        # TODO: render should be cleverer
        actions_on_turn = {}

        for loc, robot in self._state.robots.items():
            log_item = {
                'name': '',
                'target': None,
                'loc': loc,
                'hp': robot.hp,
                'player': robot.player_id,
                'loc_end': loc,
                'hp_end': robot.hp
            }

            actions_on_turn[loc] = log_item

        self._save_actions_on_turn(actions_on_turn, settings.max_turns)

    def get_scores(self):
        return self.get_state(settings.max_turns).get_scores()