Skip to content
Snippets Groups Projects
Commit c3a9f5fe authored by Fabian Heinrich's avatar Fabian Heinrich
Browse files

Merge branch '34-can-put-tomatos-on-returned-plate' into 'main'

Resolve "Can put tomatos on returned plate"

Closes #34

See merge request scs/cocosy/overcooked-simulator!8
parents 7b947393 d93a6fdd
No related branches found
No related tags found
1 merge request!8Resolve "Can put tomatos on returned plate"
Pipeline #41489 passed
from typing import Optional
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from overcooked_simulator.overcooked_environment import GameScore
import numpy as np
import numpy.typing as npt
......@@ -30,7 +35,7 @@ class Counter:
self.occupied_by = None
return give_player
def can_drop_off(self, item: HoldableItem):
def can_drop_off(self, item: HoldableItem) -> bool:
"""Checks whether an item by the player can be dropped of. More relevant for example with
ingredient dispensers, which should always be occupied and cannot take an item.
......@@ -42,7 +47,7 @@ class Counter:
"""
return self.occupied_by is None or self.occupied_by.can_combine(item)
def drop_off(self, item: HoldableItem):
def drop_off(self, item: HoldableItem) -> HoldableItem | None:
"""Takes the thing dropped of by the player.
Args:
......@@ -55,6 +60,7 @@ class Counter:
self.occupied_by = item
elif self.occupied_by.can_combine(item):
self.occupied_by.combine(item)
return None
def interact_start(self):
"""Starts an interaction by the player. Nothing happens for the standard counter."""
......@@ -100,14 +106,21 @@ class CuttingBoard(Counter):
class ServingWindow(Counter):
def drop_off(self, item):
return 5
def __init__(self, pos, game_score: GameScore):
self.game_score = game_score
super().__init__(pos)
def drop_off(self, item) -> HoldableItem | None:
reward = 5
# TODO define rewards
self.game_score.increment_score(reward)
return None
def can_score(self, item):
if isinstance(item, Plate) and isinstance(item.holds, ProgressibleItem):
return item.holds.finished
def can_drop_off(self, item: HoldableItem):
def can_drop_off(self, item: HoldableItem) -> bool:
return self.can_score(item)
def pick_up(self):
......@@ -120,16 +133,48 @@ class ServingWindow(Counter):
class PlateReturn(Counter):
def __init__(self, pos):
super().__init__(pos)
self.occupied_by = Plate()
self.occupied_by = [Plate()]
def pick_up(self):
return Plate()
"""Gets called upon a player performing the pickup action. Gives back a plate (possibly with ingredient.
def drop_off(self, item):
return 0
Returns: A plate possibly with an ingredient on it.
def can_drop_off(self, item):
return False
"""
give_player = self.occupied_by.pop()
if not self.occupied_by:
self.occupied_by.append(Plate())
return give_player
def drop_off(self, item: HoldableItem) -> HoldableItem | None:
"""Takes the ingredient dropped of by the player.
Args:
item: The ingredient to be placed on the counter.
"""
if isinstance(item, Plate):
if self.occupied_by[-1].holds:
return item
self.occupied_by.append(item)
return None
if self.occupied_by[-1].can_combine(item):
self.occupied_by[-1].combine(item)
return None
return item
def can_drop_off(self, item: HoldableItem) -> bool:
"""Checks whether an ingredient by the player can be dropped of.
Args:
item: The ingredient for which to check, if it can be placed on the counter.
Returns: True if the ingredient can be placed on the counter, False if not.
"""
# possibility to drop off empty plate on empty plate return
return (
isinstance(self.occupied_by[-1], Plate) and isinstance(item, Plate)
) or self.occupied_by[-1].can_combine(item)
def __repr__(self):
return "PlateReturn"
......@@ -142,10 +187,10 @@ class TomatoDispenser(Counter):
def pick_up(self):
return Tomato()
def drop_off(self, item: HoldableItem):
return 0
def drop_off(self, item: HoldableItem) -> HoldableItem | None:
return None
def can_drop_off(self, item: HoldableItem):
def can_drop_off(self, item: HoldableItem) -> bool:
return False
def __repr__(self):
......@@ -156,12 +201,13 @@ class Trash(Counter):
def pick_up(self):
pass
def drop_off(self, item: HoldableItem):
def drop_off(self, item: HoldableItem) -> HoldableItem | None:
if isinstance(item, Plate):
item.holds = None
return -1
return item
return None
def can_drop_off(self, item: HoldableItem):
def can_drop_off(self, item: HoldableItem) -> bool:
return True
def __repr__(self):
......
......@@ -9,14 +9,14 @@ class HoldableItem:
class Plate(HoldableItem):
def __init__(self):
def __init__(self, holds: HoldableItem = None):
self.clean = True
self.holds = None
self.holds = holds
super().__init__()
def can_combine(self, other: HoldableItem):
return self.holds is None
return self.holds is None and not isinstance(other, Plate)
def combine(self, other):
self.holds = other
......
......@@ -13,20 +13,22 @@ from overcooked_simulator.counters import (
Counter,
CuttingBoard,
Trash,
ServingWindow,
TomatoDispenser,
ServingWindow,
PlateReturn,
)
SYMBOL_TO_CHARACTER_MAP = {
"C": Counter,
"B": CuttingBoard,
"X": Trash,
"W": ServingWindow,
"T": TomatoDispenser,
"P": PlateReturn,
"E": None,
}
class GameScore:
def __init__(self):
self.score = 0
def increment_score(self, score: int):
self.score += score
print(self.score)
def read_score(self):
return self.score
class Action:
......@@ -57,6 +59,18 @@ class Environment:
self.players: dict[str, Player] = {}
self.counter_side_length: int = 40
self.layout_path: Path = layout_path
self.game_score = GameScore()
self.SYMBOL_TO_CHARACTER_MAP = {
"C": Counter,
"B": CuttingBoard,
"X": Trash,
"W": lambda pos: ServingWindow(pos, self.game_score),
"T": TomatoDispenser,
"P": PlateReturn,
"E": None,
}
self.counters: list[Counter] = self.create_counters(self.layout_path)
self.score: int = 0
self.world_width: int = 800
......@@ -82,7 +96,7 @@ class Environment:
for character in line:
character = character.capitalize()
pos = np.array([current_x, current_y])
counter_class = SYMBOL_TO_CHARACTER_MAP[character]
counter_class = self.SYMBOL_TO_CHARACTER_MAP[character]
if counter_class is not None:
counter = counter_class(pos)
counters.append(counter)
......@@ -120,7 +134,7 @@ class Environment:
elif action.action == "keyup":
player.perform_interact_hold_stop(counter)
def get_closest_counter(self, point: npt.NDArray):
def get_closest_counter(self, point: np.ndarray):
"""Determines the closest counter for a given 2d-coordinate point in the env.
Args:
......@@ -149,7 +163,7 @@ class Environment:
facing_counter = self.get_closest_counter(facing_point)
return facing_counter
def perform_movement(self, player: Player, move_vector: npt.NDArray[int]):
def perform_movement(self, player: Player, move_vector: np.array):
"""Moves a player in the direction specified in the action.action. If the player collides with a
counter or other player through this movement, then they are not moved.
(The extended code with the two ifs is for sliding movement at the counters, which feels a bit smoother.
......
......@@ -3,8 +3,8 @@ from typing import Optional
import numpy as np
import numpy.typing as npt
from overcooked_simulator.counters import Counter, Trash
from overcooked_simulator.game_items import HoldableItem, Plate
from overcooked_simulator.counters import Counter
from overcooked_simulator.game_items import HoldableItem
class Player:
......@@ -78,11 +78,7 @@ class Player:
self.holding = counter.pick_up()
elif counter.can_drop_off(self.holding):
if isinstance(counter, Trash) and isinstance(self.holding, Plate):
self.holding.holds = None
else:
counter.drop_off(self.holding)
self.holding = None
self.holding = counter.drop_off(self.holding)
elif self.holding.can_combine(counter.occupied_by):
returned_by_counter = counter.pick_up()
......
overcooked_simulator/pygame_gui/images/pixel_cook.png

2.28 KiB

import math
from pathlib import Path
import numpy as np
......@@ -31,6 +32,8 @@ KNIFE_COLOR = (120, 120, 120)
PLATE_RETURN_COLOR = (170, 170, 240)
BOARD_COLOR = (239, 193, 151)
USE_COOK_SPRITE = False
class PlayerKeySet:
"""Set of keyboard keys for controlling a player.
......@@ -152,26 +155,41 @@ class PyGameGUI:
state: The game state returned by the environment.
"""
for player in state["players"].values():
pos = player.pos
size = player.radius
color1 = RED if player.name == "p1" else GREEN
color2 = WHITE
if USE_COOK_SPRITE:
image = pygame.image.load(
self.images_path / "pixel_cook.png"
).convert_alpha()
rect = pygame.Rect(pos[0] - (size / 2), pos[1] - (size / 2), size, size)
pygame.draw.circle(self.screen, color2, pos, size)
pygame.draw.rect(self.screen, color1, rect)
rel_x, rel_y = player.facing_direction
angle = -np.rad2deg(math.atan2(rel_y, rel_x)) + 90
facing = player.facing_direction
image = pygame.transform.scale(image, (40, 40))
image = pygame.transform.rotate(image, angle)
rect = image.get_rect()
rect.center = player.pos
self.screen.blit(image, rect)
else:
pos = player.pos
size = player.radius
color1 = RED if player.name == "p1" else GREEN
color2 = WHITE
rect = pygame.Rect(pos[0] - (size / 2), pos[1] - (size / 2), size, size)
pygame.draw.circle(self.screen, color2, pos, size)
pygame.draw.rect(self.screen, color1, rect)
pos = player.pos
facing = player.facing_direction
pygame.draw.polygon(
self.screen,
BLUE,
(
(pos[0] + (facing[1] * 5), pos[1] - (facing[0] * 5)),
(pos[0] - (facing[1] * 5), pos[1] + (facing[0] * 5)),
player.pos + (facing * 20),
),
)
pygame.draw.polygon(
self.screen,
BLUE,
(
(pos[0] + (facing[1] * 5), pos[1] - (facing[0] * 5)),
(pos[0] - (facing[1] * 5), pos[1] + (facing[0] * 5)),
player.pos + (facing * 20),
),
)
if player.holding is not None:
holding_item_pos = player.pos + (20 * player.facing_direction)
self.draw_item(holding_item_pos, player.holding)
......@@ -182,19 +200,17 @@ class PyGameGUI:
if item.finished:
image = pygame.image.load(
self.images_path / "tomato_cut.png"
).convert_alpha() # or .convert_alpha()
).convert_alpha()
else:
image = pygame.image.load(
self.images_path / "tomato.png"
).convert_alpha() # or .convert_alpha()
).convert_alpha()
rect = image.get_rect()
rect.center = pos
self.screen.blit(image, rect)
if isinstance(item, Plate):
image = pygame.image.load(
self.images_path / "plate.png"
).convert_alpha() # or .convert_alpha()
image = pygame.image.load(self.images_path / "plate.png").convert_alpha()
rect = image.get_rect()
rect.center = pos
self.screen.blit(image, rect)
......@@ -284,7 +300,13 @@ class PyGameGUI:
pygame.draw.rect(self.screen, YELLOW, board_rect)
if counter.occupied_by is not None:
self.draw_item(counter.pos, counter.occupied_by)
if isinstance(counter.occupied_by, list):
for i, o in enumerate(counter.occupied_by):
self.draw_item(
np.abs([counter.pos[0], counter.pos[1] - (i * 3)]), o
)
else:
self.draw_item(counter.pos, counter.occupied_by)
def draw_counters(self, state):
"""Visualizes the counters in the environment.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment