import random from abc import ABC, abstractmethod from copy import copy from exceptions import DeadPlantError, NoMoreFruitsError from fruit import Fruit from root import Root class Plant(ABC): def __init__(self, species: str, growth_factor: float): self.species = species self.growth_factor = growth_factor self._size = 1 self.dead = False def __repr__(self): return f"{self.species}" def __str__(self): return f"{self.species} of size {self.size:.1f}" @property def growth_factor(self): return self._growth_factor @growth_factor.setter def growth_factor(self, growth_factor: float): if growth_factor <= 1: raise ValueError(f"Invalid growth_factor: {growth_factor} (>1 required)") self._growth_factor = growth_factor @property def size(self): return self._size def grow(self) -> None: if self.dead: raise DeadPlantError("{self} is dead, it can't grow") self._size *= self.growth_factor @abstractmethod def harvest(self) -> list: raise NotImplementedError class FruitBearingPlant(Plant): def __init__(self, species: str, growth_factor: float, fruit_template: Fruit): super().__init__(species, growth_factor) self.fruit_template = fruit_template self._fruits = [] def __str__(self): return f"{self.species} of size {self.size:.1f} with {len(self.fruits)} fruits" @property def fruits(self): return self._fruits @fruits.deleter def fruits(self): self._fruits.clear() def grow(self) -> None: super().grow() if random.randrange(100) < self.size: self.fruits.append(copy(self.fruit_template)) def pick(self) -> Fruit: if not self.fruits: raise NoMoreFruitsError(f"{self} is empty, you can't pick any more fruits :(") return self.fruits.pop() def harvest(self) -> list: harvested = [] while True: try: harvested.append(self.pick()) except NoMoreFruitsError: return harvested class RootVegetable(Plant): def __init__(self, species: str, growth_factor: float, calories_per_root_length: int, plant_size_to_root_length_factor: float): super().__init__(species, growth_factor) self._root = Root(self.species, calories_per_root_length) self._plant_size_to_root_length_factor = plant_size_to_root_length_factor def grow(self): super().grow() self._root.length = self._size * self._plant_size_to_root_length_factor def harvest(self) -> list: self.dead = True if self._root is None: return [] harvested = [self._root] self._root = None return harvested