def test_start_round(self):
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Lizard poisons Spock
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)
        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs=ircmsgs,
                           dungeon=dungeon,
                           ircutils=ircutils)

        self.assertFalse(unit_omega.has_full_hp())
        self.assertTrue(unit_charlie.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)
    def test_add_battle(self):
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        hit_info = unit_charlie.attack(target=unit_omega)

        battle.add_round(attacker=unit_charlie,
                         target=unit_omega,
                         hit_info=hit_info)

        self.assertEqual(len(battle.rounds), 1)
    def test_can_add_round(self):
        """
        Test that:
        1. We have not exceeded the total rounds for this battle
        2. All combatants are alive
        3. No combatant can attack the same target twice in a row
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger, total_rounds=1)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=99)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        """
        Sunny day scenario
        """
        can_add_round = battle.can_add_round(attacker=unit_charlie,
                                             target=unit_omega)

        self.assertTrue(can_add_round)

        """
        Test that we cannot exceed total rounds
        """
        dungeon = self._make_dungeon()
        battle.start_round(battle=battle,
                           ircutils="quux",
                           ircmsgs="quux",
                           irc="quux",
                           dungeon=dungeon)

        self.assertEqual(len(battle.rounds), 1)

        # Swap combatant order so it is someone else's turn
        battle.combatants = list(reversed(battle.combatants))

        cannot_exceed_rounds_error = battle.can_add_round(attacker=unit_charlie,
                                                          target=unit_omega)

        self.assertEqual(cannot_exceed_rounds_error, "Cannot add round: maximum rounds reached.")
    def test_not_your_turn(self):
        """
        Unit should not be able to attack twice in a row
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=False, level=99)
        unit_omega = self._make_unit(is_player=False, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs="foo",
                           dungeon=dungeon,
                           ircutils="quux")

        """
        unit_charle's Lizard poisons unit_omega's Spock
        """
        self.assertFalse(unit_omega.has_full_hp())
        self.assertTrue(unit_charlie.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)

        """
        Try to attack again (should work since this is not pvp)
        """
        can_start_reason = battle.start_round(battle=battle,
                                              irc="quux",
                                              ircmsgs="foo",
                                              dungeon=dungeon,
                                              ircutils="quux")

        self.assertTrue(can_start_reason)
        self.assertTrue(len(battle.rounds), 3)
    def test_cannot_start_battle_with_no_combatants(self):
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        """
        Attempting to add a battle with less than two
        combatants should raise ValueError
        """
        try:
            battlemaster.add_battle(battle=battle)
        except ValueError:
            self.assertEqual(len(battlemaster.battles), 0)
    def test_target_retaliation(self):
        """
        Tests the the following use case:
        1. Attacker strikes
        2. Target is equipped with a counter item type
        3. Attacker should miss and then take damage from
           the target unit's attack
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="paper")
        target_weapon = self._make_item(item_type="scissors")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs="foo",
                           dungeon=dungeon,
                           ircutils="quux")

        """
        Since Unit Omega had scissors equipped they should
        have dealt damage to Unit Charlie
        """
        self.assertFalse(unit_charlie.has_full_hp())
        self.assertTrue(unit_omega.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)
    def test_cannot_battle_unit_in_combat(self):
        """
        Units should not be able to start a battle
        if either of them are currently engaged in
        a battle.
        """
        unit_alpha = self._make_unit(level=13)
        unit_bravo = self._make_unit(level=13)
        unit_delta = self._make_unit(level=13)

        # Engage first two targets in battle
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger, total_rounds=2)

        battle.add_combatant(unit_alpha)
        battle.add_combatant(unit_bravo)

        self.assertEqual(len(battle.combatants), 2)

        battlemaster = Battlemaster()
        battlemaster.add_battle(battle=battle)

        """
        Make sure we can't add a combatant in battle to
        another battle simultaneously
        """
        with LogCapture():
            logger = logging.getLogger()
            another_battle = Battle(log=logger)

        another_battle.add_combatant(unit_alpha)
        another_battle.add_combatant(unit_bravo)
        another_battle.add_combatant(unit_delta)

        """
        Attempting to do this should not work
        because two of the combatants are in battle.
        """
        battlemaster.add_battle(battle=another_battle)
        self.assertEqual(len(battlemaster.battles), 1)
    def test_battle_add_challenge(self):
        """
        In order to engage in combat, the unit must consent
        to a challenge. NPCs should accept automatically; an
        attack with an NPC target generates a challenge.

        If NPC:
        - issue challenge immediately adds challenge

        if PC:
        - issue challenge adds to battlemaster.challenges
        and accepting adds hostile combatant to target

        A battle ending or a unit dying should clear
        hostile combatants
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        """
        Could adding a combatant automatically issue a challenge?
        """
        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        self.assertEqual(len(battle.combatants), 2, "Failed to add combatants")

        """
        The battlemaster issues a challenge on behalf of
        unit_charlie!
        """
        battlemaster.issue_challenge(attacker=unit_charlie,
                                     target=unit_omega)

        self.assertEqual(len(battlemaster.challenges), 1)

        """
        After the challenge has been issued, verify that
        the Battlemaster has recorded that
        """
        has_challenged = \
            battlemaster.has_accepted_challenge(attacker=unit_charlie,
                                                target=unit_omega)

        self.assertFalse(has_challenged, "Failed to issue challenge")

        # Attempt to accept challenge
        battlemaster.accept_challenge_from_target(attacker=unit_charlie,
                                                  target=unit_omega)

        accepted_challenge = \
            battlemaster.has_accepted_challenge(attacker=unit_charlie,
                                                target=unit_omega)

        self.assertTrue(accepted_challenge, "Failed to accept challenge")
    def test_add_battle_with_rounds(self):
        combatant_1 = self._make_unit(level=13, is_player=False)
        combatant_2 = self._make_unit(level=13, is_player=False)

        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battle.add_combatant(combatant_1)
        battle.add_combatant(combatant_2)

        self.assertEqual(len(battle.combatants), 2)

        battlemaster = Battlemaster()
        battlemaster.add_battle(battle=battle)

        self.assertEqual(len(battlemaster.battles), 1)

        actual_1 = battlemaster.get_battle_by_combatant(combatant=combatant_1)
        self.assertIsNotNone(actual_1)
        self.assertEqual(battle, actual_1)

        actual_2 = battlemaster.get_battle_by_combatant(combatant=combatant_2)
        self.assertIsNotNone(actual_2)
        self.assertEqual(battle, actual_2)

        self.assertTrue(combatant_1.is_alive())
        self.assertTrue(combatant_2.is_alive())

        """
        Round 1
        """
        attacker_weapon = self._make_item(item_type="rock")
        target_weapon = self._make_item(item_type="scissors")

        combatant_1.equip_item(item=attacker_weapon)
        combatant_2.equip_item(item=target_weapon)

        dungeon = self._make_dungeon()
        battle.start_round(battle=battle,
                           irc="quux",
                           ircutils="quux",
                           ircmsgs="quux",
                           dungeon=dungeon)

        self.assertEqual(len(battle.rounds), 1)

        rounds_won_for_combatant_1 = battle.get_rounds_won(
            combatant=combatant_1)
        rounds_won_for_combatant_2 = battle.get_rounds_won(
            combatant=combatant_2)

        self.assertEqual(rounds_won_for_combatant_1, 1)
        self.assertEqual(rounds_won_for_combatant_2, 0)

        """
        Round 2
        """
        round_2_attacker_weapon = self._make_item(item_type="paper")
        round_2_target_weapon = self._make_item(item_type="rock")

        combatant_2.equip_item(item=round_2_attacker_weapon)
        combatant_1.equip_item(item=round_2_target_weapon)

        battle.combatants = list(reversed(battle.combatants))

        battle.start_round(battle=battle,
                           irc="quux",
                           ircutils="quux",
                           ircmsgs="quux",
                           dungeon=dungeon)

        self.assertEqual(len(battle.rounds), 2)

        rounds_won_for_combatant_1 = battle.get_rounds_won(
            combatant=combatant_1)
        rounds_won_for_combatant_2 = battle.get_rounds_won(
            combatant=combatant_2)

        self.assertEqual(rounds_won_for_combatant_1, 1)
        self.assertEqual(rounds_won_for_combatant_2, 1)

        """
        Round 3
        """
        round_3_attacker_weapon = self._make_item(item_type="lizard")
        round_3_target_weapon = self._make_item(item_type="spock")

        combatant_1.equip_item(item=round_3_attacker_weapon)
        combatant_2.equip_item(item=round_3_target_weapon)

        battle.combatants = list(reversed(battle.combatants))

        battle.start_round(battle=battle,
                           irc="quux",
                           ircutils="quux",
                           ircmsgs="quux",
                           dungeon=dungeon)

        self.assertEqual(len(battle.rounds), 3)

        rounds_won_for_combatant_1 = battle.get_rounds_won(
            combatant=combatant_1)
        rounds_won_for_combatant_2 = battle.get_rounds_won(
            combatant=combatant_2)

        self.assertEqual(rounds_won_for_combatant_1, 2)
        self.assertEqual(rounds_won_for_combatant_2, 1)

        """
        Make sure we can't exceed max rounds
        """
        cannot_add_round_reason = battle.can_add_round(attacker=combatant_1,
                                                       target=combatant_2)
        self.assertEqual(cannot_add_round_reason, "Cannot add round: maximum rounds reached.")
        self.assertEqual(len(battle.rounds), 3)