Exemple #1
0
 def battle(self, clearing: Clearing, defender: Player) -> None:
     if self.has_trait(TRAIT_MARKSMAN):
         # Fortified MM is currently the only way for a piece to require two hits to be removed in battle
         # Marksman Vagabot is currently the only way for a player to deal hits in battle to a bot before the roll
         # To prevent messy logic in handling taking hits, we just deal two hits of damage in this case, which will
         # still functionally remove one building
         if defender.halves_damage(clearing):
             marksman_damage_result = defender.suffer_damage(clearing, 2, self, is_attacker=False)
         else:
             marksman_damage_result = defender.suffer_damage(clearing, 1, self, is_attacker=False)
         self.add_victory_points(marksman_damage_result.points_awarded +
                                 self.supplementary_score_for_removed_pieces_in_battle(
                                     defender, marksman_damage_result.removed_pieces, is_attacker=True))
     random_rolls = (random.randint(0, 3), random.randint(0, 3))
     # Defender allocates the rolls - high roll to attacker, low roll to defender, except in the case of Veterans
     roll_result = defender.allocate_rolls_as_defender(random_rolls)
     # Each battler caps their hits and adds their relevant bonus hits
     attacker_hits = (self.cap_rolled_hits(clearing, roll_result.attacker_roll) +
                      self.get_bonus_hits(clearing, defender, is_attacker=True))
     defender_hits = (defender.cap_rolled_hits(clearing, roll_result.defender_roll) +
                      defender.get_bonus_hits(clearing, defender, is_attacker=False))
     # Each battler removes their pieces and calculates how much VP the opponent should earn from the battle
     defender_damage_result = defender.suffer_damage(clearing, attacker_hits, self, is_attacker=False)
     attacker_damage_result = self.suffer_damage(clearing, defender_hits, defender, is_attacker=True)
     # Each battler scores their awarded VP, plus any bonus VP they deserve (such as Vagabot from removing warriors)
     self.add_victory_points(defender_damage_result.points_awarded +
                             self.supplementary_score_for_removed_pieces_in_battle(
                                 defender, defender_damage_result.removed_pieces, is_attacker=True))
     defender.add_victory_points(attacker_damage_result.points_awarded +
                                 defender.supplementary_score_for_removed_pieces_in_battle(
                                     self, attacker_damage_result.removed_pieces, is_attacker=False))
Exemple #2
0
 def explore_ruin(self, player: Player) -> None:
     if not self.ruin or not self.ruin.items:
         return
     item_gained = self.ruin.items[0]
     player.get_item(self.ruin.items[0])
     self.ruin.items.remove(item_gained)
     if not self.ruin.items:
         self.ruin = None
Exemple #3
0
 def test_subtract_victory_points_below_0(self):
     mock_game = Mock()
     mock_piece_stock = Mock()
     player = Player(mock_game, Faction.MECHANICAL_MARQUISE_2_0,
                     mock_piece_stock)
     player.victory_points = 1
     player.add_victory_points(-2)
     self.assertEqual(player.victory_points, 0)
Exemple #4
0
 def test_add_victory_points(self):
     mock_game = Mock()
     mock_piece_stock = Mock()
     player = Player(mock_game, Faction.MECHANICAL_MARQUISE_2_0,
                     mock_piece_stock)
     self.assertEqual(player.victory_points, 0)
     player.add_victory_points(2)
     self.assertEqual(player.victory_points, 2)
Exemple #5
0
    def test_get_item(self):
        mock_game = Mock()
        mock_piece_stock = Mock()
        player = Player(mock_game, Faction.MECHANICAL_MARQUISE_2_0,
                        mock_piece_stock)

        self.assertEqual(player.crafted_items, [])
        item = ItemToken(Item.BAG)
        player.get_item(item)
        self.assertEqual(player.crafted_items, [item])
Exemple #6
0
 def can_move_piece(self,
                    player: Player,
                    piece: Piece,
                    destination: Location,
                    requires_rule: bool = False) -> bool:
     # We'll never look for rule in move that isn't from one clearing to another clearing
     if requires_rule and isinstance(destination, Clearing):
         if not player.does_rule_clearing(
                 self) and not player.does_rule_clearing(destination):
             return False
     return super().can_move_piece(player, piece, destination,
                                   requires_rule)
Exemple #7
0
 def remove_pieces(self, player: Player, pieces: list[Piece]) -> list[Piece]:
     # Remove piece from this location
     removed_pieces = []
     for piece in pieces:
         if piece.cannot_be_removed:
             piece.resolve_effects_on_attempting_to_remove_self()  # For Vagabot 'Big Damage'
         else:
             self.piece_map(player).remove_piece(piece)
             removed_pieces.append(piece)
     # It's up to the owners of pieces that cannot be removed to override this method
     player.move_removed_pieces_into_supply(pieces, self)
     return removed_pieces
