f | class Material: | f | class Material: |
| def __init__(self, weight): | | def __init__(self, weight): |
| self.weight = weight | | self.weight = weight |
| | | |
| @property | | @property |
| def volume(self): | | def volume(self): |
| return self.weight / self.density | | return self.weight / self.density |
| | | |
| | | |
| class Concrete(Material): | | class Concrete(Material): |
| density = 2500 | | density = 2500 |
| | | |
| | | |
| class Brick(Material): | | class Brick(Material): |
| density = 2000 | | density = 2000 |
| | | |
| | | |
| class Stone(Material): | | class Stone(Material): |
| density = 1600 | | density = 1600 |
| | | |
| | | |
| class Wood(Material): | | class Wood(Material): |
| density = 600 | | density = 600 |
| | | |
| | | |
| class Steel(Material): | | class Steel(Material): |
| density = 7700 | | density = 7700 |
| | | |
| | | |
| class Factory: | | class Factory: |
n | _material_classes = {} | n | |
| _used_instances = set() | | _used_instances = set() |
| _all_created_instances = [] | | _all_created_instances = [] |
| _dynamic_classes = {} | | _dynamic_classes = {} |
n | | n | volume = 0 |
| def __init__(self): | | def __init__(self): |
| self.created_instances = [] | | self.created_instances = [] |
n | | n | self.volume = 0 |
| | | |
| def __call__(self, *args, **kwargs): | | def __call__(self, *args, **kwargs): |
| if args and kwargs: | | if args and kwargs: |
| raise ValueError("Cannot mix positional and keyword arguments.") | | raise ValueError("Cannot mix positional and keyword arguments.") |
| if not args and not kwargs: | | if not args and not kwargs: |
| raise ValueError("Must provide arguments.") | | raise ValueError("Must provide arguments.") |
| if args: | | if args: |
| return self._positional_arguments(*args) | | return self._positional_arguments(*args) |
| return self._keyword_args(**kwargs) | | return self._keyword_args(**kwargs) |
| | | |
| def _positional_arguments(self, *args): | | def _positional_arguments(self, *args): |
n | | n | if len(args) == 1: |
| | | if args[0] not in Factory._used_instances: |
| | | self.volume += args[0].volume |
| | | Factory.volume += args[0].volume |
| | | Factory._used_instances.add(args[0]) |
| | | return args[0] |
| | | raise AssertionError("This instance has already been used.") |
| for obj in args: | | for obj in args: |
| if obj in Factory._used_instances: | | if obj in Factory._used_instances: |
| raise AssertionError("This instance has already been used.") | | raise AssertionError("This instance has already been used.") |
| Factory._used_instances.add(obj) | | Factory._used_instances.add(obj) |
n | | n | self.volume -= obj.volume |
| | | Factory.volume -= obj.volume |
| base_materials = self._get_base_materials(args) | | base_materials = self._get_base_materials(args) |
| new_class_name = "_".join(sorted(base_materials.keys())) | | new_class_name = "_".join(sorted(base_materials.keys())) |
| if new_class_name in Factory._dynamic_classes: | | if new_class_name in Factory._dynamic_classes: |
| dynamic_class = Factory._dynamic_classes[new_class_name] | | dynamic_class = Factory._dynamic_classes[new_class_name] |
| else: | | else: |
| new_density = sum(base_materials.values()) / len(base_materials) | | new_density = sum(base_materials.values()) / len(base_materials) |
| | | |
| class DynamicMaterial(Material): | | class DynamicMaterial(Material): |
| density = new_density | | density = new_density |
| _base_classes = base_materials | | _base_classes = base_materials |
| | | |
| DynamicMaterial.__name__ = new_class_name | | DynamicMaterial.__name__ = new_class_name |
| Factory._dynamic_classes[new_class_name] = DynamicMaterial | | Factory._dynamic_classes[new_class_name] = DynamicMaterial |
| globals()[new_class_name] = DynamicMaterial | | globals()[new_class_name] = DynamicMaterial |
| dynamic_class = DynamicMaterial | | dynamic_class = DynamicMaterial |
| dynamic_class_weight = sum(obj.weight for obj in args) | | dynamic_class_weight = sum(obj.weight for obj in args) |
| instance = dynamic_class(dynamic_class_weight) | | instance = dynamic_class(dynamic_class_weight) |
| Factory._all_created_instances.append(instance) | | Factory._all_created_instances.append(instance) |
| self.created_instances.append(instance) | | self.created_instances.append(instance) |
n | | n | self.volume += instance.volume |
| | | Factory.volume += instance.volume |
| return instance | | return instance |
| | | |
| def _get_base_materials(self, materials): | | def _get_base_materials(self, materials): |
| base_classes = {} | | base_classes = {} |
| for obj in materials: | | for obj in materials: |
| if isinstance(obj, Material): | | if isinstance(obj, Material): |
| if hasattr(obj, "_base_classes"): | | if hasattr(obj, "_base_classes"): |
| base_classes.update(obj._base_classes) | | base_classes.update(obj._base_classes) |
| else: | | else: |
| base_classes[obj.__class__.__name__] = obj.density | | base_classes[obj.__class__.__name__] = obj.density |
| else: | | else: |
| raise ValueError(f"Unrecognized material: {obj}") | | raise ValueError(f"Unrecognized material: {obj}") |
| return base_classes | | return base_classes |
| | | |
| def _keyword_args(self, **kwargs): | | def _keyword_args(self, **kwargs): |
| result = [] | | result = [] |
| for name, weight in kwargs.items(): | | for name, weight in kwargs.items(): |
| if name not in globals() and name not in Factory._dynamic_classes: | | if name not in globals() and name not in Factory._dynamic_classes: |
| raise ValueError(f"Unknown material: {name}") | | raise ValueError(f"Unknown material: {name}") |
| material = globals().get(name) or Factory._dynamic_classes[name] | | material = globals().get(name) or Factory._dynamic_classes[name] |
n | Factory._used_instances.add(material) | n | |
| instance = material(weight) | | instance = material(weight) |
| result.append(instance) | | result.append(instance) |
| Factory._all_created_instances.append(instance) | | Factory._all_created_instances.append(instance) |
| self.created_instances.append(instance) | | self.created_instances.append(instance) |
n | | n | self.volume += instance.volume |
| | | Factory.volume += instance.volume |
| return tuple(result) | | return tuple(result) |
| | | |
n | @staticmethod | n | |
| def all_is_base_classes(classes): | | |
| for obj in classes: | | |
| if obj.__class__ not in [Concrete, Brick, Wood, Stone, Steel]: | | |
| return False | | |
| return True | | |
| | | |
| def can_build(self, volume): | | def can_build(self, volume): |
n | wall_density = 0 | n | |
| wall_weight = 0 | | |
| if self.all_is_base_classes(self.created_instances): | | |
| wall_volume = sum(obj.volume for obj in self.created_instances) | | |
| return wall_volume >= volume | | return self.volume >= volume |
| for obj in self.created_instances: | | |
| if obj in self._used_instances: | | |
| continue | | |
| obj_class_name = type(obj).__name__ | | |
| base_class_names = obj_class_name.split("_") | | |
| base_classes = [globals()[name] for name in base_class_names if name in globals()] | | |
| for cls in base_classes: | | |
| wall_density += cls.density | | |
| wall_density /= len(base_classes) | | |
| wall_weight += obj.weight | | |
| if wall_density == 0: | | |
| raise ZeroDivisionError | | |
| return wall_weight / wall_density >= volume | | |
| | | |
| @staticmethod | | @staticmethod |
| def can_build_together(volume): | | def can_build_together(volume): |
t | wall_density = 0 | t | |
| wall_weight = 0 | | |
| if Factory.all_is_base_classes(Factory._all_created_instances): | | |
| wall_volume = sum(obj.volume for obj in Factory._all_created_instances) | | |
| return wall_volume >= volume | | return Factory.volume >= volume |
| for obj in Factory._all_created_instances: | | |
| if obj in Factory._used_instances: | | |
| continue | | |
| obj_class_name = type(obj).__name__ | | |
| base_class_names = obj_class_name.split("_") | | |
| base_classes = [globals()[name] for name in base_class_names if name in globals()] | | |
| for cls in base_classes: | | |
| wall_density += cls.density | | |
| wall_density /= len(base_classes) | | |
| wall_weight += obj.weight | | |
| return wall_weight / wall_density >= volume | | |