Пример #1
0
class Player(object):

    def __init__(self, library: Library, strategy: t.Type[Strategy]):
        self._library = library
        self._strategy = strategy
        self.lands = 0

        self._hand = Multiset() #type: Multiset[t.Type[Card]]

    @property
    def hand(self) -> Multiset[t.Type[Card]]:
        return self._hand

    def turn(self, game: Game) -> Player:
        self._strategy.turn(game)
        return self

    def draw(self) -> Player:
        card = self._library.draw()
        if card:
            self._hand.add(card)
        return self

    def draw_hand(self) -> Player:
        for _ in range(7):
            self.draw()
        return self
Пример #2
0
    def _generate(self) -> None:
        self.accept()

        expansions = Multiset()

        for child in self.children():
            if isinstance(child, ExpansionSelectorBox):
                expansions.add(
                    Context.db.expansions[
                        child.expansion_selector.currentText()
                    ],
                    int(child.amounter.text()),
                )

        self._generateable.pool_generated.emit(expansions)
Пример #3
0
    def scale(self, amount: int) -> BaseCube:
        current_size = len(self)

        if not current_size:
            raise ValueError('cannot scale empty cube')

        remaining = amount - current_size
        if remaining <= 0:
            return self
        factor = (amount / current_size) - 1
        additionals: Multiset[Cubeable] = Multiset()
        factored = OrderedDict()

        for cubeable, multiplicity in self.cubeables.items():
            amount = multiplicity * factor
            whole = int(amount)
            if whole:
                additionals.add(cubeable, whole)
            remainder = amount - whole
            if remainder:
                factored[cubeable] = remainder

        s = sum(factored.values())

        return self + self.__class__(additionals) + (
            self.__class__(
                choice(
                    list(factored.keys()),
                    remaining - len(additionals),
                    replace = False,
                    p = [v / s for v in factored.values()],
                )
            ) if s else self.__class__()
        )
Пример #4
0
    def serialize(self, deck: Deck) -> t.AnyStr:
        root = ElementTree.Element('cockatrice_deck', {'version': '1'})
        ElementTree.SubElement(root, 'decknames')
        ElementTree.SubElement(root, 'comments')
        maindeck = ElementTree.SubElement(root, 'zone', {'name': 'main'})
        sideboard = ElementTree.SubElement(root, 'zone', {'name': 'side'})

        for element, printings in ((maindeck, deck.maindeck),
                                   (sideboard, deck.sideboard)):
            for cardboard, multiplicity in Multiset(
                    printing.cardboard for printing in printings).items():
                ElementTree.SubElement(
                    element,
                    'card',
                    {
                        'number':
                        str(multiplicity),
                        'price':
                        '0',
                        'name':
                        (cardboard.front_card.name
                         if cardboard.layout == Layout.FLIP else ' // '.join(
                             card.name for card in cardboard.front_cards)),
                    },
                )

        with io.BytesIO() as f:
            ElementTree.ElementTree(root).write(f,
                                                encoding='utf-8',
                                                xml_declaration=True)
            return f.getvalue()
Пример #5
0
    def check(cls, updater: CubeUpdater) -> t.Optional[ReportNotification]:
        groups = defaultdict(lambda: Multiset())
        for node in updater.new_nodes:
            for group in node.groups:
                groups[group].add(node)

        under_populated_groups = {
            group: nodes
            for group, nodes in
            groups.items()
            if len(nodes) <= 1
        }

        if not under_populated_groups:
            return None

        return cls(
            {
                key: under_populated_groups[key]
                for key in
                under_populated_groups.keys() - updater.group_map.groups.keys()
            },
            {
                key: under_populated_groups[key]
                for key in
                under_populated_groups.keys() & updater.group_map.groups.keys()
            },
        )
Пример #6
0
    def __init__(self, player: Player, on_the_play: bool = False):
        self._player = player
        self._damage_dealt = 0

        self._on_the_play = on_the_play
        self._turns = 0

        self._battlefield = Multiset() #type: Multiset[t.Type[Card]]
Пример #7
0
 def _block_from_group(self,
                       group: Group[Printing],
                       sideboard: bool = False) -> str:
     printings = Multiset(group.items)
     return '// {} ({})\n{}'.format(
         (self._sideboard_indicator if sideboard else '') + group.name,
         len(printings),
         self._printings_to_lines(printings, sideboard=sideboard),
     )
