예제 #1
0
    def destroy(self, abilities, context: Context, player: Player, game: Game):
        # cripple the lead
        destroy = abilities[0]
        assert isinstance(destroy, commands.Destroy)

        if len(
                context.builder_player.city
        ) >= 6 and context.builder_player != player and context.builder_player in destroy.choices(
                player, game):
            destroy.select(context.builder_player)
            district = max(destroy.choices(player, game),
                           key=lambda d: DistrictInfo(d).cost)
            destroy.select(district)
            return destroy

        # otherwise fire at second to lead
        for victim in sorted(
            (p for p in destroy.choices(player, game) if p != player),
                key=lambda p: len(p.city)):
            destroy.select(victim)
            if len(player.city) >= len(
                    context.builder_player.city) or player.gold >= 4:
                district = max(destroy.choices(player, game),
                               key=lambda d: DistrictInfo(d).cost)
            else:
                district = min(destroy.choices(player, game),
                               key=lambda d: DistrictInfo(d).cost)
            destroy.select(district)
            return destroy
예제 #2
0
 def _try_find_biased_color(self, player: Player):
     colors = defaultdict(int)
     for district in player.city:
         colors[DistrictInfo(district).color] += 1
     if colors:
         first_color, first_count = max(colors.items(), key=lambda p: p[1])
         del colors[first_color]
         second_count = max(colors.values()) if colors else 0
         if first_count - second_count >= 2:
             return first_color
예제 #3
0
def help_str(val):
    if isinstance(val, Character):
        info = CharacterInfo(val)
        if COLORING:
            return f'{color(info.color)}{info.name}{Style.RESET_ALL}'
        else:
            if info.color:
                return '{name} ({color})'.format(name=info.name,
                                                 color=help_str(info.color))
            else:
                return info.name

    elif isinstance(val, District):
        info = DistrictInfo(val)
        if COLORING:
            return f'{color(info.color)}{info.name}{Style.RESET_ALL} ({info.cost})'
        else:
            return '{name} ({cost} {color})'.format(name=info.name,
                                                    cost=info.cost,
                                                    color=help_str(info.color))
    elif isinstance(val, Color):
        return {
            Color.Red: 'R',
            Color.Blue: 'B',
            Color.Green: 'G',
            Color.Yellow: 'Y',
            Color.Purple: 'P',
        }[val]

    elif isinstance(val, Card):
        return '?' if not val else help_str(val)

    elif isinstance(val, shadow.ShadowPlayer):
        if COLORING:
            return f'{Fore.LIGHTWHITE_EX}{Style.BRIGHT}{val.name}{Style.RESET_ALL}'
        else:
            return val.name

    elif isinstance(val, Player):
        if COLORING:
            return f'{Fore.LIGHTWHITE_EX}{Style.BRIGHT}{val.name}{Style.RESET_ALL}'
        else:
            return val.name

    elif isinstance(val, int):
        if COLORING:
            return f'{Fore.LIGHTWHITE_EX}{Style.BRIGHT}{val}{Style.RESET_ALL}'
        else:
            return str(int)

    else:
        if COLORING:
            return f'{Fore.LIGHTWHITE_EX}{Style.BRIGHT}{val}{Style.RESET_ALL}'
        else:
            return str(val)
예제 #4
0
def score(player: Player, game: Game, with_bonuses=True):
    # SCORE-1
    score = sum(DistrictInfo(district).cost for district in player.city)

    if not with_bonuses:
        return score

    # SCORE-2
    built_colors = set()
    for district in player.city:
        built_colors.add(DistrictInfo(district).color)
    if built_colors == set(all_colors):
        score += 3

    # SCORE-3, SCORE-4
    if is_city_complete(player):
        if game.turn.first_completer == player:
            score += 4
        else:
            score += 2

    return score
