f | class Material: | f | class Material: |
n | | n | DENSITY = None |
| def __init__(self, mass): | | def __init__(self, mass): |
| self.mass = mass | | self.mass = mass |
n | self.density = None | n | |
| | | |
| @property | | @property |
| def volume(self): | | def volume(self): |
n | return self.mass / self.density | n | return self.mass / self.DENSITY |
| | | |
| | | |
| class Concrete(Material): | | class Concrete(Material): |
n | def __init__(self, mass): | n | DENSITY = 2500 |
| super().__init__(mass) | | |
| self.density = 2500 | | |
| | | |
| | | |
| class Brick(Material): | | class Brick(Material): |
n | def __init__(self, mass): | n | DENSITY = 2000 |
| super().__init__(mass) | | |
| self.density = 2000 | | |
| | | |
| | | |
| class Stone(Material): | | class Stone(Material): |
n | def __init__(self, mass): | n | DENSITY = 1600 |
| super().__init__(mass) | | |
| self.density = 1600 | | |
| | | |
| | | |
| class Wood(Material): | | class Wood(Material): |
n | def __init__(self, mass): | n | DENSITY = 600 |
| super().__init__(mass) | | |
| self.density = 600 | | |
| | | |
| | | |
| class Steel(Material): | | class Steel(Material): |
n | def __init__(self, mass): | n | DENSITY = 7700 |
| super().__init__(mass) | | |
| self.density = 7700 | | |
| | | |
| | | |
| class Factory: | | class Factory: |
n | _dynamic_classes = {} # Shared across all factories | n | |
| _invalid_instances = set() # Shared across all factories | | |
| _materials_created = [] # Tracks materials created by all factories | | |
| | | |
| def __init__(self): | | |
| self.material_classes = { | | material_classes = { |
| "Concrete": Concrete, | | "Concrete": Concrete, |
| "Brick": Brick, | | "Brick": Brick, |
| "Stone": Stone, | | "Stone": Stone, |
| "Wood": Wood, | | "Wood": Wood, |
| "Steel": Steel, | | "Steel": Steel, |
n | } | n | } # Shared across all factories, and here we also add the new created classes |
| | | _invalid_instances = set() # Shared across all factories |
| | | _materials_created = [] # Tracks materials created by all factories |
| | | |
| | | def __init__(self): |
| self.local_materials = [] # Tracks materials created by this factory | | self.local_materials = [] # Tracks materials created by this factory |
| | | |
| def __call__(self, *args, **kwargs): | | def __call__(self, *args, **kwargs): |
| if args and kwargs: | | if args and kwargs: |
| raise ValueError("Cannot mix positional and named arguments.") | | raise ValueError("Cannot mix positional and named arguments.") |
| | | |
| if not args and not kwargs: | | if not args and not kwargs: |
| raise ValueError("No arguments provided.") | | raise ValueError("No arguments provided.") |
| | | |
| if kwargs: | | if kwargs: |
| new_materials = self._handle_named_arguments(kwargs) | | new_materials = self._handle_named_arguments(kwargs) |
| # Register created materials | | # Register created materials |
| self.local_materials.extend(new_materials) | | self.local_materials.extend(new_materials) |
| Factory._materials_created.extend(new_materials) | | Factory._materials_created.extend(new_materials) |
| | | |
| else: | | else: |
| new_materials = self._handle_positional_arguments(args) | | new_materials = self._handle_positional_arguments(args) |
| # Register created materials | | # Register created materials |
| self.local_materials.append(new_materials) | | self.local_materials.append(new_materials) |
| Factory._materials_created.append(new_materials) | | Factory._materials_created.append(new_materials) |
| | | |
| return new_materials | | return new_materials |
| | | |
| def _handle_named_arguments(self, kwargs): | | def _handle_named_arguments(self, kwargs): |
| result = [] | | result = [] |
| for name, mass in kwargs.items(): | | for name, mass in kwargs.items(): |
n | if ( | n | not_in_material_classes = name not in Factory.material_classes |
| name not in self.material_classes and | | |
| name not in Factory._dynamic_classes | | |
| ): | | |
| | | |
| | | if not_in_material_classes: |
| raise ValueError(f"Invalid material name: {name}") | | raise ValueError(f"Invalid material name: {name}") |
| | | |
| material_class = self.material_classes.get(name) | | material_class = self.material_classes.get(name) |
n | | n | |
| if material_class is None: | | if material_class is None: |
n | material_class = Factory._dynamic_classes[name] | n | material_class = Factory.material_classes[name] |
| | | |
| result.append(material_class(mass)) | | result.append(material_class(mass)) |
| return tuple(result) | | return tuple(result) |
| | | |
| def _handle_positional_arguments(self, args): | | def _handle_positional_arguments(self, args): |
| for arg in args: | | for arg in args: |
| if arg in Factory._invalid_instances: | | if arg in Factory._invalid_instances: |
| raise AssertionError("This material has already been used and is now invalid.") | | raise AssertionError("This material has already been used and is now invalid.") |
| | | |
| all_material_names = [ | | all_material_names = [ |
| mat_name | | mat_name |
| for arg in args | | for arg in args |
| for mat_name in arg.__class__.__name__.split('_') | | for mat_name in arg.__class__.__name__.split('_') |
| ] | | ] |
| all_material_names.sort() | | all_material_names.sort() |
| class_name = "_".join(mat_name for mat_name in all_material_names) | | class_name = "_".join(mat_name for mat_name in all_material_names) |
| | | |
n | if class_name not in Factory._dynamic_classes: | n | if class_name not in Factory.material_classes: |
| densities = [arg.density for arg in args] | | densities = [arg.DENSITY for arg in args] |
| avg_density = sum(densities) / len(densities) | | DENSITY = sum(densities) / len(densities) |
| | | |
n | class DynamicMaterial(Material): | n | DynamicMaterial = type( |
| def __init__(self, mass): | | class_name, |
| super().__init__(mass) | | (Material,), |
| self.density = avg_density | | {"DENSITY": DENSITY}, |
| | | ) |
| | | Factory.material_classes[class_name] = DynamicMaterial |
| | | |
t | | t | |
| DynamicMaterial.__name__ = class_name | | |
| Factory._dynamic_classes[class_name] = DynamicMaterial | | |
| | | |
| new_class = Factory._dynamic_classes[class_name] | | new_class = Factory.material_classes[class_name] |
| total_mass = sum(arg.mass for arg in args) | | total_mass = sum(arg.mass for arg in args) |
| | | |
| # Mark used materials as invalid | | # Mark used materials as invalid |
| Factory._invalid_instances.update(args) | | Factory._invalid_instances.update(args) |
| | | |
| return new_class(total_mass) | | return new_class(total_mass) |
| | | |
| def can_build(self, volume: int) -> bool: | | def can_build(self, volume: int) -> bool: |
| total_volume = sum( | | total_volume = sum( |
| material.volume | | material.volume |
| for material in self.local_materials | | for material in self.local_materials |
| if material not in Factory._invalid_instances | | if material not in Factory._invalid_instances |
| ) | | ) |
| return total_volume >= volume | | return total_volume >= volume |
| | | |
| @classmethod | | @classmethod |
| def can_build_together(cls, volume: int) -> bool: | | def can_build_together(cls, volume: int) -> bool: |
| total_volume = sum( | | total_volume = sum( |
| material.volume | | material.volume |
| for material in cls._materials_created | | for material in cls._materials_created |
| if material not in cls._invalid_instances | | if material not in cls._invalid_instances |
| ) | | ) |
| return total_volume >= volume | | return total_volume >= volume |