Пример #8
0
 def duplicate_selected(self) -> None:
     cards = self._scene.selectedItems()
     if cards:
         self._undo_stack.push(
             self._scene.get_cube_modification(
                 CubeDeltaOperation(
                     Multiset(card.cubeable for card in cards).elements()),
                 cards[0].pos() + QPoint(1, 1),
             ))
Пример #9
0
    def deserialize(self, s: t.AnyStr) -> Deck:
        maindeck = Multiset()
        sideboard = Multiset()
        pattern = re.compile('({}\s+)?(\d+) \[([A-Z0-9]*)\] (.*?)\s*$'.format(
            self._sideboard_indicator.rstrip()))
        for ln in s.split('\n'):
            m = pattern.match(ln)
            if m:
                is_sideboard, qty, expansion, name = m.groups()
                (sideboard if is_sideboard else maindeck).add(
                    self._get_printing(name.replace('/', '//'), expansion),
                    int(qty),
                )

        return Deck(
            maindeck,
            sideboard,
        )
Пример #10
0
    def check(cls, updater: CubeUpdater) -> t.Optional[ReportNotification]:
        unknown_map = defaultdict(lambda: Multiset())
        for node in updater.new_nodes:
            for group in node.groups:
                if not group in updater.new_groups.groups:
                    unknown_map[group].add(node)

        if not unknown_map:
            return None

        return cls(unknown_map)
Пример #11
0
class VerboseCubePatch(Serializeable):
    def __init__(self, changes: t.Iterable[CubeChange]):
        self._changes = Multiset(changes)

    @property
    def changes(self) -> Multiset[CubeChange]:
        return self._changes

    def serialize(self) -> serialization_model:
        return {
            'changes': [(
                change.serialize(),
                multiplicity,
                change.__class__.__name__,
            ) for change, multiplicity in self._changes.items()]
        }

    @classmethod
    def deserialize(cls, value: serialization_model,
                    inflator: Inflator) -> VerboseCubePatch:
        return cls({
            CUBE_CHANGE_MAP[klass].deserialize(change, inflator): multiplicity
            for change, multiplicity, klass in value['changes']
        })

    def __hash__(self) -> int:
        return hash(self._changes)

    def __eq__(self, other) -> bool:
        return (isinstance(other, self.__class__)
                and self._changes == other._changes)

    def __repr__(self) -> str:
        return '{}({})'.format(
            self.__class__.__name__,
            {
                change: multiplicity
                for change, multiplicity in self._changes.items()
            },
        )
Пример #12
0
    def validate(self, deck: Deck) -> t.List[str]:
        errors = []

        for cardboard, multiplicity in Multiset(
                printing.cardboard for printing in deck.seventy_five
                if not (self._ignore_basics and BASIC in
                        printing.cardboard.front_card.type_line)).items():
            if multiplicity > self._max_duplicates:
                errors.append(
                    'amount of {} ({}) greater than maximum allowed amount of single cardboard ({})'
                    .format(
                        cardboard.name,
                        multiplicity,
                        self._max_duplicates,
                    ))

        return errors
Пример #13
0
    def generate_booster(self) -> Booster[T]:
        slots = Multiset(slot.sample_slot() for slot in self.slots)
        printings = Multiset()

        for value, multiplicity in slots.items():
            try:
                printings.update(
                    multiset_sample(
                        value,
                        multiplicity,
                    )
                )
            except ValueError:
                raise GenerateBoosterException('Not enough printings')

        return Booster(printings)
Пример #14
0
    def groupify(self, items: t.Iterable[T]) -> Grouping[T]:
        items = Multiset(items)

        categories = []

        for category in self._categories:
            matches = FrozenMultiset(category.criteria.matches(items, self._extraction_strategy))
            if not matches:
                continue

            categories.append(self._group_type()(category.name, matches))

            items -= matches

        if items and self._include_others:
            categories.append(self._group_type()('Others', FrozenMultiset(items)))

        return Grouping(self._name, categories)
Пример #15
0
    def __init__(self, library: Library, strategy: t.Type[Strategy]):
        self._library = library
        self._strategy = strategy
        self.lands = 0

        self._hand = Multiset() #type: Multiset[t.Type[Card]]
