class SlotIndexRegister(RestrictionRegister): """ Class which implements common functionality for all registers, which track indices of occupied slots and disallow multiple holders reside within slot with the same index. """ def __init__(self, slot_index_attr, restriction_type): # This attribute's value on holder # represents their index of slot self.__slot_index_attr = slot_index_attr self.__restriction_type = restriction_type # All holders which possess index of slot # are stored in this container # Format: {slot index: {holders}} self.__slotted_holders = KeyedSet() def register_holder(self, holder): # Skip items which don't have index specifier slot_index = holder.item.attributes.get(self.__slot_index_attr) if slot_index is None: return self.__slotted_holders.add_data(slot_index, holder) def unregister_holder(self, holder): slot_index = holder.item.attributes.get(self.__slot_index_attr) if slot_index is None: return self.__slotted_holders.rm_data(slot_index, holder) def validate(self): tainted_holders = {} for slot_index in self.__slotted_holders: slot_index_holders = self.__slotted_holders[slot_index] # If more than one item occupies the same slot, all # holders in this slot are tainted if len(slot_index_holders) > 1: for holder in slot_index_holders: tainted_holders[holder] = SlotIndexErrorData(holder_slot_index=slot_index) if tainted_holders: raise RegisterValidationError(tainted_holders) @property def restriction_type(self): return self.__restriction_type
class MaxGroupRegister(RestrictionRegister): """ Class which implements common functionality for all registers, which track maximum number of belonging to ship holders in certain state on per-group basis. """ def __init__(self, max_group_attr, restriction_type): # Attribute ID whose value contains group restriction # of holder self.__max_group_attr = max_group_attr self.__restriction_type = restriction_type # Container for all tracked holders, keyed # by their group ID # Format: {group ID: {holders}} self.__group_all = KeyedSet() # Container for holders, which have max group # restriction to become operational # Format: {holders} self.__group_restricted = set() def register_holder(self, holder): # Ignore holders which do not belong to ship if holder._domain != Domain.ship: return group = holder.item.group # Ignore holders, whose item isn't assigned # to any group if group is None: return # Having group ID is sufficient condition # to enter container of all fitted holders self.__group_all.add_data(group, holder) # To enter restriction container, original # item must have restriction attribute if self.__max_group_attr not in holder.item.attributes: return self.__group_restricted.add(holder) def unregister_holder(self, holder): # Just clear data containers group = holder.item.group self.__group_all.rm_data(group, holder) self.__group_restricted.discard(holder) def validate(self): # Container for tainted holders tainted_holders = {} # Go through all restricted holders for holder in self.__group_restricted: # Get number of registered holders, assigned to group of current # restricted holder, and holder's restriction value group = holder.item.group group_holders = len(self.__group_all.get(group) or ()) max_group_restriction = holder.item.attributes[self.__max_group_attr] # If number of registered holders from this group is bigger, # then current holder is tainted if group_holders > max_group_restriction: tainted_holders[holder] = MaxGroupErrorData( holder_group=group, max_group=max_group_restriction, group_holders=group_holders ) # Raise error if we detected any tainted holders if tainted_holders: raise RegisterValidationError(tainted_holders) @property def restriction_type(self): return self.__restriction_type
class MaxGroupRegister(RestrictionRegister): """ Class which implements common functionality for all registers, which track maximum number of belonging to ship holders in certain state on per-group basis. """ def __init__(self, max_group_attr, restriction_type): # Attribute ID whose value contains group restriction # of holder self.__max_group_attr = max_group_attr self.__restriction_type = restriction_type # Container for all tracked holders, keyed # by their group ID # Format: {group ID: {holders}} self.__group_all = KeyedSet() # Container for holders, which have max group # restriction to become operational # Format: {holders} self.__group_restricted = set() def register_holder(self, holder): # Ignore holders which do not belong to ship if holder._domain != Domain.ship: return group = holder.item.group # Ignore holders, whose item isn't assigned # to any group if group is None: return # Having group ID is sufficient condition # to enter container of all fitted holders self.__group_all.add_data(group, holder) # To enter restriction container, original # item must have restriction attribute if self.__max_group_attr not in holder.item.attributes: return self.__group_restricted.add(holder) def unregister_holder(self, holder): # Just clear data containers group = holder.item.group self.__group_all.rm_data(group, holder) self.__group_restricted.discard(holder) def validate(self): # Container for tainted holders tainted_holders = {} # Go through all restricted holders for holder in self.__group_restricted: # Get number of registered holders, assigned to group of current # restricted holder, and holder's restriction value group = holder.item.group group_holders = len(self.__group_all.get(group) or ()) max_group_restriction = holder.item.attributes[ self.__max_group_attr] # If number of registered holders from this group is bigger, # then current holder is tainted if group_holders > max_group_restriction: tainted_holders[holder] = MaxGroupErrorData( holder_group=group, max_group=max_group_restriction, group_holders=group_holders) # Raise error if we detected any tainted holders if tainted_holders: raise RegisterValidationError(tainted_holders) @property def restriction_type(self): return self.__restriction_type