1from itertools import chain, starmap
2from statistics import mean
3
4
5class AddByVolumeMixin:
6 """Allow adding isntances by their volume."""
7
8 __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other)
9
10
11class Material(AddByVolumeMixin):
12 """Represent a material."""
13
14 def __init__(self, mass):
15 self.mass = mass
16 self.obsolete = False
17
18 def use(self):
19 """Ensure not used so far and make obsolete."""
20 assert not self.obsolete and not setattr(self, 'obsolete', True)
21
22 @property
23 def volume(self):
24 """Get volume of an instance."""
25 return self.mass / self.density
26
27 @property
28 def primitive_cls_names(self):
29 """Get list of primitive class names used by this instance's class."""
30 return self.__class__.__name__.split('_')
31
32
33class Factory(AddByVolumeMixin):
34 """Factory that generates materials."""
35
36 _factories = [] # All Factory instances
37 _classes = {} # All known material types behind their name as str
38
39 def __init__(self):
40 self._factories.append(self)
41 self._products = []
42
43 @staticmethod
44 def _store_output(fun):
45 """Store objects getting out for future reference."""
46 def decorated(self, *args, **kwargs):
47 result = fun(self, *args, **kwargs)
48 self._products.extend(result if type(result) is tuple else [result])
49 return result
50 return decorated
51
52 @_store_output
53 def __call__(self, *args, **kwargs):
54 """Create new materials."""
55 if not bool(args) ^ bool(kwargs):
56 raise ValueError
57 if kwargs:
58 return tuple(self._from_classnames(**kwargs))
59 [obj.use() for obj in args] # Make materials obsolete
60
61 return self._from_products(*args)
62
63 @staticmethod
64 def _objs_prims(objects):
65 """Get a sorted list of all promitives' names from a list of objects."""
66 return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects)))
67
68 def _from_classnames(self, **kwargs):
69 """Create materials from classnames (instance called with kwargs)."""
70 if not set(kwargs.keys()).issubset(set(self._classes)):
71 raise ValueError
72 yield from (self._classes[name](mass) for name, mass in kwargs.items())
73
74 def _from_products(self, *args):
75 """Create materials from instances (instance called with args)."""
76 cls = self.generate_material_class('_'.join(self._objs_prims(args)))
77 return cls(sum(map(lambda obj: obj.mass, args)))
78
79 @classmethod
80 def generate_material_class(cls, name, density=None):
81 """Generate a new class if not existing, else return existing."""
82 if name not in cls._classes:
83 prims = map(cls._classes.__getitem__, name.split('_'))
84 density = density or mean(map(lambda obj: obj.density, prims))
85 cls._classes[name] = type(name, (Material,), {'density': density})
86 return cls._classes[name]
87
88 @property
89 def volume(self):
90 """Volume generated by this factory."""
91 return sum(filter(lambda prd: not prd.obsolete, self._products))
92
93 @classmethod
94 def can_build_together(cls, volume):
95 """Whether can build a wall from all factories' volume."""
96 return sum(cls._factories) >= volume
97
98 def can_build(self, volume):
99 """Whether can build a wall from this factory's volume."""
100 return self.volume >= volume
101
102
103_PRIMITIVES = {'Concrete': 2500, 'Brick': 2000,
104 'Stone': 1600, 'Wood': 600, 'Steel': 7700}
105Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class,
106 _PRIMITIVES.items())
.....F....
======================================================================
FAIL: test_positional_arguments_multiple_argument_from_initial_set (test.TestFactory.test_positional_arguments_multiple_argument_from_initial_set)
Test calling a factory using multiple positional arguments.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 116, in test_positional_arguments_multiple_argument_from_initial_set
concrete_wood = self.factory1(concrete, wood)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 47, in decorated
result = fun(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 59, in __call__
[obj.use() for obj in args] # Make materials obsolete
^^^^^^^^^
File "/tmp/solution.py", line 20, in use
assert not self.obsolete and not setattr(self, 'obsolete', True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
----------------------------------------------------------------------
Ran 10 tests in 0.012s
FAILED (failures=1)
| n | 1 | class Material: | n | 1 | from itertools import chain, starmap |
| 2 | from statistics import mean | ||||
| 3 | |||||
| 4 | |||||
| 5 | class AddByVolumeMixin: | ||||
| 6 | """Allow adding isntances by their volume.""" | ||||
| 7 | |||||
| 8 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | ||||
| 9 | |||||
| 10 | |||||
| 11 | class Material(AddByVolumeMixin): | ||||
| 12 | """Represent a material.""" | ||||
| 13 | |||||
| 2 | def __init__(self, mass): | 14 | def __init__(self, mass): | ||
| n | 3 | self._mass = mass | n | 15 | self.mass = mass |
| 4 | self._used = False | 16 | self.obsolete = False | ||
| 5 | 17 | ||||
| n | 6 | @property | n | ||
| 7 | def mass(self): | 18 | def use(self): | ||
| 8 | return self._mass | 19 | """Ensure not used so far and make obsolete.""" | ||
| 20 | assert not self.obsolete and not setattr(self, 'obsolete', True) | ||||
| 9 | 21 | ||||
| 10 | @property | 22 | @property | ||
| 11 | def volume(self): | 23 | def volume(self): | ||
| n | n | 24 | """Get volume of an instance.""" | ||
| 12 | return float(self._mass) / self.density | 25 | return self.mass / self.density | ||
| 13 | 26 | ||||
| n | 14 | class Concrete(Material): | n | 27 | @property |
| 15 | density = 2500 | 28 | def primitive_cls_names(self): | ||
| 29 | """Get list of primitive class names used by this instance's class.""" | ||||
| 30 | return self.__class__.__name__.split('_') | ||||
| 16 | 31 | ||||
| n | 17 | class Brick(Material): | n | ||
| 18 | density = 2000 | ||||
| 19 | 32 | ||||
| n | 20 | class Stone(Material): | n | 33 | class Factory(AddByVolumeMixin): |
| 21 | density = 1600 | 34 | """Factory that generates materials.""" | ||
| 22 | 35 | ||||
| n | 23 | class Wood(Material): | n | 36 | _factories = [] # All Factory instances |
| 24 | density = 600 | 37 | _classes = {} # All known material types behind their name as str | ||
| 25 | |||||
| 26 | class Steel(Material): | ||||
| 27 | density = 7700 | ||||
| 28 | |||||
| 29 | class Factory: | ||||
| 30 | _created_materials = [] | ||||
| 31 | _alloy_classes = {} | ||||
| 32 | 38 | ||||
| 33 | def __init__(self): | 39 | def __init__(self): | ||
| n | n | 40 | self._factories.append(self) | ||
| 34 | self._materials = [] | 41 | self._products = [] | ||
| 35 | 42 | ||||
| n | n | 43 | @staticmethod | ||
| 44 | def _store_output(fun): | ||||
| 45 | """Store objects getting out for future reference.""" | ||||
| 46 | def decorated(self, *args, **kwargs): | ||||
| 47 | result = fun(self, *args, **kwargs) | ||||
| 48 | self._products.extend(result if type(result) is tuple else [result]) | ||||
| 49 | return result | ||||
| 50 | return decorated | ||||
| 51 | |||||
| 52 | @_store_output | ||||
| 36 | def __call__(self, *args, **kwargs): | 53 | def __call__(self, *args, **kwargs): | ||
| n | 37 | if not args and not kwargs: | n | 54 | """Create new materials.""" |
| 38 | raise ValueError("Arguments required") | 55 | if not bool(args) ^ bool(kwargs): | ||
| 56 | raise ValueError | ||||
| 57 | if kwargs: | ||||
| 58 | return tuple(self._from_classnames(**kwargs)) | ||||
| 59 | [obj.use() for obj in args] # Make materials obsolete | ||||
| 39 | 60 | ||||
| n | 40 | if args and kwargs: | n | 61 | return self._from_products(*args) |
| 41 | raise ValueError("Cannot mix positional and keyword arguments") | ||||
| 42 | 62 | ||||
| n | 43 | if kwargs: | n | 63 | @staticmethod |
| 44 | return self._handle_kwargs(kwargs) | 64 | def _objs_prims(objects): | ||
| 65 | """Get a sorted list of all promitives' names from a list of objects.""" | ||||
| 66 | return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects))) | ||||
| 45 | 67 | ||||
| n | 46 | return self._handle_args(args) | n | 68 | def _from_classnames(self, **kwargs): |
| 69 | """Create materials from classnames (instance called with kwargs).""" | ||||
| 70 | if not set(kwargs.keys()).issubset(set(self._classes)): | ||||
| 71 | raise ValueError | ||||
| 72 | yield from (self._classes[name](mass) for name, mass in kwargs.items()) | ||||
| 47 | 73 | ||||
| n | 48 | def _handle_kwargs(self, kwargs): | n | 74 | def _from_products(self, *args): |
| 49 | result = [] | 75 | """Create materials from instances (instance called with args).""" | ||
| 50 | for name, mass in kwargs.items(): | 76 | cls = self.generate_material_class('_'.join(self._objs_prims(args))) | ||
| 51 | cls = self._get_material_class(name) | 77 | return cls(sum(map(lambda obj: obj.mass, args))) | ||
| 52 | material = cls(mass) | ||||
| 53 | self._materials.append(material) | ||||
| 54 | result.append(material) | ||||
| 55 | return tuple(result) | ||||
| 56 | 78 | ||||
| n | 57 | def _handle_args(self, args): | n | 79 | @classmethod |
| 58 | for arg in args: | 80 | def generate_material_class(cls, name, density=None): | ||
| 59 | if getattr(arg, '_used', False): | 81 | """Generate a new class if not existing, else return existing.""" | ||
| 60 | raise AssertionError("Material already used") | 82 | if name not in cls._classes: | ||
| 61 | arg._used = True | 83 | prims = map(cls._classes.__getitem__, name.split('_')) | ||
| 84 | density = density or mean(map(lambda obj: obj.density, prims)) | ||||
| 85 | cls._classes[name] = type(name, (Material,), {'density': density}) | ||||
| 86 | return cls._classes[name] | ||||
| 62 | 87 | ||||
| n | 63 | base_classes = self._get_base_classes(args) | n | 88 | @property |
| 64 | class_name = '_'.join(sorted(cls.__name__ for cls in base_classes)) | 89 | def volume(self): | ||
| 65 | 90 | """Volume generated by this factory.""" | |||
| 66 | if class_name not in Factory._alloy_classes: | 91 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | ||
| 67 | density = sum(cls.density for cls in base_classes) / len(base_classes) | ||||
| 68 | Factory._alloy_classes[class_name] = type(class_name, (Material,), {'density': density}) | ||||
| 69 | |||||
| 70 | total_mass = sum(arg.mass for arg in args) | ||||
| 71 | result = Factory._alloy_classes[class_name](total_mass) | ||||
| 72 | self._materials.append(result) | ||||
| 73 | return result | ||||
| 74 | |||||
| 75 | def _get_material_class(self, name): | ||||
| 76 | for cls in [Concrete, Brick, Stone, Wood, Steel] + list(Factory._alloy_classes.values()): | ||||
| 77 | if cls.__name__ == name: | ||||
| 78 | return cls | ||||
| 79 | raise ValueError(f"Invalid material name: {name}") | ||||
| 80 | |||||
| 81 | def _get_base_classes(self, materials): | ||||
| 82 | result = set() | ||||
| 83 | for material in materials: | ||||
| 84 | material_type = type(material) | ||||
| 85 | if material_type in [Concrete, Brick, Stone, Wood, Steel]: | ||||
| 86 | result.add(material_type) | ||||
| 87 | else: | ||||
| 88 | class_name = material_type.__name__ | ||||
| 89 | base_names = class_name.split('_') | ||||
| 90 | for name in base_names: | ||||
| 91 | for cls in [Concrete, Brick, Stone, Wood, Steel]: | ||||
| 92 | if cls.__name__ == name: | ||||
| 93 | result.add(cls) | ||||
| 94 | return result | ||||
| 95 | |||||
| 96 | def can_build(self, volume): | ||||
| 97 | total_volume = sum(m.volume for m in self._materials if not getattr(m, '_used', False)) | ||||
| 98 | return total_volume >= volume | ||||
| 99 | 92 | ||||
| 100 | @classmethod | 93 | @classmethod | ||
| 101 | def can_build_together(cls, volume): | 94 | def can_build_together(cls, volume): | ||
| t | 102 | total_volume = sum(m.volume for m in cls._created_materials if not getattr(m, '_used', False)) | t | 95 | """Whether can build a wall from all factories' volume.""" |
| 96 | return sum(cls._factories) >= volume | ||||
| 97 | |||||
| 98 | def can_build(self, volume): | ||||
| 99 | """Whether can build a wall from this factory's volume.""" | ||||
| 103 | return total_volume >= volume | 100 | return self.volume >= volume | ||
| 101 | |||||
| 102 | |||||
| 103 | _PRIMITIVES = {'Concrete': 2500, 'Brick': 2000, | ||||
| 104 | 'Stone': 1600, 'Wood': 600, 'Steel': 7700} | ||||
| 105 | Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class, | ||||
| 106 | _PRIMITIVES.items()) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| n | 1 | from itertools import chain, starmap | n | 1 | class Material: |
| 2 | from statistics import mean | 2 | def __init__(self, mass): | ||
| 3 | self._mass = mass | ||||
| 4 | self._used = False | ||||
| 3 | 5 | ||||
| n | 4 | n | 6 | @property | |
| 5 | class AddByVolumeMixin: | ||||
| 6 | """Allow adding isntances by their volume.""" | ||||
| 7 | |||||
| 8 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | ||||
| 9 | |||||
| 10 | |||||
| 11 | class Material(AddByVolumeMixin): | ||||
| 12 | """Represent a material.""" | ||||
| 13 | |||||
| 14 | def __init__(self, mass): | ||||
| 15 | self.mass = mass | ||||
| 16 | self.obsolete = False | ||||
| 17 | |||||
| 18 | def use(self): | 7 | def mass(self): | ||
| 19 | """Ensure not used so far and make obsolete.""" | 8 | return self._mass | ||
| 20 | assert not self.obsolete and not setattr(self, 'obsolete', True) | ||||
| 21 | 9 | ||||
| 22 | @property | 10 | @property | ||
| 23 | def volume(self): | 11 | def volume(self): | ||
| n | 24 | """Get volume of an instance.""" | n | ||
| 25 | return self.mass / self.density | 12 | return float(self._mass) / self.density | ||
| 26 | 13 | ||||
| n | 27 | @property | n | 14 | class Concrete(Material): |
| 28 | def primitive_cls_names(self): | 15 | density = 2500 | ||
| 29 | """Get list of primitive class names used by this instance's class.""" | ||||
| 30 | return self.__class__.__name__.split('_') | ||||
| 31 | 16 | ||||
| n | n | 17 | class Brick(Material): | ||
| 18 | density = 2000 | ||||
| 32 | 19 | ||||
| n | 33 | class Factory(AddByVolumeMixin): | n | 20 | class Stone(Material): |
| 34 | """Factory that generates materials.""" | 21 | density = 1600 | ||
| 35 | 22 | ||||
| n | 36 | _factories = [] # All Factory instances | n | 23 | class Wood(Material): |
| 37 | _classes = {} # All known material types behind their name as str | 24 | density = 600 | ||
| 25 | |||||
| 26 | class Steel(Material): | ||||
| 27 | density = 7700 | ||||
| 28 | |||||
| 29 | class Factory: | ||||
| 30 | _created_materials = [] | ||||
| 31 | _alloy_classes = {} | ||||
| 38 | 32 | ||||
| 39 | def __init__(self): | 33 | def __init__(self): | ||
| n | 40 | self._factories.append(self) | n | ||
| 41 | self._products = [] | 34 | self._materials = [] | ||
| 42 | 35 | ||||
| n | 43 | @staticmethod | n | ||
| 44 | def _store_output(fun): | ||||
| 45 | """Store objects getting out for future reference.""" | ||||
| 46 | def decorated(self, *args, **kwargs): | 36 | def __call__(self, *args, **kwargs): | ||
| 47 | result = fun(self, *args, **kwargs) | 37 | if not args and not kwargs: | ||
| 48 | self._products.extend(result if type(result) is tuple else [result]) | 38 | raise ValueError("Arguments required") | ||
| 49 | return result | ||||
| 50 | return decorated | ||||
| 51 | 39 | ||||
| n | 52 | @_store_output | n | 40 | if args and kwargs: |
| 53 | def __call__(self, *args, **kwargs): | 41 | raise ValueError("Cannot mix positional and keyword arguments") | ||
| 54 | """Create new materials.""" | 42 | |||
| 55 | if not bool(args) ^ bool(kwargs): | ||||
| 56 | raise ValueError | ||||
| 57 | if kwargs: | 43 | if kwargs: | ||
| n | 58 | return tuple(self._from_classnames(**kwargs)) | n | 44 | return self._handle_kwargs(kwargs) |
| 59 | [obj.use() for obj in args] # Make materials obsolete | ||||
| 60 | return self._from_products(*args) | ||||
| 61 | 45 | ||||
| n | 62 | @staticmethod | n | 46 | return self._handle_args(args) |
| 63 | def _objs_prims(objects): | ||||
| 64 | """Get a sorted list of all promitives' names from a list of objects.""" | ||||
| 65 | return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects))) | ||||
| 66 | 47 | ||||
| n | 67 | def _from_classnames(self, **kwargs): | n | 48 | def _handle_kwargs(self, kwargs): |
| 68 | """Create materials from classnames (instance called with kwargs).""" | 49 | result = [] | ||
| 69 | if not set(kwargs.keys()).issubset(set(self._classes)): | 50 | for name, mass in kwargs.items(): | ||
| 70 | raise ValueError | 51 | cls = self._get_material_class(name) | ||
| 71 | yield from (self._classes[name](mass) for name, mass in kwargs.items()) | 52 | material = cls(mass) | ||
| 53 | self._materials.append(material) | ||||
| 54 | result.append(material) | ||||
| 55 | return tuple(result) | ||||
| 72 | 56 | ||||
| n | 73 | def _from_products(self, *args): | n | 57 | def _handle_args(self, args): |
| 74 | """Create materials from instances (instance called with args).""" | 58 | for arg in args: | ||
| 75 | cls = self.generate_material_class('_'.join(self._objs_prims(args))) | 59 | if getattr(arg, '_used', False): | ||
| 76 | return cls(sum(map(lambda obj: obj.mass, args))) | 60 | raise AssertionError("Material already used") | ||
| 61 | arg._used = True | ||||
| 77 | 62 | ||||
| n | 78 | @classmethod | n | 63 | base_classes = self._get_base_classes(args) |
| 79 | def generate_material_class(cls, name, density=None): | 64 | class_name = '_'.join(sorted(cls.__name__ for cls in base_classes)) | ||
| 80 | """Generate a new class if not existing, else return existing.""" | ||||
| 81 | if name not in cls._classes: | ||||
| 82 | prims = map(cls._classes.__getitem__, name.split('_')) | ||||
| 83 | density = density or mean(map(lambda obj: obj.density, prims)) | ||||
| 84 | cls._classes[name] = type(name, (Material,), {'density': density}) | ||||
| 85 | return cls._classes[name] | ||||
| 86 | 65 | ||||
| n | 87 | @property | n | 66 | if class_name not in Factory._alloy_classes: |
| 88 | def volume(self): | 67 | density = sum(cls.density for cls in base_classes) / len(base_classes) | ||
| 89 | """Volume generated by this factory.""" | 68 | Factory._alloy_classes[class_name] = type(class_name, (Material,), {'density': density}) | ||
| 90 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | 69 | |||
| 70 | total_mass = sum(arg.mass for arg in args) | ||||
| 71 | result = Factory._alloy_classes[class_name](total_mass) | ||||
| 72 | self._materials.append(result) | ||||
| 73 | return result | ||||
| 74 | |||||
| 75 | def _get_material_class(self, name): | ||||
| 76 | for cls in [Concrete, Brick, Stone, Wood, Steel] + list(Factory._alloy_classes.values()): | ||||
| 77 | if cls.__name__ == name: | ||||
| 78 | return cls | ||||
| 79 | raise ValueError(f"Invalid material name: {name}") | ||||
| 80 | |||||
| 81 | def _get_base_classes(self, materials): | ||||
| 82 | result = set() | ||||
| 83 | for material in materials: | ||||
| 84 | material_type = type(material) | ||||
| 85 | if material_type in [Concrete, Brick, Stone, Wood, Steel]: | ||||
| 86 | result.add(material_type) | ||||
| 87 | else: | ||||
| 88 | class_name = material_type.__name__ | ||||
| 89 | base_names = class_name.split('_') | ||||
| 90 | for name in base_names: | ||||
| 91 | for cls in [Concrete, Brick, Stone, Wood, Steel]: | ||||
| 92 | if cls.__name__ == name: | ||||
| 93 | result.add(cls) | ||||
| 94 | return result | ||||
| 95 | |||||
| 96 | def can_build(self, volume): | ||||
| 97 | total_volume = sum(m.volume for m in self._materials if not getattr(m, '_used', False)) | ||||
| 98 | return total_volume >= volume | ||||
| 91 | 99 | ||||
| 92 | @classmethod | 100 | @classmethod | ||
| 93 | def can_build_together(cls, volume): | 101 | def can_build_together(cls, volume): | ||
| t | 94 | """Whether can build a wall from all factories' volume.""" | t | 102 | total_volume = sum(m.volume for m in cls._created_materials if not getattr(m, '_used', False)) |
| 95 | return sum(cls._factories) >= volume | ||||
| 96 | |||||
| 97 | def can_build(self, volume): | ||||
| 98 | """Whether can build a wall from this factory's volume.""" | ||||
| 99 | return self.volume >= volume | 103 | return total_volume >= volume | ||
| 100 | |||||
| 101 | |||||
| 102 | _PRIMITIVES = {'Concrete': 2500, 'Brick': 2000, | ||||
| 103 | 'Stone': 1600, 'Wood': 600, 'Steel': 7700} | ||||
| 104 | Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class, | ||||
| 105 | _PRIMITIVES.items()) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | from itertools import chain, starmap | f | 1 | from itertools import chain, starmap |
| 2 | from statistics import mean | 2 | from statistics import mean | ||
| 3 | 3 | ||||
| 4 | 4 | ||||
| 5 | class AddByVolumeMixin: | 5 | class AddByVolumeMixin: | ||
| 6 | """Allow adding isntances by their volume.""" | 6 | """Allow adding isntances by their volume.""" | ||
| 7 | 7 | ||||
| 8 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | 8 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | ||
| 9 | 9 | ||||
| 10 | 10 | ||||
| 11 | class Material(AddByVolumeMixin): | 11 | class Material(AddByVolumeMixin): | ||
| 12 | """Represent a material.""" | 12 | """Represent a material.""" | ||
| 13 | 13 | ||||
| 14 | def __init__(self, mass): | 14 | def __init__(self, mass): | ||
| 15 | self.mass = mass | 15 | self.mass = mass | ||
| 16 | self.obsolete = False | 16 | self.obsolete = False | ||
| 17 | 17 | ||||
| 18 | def use(self): | 18 | def use(self): | ||
| 19 | """Ensure not used so far and make obsolete.""" | 19 | """Ensure not used so far and make obsolete.""" | ||
| 20 | assert not self.obsolete and not setattr(self, 'obsolete', True) | 20 | assert not self.obsolete and not setattr(self, 'obsolete', True) | ||
| 21 | 21 | ||||
| 22 | @property | 22 | @property | ||
| 23 | def volume(self): | 23 | def volume(self): | ||
| 24 | """Get volume of an instance.""" | 24 | """Get volume of an instance.""" | ||
| 25 | return self.mass / self.density | 25 | return self.mass / self.density | ||
| 26 | 26 | ||||
| 27 | @property | 27 | @property | ||
| 28 | def primitive_cls_names(self): | 28 | def primitive_cls_names(self): | ||
| 29 | """Get list of primitive class names used by this instance's class.""" | 29 | """Get list of primitive class names used by this instance's class.""" | ||
| 30 | return self.__class__.__name__.split('_') | 30 | return self.__class__.__name__.split('_') | ||
| 31 | 31 | ||||
| 32 | 32 | ||||
| 33 | class Factory(AddByVolumeMixin): | 33 | class Factory(AddByVolumeMixin): | ||
| 34 | """Factory that generates materials.""" | 34 | """Factory that generates materials.""" | ||
| 35 | 35 | ||||
| 36 | _factories = [] # All Factory instances | 36 | _factories = [] # All Factory instances | ||
| 37 | _classes = {} # All known material types behind their name as str | 37 | _classes = {} # All known material types behind their name as str | ||
| 38 | 38 | ||||
| 39 | def __init__(self): | 39 | def __init__(self): | ||
| 40 | self._factories.append(self) | 40 | self._factories.append(self) | ||
| 41 | self._products = [] | 41 | self._products = [] | ||
| 42 | 42 | ||||
| 43 | @staticmethod | 43 | @staticmethod | ||
| 44 | def _store_output(fun): | 44 | def _store_output(fun): | ||
| 45 | """Store objects getting out for future reference.""" | 45 | """Store objects getting out for future reference.""" | ||
| 46 | def decorated(self, *args, **kwargs): | 46 | def decorated(self, *args, **kwargs): | ||
| 47 | result = fun(self, *args, **kwargs) | 47 | result = fun(self, *args, **kwargs) | ||
| 48 | self._products.extend(result if type(result) is tuple else [result]) | 48 | self._products.extend(result if type(result) is tuple else [result]) | ||
| 49 | return result | 49 | return result | ||
| 50 | return decorated | 50 | return decorated | ||
| 51 | 51 | ||||
| 52 | @_store_output | 52 | @_store_output | ||
| 53 | def __call__(self, *args, **kwargs): | 53 | def __call__(self, *args, **kwargs): | ||
| 54 | """Create new materials.""" | 54 | """Create new materials.""" | ||
| 55 | if not bool(args) ^ bool(kwargs): | 55 | if not bool(args) ^ bool(kwargs): | ||
| 56 | raise ValueError | 56 | raise ValueError | ||
| 57 | if kwargs: | 57 | if kwargs: | ||
| 58 | return tuple(self._from_classnames(**kwargs)) | 58 | return tuple(self._from_classnames(**kwargs)) | ||
| 59 | [obj.use() for obj in args] # Make materials obsolete | 59 | [obj.use() for obj in args] # Make materials obsolete | ||
| 60 | return self._from_products(*args) | 60 | return self._from_products(*args) | ||
| 61 | 61 | ||||
| 62 | @staticmethod | 62 | @staticmethod | ||
| 63 | def _objs_prims(objects): | 63 | def _objs_prims(objects): | ||
| 64 | """Get a sorted list of all promitives' names from a list of objects.""" | 64 | """Get a sorted list of all promitives' names from a list of objects.""" | ||
| 65 | return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects))) | 65 | return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects))) | ||
| 66 | 66 | ||||
| 67 | def _from_classnames(self, **kwargs): | 67 | def _from_classnames(self, **kwargs): | ||
| 68 | """Create materials from classnames (instance called with kwargs).""" | 68 | """Create materials from classnames (instance called with kwargs).""" | ||
| 69 | if not set(kwargs.keys()).issubset(set(self._classes)): | 69 | if not set(kwargs.keys()).issubset(set(self._classes)): | ||
| 70 | raise ValueError | 70 | raise ValueError | ||
| 71 | yield from (self._classes[name](mass) for name, mass in kwargs.items()) | 71 | yield from (self._classes[name](mass) for name, mass in kwargs.items()) | ||
| 72 | 72 | ||||
| 73 | def _from_products(self, *args): | 73 | def _from_products(self, *args): | ||
| 74 | """Create materials from instances (instance called with args).""" | 74 | """Create materials from instances (instance called with args).""" | ||
| 75 | cls = self.generate_material_class('_'.join(self._objs_prims(args))) | 75 | cls = self.generate_material_class('_'.join(self._objs_prims(args))) | ||
| 76 | return cls(sum(map(lambda obj: obj.mass, args))) | 76 | return cls(sum(map(lambda obj: obj.mass, args))) | ||
| 77 | 77 | ||||
| 78 | @classmethod | 78 | @classmethod | ||
| 79 | def generate_material_class(cls, name, density=None): | 79 | def generate_material_class(cls, name, density=None): | ||
| 80 | """Generate a new class if not existing, else return existing.""" | 80 | """Generate a new class if not existing, else return existing.""" | ||
| 81 | if name not in cls._classes: | 81 | if name not in cls._classes: | ||
| 82 | prims = map(cls._classes.__getitem__, name.split('_')) | 82 | prims = map(cls._classes.__getitem__, name.split('_')) | ||
| 83 | density = density or mean(map(lambda obj: obj.density, prims)) | 83 | density = density or mean(map(lambda obj: obj.density, prims)) | ||
| 84 | cls._classes[name] = type(name, (Material,), {'density': density}) | 84 | cls._classes[name] = type(name, (Material,), {'density': density}) | ||
| 85 | return cls._classes[name] | 85 | return cls._classes[name] | ||
| 86 | 86 | ||||
| 87 | @property | 87 | @property | ||
| 88 | def volume(self): | 88 | def volume(self): | ||
| 89 | """Volume generated by this factory.""" | 89 | """Volume generated by this factory.""" | ||
| 90 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | 90 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | ||
| 91 | 91 | ||||
| 92 | @classmethod | 92 | @classmethod | ||
| 93 | def can_build_together(cls, volume): | 93 | def can_build_together(cls, volume): | ||
| 94 | """Whether can build a wall from all factories' volume.""" | 94 | """Whether can build a wall from all factories' volume.""" | ||
| 95 | return sum(cls._factories) >= volume | 95 | return sum(cls._factories) >= volume | ||
| 96 | 96 | ||||
| 97 | def can_build(self, volume): | 97 | def can_build(self, volume): | ||
| 98 | """Whether can build a wall from this factory's volume.""" | 98 | """Whether can build a wall from this factory's volume.""" | ||
| 99 | return self.volume >= volume | 99 | return self.volume >= volume | ||
| 100 | 100 | ||||
| t | t | 101 | |||
| 101 | _PRIMITIVES = {'Concrete': 2500, 'Brick': 2000, | 102 | _PRIMITIVES = {'Concrete': 2500, 'Brick': 2000, | ||
| 102 | 'Stone': 1600, 'Wood': 600, 'Steel': 7700} | 103 | 'Stone': 1600, 'Wood': 600, 'Steel': 7700} | ||
| 103 | Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class, | 104 | Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class, | ||
| 104 | _PRIMITIVES.items()) | 105 | _PRIMITIVES.items()) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | from itertools import chain, starmap | f | 1 | from itertools import chain, starmap |
| 2 | from statistics import mean | 2 | from statistics import mean | ||
| 3 | 3 | ||||
| 4 | 4 | ||||
| 5 | class AddByVolumeMixin: | 5 | class AddByVolumeMixin: | ||
| 6 | """Allow adding isntances by their volume.""" | 6 | """Allow adding isntances by their volume.""" | ||
| 7 | 7 | ||||
| 8 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | 8 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | ||
| 9 | 9 | ||||
| 10 | 10 | ||||
| 11 | class Material(AddByVolumeMixin): | 11 | class Material(AddByVolumeMixin): | ||
| 12 | """Represent a material.""" | 12 | """Represent a material.""" | ||
| 13 | 13 | ||||
| 14 | def __init__(self, mass): | 14 | def __init__(self, mass): | ||
| 15 | self.mass = mass | 15 | self.mass = mass | ||
| 16 | self.obsolete = False | 16 | self.obsolete = False | ||
| t | 17 | t | |||
| 18 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | ||||
| 19 | 17 | ||||
| 20 | def use(self): | 18 | def use(self): | ||
| 21 | """Ensure not used so far and make obsolete.""" | 19 | """Ensure not used so far and make obsolete.""" | ||
| 22 | assert not self.obsolete and not setattr(self, 'obsolete', True) | 20 | assert not self.obsolete and not setattr(self, 'obsolete', True) | ||
| 23 | 21 | ||||
| 24 | @property | 22 | @property | ||
| 25 | def volume(self): | 23 | def volume(self): | ||
| 26 | """Get volume of an instance.""" | 24 | """Get volume of an instance.""" | ||
| 27 | return self.mass / self.density | 25 | return self.mass / self.density | ||
| 28 | 26 | ||||
| 29 | @property | 27 | @property | ||
| 30 | def primitive_cls_names(self): | 28 | def primitive_cls_names(self): | ||
| 31 | """Get list of primitive class names used by this instance's class.""" | 29 | """Get list of primitive class names used by this instance's class.""" | ||
| 32 | return self.__class__.__name__.split('_') | 30 | return self.__class__.__name__.split('_') | ||
| 33 | 31 | ||||
| 34 | 32 | ||||
| 35 | class Factory(AddByVolumeMixin): | 33 | class Factory(AddByVolumeMixin): | ||
| 36 | """Factory that generates materials.""" | 34 | """Factory that generates materials.""" | ||
| 37 | 35 | ||||
| 38 | _factories = [] # All Factory instances | 36 | _factories = [] # All Factory instances | ||
| 39 | _classes = {} # All known material types behind their name as str | 37 | _classes = {} # All known material types behind their name as str | ||
| 40 | 38 | ||||
| 41 | def __init__(self): | 39 | def __init__(self): | ||
| 42 | self._factories.append(self) | 40 | self._factories.append(self) | ||
| 43 | self._products = [] | 41 | self._products = [] | ||
| 44 | 42 | ||||
| 45 | @staticmethod | 43 | @staticmethod | ||
| 46 | def _store_output(fun): | 44 | def _store_output(fun): | ||
| 47 | """Store objects getting out for future reference.""" | 45 | """Store objects getting out for future reference.""" | ||
| 48 | def decorated(self, *args, **kwargs): | 46 | def decorated(self, *args, **kwargs): | ||
| 49 | result = fun(self, *args, **kwargs) | 47 | result = fun(self, *args, **kwargs) | ||
| 50 | self._products.extend(result if type(result) is tuple else [result]) | 48 | self._products.extend(result if type(result) is tuple else [result]) | ||
| 51 | return result | 49 | return result | ||
| 52 | return decorated | 50 | return decorated | ||
| 53 | 51 | ||||
| 54 | @_store_output | 52 | @_store_output | ||
| 55 | def __call__(self, *args, **kwargs): | 53 | def __call__(self, *args, **kwargs): | ||
| 56 | """Create new materials.""" | 54 | """Create new materials.""" | ||
| 57 | if not bool(args) ^ bool(kwargs): | 55 | if not bool(args) ^ bool(kwargs): | ||
| 58 | raise ValueError | 56 | raise ValueError | ||
| 59 | if kwargs: | 57 | if kwargs: | ||
| 60 | return tuple(self._from_classnames(**kwargs)) | 58 | return tuple(self._from_classnames(**kwargs)) | ||
| 61 | [obj.use() for obj in args] # Make materials obsolete | 59 | [obj.use() for obj in args] # Make materials obsolete | ||
| 62 | return self._from_products(*args) | 60 | return self._from_products(*args) | ||
| 63 | 61 | ||||
| 64 | @staticmethod | 62 | @staticmethod | ||
| 65 | def _objs_prims(objects): | 63 | def _objs_prims(objects): | ||
| 66 | """Get a sorted list of all promitives' names from a list of objects.""" | 64 | """Get a sorted list of all promitives' names from a list of objects.""" | ||
| 67 | return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects))) | 65 | return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects))) | ||
| 68 | 66 | ||||
| 69 | def _from_classnames(self, **kwargs): | 67 | def _from_classnames(self, **kwargs): | ||
| 70 | """Create materials from classnames (instance called with kwargs).""" | 68 | """Create materials from classnames (instance called with kwargs).""" | ||
| 71 | if not set(kwargs.keys()).issubset(set(self._classes)): | 69 | if not set(kwargs.keys()).issubset(set(self._classes)): | ||
| 72 | raise ValueError | 70 | raise ValueError | ||
| 73 | yield from (self._classes[name](mass) for name, mass in kwargs.items()) | 71 | yield from (self._classes[name](mass) for name, mass in kwargs.items()) | ||
| 74 | 72 | ||||
| 75 | def _from_products(self, *args): | 73 | def _from_products(self, *args): | ||
| 76 | """Create materials from instances (instance called with args).""" | 74 | """Create materials from instances (instance called with args).""" | ||
| 77 | cls = self.generate_material_class('_'.join(self._objs_prims(args))) | 75 | cls = self.generate_material_class('_'.join(self._objs_prims(args))) | ||
| 78 | return cls(sum(map(lambda obj: obj.mass, args))) | 76 | return cls(sum(map(lambda obj: obj.mass, args))) | ||
| 79 | 77 | ||||
| 80 | @classmethod | 78 | @classmethod | ||
| 81 | def generate_material_class(cls, name, density=None): | 79 | def generate_material_class(cls, name, density=None): | ||
| 82 | """Generate a new class if not existing, else return existing.""" | 80 | """Generate a new class if not existing, else return existing.""" | ||
| 83 | if name not in cls._classes: | 81 | if name not in cls._classes: | ||
| 84 | prims = map(cls._classes.__getitem__, name.split('_')) | 82 | prims = map(cls._classes.__getitem__, name.split('_')) | ||
| 85 | density = density or mean(map(lambda obj: obj.density, prims)) | 83 | density = density or mean(map(lambda obj: obj.density, prims)) | ||
| 86 | cls._classes[name] = type(name, (Material,), {'density': density}) | 84 | cls._classes[name] = type(name, (Material,), {'density': density}) | ||
| 87 | return cls._classes[name] | 85 | return cls._classes[name] | ||
| 88 | 86 | ||||
| 89 | @property | 87 | @property | ||
| 90 | def volume(self): | 88 | def volume(self): | ||
| 91 | """Volume generated by this factory.""" | 89 | """Volume generated by this factory.""" | ||
| 92 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | 90 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | ||
| 93 | 91 | ||||
| 94 | @classmethod | 92 | @classmethod | ||
| 95 | def can_build_together(cls, volume): | 93 | def can_build_together(cls, volume): | ||
| 96 | """Whether can build a wall from all factories' volume.""" | 94 | """Whether can build a wall from all factories' volume.""" | ||
| 97 | return sum(cls._factories) >= volume | 95 | return sum(cls._factories) >= volume | ||
| 98 | 96 | ||||
| 99 | def can_build(self, volume): | 97 | def can_build(self, volume): | ||
| 100 | """Whether can build a wall from this factory's volume.""" | 98 | """Whether can build a wall from this factory's volume.""" | ||
| 101 | return self.volume >= volume | 99 | return self.volume >= volume | ||
| 102 | 100 | ||||
| 103 | _PRIMITIVES = {'Concrete': 2500, 'Brick': 2000, | 101 | _PRIMITIVES = {'Concrete': 2500, 'Brick': 2000, | ||
| 104 | 'Stone': 1600, 'Wood': 600, 'Steel': 7700} | 102 | 'Stone': 1600, 'Wood': 600, 'Steel': 7700} | ||
| 105 | Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class, | 103 | Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class, | ||
| 106 | _PRIMITIVES.items()) | 104 | _PRIMITIVES.items()) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| n | 1 | from itertools import chain | n | 1 | from itertools import chain, starmap |
| 2 | from statistics import mean | 2 | from statistics import mean | ||
| 3 | 3 | ||||
| 4 | 4 | ||||
| 5 | class AddByVolumeMixin: | 5 | class AddByVolumeMixin: | ||
| n | n | 6 | """Allow adding isntances by their volume.""" | ||
| 6 | 7 | ||||
| n | 7 | def __add__(self, other): | n | 8 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) |
| 8 | if isinstance(other, type(self)): | ||||
| 9 | return self.volume + other.volume | ||||
| 10 | return self.volume + other | ||||
| 11 | __radd__ = __add__ | ||||
| 12 | 9 | ||||
| 13 | 10 | ||||
| 14 | class Material(AddByVolumeMixin): | 11 | class Material(AddByVolumeMixin): | ||
| n | 15 | n | 12 | """Represent a material.""" | |
| 16 | density = ... | ||||
| 17 | 13 | ||||
| 18 | def __init__(self, mass): | 14 | def __init__(self, mass): | ||
| 19 | self.mass = mass | 15 | self.mass = mass | ||
| 20 | self.obsolete = False | 16 | self.obsolete = False | ||
| 21 | 17 | ||||
| n | n | 18 | __radd__ = lambda self, other: self.volume + getattr(other, 'volume', other) | ||
| 19 | |||||
| 20 | def use(self): | ||||
| 21 | """Ensure not used so far and make obsolete.""" | ||||
| 22 | assert not self.obsolete and not setattr(self, 'obsolete', True) | ||||
| 23 | |||||
| 22 | @property | 24 | @property | ||
| 23 | def volume(self): | 25 | def volume(self): | ||
| n | n | 26 | """Get volume of an instance.""" | ||
| 24 | return self.mass / self.density | 27 | return self.mass / self.density | ||
| 25 | 28 | ||||
| 26 | @property | 29 | @property | ||
| 27 | def primitive_cls_names(self): | 30 | def primitive_cls_names(self): | ||
| n | n | 31 | """Get list of primitive class names used by this instance's class.""" | ||
| 28 | return self.__class__.__name__.split('_') | 32 | return self.__class__.__name__.split('_') | ||
| 29 | 33 | ||||
| 30 | 34 | ||||
| n | 31 | class Concrete(Material): | n | 35 | class Factory(AddByVolumeMixin): |
| 32 | density = 2500 | 36 | """Factory that generates materials.""" | ||
| 33 | 37 | ||||
| n | 34 | n | 38 | _factories = [] # All Factory instances | |
| 35 | class Brick(Material): | 39 | _classes = {} # All known material types behind their name as str | ||
| 36 | density = 2000 | ||||
| 37 | |||||
| 38 | |||||
| 39 | class Stone(Material): | ||||
| 40 | density = 1600 | ||||
| 41 | |||||
| 42 | |||||
| 43 | class Wood(Material): | ||||
| 44 | density = 600 | ||||
| 45 | |||||
| 46 | |||||
| 47 | class Steel(Material): | ||||
| 48 | density = 7700 | ||||
| 49 | |||||
| 50 | |||||
| 51 | class Factory(AddByVolumeMixin): | ||||
| 52 | |||||
| 53 | _factories = [] | ||||
| 54 | _classes = {cls.__name__: cls for cls in (Concrete, Brick, | ||||
| 55 | Stone, Wood, Steel)} | ||||
| 56 | 40 | ||||
| 57 | def __init__(self): | 41 | def __init__(self): | ||
| 58 | self._factories.append(self) | 42 | self._factories.append(self) | ||
| 59 | self._products = [] | 43 | self._products = [] | ||
| 60 | 44 | ||||
| n | n | 45 | @staticmethod | ||
| 46 | def _store_output(fun): | ||||
| 47 | """Store objects getting out for future reference.""" | ||||
| 48 | def decorated(self, *args, **kwargs): | ||||
| 49 | result = fun(self, *args, **kwargs) | ||||
| 50 | self._products.extend(result if type(result) is tuple else [result]) | ||||
| 51 | return result | ||||
| 52 | return decorated | ||||
| 53 | |||||
| 54 | @_store_output | ||||
| 61 | def __call__(self, *args, **kwargs): | 55 | def __call__(self, *args, **kwargs): | ||
| n | n | 56 | """Create new materials.""" | ||
| 62 | if not bool(args) ^ bool(kwargs): | 57 | if not bool(args) ^ bool(kwargs): | ||
| 63 | raise ValueError | 58 | raise ValueError | ||
| 64 | if kwargs: | 59 | if kwargs: | ||
| 65 | return tuple(self._from_classnames(**kwargs)) | 60 | return tuple(self._from_classnames(**kwargs)) | ||
| n | 66 | self._handle_obsolete_objects(args) | n | 61 | [obj.use() for obj in args] # Make materials obsolete |
| 67 | return self._from_products(*args) | 62 | return self._from_products(*args) | ||
| 68 | 63 | ||||
| 69 | @staticmethod | 64 | @staticmethod | ||
| n | 70 | def _primitives_from_objects(objects): | n | 65 | def _objs_prims(objects): |
| 66 | """Get a sorted list of all promitives' names from a list of objects.""" | ||||
| 71 | return sorted(chain(*map(lambda arg: arg.primitive_cls_names, objects))) | 67 | return sorted(chain(*map(lambda obj: obj.primitive_cls_names, objects))) | ||
| 72 | |||||
| 73 | @staticmethod | ||||
| 74 | def _handle_obsolete_objects(objects): | ||||
| 75 | for obj in objects: | ||||
| 76 | if obj.obsolete: | ||||
| 77 | raise AssertionError | ||||
| 78 | obj.obsolete = True | ||||
| 79 | 68 | ||||
| 80 | def _from_classnames(self, **kwargs): | 69 | def _from_classnames(self, **kwargs): | ||
| n | 81 | for cls_name, mass in kwargs.items(): | n | 70 | """Create materials from classnames (instance called with kwargs).""" |
| 82 | try: | 71 | if not set(kwargs.keys()).issubset(set(self._classes)): | ||
| 83 | material = self._classes[cls_name](mass) | 72 | raise ValueError | ||
| 84 | except KeyError: | 73 | yield from (self._classes[name](mass) for name, mass in kwargs.items()) | ||
| 85 | raise ValueError # Unknown class requested | ||||
| 86 | self._products.append(material) | ||||
| 87 | yield material | ||||
| 88 | |||||
| 89 | def _class_from_class_names(self, cls_name): | ||||
| 90 | if cls_name in self._classes: | ||||
| 91 | return self._classes[cls_name] | ||||
| 92 | primitives = list(map(self._classes.__getitem__, cls_name.split('_'))) | ||||
| 93 | avg_dencity = mean(map(lambda obj: obj.density, primitives)) | ||||
| 94 | cls = type(cls_name, (Material,), {'density': avg_dencity}) | ||||
| 95 | self._classes[cls_name] = cls | ||||
| 96 | return cls | ||||
| 97 | 74 | ||||
| 98 | def _from_products(self, *args): | 75 | def _from_products(self, *args): | ||
| n | 99 | primitives_cls_names = self._primitives_from_objects(args) | n | 76 | """Create materials from instances (instance called with args).""" |
| 100 | cls_name = '_'.join(primitives_cls_names) | 77 | cls = self.generate_material_class('_'.join(self._objs_prims(args))) | ||
| 101 | cls = self._class_from_class_names(cls_name) | ||||
| 102 | mass = sum(map(lambda obj: obj.mass, args)) | 78 | return cls(sum(map(lambda obj: obj.mass, args))) | ||
| 103 | self._products.append(material:=cls(mass)) | 79 | |||
| 104 | return material | 80 | @classmethod | ||
| 81 | def generate_material_class(cls, name, density=None): | ||||
| 82 | """Generate a new class if not existing, else return existing.""" | ||||
| 83 | if name not in cls._classes: | ||||
| 84 | prims = map(cls._classes.__getitem__, name.split('_')) | ||||
| 85 | density = density or mean(map(lambda obj: obj.density, prims)) | ||||
| 86 | cls._classes[name] = type(name, (Material,), {'density': density}) | ||||
| 87 | return cls._classes[name] | ||||
| 105 | 88 | ||||
| 106 | @property | 89 | @property | ||
| 107 | def volume(self): | 90 | def volume(self): | ||
| n | n | 91 | """Volume generated by this factory.""" | ||
| 108 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | 92 | return sum(filter(lambda prd: not prd.obsolete, self._products)) | ||
| 109 | 93 | ||||
| 110 | @classmethod | 94 | @classmethod | ||
| 111 | def can_build_together(cls, volume): | 95 | def can_build_together(cls, volume): | ||
| n | n | 96 | """Whether can build a wall from all factories' volume.""" | ||
| 112 | return sum(cls._factories) >= volume | 97 | return sum(cls._factories) >= volume | ||
| 113 | 98 | ||||
| 114 | def can_build(self, volume): | 99 | def can_build(self, volume): | ||
| n | n | 100 | """Whether can build a wall from this factory's volume.""" | ||
| 115 | return self.volume >= volume | 101 | return self.volume >= volume | ||
| t | t | 102 | |||
| 103 | _PRIMITIVES = {'Concrete': 2500, 'Brick': 2000, | ||||
| 104 | 'Stone': 1600, 'Wood': 600, 'Steel': 7700} | ||||
| 105 | Concrete, Brick, Stone, Wood, Steel = starmap(Factory.generate_material_class, | ||||
| 106 | _PRIMITIVES.items()) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
26.11.2024 17:55