예제 #5
0
    def _update(self):
        self._possible_commands.clear()

        if not self._used_commands[CommandSpecifier.Action]:
            self._possible_commands[CommandSpecifier.Action] = rules.possible_actions(self._game)

        if not self._used_commands[CommandSpecifier.Ability]:
            char_workflow = rules.CharacterWorkflow(self._player.char)
            for ability in char_workflow.abilities:
                if isinstance(ability, commands.InteractiveCommand):
                    if not ability.ready and not ability.choices(self._player, self._game):  # rare case when Destroy cannot be applied
                        continue
                if ability.restriction & commands.Restriction.OnAfterAction:
                    if not self._used_commands[CommandSpecifier.Action]:
                        continue
                    if ability.restriction & commands.Restriction.Compulsory:
                        assert not isinstance(ability, commands.InteractiveCommand)
                        ability.apply(self._player, self._game)
                        self._used_commands[CommandSpecifier.Ability].append(ability)
                        continue
                if ability.restriction & commands.Restriction.OnEndTurn:
                    if not self._used_commands[CommandSpecifier.Action]:
                        continue
                self._possible_commands[CommandSpecifier.Ability].append(ability)

        # BUILD
        if self._used_commands[CommandSpecifier.Action]:
            if len(self._used_commands[CommandSpecifier.Build]) < rules.how_many_districts_can_build(self._player):
                build_command = commands.Build()
                if build_command.choices(self._player, self._game):
                    self._possible_commands[CommandSpecifier.Build].append(build_command)

        # INCOME
        if not self._used_commands[CommandSpecifier.Income]:
            color = CharacterInfo(self._player.char).color
            income = sum(DistrictInfo(district).color == color for district in self._player.city)
            if income:
                self._possible_commands[CommandSpecifier.Income].append(commands.CashIn(income, source='income', restriction=commands.Restriction.Compulsory))

        self._assign_specifiers()
예제 #6
0
def how_much_cost_to_destroy(district: District, player: Player):
    return DistrictInfo(district).cost - 1
예제 #7
0
def how_much_cost_to_build(district: District, player: Player):
    return DistrictInfo(district).cost
예제 #8
0
    def decide(self, player: Player, game: Game, sink: CommandsSink):
        """ Should execute commands via sink """
        assert player in game.players

        context = self.create_context(player, game)

        # take income first
        if sink.possible_income:
            return sink.possible_income[0]

        # build
        if sink.possible_builds:
            build = sink.possible_builds[0]
            best_builds = sorted(build.choices(player, game),
                                 key=lambda d: DistrictInfo(d).cost,
                                 reverse=True)
            build.select(best_builds[0])
            return build

        # draw cards or take money
        if sink.possible_actions:
            take_gold = next(action for action in sink.possible_actions
                             if isinstance(action, commands.CashIn))
            take_cards = next((action for action in sink.possible_actions
                               if isinstance(action, commands.DrawSomeCards)),
                              None)
            if not take_cards:
                return take_gold

            # architect may always take gold
            if player.char == Character.Architect:
                return take_gold

            if player.gold < 4:
                return take_gold

            best_card = next(
                (card for card in take_cards.choices(player, game)
                 if rules.how_much_cost_to_build(card, player) <= player.gold
                 and rules.can_be_built(card, player)), None)
            if not best_card:
                best_card = next((card
                                  for card in take_cards.choices(player, game)
                                  if rules.how_much_cost_to_build(
                                      card, player) <= player.gold), None)
                if not best_card:
                    best_card = take_cards.choices(player, game)[0]

            assert best_card
            take_cards.select(best_card)
            return take_cards

        # play powers
        if sink.possible_abilities:
            handlers = {
                Character.Thief: self.rob,
                Character.Warlord: self.destroy,
                Character.Assassin: self.kill,
                Character.Magician: self.do_tricks,
            }
            if player.char in handlers:
                command = handlers[player.char](sink.possible_abilities,
                                                context, player, game)
                if isinstance(command, commands.InteractiveCommand):
                    assert command.ready
                return command