Домашни > Another brick in the wall > Решения > Решението на Йоан Байчев

Резултати
10 точки от тестове
1 точки от учител

11 точки общо

10 успешни теста
0 неуспешни теста
Код

  1MATERIAL_BITS = {
  2    'Concrete': 1 << 0,  # 1 (binary 00001)
  3    'Brick':    1 << 1,  # 2 (binary 00010)
  4    'Stone':    1 << 2,  # 4 (binary 00100)
  5    'Wood':     1 << 3,  # 8 (binary 01000)
  6    'Steel':    1 << 4,  # 16 (binary 10000)
  7}
  8
  9BASE_MATERIAL_DENSITIES = {
 10    'Concrete': 2500,
 11    'Brick': 2000,
 12    'Stone': 1600,
 13    'Wood': 600,
 14    'Steel': 7700,
 15}
 16
 17
 18class Material:
 19    def __init__(self, mass: float):
 20        if mass <= 0:
 21            raise ValueError("Mass must be a positive number.")
 22        self.mass: float = mass
 23        self.used: bool = False
 24        self._volume: float = None  # Cached value for the volume
 25
 26    def __hash__(self):
 27        return id(self)  # Hashing the object by its unique id
 28
 29    @property
 30    def volume(self):
 31        if self._volume is None:
 32            self._volume = self.mass / self.density  
 33        return self._volume
 34
 35
 36class Concrete(Material):
 37    density = BASE_MATERIAL_DENSITIES['Concrete']
 38    material_bit = MATERIAL_BITS['Concrete']
 39
 40
 41class Brick(Material):
 42    density = BASE_MATERIAL_DENSITIES['Brick']
 43    material_bit = MATERIAL_BITS['Brick']
 44
 45
 46class Stone(Material):
 47    density = BASE_MATERIAL_DENSITIES['Stone']
 48    material_bit = MATERIAL_BITS['Stone']
 49
 50
 51class Wood(Material):
 52    density = BASE_MATERIAL_DENSITIES['Wood']
 53    material_bit = MATERIAL_BITS['Wood']
 54
 55
 56class Steel(Material):
 57    density = BASE_MATERIAL_DENSITIES['Steel']
 58    material_bit = MATERIAL_BITS['Steel']
 59
 60
 61class Factory:
 62    material_classes = {
 63        'Concrete': Concrete,
 64        'Brick': Brick,
 65        'Stone': Stone,
 66        'Wood': Wood,
 67        'Steel': Steel,
 68    }
 69    dynamic_classes = {}
 70    all_materials = set()
 71
 72    # Precompute bitmask to material names mapping for quick lookup
 73    _bitmask_to_materials = {
 74        bitmask: [name for name, bit in MATERIAL_BITS.items() if bitmask & bit]
 75        for bitmask in range(1, 1 << len(MATERIAL_BITS))
 76    }
 77
 78    def __init__(self):
 79        self.material_bitmask = 0  # Represent current materials as a bitmask
 80        self.materials = set()      # Set of Material instances managed by this factory
 81
 82    def __call__(self, *args, **kwargs):
 83        self._validate_call(args, kwargs)
 84
 85        if kwargs:
 86            return self._create_materials(kwargs)
 87        else:
 88            return self._use_materials(args)
 89
 90    def _validate_call(self, args, kwargs):
 91        if (args and kwargs) or (not args and not kwargs):
 92            raise ValueError("Invalid input.")
 93
 94    def _create_materials(self, kwargs):
 95        materials_created = []
 96        for material_name, mass in kwargs.items():
 97            material_class = self._get_material_class(material_name)
 98            material = self._instantiate_material(material_class, mass)
 99            self._add_material(material)
100            materials_created.append(material)
101            self.material_bitmask |= material_class.material_bit  # Use material_class.material_bit
102        return tuple(materials_created)
103
104    def _get_material_class(self, material_name):
105        material_class = self.material_classes.get(material_name)
106        if material_class is None:
107            raise ValueError(f"Unknown material: {material_name}")
108        return material_class
109
110    def _instantiate_material(self, material_class, mass):
111        return material_class(mass)
112
113    def _add_material(self, material):
114        self.materials.add(material)  # Add to factory's materials
115        Factory.all_materials.add(material)  # Add to global all_materials
116        # Update the bitmask with the material's bit
117        self.material_bitmask |= material.material_bit
118
119    def _use_materials(self, materials):
120        self._validate_materials(materials)
121        self._mark_materials_as_used(materials)
122
123        material_bitmask, total_mass = self._calculate_bitmask_and_mass(materials)
124        new_class = self._get_or_create_dynamic_class(material_bitmask)
125
126        new_material = self._create_new_material(new_class, total_mass)
127        self.material_bitmask |= new_class.material_bit  # Set the bit for the new composite material
128        return new_material
129
130    def _validate_materials(self, materials):
131        known_classes = tuple(self.material_classes.values()) + tuple(self.dynamic_classes.values())
132
133        for material in materials:
134            if not isinstance(material, known_classes):
135                raise ValueError(f"Invalid material instance: {material}")
136            if material.used:
137                raise AssertionError(f"Material {material} is already used.")
138
139    def _mark_materials_as_used(self, materials):
140        for material in materials:
141            material.used = True
142            self.materials.discard(material)  # Remove from factory's materials
143            self.material_bitmask &= ~material.material_bit  # Clear the bit for the used material
144            Factory.all_materials.discard(material)  # Remove from global all_materials
145
146    def _calculate_bitmask_and_mass(self, materials):
147        material_bitmask = 0
148        total_mass = 0
149        for material in materials:
150            material_bitmask |= material.material_bit  # Combine material bits using bitwise OR
151            total_mass += material.mass
152        return material_bitmask, total_mass
153
154    def _get_or_create_dynamic_class(self, material_bitmask):
155        # Try to find existing class by material_bitmask
156        for cls in self.dynamic_classes.values():
157            if cls.material_bit == material_bitmask:
158                return cls
159        # Generate class name
160        base_materials = self._determine_base_materials(material_bitmask)
161        class_name = self._generate_class_name(base_materials)
162        # Check if class already exists in material_classes
163        if class_name in self.material_classes:
164            return self.material_classes[class_name]
165        # Create new dynamic class
166        return self._create_dynamic_class(material_bitmask)
167
168    def _create_dynamic_class(self, material_bitmask):
169        base_materials = self._determine_base_materials(material_bitmask)
170        density = self._calculate_density(base_materials)
171        class_name = self._generate_class_name(base_materials)
172
173        attributes = {
174            'density': density,
175            'material_bit': material_bitmask,
176        }
177        new_class = type(class_name, (Material,), attributes)
178        # Store the new class using the class name as the key
179        self.material_classes[class_name] = new_class
180        self.dynamic_classes[material_bitmask] = new_class
181        return new_class
182
183    def _determine_base_materials(self, material_bitmask):
184        # Use precomputed bitmask to materials mapping for efficiency
185        return self._bitmask_to_materials.get(material_bitmask, [])
186
187    def _calculate_density(self, base_materials):
188        densities = [BASE_MATERIAL_DENSITIES[name] for name in base_materials]
189        return sum(densities) / len(densities)
190
191    def _generate_class_name(self, base_materials):
192        # Generate class name by sorting material names to ensure consistency
193        return '_'.join(sorted(base_materials))
194
195    def _create_new_material(self, new_class, total_mass):
196        new_material = new_class(total_mass)
197        self.materials.add(new_material)  # Add to factory's materials
198        Factory.all_materials.add(new_material)  # Add to global all_materials
199        return new_material
200
201    def can_build(self, volume_needed):
202        # Calculate total volume of current materials in this factory
203        total_volume = sum(material.volume for material in self.materials)
204        return total_volume >= volume_needed
205
206    @classmethod
207    def can_build_together(cls, volume_needed):
208        # Calculate total volume of all materials across all factories
209        total_volume = sum(material.volume for material in cls.all_materials)
210        return total_volume >= volume_needed