Пример #16
0
    def as_verbose(self, meta_cube: MetaCube) -> VerboseCubePatch:
        group_updates = set()

        for group, new_weight in self.group_map_delta_operation.groups.items():
            current_weight = meta_cube.group_map.groups.get(group)
            if current_weight is None:
                group_updates.add(AddGroup(group, new_weight))
            else:
                if -new_weight == current_weight:
                    group_updates.add(RemoveGroup(
                        group,
                        new_weight,
                    ))
                else:
                    group_updates.add(
                        GroupWeightChange(
                            group,
                            current_weight,
                            current_weight + new_weight,
                        ))

        new_laps: Multiset[Lap] = Multiset({
            lap: multiplicity
            for lap, multiplicity in self._cube_delta_operation.laps
            if multiplicity > 0
        })
        removed_laps: Multiset[Lap] = Multiset({
            lap: -multiplicity
            for lap, multiplicity in self._cube_delta_operation.laps
            if multiplicity < 0
        })

        new_printings: Multiset[Printing] = Multiset({
            printing: multiplicity
            for printing, multiplicity in self._cube_delta_operation.printings
            if multiplicity > 0
        })
        removed_printings: Multiset[Printing] = Multiset({
            printing: -multiplicity
            for printing, multiplicity in self._cube_delta_operation.printings
            if multiplicity < 0
        })

        new_nodes: Multiset[ConstrainedNode] = Multiset({
            node: multiplicity
            for node, multiplicity in self._node_delta_operation.nodes.items()
            if multiplicity > 0
        })

        removed_nodes: Multiset[ConstrainedNode] = Multiset({
            node: -multiplicity
            for node, multiplicity in self._node_delta_operation.nodes.items()
            if multiplicity < 0
        })

        new_printings_cardboard_map = defaultdict(lambda: [])

        for printing in new_printings:
            new_printings_cardboard_map[printing.cardboard].append(printing)

        removed_printings_cardboard_map = defaultdict(lambda: [])

        for printing in removed_printings:
            removed_printings_cardboard_map[printing.cardboard].append(
                printing)

        for printings in itertools.chain(
                new_printings_cardboard_map.values(),
                removed_printings_cardboard_map.values(),
        ):
            printings.sort(key=lambda p: p.expansion.code)

        printing_changes: Multiset[t.Tuple[Printing, Printing]] = Multiset()

        for cardboard in new_printings_cardboard_map.keys(
        ) & removed_printings_cardboard_map.keys():
            new = new_printings_cardboard_map[cardboard]
            removed = removed_printings_cardboard_map[cardboard]
            while new and removed:
                _new = new.pop()
                _removed = removed.pop()
                printing_changes.add((_removed, _new))
                new_printings.remove(_new, 1)
                removed_printings.remove(_removed, 1)

        new_unnested_nodes = sorted(
            (node for node in new_nodes if all(
                isinstance(child, Printing) for child in node.node.children)),
            key=lambda node: len(node.node.children),
            reverse=True,
        )

        printings_moved_to_nodes = Multiset()

        for node in new_unnested_nodes:
            if node.node.children <= removed_printings:
                printings_moved_to_nodes.add((
                    node.node.children,
                    node,
                ))
                removed_printings -= node.node.children

        for _, node in printings_moved_to_nodes:
            new_nodes.remove(node, 1)

        removed_unnested_nodes = sorted(
            (node for node in removed_nodes if all(
                isinstance(child, Printing) for child in node.node.children)),
            key=lambda node: len(node.node.children),
            reverse=True,
        )

        nodes_moved_to_printings = Multiset()

        for node in removed_unnested_nodes:
            if node.node.children <= new_printings:
                nodes_moved_to_printings.add((
                    node.node.children,
                    node,
                ))
                new_printings -= node.node.children

        for _, node in nodes_moved_to_printings:
            removed_nodes.remove(node, 1)

        removed_nodes_by_node: t.Dict[PrintingNode,
                                      t.List[ConstrainedNode]] = {}

        for node in removed_nodes:
            try:
                removed_nodes_by_node[node.node].append(node)
            except KeyError:
                removed_nodes_by_node[node.node] = [node]

        nodes_to_traps: Multiset[t.Tuple[Trap, ConstrainedNode]] = Multiset()

        for lap in new_laps:
            if isinstance(lap, Trap) and lap.node in removed_nodes_by_node:
                nodes_to_traps.add(
                    (lap, removed_nodes_by_node[lap.node].pop()))
                if not removed_nodes_by_node[lap.node]:
                    del removed_nodes_by_node[lap.node]

        for trap, node in nodes_to_traps:
            new_laps.remove(trap, 1)
            removed_nodes.remove(node, 1)

        new_nodes_by_node: t.Dict[PrintingNode, t.List[ConstrainedNode]] = {}

        for node in new_nodes:
            try:
                new_nodes_by_node[node.node].append(node)
            except KeyError:
                new_nodes_by_node[node.node] = [node]

        traps_to_nodes: Multiset[t.Tuple[Trap, ConstrainedNode]] = Multiset()

        for lap in removed_laps:
            if isinstance(lap, Trap) and lap.node in new_nodes_by_node:
                traps_to_nodes.add((lap, new_nodes_by_node[lap.node].pop()))
                if not new_nodes_by_node[lap.node]:
                    del new_nodes_by_node[lap.node]

        for trap, node in traps_to_nodes:
            removed_laps.remove(trap, 1)
            new_nodes.remove(node, 1)

        altered_nodes = []

        for new_node in new_nodes:
            for removed_node in copy.copy(removed_nodes):
                if new_node.node == removed_node.node:
                    removed_nodes.remove(removed_node, 1)
                    altered_nodes.append([removed_node, new_node])
                    break

        for _, new_node in altered_nodes:
            new_nodes.remove(new_node, 1)

        return VerboseCubePatch(
            itertools.chain(
                (AddInfinite(cardboard)
                 for cardboard in self._infinites_delta_operation.added),
                (RemoveInfinite(cardboard)
                 for cardboard in self._infinites_delta_operation.removed),
                group_updates, (PrintingChange(before, after)
                                for before, after in printing_changes),
                (NewCubeable(lap) for lap in new_laps),
                (RemovedCubeable(lap)
                 for lap in removed_laps), (NewCubeable(printing)
                                            for printing in new_printings),
                (RemovedCubeable(printing) for printing in removed_printings),
                (NewNode(node)
                 for node in new_nodes), (RemovedNode(node)
                                          for node in removed_nodes),
                (PrintingsToNode(printings, node)
                 for printings, node in printings_moved_to_nodes),
                (NodeToPrintings(node, printings)
                 for printings, node in nodes_moved_to_printings),
                (NodeToTrap(trap, node) for trap, node in nodes_to_traps),
                (TrapToNode(trap, node) for trap, node in traps_to_nodes),
                (AlteredNode(before, after)
                 for before, after in altered_nodes)))
