Exemple #1
0
 def load_smuggler(self, s: str):
     actions = phase_subtokens(s)
     for idx, action in enumerate(actions):
         if not action:
             assert idx == len(actions) - 1
             break
         subtokens = action_subtokens(action)
         if action.upper().startswith('CARD'):
             card = self.determine_card(action, self.gs)
             yield load_all_phase_card_action(card, subtokens[1:])
             continue
         # Just 'Y' could match if we don't require the length of name_subtokens to be 2, which would be an issue
         name_subtokens = tokens(subtokens[0])
         if tokens_match(name_subtokens,
                         ['YELLOW', 'TILE']) and len(name_subtokens) == 2:
             assert len(
                 subtokens) == 2, 'Location required with yellow tile'
             yield YellowTileAction(
                 self.location_transformer.apply(Location(int(
                     subtokens[1]))))
             continue
         gain, cost, roll = subtokens
         yield EncounterSmuggler(load_good(gain),
                                 Pay() if cost == '-2' else load_good(cost),
                                 load_roll(roll))
Exemple #2
0
 def load_choose_reward(self, s: str) -> typing.Iterator[PlayerAction]:
     actions = phase_subtokens(s)
     for idx, action in enumerate(actions):
         if not action:
             assert idx == len(actions) - 1
             break
         subtokens = action_subtokens(action)
         if action.upper().startswith('CARD'):
             card = self.determine_card(action, self.gs)
             yield load_all_phase_card_action(card, subtokens[1:])
             continue
         # Just 'Y' could match if we don't require the length of name_subtokens to be 2, which would be an issue
         name_subtokens = tokens(subtokens[0])
         if tokens_match(name_subtokens,
                         ['YELLOW', 'TILE']) and len(name_subtokens) == 2:
             assert len(
                 subtokens) == 2, 'Location required with yellow tile'
             yield YellowTileAction(
                 self.location_transformer.apply(Location(int(
                     subtokens[1]))))
             continue
         for subtoken in subtokens:
             if subtoken in {'3', '6', '9', '12'}:
                 for _ in range(int(subtoken) // 3):
                     yield ChooseReward(ChooseReward.LIRA)
                 continue
             card = load_exact_card(subtoken)
             yield ChooseReward(card)
Exemple #3
0
def load_possible_red_tile_action(s: str) -> typing.Union[Roll, RedTileAction]:
    subtokens = action_subtokens(s)
    if len(subtokens) == 1:
        return load_roll(subtokens[0])

    first = subtokens[0]
    assert tokens_match(tokens(first), ['RED', 'TILE']), '{} is not RedTile'.format(first)
    assert len(subtokens) == 4, 'RedTile requires 3 inputs, got'.format(len(subtokens) - 1)
    initial_roll = load_roll(subtokens[1])
    final_roll = load_roll(subtokens[3])
    method = {'F': RedTileAction.TO_FOUR, 'R': RedTileAction.REROLL, '4': RedTileAction.TO_FOUR}[subtokens[2]]
    return RedTileAction(initial_roll, final_roll, method)
Exemple #4
0
def load_market_action(s: str, ps: PlayerState, ts: MarketTileState) -> MarketAction:
    subtokens = action_subtokens(s)
    assert len(subtokens) == 2, f'Expected 2 subtokens for market action; got {s}'
    assert ts.demand is not None, 'No demand currently set on tile'

    goods, new_demand = subtokens
    if 'ALL'.startswith(goods.upper()):
        max_demand: typing.Counter[Good] = Counter()
        for good in Good:
            max_demand[good] = min(ps.cart_contents[good], ts.demand[good])
        assert 0 < sum(max_demand.values()) <= 5, f'All would imply {sum(max_demand.values())} goods'
        goods = max_demand
    else:
        goods = load_good_counter(goods)

    new_demand = load_good_counter(new_demand)

    return MarketAction(goods, new_demand)
Exemple #5
0
def load_caravansary_action(s: str) -> CaravansaryAction:
    subtokens = action_subtokens(s)
    assert len(subtokens) == 3, f'Expected 3 subtokens for caravansary action; got {s}'

    first, second = subtokens[:2]
    if 'DISCARD'.startswith(first.upper()):
        first_gain = CaravansaryAction.DISCARD
    else:
        first_gain = load_exact_card(first)

    if 'DISCARD'.startswith(second.upper()):
        second_gain = CaravansaryAction.DISCARD
    else:
        second_gain = load_exact_card(second)

    cost = load_exact_card(subtokens[2])

    return CaravansaryAction((first_gain, second_gain), cost)
Exemple #6
0
 def determine_card(cls,
                    card_action: str,
                    gs: GameState,
                    tile: typing.Optional[Tile] = None) -> Card:
     subtokens = action_subtokens(card_action)
     player_state = gs.player_states[gs.turn_state.current_player]
     if subtokens[0].upper() == 'CARD':
         cards: typing.Set[Card] = {
             k
             for k, v in player_state.hand.items() if v > 0
         }
     else:
         pre, card_desc = subtokens[0].split('-')
         assert pre.upper() == 'CARD'
         cards = load_card(card_desc)
     possible_cards = cards & cls.allowed_cards(gs, tile)
     assert possible_cards, f'{card_action} does not match any currently legal cards'
     assert len(
         possible_cards
     ) == 1, f'{card_action} matched multiple legal cards: {possible_cards}'
     return possible_cards.pop()
Exemple #7
0
def load_warehouse_action(s: str) -> typing.Union[GenericTileAction, GreenTileAction]:
    if not s:
        return GenericTileAction()
    gt, good = action_subtokens(s)
    assert tokens_match(tokens(gt), ['GREEN', 'TILE']), '{} is not GreenTile'.format(gt)
    return GreenTileAction(load_good(good))
Exemple #8
0
    def load_phase_3(
            self,
            s: str,
            tile: typing.Optional[Tile] = None
    ) -> typing.Iterator[PlayerAction]:
        actions = phase_subtokens(s)
        player_state = self.gs.player_states[self.gs.turn_state.current_player]
        tile = self.gs.location_map[
            player_state.location] if tile is None else tile
        tile_state = typing.cast(MarketTileState, self.gs.tile_states[tile])
        for idx, action in enumerate(actions):
            if action == '!':
                yield SkipTileAction()
                return
            if action == '':
                assert tile in self.GENERIC_ACTION_TILES, f'{tile} is not generic'
                yield GenericTileAction()
                continue
            subtokens = action_subtokens(action)
            if action.upper().startswith('CARD'):
                card = self.determine_card(action, self.gs, tile)
                if card in {Card.DOUBLE_PO, Card.DOUBLE_DEALER}:
                    assert len(subtokens) == 1
                    yield DoubleCardAction(
                        card, (GenericTileAction(), GenericTileAction()))
                    continue
                if card is Card.DOUBLE_SULTAN:
                    _, first_cost, second_cost = subtokens
                    yield DoubleCardAction(
                        card,
                        (SultansPalaceAction(load_good_counter(first_cost)),
                         SultansPalaceAction(load_good_counter(second_cost))))
                    continue
                if card is Card.SELL_ANY:
                    assert len(
                        subtokens
                    ) == 3, 'Incorrect number of arguments for sell any goods card'
                    assert tile is Tile.SMALL_MARKET, 'Can only use sell any goods card at small marker'
                    if not 'ALL'.startswith(subtokens[1].upper()):
                        yield SellAnyCardAction(
                            load_market_action(' '.join(subtokens[1:]),
                                               player_state, tile_state))
                        continue
                    assert 0 < sum(player_state.cart_contents.values()) <= 5, \
                        f'All is ambiguous with {sum(player_state.cart_contents.values())} goods'
                    yield SellAnyCardAction(
                        MarketAction(player_state.cart_contents.copy(),
                                     load_good_counter(subtokens[2])))
                    continue
                yield load_all_phase_card_action(card, subtokens[1:])
                continue
            # Just 'Y' could match if we don't require the length of name_subtokens to be 2, which would be an issue
            name_subtokens = tokens(subtokens[0])
            if tokens_match(name_subtokens,
                            ['YELLOW', 'TILE']) and len(name_subtokens) == 2:
                assert len(
                    subtokens) == 2, 'Location required with yellow tile'
                yield YellowTileAction(
                    self.location_transformer.apply(Location(int(
                        subtokens[1]))))
                continue

            if tile is Tile.POLICE_STATION:
                location = Location(int(subtokens[0]))
                dest_tile = self.gs.location_map[
                    self.location_transformer.apply(location)]
                args = ' '.join(subtokens[1:])
                dest_actions = list(self.load_phase_3(args, tile=dest_tile))
                assert len(dest_actions) == 1
                # This is manually type checked in the constructor
                dest_action = typing.cast(
                    typing.Union[PlaceTileAction, GreenTileAction,
                                 DoubleCardAction, SellAnyCardAction],
                    dest_actions[0])
                yield PoliceStationAction(location, dest_action)
                continue

            if tile is Tile.FOUNTAIN:
                if len(subtokens) == 1 and subtokens[0].upper() == 'ALL':
                    yield GenericTileAction()
                    continue
                locations = map(
                    self.location_transformer.apply,
                    typing.cast(typing.Iterator[Location], map(int,
                                                               subtokens)))
                yield FountainAction(locations)
                continue

            if tile in {Tile.SMALL_MARKET, Tile.LARGE_MARKET}:
                yield load_market_action(action, player_state, tile_state)
                continue

            lookup_table = {
                Tile.GREAT_MOSQUE:
                load_mosque_action,
                # Post office is always generic or card
                Tile.FABRIC_WAREHOUSE:
                load_warehouse_action,
                Tile.SMALL_MOSQUE:
                load_mosque_action,
                Tile.FRUIT_WAREHOUSE:
                load_warehouse_action,
                # Police station is special, above
                # Fountain is special, above
                Tile.SPICE_WAREHOUSE:
                load_warehouse_action,
                Tile.BLACK_MARKET:
                load_black_market_action,
                Tile.CARAVANSARY:
                load_caravansary_action,
                # Small market is special, above
                Tile.TEA_HOUSE:
                load_tea_house_action,
                Tile.SULTANS_PALACE:
                load_sultans_palace_action,
                # Large market is special, above
                # Wainwright is always generic
                # Gemstone dealer is always generic
            }
            yield lookup_table[tile](action)
Exemple #9
0
    def load_phases_12(self, s: str) -> typing.Iterator[PlayerAction]:
        dont_pay = False
        if s.endswith('!$'):
            dont_pay = True
            s = s[:-2].strip()
        actions = phase_subtokens(s)
        player_state = self.gs.player_states[self.gs.turn_state.current_player]
        for idx, action in enumerate(actions):
            skip_assistant = False
            if action.endswith('!'):
                skip_assistant = True
                action = action[:-1]
            action = action.rstrip()

            if action.isdigit():
                yield Move(self.location_transformer.apply(
                    Location(int(action))),
                           skip_assistant=skip_assistant)
                if skip_assistant:
                    assert idx == len(
                        actions) - 1, 'Assistant skip was not last action'
                    yield YieldTurn()
                    return
                continue

            subtokens = action_subtokens(action)
            assert subtokens, 'Empty action not allowed in phase 1'
            if action.upper().startswith('CARD'):
                card = self.determine_card(action, self.gs)

                if card is Card.NO_MOVE:
                    assert len(subtokens) == 1
                    yield NoMoveCardAction(skip_assistant)
                    if skip_assistant:
                        assert idx == len(
                            actions) - 1, 'Assistant skip was not last action'
                        yield YieldTurn()
                        return
                    continue

                if card is Card.EXTRA_MOVE:
                    assert len(subtokens
                               ) == 2, 'Location required with extra move card'
                    yield ExtraMoveCardAction(
                        Move(self.location_transformer.apply(
                            Location(int(subtokens[1]))),
                             skip_assistant=skip_assistant))
                    if skip_assistant:
                        assert idx == len(
                            actions) - 1, 'Assistant skip was not last action'
                        yield YieldTurn()
                        return
                    continue

                if card is Card.RETURN_ASSISTANT:
                    assert len(
                        subtokens
                    ) == 2, 'Location required with return assistant card'
                    yield ReturnAssistantCardAction(
                        self.location_transformer.apply(
                            Location(int(subtokens[1]))))
                    continue

                yield load_all_phase_card_action(card, subtokens[1:])
                continue

            assert tokens_match(tokens(subtokens[0]),
                                ['YELLOW', 'TILE']), f'Unknown action {action}'
            assert len(subtokens) == 2, 'Location required with yellow tile'
            yield YellowTileAction(
                self.location_transformer.apply(Location(int(subtokens[1]))))
            continue

        # After applying all the explicit actions
        tile_state = self.gs.tile_states[self.gs.location_map[
            player_state.location]]
        if len(tile_state.players) > 1 and self.gs.location_map[
                player_state.location] is not Tile.FOUNTAIN:
            if dont_pay:
                yield YieldTurn()
            else:
                yield Pay()
        else:
            assert not dont_pay, 'Payment not required'