..........
----------------------------------------------------------------------
Ran 10 tests in 0.014s

OK

Дискусия
Виктор Бечев
23.11.2024 16:21

Разбито изглежда значително по-добре! Що се отнася до речника с битовата маска - побитовите операции са доста бързи и би следвало да можеш да се оправиш само с тях, без дедикиран речник, но не пречи.
Йоан Байчев
22.11.2024 16:47

Относно новата версия на кода: създавам речник, който свързва всяка възможно битова маска със списък от материали, които съставляват тази маска. Това позволява бързо идентифициране на материалите при работа с комбинации. Мотивацията ми за това е по-бързо търсене и лесна подръжка, ако се добавят нови материали, просто се разширява речникът MATERIAL_BITS и новите комбинации автоматично се включват в _bitmask_to_materials , без необходимост от допълнителни промени в логиката на кода. Също добавих инициализиране на self.material_bitmask = 0 в класа Factory, което задава дефоултно състояние на фабриката без налични материали.
Виктор Бечев
21.11.2024 14:09

Четейки си мисля друго - при положение, че използваш битовете, трябва ли ти наистина `base_materials`? На практика изразяват едно и също, просто побитовата репрезентация го прави с единици за участващ материал, а `base_materials` е просто human-readable вариант. Можеш да минеш без едно от двете и така и така си го направил яко с битовете - why not remove `base_materials`?
Виктор Бечев
21.11.2024 14:07

Битовата репрезентация е доста cool и умно решение!
Йоан Байчев
20.11.2024 23:56

Кеширам обема в Material като това би било особено полезно в сценарии, където свойството volume се достъпва многократно. Чрез битовата репрезентация симулирам уникално идентифициране като също избягвам конфликти на имена, когато комбинирам два материала използвам логическо или "|", което се обработва доста по-бързо. Чрез логическото и "&" можем бързо да определим кои материали са включени в дадена комбинация.
История