Пример #17
0
 def __init__(self, changes: t.Iterable[CubeChange]):
     self._changes = Multiset(changes)
Пример #18
0
    def _set_pick_point(self, pick_point: t.Optional[PickPoint],
                        new: bool) -> None:
        if not new:
            return

        self._update_pick_meta()
        self._booster_view.cube_image_view.cancel_drags()

        if pick_point is None:
            self._clear()
            return

        release_id = (pick_point.round.booster_specification.release.id
                      if isinstance(pick_point.round.booster_specification,
                                    CubeBoosterSpecification) else [])

        previous_picks = (
            self._draft_model.draft_client.history.preceding_picks(pick_point)
            if Context.settings.value('ghost_cards', True, bool) else None)

        ghost_cards: t.List[PhysicalCard] = [
            PhysicalCard.from_cubeable(
                cubeable, release_id=release_id, values={'ghost': True})
            for cubeable in previous_picks[0].booster.cubeables -
            pick_point.booster.cubeables
        ] if previous_picks else []

        cards = [
            PhysicalCard.from_cubeable(cubeable, release_id=release_id)
            for cubeable in pick_point.booster.cubeables
        ] + ghost_cards

        if self._draft_model.draft_client.rating_map is not None and settings.SHOW_PICKABLE_RATINGS.get_value(
        ):
            for card in cards:
                rating = self._draft_model.draft_client.rating_map.get(
                    cardboardize(card.cubeable))
                if rating is not None:
                    card.set_info_text(str(rating.rating))
                    card.values['rating'] = rating.rating

        self._booster_scene.get_cube_modification(
            add=cards,
            remove=self._booster_scene.items(),
            closed_operation=True,
        ).redo()
        self._booster_scene.get_default_sort().redo()

        for ghost_card in ghost_cards:
            ghost_card.add_highlight(GHOST_COLOR)
            ghost_card.values['ghost'] = True

        picks = Multiset()
        burns = Multiset()

        for _pick_point in previous_picks + ([pick_point]
                                             if pick_point.pick else []):
            if isinstance(_pick_point.pick, BurnPick):
                picks.add(_pick_point.pick.pick)
                if _pick_point.pick.burn is not None:
                    burns.add(_pick_point.pick.burn)
            else:
                picks.add(_pick_point.pick.cubeable)

        if pick_point == self._draft_model.draft_client.history.current:
            if self._draft_model.pick is not None:
                picks.add(self._draft_model.pick.cubeable)
            if self._draft_model.burn is not None:
                burns.add(self._draft_model.burn.cubeable)

        self._highlight_picks_burns(cards, picks, burns)
