def new_no_garbage_cube(self) -> Cube: if self._new_no_garbage_cube is None: self._new_no_garbage_cube = ( self.cube + ~CubeDeltaOperation(self.cube.garbage_traps.elements()) + self._patch.cube_delta_operation) return self._new_no_garbage_cube
def __init__( self, cube_delta_operation: t.Optional[CubeDeltaOperation] = None, node_delta_operation: t.Optional[NodesDeltaOperation] = None, group_map_delta_operation: t.Optional[GroupMapDeltaOperation] = None, infinites_delta_operation: t.Optional[InfinitesDeltaOperation] = None, ): self._cube_delta_operation = CubeDeltaOperation( ) if cube_delta_operation is None else cube_delta_operation self._node_delta_operation = NodesDeltaOperation( ) if node_delta_operation is None else node_delta_operation self._group_map_delta_operation = (GroupMapDeltaOperation() if group_map_delta_operation is None else group_map_delta_operation) self._infinites_delta_operation = (InfinitesDeltaOperation() if infinites_delta_operation is None else infinites_delta_operation)
def from_meta_delta(cls, from_meta: MetaCube, to_meta: MetaCube) -> CubePatch: return cls( cube_delta_operation=( CubeDeltaOperation(to_meta.cube.cubeables.elements()) - CubeDeltaOperation(from_meta.cube.cubeables.elements())), node_delta_operation=( NodesDeltaOperation(to_meta.node_collection.nodes.elements()) - NodesDeltaOperation( from_meta.node_collection.nodes.elements())), group_map_delta_operation=( GroupMapDeltaOperation(to_meta.group_map.groups) - GroupMapDeltaOperation(from_meta.group_map.groups)), infinites_delta_operation=InfinitesDeltaOperation.from_change( from_meta.infinites, to_meta.infinites, ), )
def add_cubeables(self, amount: int = 1) -> None: add = self._get_cubeables(amount) if add: self._hand_scene.get_cube_modification( CubeDeltaOperation(add), closed_operation=True, ).redo() self._undo_stack.clear() self._hand_view.update()
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), ))
def as_patch(self) -> CubePatch: return CubePatch( CubeDeltaOperation({ self._trap: 1, }), NodesDeltaOperation({ self._node: -1, }), )
def as_patch(self) -> CubePatch: return CubePatch( CubeDeltaOperation({ printing: multiplicity for printing, multiplicity in self._after.items() }), NodesDeltaOperation({ self._before: -1, }), )
def setData(self, index: QModelIndex, value: int, role: int = ...) -> bool: if role != Qt.EditRole or index.column() != 0: return False try: cubeable, quantity = self._lines.items()[index.row()] except IndexError: return False self._undo_stack.push( self._cube_scene.get_cube_modification( modification=CubeDeltaOperation({cubeable: value - quantity}))) return True
def _paste(self): mime = Context.clipboard.mimeData() cards = mime.data('cards') if not cards: return cube = PickleStrategy(Context.db).deserialize(Cube, cards) self._undo_stack.push( self._scene.get_cube_modification( CubeDeltaOperation(cube.cubeables.elements()), QPoint() if self._last_move_event_pos is None else self.mapToScene(self._last_move_event_pos), ))
def deserialize(cls, value: serialization_model, inflator: Inflator) -> CubePatch: return cls( cube_delta_operation=CubeDeltaOperation.deserialize( value['cube_delta'], inflator), node_delta_operation=(NodesDeltaOperation.deserialize( value['nodes_delta'], inflator) if 'nodes_delta' in value else NodesDeltaOperation()), group_map_delta_operation=(GroupMapDeltaOperation.deserialize( value['groups_delta'], inflator) if 'groups_delta' in value else GroupMapDeltaOperation()), infinites_delta_operation=(InfinitesDeltaOperation.deserialize( value['infinites_delta'], inflator) if 'infinites_delta' in value else InfinitesDeltaOperation()), )
def _on_printing_selected(self, printing: Printing) -> None: modifiers = EmbargoApp.current.keyboardModifiers() if modifiers & QtCore.Qt.ControlModifier: amount, ok = QInputDialog.getInt( self, 'Choose printing amount', '', 4, 1, 99, ) if not ok: amount = 0 else: amount = 4 if modifiers & QtCore.Qt.ShiftModifier else 1 if amount: self.add_printings.emit(CubeDeltaOperation({printing: amount}))
def removeRows(self, row: int, count: int, parent: QModelIndex = ...) -> bool: with self._changing: self.beginRemoveRows(parent, row, row - 1 + count) if row + count > len(self._lines): return False self._undo_stack.push( self._cube_scene.get_cube_modification( modification=CubeDeltaOperation({ cubeable: -value for cubeable, value in self._lines.items()[row:row + count] }))) self.endRemoveRows() return True
def cube_delta_operation(self): return CubeDeltaOperation( Counter(card.cubeable for card in self.added) - Counter(card.cubeable for card in self.removed))
def test(): db = Loader.load() strategy = JsonId(db) cube = CubeLoader(db).load() constrained_nodes = NodeCollection( ConstrainedNodeFetcher(db).fetch_garbage()) groups = GroupMap(_GROUP_WEIGHTS) # s = '{"cube_delta": {}, "nodes_delta": {"nodes": []}}' # patch = strategy.deserialize(CubePatch, s) patch = CubePatch( CubeDeltaOperation({ db.cardboards['Brainstorm'].from_expansion('ICE'): -1, db.cardboards['Brainstorm'].from_expansion('EMA'): 1, # Trap( # AllNode( # ( # db.cardboards['Brainstorm'].from_expansion('ICE'), # db.cardboards['Web'].from_expansion('LEA'), # ) # ), # intention_type=IntentionType.SYNERGY, # ): 2 }), NodesDeltaOperation({ # ConstrainedNode( # node = AllNode( # ( # db.cardboards['Web'].from_expansion('LEA'), # ) # ), # groups = ['ok', 'lmao'], # value = 2, # ): 1, ConstrainedNode( node=AllNode(( db.cardboards['Brainstorm'].from_expansion('ICE'), db.cardboards['Web'].from_expansion('LEA'), )), groups=['lolHAHA'], value=1, ): 1, })) print(patch) meta_cube = MetaCube(cube, constrained_nodes, groups) verbose_patch = patch.as_verbose(meta_cube) print(verbose_patch) updater = CubeUpdater(meta_cube, patch) print(updater) report = UpdateReport(updater) for notification in report.notifications: print(notification.title + '\n' + notification.content + '\n\n')
class CubePatch(Serializeable, PersistentHashable): def __init__( self, cube_delta_operation: t.Optional[CubeDeltaOperation] = None, node_delta_operation: t.Optional[NodesDeltaOperation] = None, group_map_delta_operation: t.Optional[GroupMapDeltaOperation] = None, infinites_delta_operation: t.Optional[InfinitesDeltaOperation] = None, ): self._cube_delta_operation = CubeDeltaOperation( ) if cube_delta_operation is None else cube_delta_operation self._node_delta_operation = NodesDeltaOperation( ) if node_delta_operation is None else node_delta_operation self._group_map_delta_operation = (GroupMapDeltaOperation() if group_map_delta_operation is None else group_map_delta_operation) self._infinites_delta_operation = (InfinitesDeltaOperation() if infinites_delta_operation is None else infinites_delta_operation) @property def cube_delta_operation(self) -> CubeDeltaOperation: return self._cube_delta_operation @property def node_delta_operation(self) -> NodesDeltaOperation: return self._node_delta_operation @property def group_map_delta_operation(self) -> GroupMapDeltaOperation: return self._group_map_delta_operation @property def infinites_delta_operation(self) -> InfinitesDeltaOperation: return self._infinites_delta_operation @classmethod def from_meta_delta(cls, from_meta: MetaCube, to_meta: MetaCube) -> CubePatch: return cls( cube_delta_operation=( CubeDeltaOperation(to_meta.cube.cubeables.elements()) - CubeDeltaOperation(from_meta.cube.cubeables.elements())), node_delta_operation=( NodesDeltaOperation(to_meta.node_collection.nodes.elements()) - NodesDeltaOperation( from_meta.node_collection.nodes.elements())), group_map_delta_operation=( GroupMapDeltaOperation(to_meta.group_map.groups) - GroupMapDeltaOperation(from_meta.group_map.groups)), infinites_delta_operation=InfinitesDeltaOperation.from_change( from_meta.infinites, to_meta.infinites, ), ) 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))) def serialize(self) -> serialization_model: return { 'cube_delta': self._cube_delta_operation, 'nodes_delta': self._node_delta_operation, 'groups_delta': self._group_map_delta_operation, 'infinites_delta': self._infinites_delta_operation, } @classmethod def deserialize(cls, value: serialization_model, inflator: Inflator) -> CubePatch: return cls( cube_delta_operation=CubeDeltaOperation.deserialize( value['cube_delta'], inflator), node_delta_operation=(NodesDeltaOperation.deserialize( value['nodes_delta'], inflator) if 'nodes_delta' in value else NodesDeltaOperation()), group_map_delta_operation=(GroupMapDeltaOperation.deserialize( value['groups_delta'], inflator) if 'groups_delta' in value else GroupMapDeltaOperation()), infinites_delta_operation=(InfinitesDeltaOperation.deserialize( value['infinites_delta'], inflator) if 'infinites_delta' in value else InfinitesDeltaOperation()), ) def _calc_persistent_hash(self) -> t.Iterator[t.ByteString]: yield self._cube_delta_operation.persistent_hash().encode('ASCII') yield self._node_delta_operation.persistent_hash().encode('ASCII') yield self._group_map_delta_operation.persistent_hash().encode('ASCII') yield self._infinites_delta_operation.persistent_hash().encode('ASCII') def __hash__(self) -> int: return hash(( self._cube_delta_operation, self._node_delta_operation, self._group_map_delta_operation, self._infinites_delta_operation, )) def __eq__(self, other: object) -> bool: return (isinstance(other, self.__class__) and self._cube_delta_operation == other._cube_delta_operation and self._node_delta_operation == other._node_delta_operation and self._group_map_delta_operation == other.group_map_delta_operation and self._infinites_delta_operation == other._infinites_delta_operation) def __repr__(self): return '{}({}, {}, {}, {})'.format( self.__class__.__name__, self._cube_delta_operation, self._node_delta_operation, self._group_map_delta_operation, self._infinites_delta_operation, ) def __mul__(self, other: int) -> CubePatch: return self.__class__( self._cube_delta_operation * other, self._node_delta_operation * other, self._group_map_delta_operation * other, self._infinites_delta_operation, ) def __add__( self, other: t.Union[CubePatch, MetaCube]) -> t.Union[CubePatch, MetaCube]: if isinstance(other, MetaCube): return MetaCube( other.cube + self._cube_delta_operation, other.node_collection + self._node_delta_operation, other.group_map + self._group_map_delta_operation, other.infinites + self._infinites_delta_operation, ) return self.__class__( self._cube_delta_operation + other._cube_delta_operation, self._node_delta_operation + other._node_delta_operation, self._group_map_delta_operation + other._group_map_delta_operation, self._infinites_delta_operation + other._infinites_delta_operation, ) __radd__ = __add__ def __sub__(self, other: CubePatch) -> CubePatch: return self.__class__( self._cube_delta_operation - other._cube_delta_operation, self._node_delta_operation - other._node_delta_operation, self._group_map_delta_operation - other._group_map_delta_operation, self._infinites_delta_operation - other._infinites_delta_operation, )
def as_patch(self) -> CubePatch: return CubePatch( CubeDeltaOperation({ self._before: -1, self._after: 1, }), )
def as_patch(self) -> CubePatch: return CubePatch(CubeDeltaOperation({ self._cubeable: -1, }))