f1MATERIAL_BITS = {f1MATERIAL_BITS = {
2    'Concrete': 1 << 0,  # 1 (binary 00001)2    'Concrete': 1 << 0,  # 1 (binary 00001)
3    'Brick':    1 << 1,  # 2 (binary 00010)3    'Brick':    1 << 1,  # 2 (binary 00010)
4    'Stone':    1 << 2,  # 4 (binary 00100)4    'Stone':    1 << 2,  # 4 (binary 00100)
5    'Wood':     1 << 3,  # 8 (binary 01000)5    'Wood':     1 << 3,  # 8 (binary 01000)
6    'Steel':    1 << 4,  # 16 (binary 10000)6    'Steel':    1 << 4,  # 16 (binary 10000)
7}7}
88
9BASE_MATERIAL_DENSITIES = {9BASE_MATERIAL_DENSITIES = {
10    'Concrete': 2500,10    'Concrete': 2500,
11    'Brick': 2000,11    'Brick': 2000,
12    'Stone': 1600,12    'Stone': 1600,
13    'Wood': 600,13    'Wood': 600,
14    'Steel': 7700,14    'Steel': 7700,
15}15}
1616
1717
18class Material:18class Material:
19    def __init__(self, mass: float):19    def __init__(self, mass: float):
20        if mass <= 0:20        if mass <= 0:
21            raise ValueError("Mass must be a positive number.")21            raise ValueError("Mass must be a positive number.")
22        self.mass: float = mass22        self.mass: float = mass
23        self.used: bool = False23        self.used: bool = False
24        self._volume: float = None  # Cached value for the volume24        self._volume: float = None  # Cached value for the volume
2525
26    def __hash__(self):26    def __hash__(self):
27        return id(self)  # Hashing the object by its unique id27        return id(self)  # Hashing the object by its unique id
2828
29    @property29    @property
30    def volume(self):30    def volume(self):
31        if self._volume is None:31        if self._volume is None:
32            self._volume = self.mass / self.density  32            self._volume = self.mass / self.density  
33        return self._volume33        return self._volume
3434
3535
36class Concrete(Material):36class Concrete(Material):
37    density = BASE_MATERIAL_DENSITIES['Concrete']37    density = BASE_MATERIAL_DENSITIES['Concrete']
38    material_bit = MATERIAL_BITS['Concrete']38    material_bit = MATERIAL_BITS['Concrete']
3939
4040
41class Brick(Material):41class Brick(Material):
42    density = BASE_MATERIAL_DENSITIES['Brick']42    density = BASE_MATERIAL_DENSITIES['Brick']
43    material_bit = MATERIAL_BITS['Brick']43    material_bit = MATERIAL_BITS['Brick']
4444
4545
46class Stone(Material):46class Stone(Material):
47    density = BASE_MATERIAL_DENSITIES['Stone']47    density = BASE_MATERIAL_DENSITIES['Stone']
48    material_bit = MATERIAL_BITS['Stone']48    material_bit = MATERIAL_BITS['Stone']
4949
5050
51class Wood(Material):51class Wood(Material):
52    density = BASE_MATERIAL_DENSITIES['Wood']52    density = BASE_MATERIAL_DENSITIES['Wood']
53    material_bit = MATERIAL_BITS['Wood']53    material_bit = MATERIAL_BITS['Wood']
5454
5555
56class Steel(Material):56class Steel(Material):
57    density = BASE_MATERIAL_DENSITIES['Steel']57    density = BASE_MATERIAL_DENSITIES['Steel']
58    material_bit = MATERIAL_BITS['Steel']58    material_bit = MATERIAL_BITS['Steel']
5959
6060
61class Factory:61class Factory:
62    material_classes = {62    material_classes = {
63        'Concrete': Concrete,63        'Concrete': Concrete,
64        'Brick': Brick,64        'Brick': Brick,
65        'Stone': Stone,65        'Stone': Stone,
66        'Wood': Wood,66        'Wood': Wood,
67        'Steel': Steel,67        'Steel': Steel,
68    }68    }
69    dynamic_classes = {}69    dynamic_classes = {}
70    all_materials = set()70    all_materials = set()
7171
72    # Precompute bitmask to material names mapping for quick lookup72    # Precompute bitmask to material names mapping for quick lookup
73    _bitmask_to_materials = {73    _bitmask_to_materials = {
74        bitmask: [name for name, bit in MATERIAL_BITS.items() if bitmask & bit]74        bitmask: [name for name, bit in MATERIAL_BITS.items() if bitmask & bit]
75        for bitmask in range(1, 1 << len(MATERIAL_BITS))75        for bitmask in range(1, 1 << len(MATERIAL_BITS))
76    }76    }
7777
78    def __init__(self):78    def __init__(self):
79        self.material_bitmask = 0  # Represent current materials as a bitmask79        self.material_bitmask = 0  # Represent current materials as a bitmask
80        self.materials = set()      # Set of Material instances managed by this factory80        self.materials = set()      # Set of Material instances managed by this factory
8181
82    def __call__(self, *args, **kwargs):82    def __call__(self, *args, **kwargs):
83        self._validate_call(args, kwargs)83        self._validate_call(args, kwargs)
8484
85        if kwargs:85        if kwargs:
86            return self._create_materials(kwargs)86            return self._create_materials(kwargs)
87        else:87        else:
88            return self._use_materials(args)88            return self._use_materials(args)
8989
90    def _validate_call(self, args, kwargs):90    def _validate_call(self, args, kwargs):
91        if (args and kwargs) or (not args and not kwargs):91        if (args and kwargs) or (not args and not kwargs):
92            raise ValueError("Invalid input.")92            raise ValueError("Invalid input.")
9393
94    def _create_materials(self, kwargs):94    def _create_materials(self, kwargs):
95        materials_created = []95        materials_created = []
96        for material_name, mass in kwargs.items():96        for material_name, mass in kwargs.items():
97            material_class = self._get_material_class(material_name)97            material_class = self._get_material_class(material_name)
98            material = self._instantiate_material(material_class, mass)98            material = self._instantiate_material(material_class, mass)
99            self._add_material(material)99            self._add_material(material)
100            materials_created.append(material)100            materials_created.append(material)
n101            self.material_bitmask |= MATERIAL_BITS[material_name]  # Set the bit for the created materialn101            self.material_bitmask |= material_class.material_bit  # Use material_class.material_bit
102        return tuple(materials_created)102        return tuple(materials_created)
103103
104    def _get_material_class(self, material_name):104    def _get_material_class(self, material_name):
n105        material_class = self.material_classes.get(material_name) or self.dynamic_classes.get(material_name)n105        material_class = self.material_classes.get(material_name)
106        if material_class is None:106        if material_class is None:
107            raise ValueError(f"Unknown material: {material_name}")107            raise ValueError(f"Unknown material: {material_name}")
108        return material_class108        return material_class
109109
110    def _instantiate_material(self, material_class, mass):110    def _instantiate_material(self, material_class, mass):
111        return material_class(mass)111        return material_class(mass)
112112
113    def _add_material(self, material):113    def _add_material(self, material):
114        self.materials.add(material)  # Add to factory's materials114        self.materials.add(material)  # Add to factory's materials
115        Factory.all_materials.add(material)  # Add to global all_materials115        Factory.all_materials.add(material)  # Add to global all_materials
116        # Update the bitmask with the material's bit116        # Update the bitmask with the material's bit
n117        self.material_bitmask |= material.material_bit  # Already done in _create_materialsn117        self.material_bitmask |= material.material_bit
118118
119    def _use_materials(self, materials):119    def _use_materials(self, materials):
120        self._validate_materials(materials)120        self._validate_materials(materials)
121        self._mark_materials_as_used(materials)121        self._mark_materials_as_used(materials)
122122
123        material_bitmask, total_mass = self._calculate_bitmask_and_mass(materials)123        material_bitmask, total_mass = self._calculate_bitmask_and_mass(materials)
124        new_class = self._get_or_create_dynamic_class(material_bitmask)124        new_class = self._get_or_create_dynamic_class(material_bitmask)
125125
126        new_material = self._create_new_material(new_class, total_mass)126        new_material = self._create_new_material(new_class, total_mass)
127        self.material_bitmask |= new_class.material_bit  # Set the bit for the new composite material127        self.material_bitmask |= new_class.material_bit  # Set the bit for the new composite material
128        return new_material128        return new_material
129129
130    def _validate_materials(self, materials):130    def _validate_materials(self, materials):
131        known_classes = tuple(self.material_classes.values()) + tuple(self.dynamic_classes.values())131        known_classes = tuple(self.material_classes.values()) + tuple(self.dynamic_classes.values())
132132
133        for material in materials:133        for material in materials:
134            if not isinstance(material, known_classes):134            if not isinstance(material, known_classes):
135                raise ValueError(f"Invalid material instance: {material}")135                raise ValueError(f"Invalid material instance: {material}")
136            if material.used:136            if material.used:
137                raise AssertionError(f"Material {material} is already used.")137                raise AssertionError(f"Material {material} is already used.")
138138
139    def _mark_materials_as_used(self, materials):139    def _mark_materials_as_used(self, materials):
140        for material in materials:140        for material in materials:
141            material.used = True141            material.used = True
142            self.materials.discard(material)  # Remove from factory's materials142            self.materials.discard(material)  # Remove from factory's materials
143            self.material_bitmask &= ~material.material_bit  # Clear the bit for the used material143            self.material_bitmask &= ~material.material_bit  # Clear the bit for the used material
144            Factory.all_materials.discard(material)  # Remove from global all_materials144            Factory.all_materials.discard(material)  # Remove from global all_materials
145145
146    def _calculate_bitmask_and_mass(self, materials):146    def _calculate_bitmask_and_mass(self, materials):
147        material_bitmask = 0147        material_bitmask = 0
148        total_mass = 0148        total_mass = 0
149        for material in materials:149        for material in materials:
150            material_bitmask |= material.material_bit  # Combine material bits using bitwise OR150            material_bitmask |= material.material_bit  # Combine material bits using bitwise OR
151            total_mass += material.mass151            total_mass += material.mass
152        return material_bitmask, total_mass152        return material_bitmask, total_mass
153153
154    def _get_or_create_dynamic_class(self, material_bitmask):154    def _get_or_create_dynamic_class(self, material_bitmask):
n155        if material_bitmask in self.dynamic_classes:n155        # Try to find existing class by material_bitmask
156            return self.dynamic_classes[material_bitmask]156        for cls in self.dynamic_classes.values():
157        else:157            if cls.material_bit == material_bitmask:
158                return cls
159        # Generate class name
160        base_materials = self._determine_base_materials(material_bitmask)
161        class_name = self._generate_class_name(base_materials)
162        # Check if class already exists in material_classes
163        if class_name in self.material_classes:
164            return self.material_classes[class_name]
165        # Create new dynamic class
158            return self._create_dynamic_class(material_bitmask)166        return self._create_dynamic_class(material_bitmask)
159167
160    def _create_dynamic_class(self, material_bitmask):168    def _create_dynamic_class(self, material_bitmask):
161        base_materials = self._determine_base_materials(material_bitmask)169        base_materials = self._determine_base_materials(material_bitmask)
162        density = self._calculate_density(base_materials)170        density = self._calculate_density(base_materials)
163        class_name = self._generate_class_name(base_materials)171        class_name = self._generate_class_name(base_materials)
164172
165        attributes = {173        attributes = {
166            'density': density,174            'density': density,
167            'material_bit': material_bitmask,175            'material_bit': material_bitmask,
168        }176        }
169        new_class = type(class_name, (Material,), attributes)177        new_class = type(class_name, (Material,), attributes)
tt178        # Store the new class using the class name as the key
179        self.material_classes[class_name] = new_class
170        self.dynamic_classes[material_bitmask] = new_class180        self.dynamic_classes[material_bitmask] = new_class
171        return new_class181        return new_class
172182
173    def _determine_base_materials(self, material_bitmask):183    def _determine_base_materials(self, material_bitmask):
174        # Use precomputed bitmask to materials mapping for efficiency184        # Use precomputed bitmask to materials mapping for efficiency
175        return self._bitmask_to_materials.get(material_bitmask, [])185        return self._bitmask_to_materials.get(material_bitmask, [])
176186
177    def _calculate_density(self, base_materials):187    def _calculate_density(self, base_materials):
178        densities = [BASE_MATERIAL_DENSITIES[name] for name in base_materials]188        densities = [BASE_MATERIAL_DENSITIES[name] for name in base_materials]
179        return sum(densities) / len(densities)189        return sum(densities) / len(densities)
180190
181    def _generate_class_name(self, base_materials):191    def _generate_class_name(self, base_materials):
182        # Generate class name by sorting material names to ensure consistency192        # Generate class name by sorting material names to ensure consistency
183        return '_'.join(sorted(base_materials))193        return '_'.join(sorted(base_materials))
184194
185    def _create_new_material(self, new_class, total_mass):195    def _create_new_material(self, new_class, total_mass):
186        new_material = new_class(total_mass)196        new_material = new_class(total_mass)
187        self.materials.add(new_material)  # Add to factory's materials197        self.materials.add(new_material)  # Add to factory's materials
188        Factory.all_materials.add(new_material)  # Add to global all_materials198        Factory.all_materials.add(new_material)  # Add to global all_materials
189        return new_material199        return new_material
190200
191    def can_build(self, volume_needed):201    def can_build(self, volume_needed):
192        # Calculate total volume of current materials in this factory202        # Calculate total volume of current materials in this factory
193        total_volume = sum(material.volume for material in self.materials)203        total_volume = sum(material.volume for material in self.materials)
194        return total_volume >= volume_needed204        return total_volume >= volume_needed
195205
196    @classmethod206    @classmethod
197    def can_build_together(cls, volume_needed):207    def can_build_together(cls, volume_needed):
198        # Calculate total volume of all materials across all factories208        # Calculate total volume of all materials across all factories
199        total_volume = sum(material.volume for material in cls.all_materials)209        total_volume = sum(material.volume for material in cls.all_materials)
200        return total_volume >= volume_needed210        return total_volume >= volume_needed
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1MATERIAL_BITS = {f1MATERIAL_BITS = {
2    'Concrete': 1 << 0,  # 1 (binary 00001)2    'Concrete': 1 << 0,  # 1 (binary 00001)
3    'Brick':    1 << 1,  # 2 (binary 00010)3    'Brick':    1 << 1,  # 2 (binary 00010)
4    'Stone':    1 << 2,  # 4 (binary 00100)4    'Stone':    1 << 2,  # 4 (binary 00100)
5    'Wood':     1 << 3,  # 8 (binary 01000)5    'Wood':     1 << 3,  # 8 (binary 01000)
6    'Steel':    1 << 4,  # 16 (binary 10000)6    'Steel':    1 << 4,  # 16 (binary 10000)
7}7}
88
9BASE_MATERIAL_DENSITIES = {9BASE_MATERIAL_DENSITIES = {
10    'Concrete': 2500,10    'Concrete': 2500,
11    'Brick': 2000,11    'Brick': 2000,
12    'Stone': 1600,12    'Stone': 1600,
13    'Wood': 600,13    'Wood': 600,
14    'Steel': 7700,14    'Steel': 7700,
15}15}
1616
1717
18class Material:18class Material:
19    def __init__(self, mass: float):19    def __init__(self, mass: float):
20        if mass <= 0:20        if mass <= 0:
21            raise ValueError("Mass must be a positive number.")21            raise ValueError("Mass must be a positive number.")
22        self.mass: float = mass22        self.mass: float = mass
23        self.used: bool = False23        self.used: bool = False
24        self._volume: float = None  # Cached value for the volume24        self._volume: float = None  # Cached value for the volume
2525
26    def __hash__(self):26    def __hash__(self):
27        return id(self)  # Hashing the object by its unique id27        return id(self)  # Hashing the object by its unique id
2828
29    @property29    @property
30    def volume(self):30    def volume(self):
31        if self._volume is None:31        if self._volume is None:
32            self._volume = self.mass / self.density  32            self._volume = self.mass / self.density  
33        return self._volume33        return self._volume
3434
3535
36class Concrete(Material):36class Concrete(Material):
37    density = BASE_MATERIAL_DENSITIES['Concrete']37    density = BASE_MATERIAL_DENSITIES['Concrete']
38    material_bit = MATERIAL_BITS['Concrete']38    material_bit = MATERIAL_BITS['Concrete']
3939
4040
41class Brick(Material):41class Brick(Material):
42    density = BASE_MATERIAL_DENSITIES['Brick']42    density = BASE_MATERIAL_DENSITIES['Brick']
43    material_bit = MATERIAL_BITS['Brick']43    material_bit = MATERIAL_BITS['Brick']
4444
4545
46class Stone(Material):46class Stone(Material):
47    density = BASE_MATERIAL_DENSITIES['Stone']47    density = BASE_MATERIAL_DENSITIES['Stone']
48    material_bit = MATERIAL_BITS['Stone']48    material_bit = MATERIAL_BITS['Stone']
4949
5050
51class Wood(Material):51class Wood(Material):
52    density = BASE_MATERIAL_DENSITIES['Wood']52    density = BASE_MATERIAL_DENSITIES['Wood']
53    material_bit = MATERIAL_BITS['Wood']53    material_bit = MATERIAL_BITS['Wood']
5454
5555
56class Steel(Material):56class Steel(Material):
57    density = BASE_MATERIAL_DENSITIES['Steel']57    density = BASE_MATERIAL_DENSITIES['Steel']
58    material_bit = MATERIAL_BITS['Steel']58    material_bit = MATERIAL_BITS['Steel']
5959
6060
61class Factory:61class Factory:
62    material_classes = {62    material_classes = {
63        'Concrete': Concrete,63        'Concrete': Concrete,
64        'Brick': Brick,64        'Brick': Brick,
65        'Stone': Stone,65        'Stone': Stone,
66        'Wood': Wood,66        'Wood': Wood,
67        'Steel': Steel,67        'Steel': Steel,
68    }68    }
n69    dynamic_classes = {}  # Stores dynamically created composite material classesn69    dynamic_classes = {}
70    all_materials = set()  # Set of all material instances created across all factories70    all_materials = set()
71 
72    # Precompute bitmask to material names mapping for quick lookup
73    _bitmask_to_materials = {
74        bitmask: [name for name, bit in MATERIAL_BITS.items() if bitmask & bit]
75        for bitmask in range(1, 1 << len(MATERIAL_BITS))
76    }
7177
72    def __init__(self):78    def __init__(self):
nn79        self.material_bitmask = 0  # Represent current materials as a bitmask
73        self.materials = set()  # Set of material instances created by this factory80        self.materials = set()      # Set of Material instances managed by this factory
7481
75    def __call__(self, *args, **kwargs):82    def __call__(self, *args, **kwargs):
76        self._validate_call(args, kwargs)83        self._validate_call(args, kwargs)
7784
78        if kwargs:85        if kwargs:
79            return self._create_materials(kwargs)86            return self._create_materials(kwargs)
80        else:87        else:
81            return self._use_materials(args)88            return self._use_materials(args)
8289
83    def _validate_call(self, args, kwargs):90    def _validate_call(self, args, kwargs):
84        if (args and kwargs) or (not args and not kwargs):91        if (args and kwargs) or (not args and not kwargs):
85            raise ValueError("Invalid input.")92            raise ValueError("Invalid input.")
8693
87    def _create_materials(self, kwargs):94    def _create_materials(self, kwargs):
88        materials_created = []95        materials_created = []
89        for material_name, mass in kwargs.items():96        for material_name, mass in kwargs.items():
90            material_class = self._get_material_class(material_name)97            material_class = self._get_material_class(material_name)
91            material = self._instantiate_material(material_class, mass)98            material = self._instantiate_material(material_class, mass)
92            self._add_material(material)99            self._add_material(material)
93            materials_created.append(material)100            materials_created.append(material)
nn101            self.material_bitmask |= MATERIAL_BITS[material_name]  # Set the bit for the created material
94        return tuple(materials_created)102        return tuple(materials_created)
95103
96    def _get_material_class(self, material_name):104    def _get_material_class(self, material_name):
97        material_class = self.material_classes.get(material_name) or self.dynamic_classes.get(material_name)105        material_class = self.material_classes.get(material_name) or self.dynamic_classes.get(material_name)
98        if material_class is None:106        if material_class is None:
99            raise ValueError(f"Unknown material: {material_name}")107            raise ValueError(f"Unknown material: {material_name}")
100        return material_class108        return material_class
101109
102    def _instantiate_material(self, material_class, mass):110    def _instantiate_material(self, material_class, mass):
103        return material_class(mass)111        return material_class(mass)
104112
105    def _add_material(self, material):113    def _add_material(self, material):
n106        self.materials.add(material)n114        self.materials.add(material)  # Add to factory's materials
107        Factory.all_materials.add(material)115        Factory.all_materials.add(material)  # Add to global all_materials
116        # Update the bitmask with the material's bit
117        self.material_bitmask |= material.material_bit  # Already done in _create_materials
108118
109    def _use_materials(self, materials):119    def _use_materials(self, materials):
110        self._validate_materials(materials)120        self._validate_materials(materials)
111        self._mark_materials_as_used(materials)121        self._mark_materials_as_used(materials)
112122
113        material_bitmask, total_mass = self._calculate_bitmask_and_mass(materials)123        material_bitmask, total_mass = self._calculate_bitmask_and_mass(materials)
114        new_class = self._get_or_create_dynamic_class(material_bitmask)124        new_class = self._get_or_create_dynamic_class(material_bitmask)
115125
116        new_material = self._create_new_material(new_class, total_mass)126        new_material = self._create_new_material(new_class, total_mass)
nn127        self.material_bitmask |= new_class.material_bit  # Set the bit for the new composite material
117        return new_material128        return new_material
118129
119    def _validate_materials(self, materials):130    def _validate_materials(self, materials):
120        known_classes = tuple(self.material_classes.values()) + tuple(self.dynamic_classes.values())131        known_classes = tuple(self.material_classes.values()) + tuple(self.dynamic_classes.values())
121132
122        for material in materials:133        for material in materials:
123            if not isinstance(material, known_classes):134            if not isinstance(material, known_classes):
124                raise ValueError(f"Invalid material instance: {material}")135                raise ValueError(f"Invalid material instance: {material}")
125            if material.used:136            if material.used:
126                raise AssertionError(f"Material {material} is already used.")137                raise AssertionError(f"Material {material} is already used.")
127138
128    def _mark_materials_as_used(self, materials):139    def _mark_materials_as_used(self, materials):
129        for material in materials:140        for material in materials:
130            material.used = True141            material.used = True
n131            self.materials.discard(material)n142            self.materials.discard(material)  # Remove from factory's materials
132            Factory.all_materials.discard(material)143            self.material_bitmask &= ~material.material_bit  # Clear the bit for the used material
144            Factory.all_materials.discard(material)  # Remove from global all_materials
133145
134    def _calculate_bitmask_and_mass(self, materials):146    def _calculate_bitmask_and_mass(self, materials):
135        material_bitmask = 0147        material_bitmask = 0
136        total_mass = 0148        total_mass = 0
137        for material in materials:149        for material in materials:
138            material_bitmask |= material.material_bit  # Combine material bits using bitwise OR150            material_bitmask |= material.material_bit  # Combine material bits using bitwise OR
139            total_mass += material.mass151            total_mass += material.mass
140        return material_bitmask, total_mass152        return material_bitmask, total_mass
141153
142    def _get_or_create_dynamic_class(self, material_bitmask):154    def _get_or_create_dynamic_class(self, material_bitmask):
143        if material_bitmask in self.dynamic_classes:155        if material_bitmask in self.dynamic_classes:
144            return self.dynamic_classes[material_bitmask]156            return self.dynamic_classes[material_bitmask]
145        else:157        else:
146            return self._create_dynamic_class(material_bitmask)158            return self._create_dynamic_class(material_bitmask)
147159
148    def _create_dynamic_class(self, material_bitmask):160    def _create_dynamic_class(self, material_bitmask):
149        base_materials = self._determine_base_materials(material_bitmask)161        base_materials = self._determine_base_materials(material_bitmask)
150        density = self._calculate_density(base_materials)162        density = self._calculate_density(base_materials)
151        class_name = self._generate_class_name(base_materials)163        class_name = self._generate_class_name(base_materials)
152164
153        attributes = {165        attributes = {
154            'density': density,166            'density': density,
155            'material_bit': material_bitmask,167            'material_bit': material_bitmask,
156        }168        }
157        new_class = type(class_name, (Material,), attributes)169        new_class = type(class_name, (Material,), attributes)
158        self.dynamic_classes[material_bitmask] = new_class170        self.dynamic_classes[material_bitmask] = new_class
159        return new_class171        return new_class
160172
161    def _determine_base_materials(self, material_bitmask):173    def _determine_base_materials(self, material_bitmask):
n162        return [name for name, bit in MATERIAL_BITS.items() if material_bitmask & bit]  # Check if material is included using bitwise ANDn174        # Use precomputed bitmask to materials mapping for efficiency
175        return self._bitmask_to_materials.get(material_bitmask, [])
163176
164    def _calculate_density(self, base_materials):177    def _calculate_density(self, base_materials):
165        densities = [BASE_MATERIAL_DENSITIES[name] for name in base_materials]178        densities = [BASE_MATERIAL_DENSITIES[name] for name in base_materials]
166        return sum(densities) / len(densities)179        return sum(densities) / len(densities)
167180
168    def _generate_class_name(self, base_materials):181    def _generate_class_name(self, base_materials):
n169        return '_'.join(sorted(base_materials))  # Generate class name by sorting material namesn182        # Generate class name by sorting material names to ensure consistency
183        return '_'.join(sorted(base_materials))
170184
171    def _create_new_material(self, new_class, total_mass):185    def _create_new_material(self, new_class, total_mass):
172        new_material = new_class(total_mass)186        new_material = new_class(total_mass)
n173        self.materials.add(new_material)n187        self.materials.add(new_material)  # Add to factory's materials
174        Factory.all_materials.add(new_material)188        Factory.all_materials.add(new_material)  # Add to global all_materials
175        return new_material189        return new_material
176190
177    def can_build(self, volume_needed):191    def can_build(self, volume_needed):
nn192        # Calculate total volume of current materials in this factory
178        total_volume = sum(material.volume for material in self.materials)193        total_volume = sum(material.volume for material in self.materials)
179        return total_volume >= volume_needed194        return total_volume >= volume_needed
180195
181    @classmethod196    @classmethod
182    def can_build_together(cls, volume_needed):197    def can_build_together(cls, volume_needed):
tt198        # Calculate total volume of all materials across all factories
183        total_volume = sum(material.volume for material in cls.all_materials)199        total_volume = sum(material.volume for material in cls.all_materials)
184        return total_volume >= volume_needed200        return total_volume >= volume_needed
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

