diff --git a/overcooked_simulator/counters.py b/overcooked_simulator/counters.py index b77bc0ca4e2d110022183c039fafe9f81d3e5417..b480c1c109d390defd848f9d1727bb713c4696d3 100644 --- a/overcooked_simulator/counters.py +++ b/overcooked_simulator/counters.py @@ -15,7 +15,7 @@ from overcooked_simulator.game_items import ( Plate, Tomato, Pot, - Pan, + CookingEquipment, ) @@ -33,9 +33,17 @@ class Counter: Returns: The item which the counter is occupied by. None if nothing is there. """ - give_player = self.occupied_by + if on_hands: + if self.occupied_by: + occupied_by = self.occupied_by + self.occupied_by = None + return occupied_by + return None + if self.occupied_by and isinstance(self.occupied_by, CookingEquipment): + return self.occupied_by.release() + occupied_by = self.occupied_by self.occupied_by = None - return give_player + return occupied_by def can_drop_off(self, item: Item) -> bool: """Checks whether an item by the player can be dropped of. More relevant for example with @@ -61,7 +69,7 @@ class Counter: if self.occupied_by is None: self.occupied_by = item elif self.occupied_by.can_combine(item): - self.occupied_by.combine(item) + return self.occupied_by.combine(item) return None def interact_start(self): @@ -160,8 +168,7 @@ class PlateReturn(Counter): self.occupied_by.append(item) return None if self.occupied_by[-1].can_combine(item): - self.occupied_by[-1].combine(item) - return None + return self.occupied_by[-1].combine(item) return item def can_drop_off(self, item: Item) -> bool: @@ -201,6 +208,9 @@ class Trash(Counter): if isinstance(item, Plate): item.holds = None return item + if isinstance(item, CookingEquipment): + item.content = None + return item return None def can_drop_off(self, item: Item) -> bool: @@ -213,32 +223,25 @@ class Stove(Counter): occupied_by = Pot() super().__init__(pos, occupied_by) - def pick_up(self, on_hands: bool = True): - if on_hands: - if self.occupied_by: - occupied_by = self.occupied_by - self.occupied_by = None - return occupied_by - return None - if self.occupied_by and self.occupied_by.holds: - soup = self.occupied_by.holds.pop(0) - return soup - - def drop_off(self, item) -> Item | None: - if isinstance(item, (Pot, Pan)): - self.occupied_by = item - else: - self.occupied_by.combine(item) - return None - - def can_drop_off(self, item) -> bool: - if isinstance(item, (Pot, Pan)): - return not self.occupied_by - if self.occupied_by: - return self.occupied_by.can_combine(item) - return False + # def drop_off(self, item) -> Item | None: + # if isinstance(item, (Pot, Pan)): + # self.occupied_by = item + # else: + # self.occupied_by.combine(item) + # return None + # + # def can_drop_off(self, item) -> bool: + # if isinstance(item, (Pot, Pan)): + # return not self.occupied_by + # if self.occupied_by: + # return self.occupied_by.can_combine(item) + # return False def progress(self): """Called by environment step function for time progression""" - if self.occupied_by and self.occupied_by.can_cook(): + if ( + self.occupied_by + and isinstance(self.occupied_by, CookingEquipment) + and self.occupied_by.can_progress() + ): self.occupied_by.progress() diff --git a/overcooked_simulator/game_items.py b/overcooked_simulator/game_items.py index dc2375a2cab8fa9f9957a62522458b03ddd96b84..1d5098de7b8923d316851859430221a53554abea 100644 --- a/overcooked_simulator/game_items.py +++ b/overcooked_simulator/game_items.py @@ -1,46 +1,69 @@ +from __future__ import annotations + + class Item: """Base class for game items which can be held by a player.""" + def __init__(self, name: str = None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.name = self.__class__.__name__ if name is None else name + def can_combine(self, other): return False - def combine(self, other): + def combine(self, other) -> Item | None: pass def __repr__(self): - return self.__class__.__name__ + return f"{self.name}({self.extra_repr})" + + @property + def extra_repr(self): + return "" class Plate(Item): def __init__(self, holds: Item = None): + super().__init__() self.clean = True self.holds = holds - super().__init__() - def can_combine(self, other: Item) -> bool: if self.holds is None: - if isinstance(other, (Pot, Pan)): - return other.finished + if isinstance(other, CookingEquipment): + return other.can_release_content() return not isinstance(other, Plate) return False def combine(self, other): + if isinstance(other, CookingEquipment): + self.holds = other.release() + return other self.holds = other - def __repr__(self): - return f"Plate({self.holds})" + @property + def extra_repr(self): + return self.holds -class ProgressibleItem(Item): +class ProgressibleItem: """Class for items which need to be processed (cut, cooked, ...)""" - def __init__(self, finished: bool = False, steps_needed: int = 1500): + def __init__( + self, + finished: bool = False, + steps_needed: int = 1500, + finished_name: str = None, + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) self.progressed_steps = steps_needed if finished else 0 self.steps_needed = steps_needed self.finished = finished - self.finished_name = "Cutted" - super().__init__() + self.finished_name = ( + f"Cutted{self.name}" if finished_name is None else finished_name + ) def progress(self): """Progresses the item process as long as it is not finished.""" @@ -50,14 +73,21 @@ class ProgressibleItem(Item): if not self.finished: self.progressed_steps += 1 + def can_progress(self) -> bool: + return True + def finished_call(self): - pass + self.name = self.finished_name + + def reset(self): + self.finished = False + self.progressed_steps = 0 def __repr__(self): if self.finished: - return f"{self.finished_name}{self.__class__.__name__}()" + return f"{self.name}({self.extra_repr})" else: - return f"{self.__class__.__name__}(progress={int(self.progressed_steps / self.steps_needed * 100)}%)" + return f"{self.name}(progress={int(self.progressed_steps / self.steps_needed * 100)}%,{self.extra_repr})" class CuttableItem(ProgressibleItem): @@ -66,7 +96,7 @@ class CuttableItem(ProgressibleItem): pass -class Tomato(CuttableItem): +class Tomato(CuttableItem, Item): """Item class representing a tomato. Can be cut on the cutting board""" def can_combine(self, other): @@ -76,42 +106,82 @@ class Tomato(CuttableItem): super().__init__(steps_needed=1500) -class Pot(ProgressibleItem): - def __init__(self, holds: Item = None): - self.holds = [] if holds is None else holds - super().__init__(steps_needed=1500) - self.finished_name = "Cooked" +class CookingEquipment(Item): + def __init__(self, content: Meal = None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.content = content def can_combine(self, other): - # TODO based on recipes + if self.content is None: + # TODO check other is start of a meal, create meal + return True + return self.content.can_combine(other) + + def combine(self, other): + if not self.content: + # find starting meal for other + self.content = Soup() + self.content.combine(other) + + def can_progress(self, counter_type="Stove") -> bool: return ( - isinstance(other, Tomato) - # and other.finished - and len(self.holds) < 3 - and all([isinstance(h, Tomato) for h in self.holds]) + self.content + and isinstance(self.content, ProgressibleItem) + and self.content.can_progress() ) - def finished_call(self): - self.holds = [TomatoSoup()] + def progress(self): + self.content.progress() - def combine(self, other): - self.holds.append(other) + def can_release_content(self) -> bool: + return ( + self.content + and isinstance(self.content, ProgressibleItem) + and self.content.finished + ) - def can_cook(self) -> bool: - return self.holds and len(self.holds) == 3 + def release(self): + content = self.content + self.content = None + return content - def __repr__(self): - if self.finished: - return f"{self.finished_name}{self.__class__.__name__}({self.holds})" - else: - return f"{self.__class__.__name__}({self.holds},progress={int(self.progressed_steps / self.steps_needed * 100)}%)" + @property + def extra_repr(self): + return self.content + + +class Pot(CookingEquipment): + def __init__(self, holds: Meal = None): + super().__init__() + + +class Meal(Item): + def __init__(self, parts=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.parts = [] if parts is None else parts + # self.rules ... + + def can_combine(self, other) -> bool: + return ( + isinstance(other, Tomato) + and all([isinstance(o, other.__class__) for o in self.parts]) + and len(self.parts) < 3 + ) # rules + + def combine(self, other): + self.parts.append(other) + + @property + def extra_repr(self): + return self.parts -class TomatoSoup(Item): - ... +class Soup(ProgressibleItem, Meal): + def can_progress(self) -> bool: + return len(self.parts) == 3 -class Pan(ProgressibleItem): +class Pan(CookingEquipment): def __init__(self): super().__init__(steps_needed=1500) diff --git a/overcooked_simulator/pygame_gui/pygame_gui.py b/overcooked_simulator/pygame_gui/pygame_gui.py index b1f9d75b19da4d6edb4af2c80519c250ffc9a98d..df41500fcfdbe366ff8c5e48b0dcc373b85c0318 100644 --- a/overcooked_simulator/pygame_gui/pygame_gui.py +++ b/overcooked_simulator/pygame_gui/pygame_gui.py @@ -18,7 +18,7 @@ from overcooked_simulator.game_items import ( Plate, Item, Pot, - TomatoSoup, + Soup, ) from overcooked_simulator.game_items import Tomato from overcooked_simulator.overcooked_environment import Action @@ -231,8 +231,11 @@ class PyGameGUI: if isinstance(item, Pot): pot_size = 15 pygame.draw.circle(self.screen, GREY, pos, pot_size) + if item.content: + self.draw_item(pos, item.content) + if isinstance(item, Soup): if not item.finished: - match len(item.holds): + match len(item.parts): case 1: pygame.draw.circle(self.screen, RED, pos, 4) case 2: @@ -240,17 +243,14 @@ class PyGameGUI: case 3: pygame.draw.circle(self.screen, RED, pos, 12) else: - print(item.holds) - self.draw_item(pos, item.holds[0]) - if isinstance(item, TomatoSoup): - """https://www.readersdigest.ca/wp-content/uploads/2020/11/The-Best-Ever-Tomato-Soup_EXPS_THSO18_222724_D03_06_5b-4.jpg""" - image = pygame.image.load( - self.images_path / "tomato_soup.png" - ).convert_alpha() - image = pygame.transform.scale(image, (24, 24)) - rect = image.get_rect() - rect.center = pos - self.screen.blit(image, rect) + """https://www.readersdigest.ca/wp-content/uploads/2020/11/The-Best-Ever-Tomato-Soup_EXPS_THSO18_222724_D03_06_5b-4.jpg""" + image = pygame.image.load( + self.images_path / "tomato_soup.png" + ).convert_alpha() + image = pygame.transform.scale(image, (24, 24)) + rect = image.get_rect() + rect.center = pos + self.screen.blit(image, rect) if isinstance(item, ProgressibleItem) and not item.finished: self.draw_progress_bar(pos, item.progressed_steps, item.steps_needed)