Пример #19
0
def check_deck_subset_pool(
    pool: Cube,
    deck: BaseMultiset[Printing],
    exempt_cardboards: t.AbstractSet[Cardboard] = frozenset(),
    *,
    strict: bool = True,
) -> Errors:
    """
    I am to lazy to make this work properly for cases that aren't required yet.
    This does not work properly if copies of the same printing are present in both tickets and traps.
    This also does not work properly if tickets have overlap (although it is not even really well defined how this
    should work)
    """

    if not strict:
        pool = pool.as_cardboards
        deck = FrozenMultiset(p.cardboard for p in deck)

    printings = Multiset(pool.models)
    anys: Multiset[AnyNode] = Multiset()

    for child in itertools.chain(*(trap.node.flattened
                                   for trap in pool.traps)):
        if isinstance(child, NodeAny):
            anys.add(child)
        else:
            printings.add(child)

    ticket_printings = set(itertools.chain(*pool.tickets))

    unaccounted_printings = Multiset({
        printing: multiplicity
        for printing, multiplicity in deck.items() if (
            (printing.cardboard not in exempt_cardboards) if strict else
            (printing not in exempt_cardboards))
    }) - printings

    printings_in_tickets = Multiset()
    printing_to_anys = defaultdict(list)

    flattened_anys = {
        _any: FrozenMultiset(_any.flattened_options)
        for _any in anys.distinct_elements()
    }

    for _any, options in flattened_anys.items():
        for option in options:
            for printing in option:
                printing_to_anys[printing].append(_any)

    any_potential_option_uses = defaultdict(Multiset)

    for unaccounted_printing in unaccounted_printings:
        _anys = printing_to_anys.get(unaccounted_printing)

        if not _anys:
            if unaccounted_printing in ticket_printings:
                printings_in_tickets.add(unaccounted_printing)
                continue
            return Errors([f'Pool does not contain {unaccounted_printing}'])

        for _any in _anys:
            for option in flattened_anys[_any]:
                if unaccounted_printing in option:
                    any_potential_option_uses[_any].add(option)

    uncontested_options = Multiset()
    contested_options = []
    for _any, options in any_potential_option_uses.items():
        for _ in range(anys.elements().get(_any, 0)):
            if not options:
                continue
            if len(options) == 1:
                uncontested_options.update(options.__iter__().__next__())
            else:
                contested_options.append(flattened_anys[_any])

    contested_printings = Multiset(
        printing for printing in unaccounted_printings - uncontested_options
        if not printing in printings_in_tickets)

    combination_printings = Multiset()
    if contested_options:
        solution_found = False
        for combination in itertools.product(*contested_options):
            combination_printings = Multiset(itertools.chain(*combination))
            if contested_printings <= combination_printings:
                solution_found = True
                break
    else:
        solution_found = True

    if not solution_found:
        return Errors(['No suitable combination of any choices'])

    unaccounted_printings -= combination_printings + uncontested_options

    if not unaccounted_printings:
        return Errors()

    printings_to_tickets = defaultdict(set)

    for ticket in pool.tickets:
        for printing in ticket:
            printings_to_tickets[printing].add(ticket)

    tickets_to_printings = defaultdict(list)

    for printing, multiplicity in unaccounted_printings.items():
        for ticket in printings_to_tickets[printing]:
            tickets_to_printings[ticket].append(printing)

    uncontested_tickets = []
    contested_tickets_printings = []
    contested_tickets_tickets = []

    for ticket, printings in tickets_to_printings.items():
        if len(printings) == 1:
            uncontested_tickets.append((ticket, printings[0]))
        else:
            contested_tickets_printings.append(printings)
            contested_tickets_tickets.append(ticket)

    for ticket, printing in uncontested_tickets:
        if _amount_printing_to_required_tickets(
                unaccounted_printings[printing]) > pool.tickets[ticket]:
            return Errors([f'Not enough tickets to pay for {printing}'])

    if contested_tickets_printings:
        solution_found = False
        for combination in itertools.product(*contested_tickets_printings):
            _printings_to_tickets = defaultdict(list)

            for ticket, printing in zip(contested_tickets_tickets,
                                        combination):
                _printings_to_tickets[printing].append(ticket)

            for printing, tickets in _printings_to_tickets.items():
                if _amount_printing_to_required_tickets(
                        unaccounted_printings[printing]) <= sum(
                            pool.tickets[ticket] for ticket in tickets):
                    solution_found = True
                    break

            if solution_found:
                break
    else:
        solution_found = True

    if not solution_found:
        return Errors(['No suitable combination of tickets'])

    return Errors()