n1class Material:n
2    def __init__(self, mass):
3        self.mass = mass
4        self.used = False  
5        self._volume = None  # Cached value for the volume
6 
7    def __hash__(self):
8        return id(self)  # Hashing the object by its unique id
9 
10    def __eq__(self, other):
11        return self is other  
12 
13    @property
14    def volume(self):  # Caching the Volume Calculation
15        if self._volume is None:
16            self._volume = round(self.mass / self.density, 2)
17        return self._volume
18 
19 
20MATERIAL_BITS = {1MATERIAL_BITS = {
21    'Concrete': 1 << 0,  # 1 (binary 00001)2    'Concrete': 1 << 0,  # 1 (binary 00001)
22    'Brick':    1 << 1,  # 2 (binary 00010)3    'Brick':    1 << 1,  # 2 (binary 00010)
23    'Stone':    1 << 2,  # 4 (binary 00100)4    'Stone':    1 << 2,  # 4 (binary 00100)
24    'Wood':     1 << 3,  # 8 (binary 01000)5    'Wood':     1 << 3,  # 8 (binary 01000)
25    'Steel':    1 << 4,  # 16 (binary 10000)6    'Steel':    1 << 4,  # 16 (binary 10000)
26}7}
278
28BASE_MATERIAL_DENSITIES = {9BASE_MATERIAL_DENSITIES = {
29    'Concrete': 2500,10    'Concrete': 2500,
30    'Brick': 2000,11    'Brick': 2000,
31    'Stone': 1600,12    'Stone': 1600,
32    'Wood': 600,13    'Wood': 600,
33    'Steel': 7700,14    'Steel': 7700,
34}15}
3516
nn17 
18class Material:
19    def __init__(self, mass: float):
20        if mass <= 0:
21            raise ValueError("Mass must be a positive number.")
22        self.mass: float = mass
23        self.used: bool = False
24        self._volume: float = None  # Cached value for the volume
25 
26    def __hash__(self):
27        return id(self)  # Hashing the object by its unique id
28 
29    @property
30    def volume(self):
31        if self._volume is None:
32            self._volume = self.mass / self.density  
33        return self._volume
34 
35 
36class Concrete(Material):36class Concrete(Material):
n37    density = 2500n37    density = BASE_MATERIAL_DENSITIES['Concrete']
38    base_materials = {'Concrete'}
39    material_bit = MATERIAL_BITS['Concrete']38    material_bit = MATERIAL_BITS['Concrete']
4039
4140
42class Brick(Material):41class Brick(Material):
n43    density = 2000n42    density = BASE_MATERIAL_DENSITIES['Brick']
44    base_materials = {'Brick'}
45    material_bit = MATERIAL_BITS['Brick']43    material_bit = MATERIAL_BITS['Brick']
4644
4745
48class Stone(Material):46class Stone(Material):
n49    density = 1600n47    density = BASE_MATERIAL_DENSITIES['Stone']
50    base_materials = {'Stone'}
51    material_bit = MATERIAL_BITS['Stone']48    material_bit = MATERIAL_BITS['Stone']
5249
5350
54class Wood(Material):51class Wood(Material):
n55    density = 600n52    density = BASE_MATERIAL_DENSITIES['Wood']
56    base_materials = {'Wood'}
57    material_bit = MATERIAL_BITS['Wood']53    material_bit = MATERIAL_BITS['Wood']
5854
5955
60class Steel(Material):56class Steel(Material):
n61    density = 7700n57    density = BASE_MATERIAL_DENSITIES['Steel']
62    base_materials = {'Steel'}
63    material_bit = MATERIAL_BITS['Steel']58    material_bit = MATERIAL_BITS['Steel']
6459
6560
66class Factory:61class Factory:
67    material_classes = {62    material_classes = {
68        'Concrete': Concrete,63        'Concrete': Concrete,
69        'Brick': Brick,64        'Brick': Brick,
70        'Stone': Stone,65        'Stone': Stone,
71        'Wood': Wood,66        'Wood': Wood,
72        'Steel': Steel,67        'Steel': Steel,
73    }68    }
74    dynamic_classes = {}  # Stores dynamically created composite material classes69    dynamic_classes = {}  # Stores dynamically created composite material classes
75    all_materials = set()  # Set of all material instances created across all factories70    all_materials = set()  # Set of all material instances created across all factories
7671
77    def __init__(self):72    def __init__(self):
78        self.materials = set()  # Set of material instances created by this factory73        self.materials = set()  # Set of material instances created by this factory
7974
80    def __call__(self, *args, **kwargs):75    def __call__(self, *args, **kwargs):
n81        if (args and kwargs) or (not args and not kwargs):n76        self._validate_call(args, kwargs)
82            raise ValueError("Error")
83 
84        # Local references for faster access
85        material_classes = Factory.material_classes
86        dynamic_classes = Factory.dynamic_classes
87        all_materials = Factory.all_materials
88 
89        known_classes = tuple(material_classes.values()) + tuple(dynamic_classes.values())
9077
91        if kwargs:78        if kwargs:
n92            materials_created = []n79            return self._create_materials(kwargs)
93            for material_name, mass in kwargs.items():
94                material_class = material_classes.get(material_name) or dynamic_classes.get(material_name)
95                if material_class is None:
96                    raise ValueError(f"Unknown material: {material_name}")
97                material = material_class(mass)
98                self.materials.add(material)
99                all_materials.add(material)
100                materials_created.append(material)
101            return tuple(materials_created)
102        else:80        else:
n103            for material in args:n81            return self._use_materials(args)
104                if not isinstance(material, known_classes):
105                    raise ValueError("Error")
106                if material.used:
107                    raise AssertionError("Error")
108            for material in args:
109                material.used = True  
110                self.materials.discard(material) 
111                all_materials.discard(material)   
11282
n113            material_bitmask = 0n83    def _validate_call(self, args, kwargs):
114            total_mass = 084        if (args and kwargs) or (not args and not kwargs):
115            for material in args:85            raise ValueError("Invalid input.")
116                material_bitmask |= material.material_bit  # Combine material bits using bitwise OR
117                total_mass += material.mass
11886
n119            if material_bitmask in dynamic_classes:n87    def _create_materials(self, kwargs):
120                new_class = dynamic_classes[material_bitmask]88        materials_created = []
121            else:89        for material_name, mass in kwargs.items():
122                # Determine base materials and their densities from the material_bitmask90            material_class = self._get_material_class(material_name)
123                base_materials = []91            material = self._instantiate_material(material_class, mass)
124                densities = []92            self._add_material(material)
125                for material_name, bit in MATERIAL_BITS.items():93            materials_created.append(material)
126                    if material_bitmask & bit:  # Check if material is included using bitwise AND94        return tuple(materials_created)
127                        base_materials.append(material_name)
128                        densities.append(BASE_MATERIAL_DENSITIES[material_name])
129                density = sum(densities) / len(densities)
130                class_name = '_'.join(sorted(base_materials))
13195
n132                attributes = {n96    def _get_material_class(self, material_name):
133                    'density': density,97        material_class = self.material_classes.get(material_name) or self.dynamic_classes.get(material_name)
134                    'base_materials': set(base_materials),98        if material_class is None:
135                    'material_bit': material_bitmask,99            raise ValueError(f"Unknown material: {material_name}")
136                }100        return material_class
137                new_class = type(class_name, (Material,), attributes)
138                dynamic_classes[material_bitmask] = new_class
139101
nn102    def _instantiate_material(self, material_class, mass):
103        return material_class(mass)
104 
105    def _add_material(self, material):
106        self.materials.add(material)
107        Factory.all_materials.add(material)
108 
109    def _use_materials(self, materials):
110        self._validate_materials(materials)
111        self._mark_materials_as_used(materials)
112 
113        material_bitmask, total_mass = self._calculate_bitmask_and_mass(materials)
114        new_class = self._get_or_create_dynamic_class(material_bitmask)
115 
116        new_material = self._create_new_material(new_class, total_mass)
117        return new_material
118 
119    def _validate_materials(self, materials):
120        known_classes = tuple(self.material_classes.values()) + tuple(self.dynamic_classes.values())
121 
122        for material in materials:
123            if not isinstance(material, known_classes):
124                raise ValueError(f"Invalid material instance: {material}")
125            if material.used:
126                raise AssertionError(f"Material {material} is already used.")
127 
128    def _mark_materials_as_used(self, materials):
129        for material in materials:
130            material.used = True
131            self.materials.discard(material)
132            Factory.all_materials.discard(material)
133 
134    def _calculate_bitmask_and_mass(self, materials):
135        material_bitmask = 0
136        total_mass = 0
137        for material in materials:
138            material_bitmask |= material.material_bit  # Combine material bits using bitwise OR
139            total_mass += material.mass
140        return material_bitmask, total_mass
141 
142    def _get_or_create_dynamic_class(self, material_bitmask):
143        if material_bitmask in self.dynamic_classes:
144            return self.dynamic_classes[material_bitmask]
145        else:
146            return self._create_dynamic_class(material_bitmask)
147 
148    def _create_dynamic_class(self, material_bitmask):
149        base_materials = self._determine_base_materials(material_bitmask)
150        density = self._calculate_density(base_materials)
151        class_name = self._generate_class_name(base_materials)
152 
153        attributes = {
154            'density': density,
155            'material_bit': material_bitmask,
156        }
157        new_class = type(class_name, (Material,), attributes)
158        self.dynamic_classes[material_bitmask] = new_class
159        return new_class
160 
161    def _determine_base_materials(self, material_bitmask):
162        return [name for name, bit in MATERIAL_BITS.items() if material_bitmask & bit]  # Check if material is included using bitwise AND
163 
164    def _calculate_density(self, base_materials):
165        densities = [BASE_MATERIAL_DENSITIES[name] for name in base_materials]
166        return sum(densities) / len(densities)
167 
168    def _generate_class_name(self, base_materials):
169        return '_'.join(sorted(base_materials))  # Generate class name by sorting material names
170 
171    def _create_new_material(self, new_class, total_mass):
140            new_material = new_class(total_mass)172        new_material = new_class(total_mass)
141            self.materials.add(new_material)173        self.materials.add(new_material)
142            all_materials.add(new_material)174        Factory.all_materials.add(new_material)
143            return new_material175        return new_material
144176
145    def can_build(self, volume_needed):177    def can_build(self, volume_needed):
n146        total_volume = 0n178        total_volume = sum(material.volume for material in self.materials)
147        for material in self.materials:
148            total_volume += material.volume
149            if total_volume >= volume_needed:179        return total_volume >= volume_needed
150                return True  # Early exit if required volume is met
151        return False
152180
153    @classmethod181    @classmethod
154    def can_build_together(cls, volume_needed):182    def can_build_together(cls, volume_needed):
t155        total_volume = 0t183        total_volume = sum(material.volume for material in cls.all_materials)
156        for material in cls.all_materials:
157            total_volume += material.volume
158            if total_volume >= volume_needed:184        return total_volume >= volume_needed
159                return True  # Early exit if required volume is met
160        return False
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op