Exemple #8
0
 def battle(self, clearing: Clearing, defender: Player) -> None:
     random_rolls = (random.randint(0, 3), random.randint(0, 3))
     # Defender allocates the rolls - high roll to attacker, low roll to defender, except in the case of Veterans
     roll_result = defender.allocate_rolls_as_defender(random_rolls)
     # Each battler caps their hits and adds their relevant bonus hits
     attacker_hits = (
         self.cap_rolled_hits(clearing, roll_result.attacker_roll) +
         self.get_bonus_hits(clearing, defender, is_attacker=True))
     defender_hits = (
         defender.cap_rolled_hits(clearing, roll_result.defender_roll) +
         defender.get_bonus_hits(clearing, defender, is_attacker=False))
     # Each battler removes their pieces and calculates how much VP the opponent should earn from the battle
     defender_damage_result = defender.suffer_damage(clearing,
                                                     attacker_hits,
                                                     self,
                                                     is_attacker=False)
     attacker_damage_result = self.suffer_damage(clearing,
                                                 defender_hits,
                                                 defender,
                                                 is_attacker=True)
     # Each battler scores their awarded VP, plus any bonus VP they deserve (such as Vagabot from removing warriors)
     self.add_victory_points(
         defender_damage_result.points_awarded +
         self.supplementary_score_for_removed_pieces_in_battle(
             defender,
             defender_damage_result.removed_pieces,
             is_attacker=True))
     if self.has_trait(TRAIT_WAR_TAX):
         removed_buildings_and_tokens = [
             piece for piece in defender_damage_result.removed_pieces
             if isinstance(piece, Building) or isinstance(piece, Token)
         ]
         defender.add_victory_points(len(removed_buildings_and_tokens) * -1)
     defender.add_victory_points(
         attacker_damage_result.points_awarded +
         defender.supplementary_score_for_removed_pieces_in_battle(
             self, attacker_damage_result.removed_pieces, is_attacker=False)
     )
Exemple #9
0
    def find_shortest_legal_paths_to_destination_clearing(
            self,
            player: Player,
            moving_piece: Piece,
            destination: Clearing,
            ignore_move: bool = False) -> list[list[Clearing]]:
        if not destination.can_move_piece_into(player, moving_piece):
            return []

        # TODO: Test and clean
        clearing_paths: deque[list[Clearing]] = deque([[]])
        all_shortest_paths: list[list[Clearing]] = []

        while clearing_paths:
            current_path = clearing_paths.popleft()
            if not current_path:
                current_location = self
            else:
                current_location = current_path[-1]

            for adjacent_clearing in player.get_adjacent_clearings(
                    current_location):
                # Don't double back on yourself - that adds to the path length uselessly
                if adjacent_clearing in current_path:
                    continue
                # Skip impossible moves, unless ignore_move is True
                if not ignore_move and not current_location.can_move_piece(
                        player, moving_piece, adjacent_clearing):
                    continue
                next_path = [c for c in current_path]
                next_path.append(adjacent_clearing)
                if adjacent_clearing == destination:
                    all_shortest_paths.append(
                        [c for c in current_path if isinstance(c, Clearing)])
                # Breadth-first-search: Once we find a path to the destination N clearings away, we know no path to
                # the destination is shorter than N, so don't add any more to the clearing_paths queue
                elif not all_shortest_paths:
                    clearing_paths.append(next_path)

        return all_shortest_paths
Exemple #10
0
 def craft_item(self, item: Item, player: Player, score_points: int) -> None:
     item_token = self.get_item_if_available(item)
     if item_token:
         self.item_supply.remove(item_token)
         player.get_item(item_token)
         player.add_victory_points(score_points)
Exemple #11
0
 def remove_pieces_in_battle_to_supply(self, player: Player, pieces: list[Piece], is_attacker: bool = True) -> None:
     # Remove piece from this clearing
     for piece in pieces:
         self.piece_map(player).remove_piece(piece)
     player.move_removed_pieces_into_supply_from_battle(pieces, self, is_attacker)
Exemple #12
0
def sort_clearings_by_ruled_by_self(clearings: list[Clearing],
                                    acting_player: Player,
                                    descending: bool = True) -> list[Clearing]:
    return sorted(clearings,
                  key=lambda c: acting_player.does_rule_clearing(c),
                  reverse=descending)