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

Merge remote-tracking branch 'origin/34-can-put-tomatos-on-returned-plate'...

Merge remote-tracking branch 'origin/34-can-put-tomatos-on-returned-plate' into 34-can-put-tomatos-on-returned-plate

# Conflicts:
#	overcooked_simulator/pygame_gui/pygame_gui.py
parents deba7b04 44a94912
No related branches found
No related tags found
1 merge request!8Resolve "Can put tomatos on returned plate"
Pipeline #41482 passed
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING: if TYPE_CHECKING:
from overcooked_simulator.overcooked_environment import GameScore from overcooked_simulator.overcooked_environment import GameScore
from typing import Optional
import numpy as np import numpy as np
import numpy.typing as npt
from overcooked_simulator.game_items import ( from overcooked_simulator.game_items import (
CuttableItem, CuttableItem,
...@@ -20,8 +20,8 @@ from overcooked_simulator.game_items import ( ...@@ -20,8 +20,8 @@ from overcooked_simulator.game_items import (
class Counter: class Counter:
"""Simple class for a counter at a specified position (center of counter). Can hold things on top.""" """Simple class for a counter at a specified position (center of counter). Can hold things on top."""
def __init__(self, pos: np.ndarray): def __init__(self, pos: npt.NDArray[float]):
self.pos: np.ndarray = pos self.pos: npt.NDArray[float] = pos
self.occupied_by: Optional[HoldableItem] = None self.occupied_by: Optional[HoldableItem] = None
def pick_up(self): def pick_up(self):
...@@ -35,7 +35,7 @@ class Counter: ...@@ -35,7 +35,7 @@ class Counter:
self.occupied_by = None self.occupied_by = None
return give_player 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 """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. ingredient dispensers, which should always be occupied and cannot take an item.
...@@ -47,19 +47,20 @@ class Counter: ...@@ -47,19 +47,20 @@ class Counter:
""" """
return self.occupied_by is None or self.occupied_by.can_combine(item) 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. """Takes the thing dropped of by the player.
Args: Args:
item: The item to be placed on the counter. item: The item to be placed on the counter.
Returns: TODO Return information, wether the score is affected (Serving Window?) Returns: TODO Return information, whether the score is affected (Serving Window?)
""" """
if self.occupied_by is None: if self.occupied_by is None:
self.occupied_by = item self.occupied_by = item
elif self.occupied_by.can_combine(item): elif self.occupied_by.can_combine(item):
self.occupied_by.combine(item) self.occupied_by.combine(item)
return None
def interact_start(self): def interact_start(self):
"""Starts an interaction by the player. Nothing happens for the standard counter.""" """Starts an interaction by the player. Nothing happens for the standard counter."""
...@@ -105,19 +106,21 @@ class CuttingBoard(Counter): ...@@ -105,19 +106,21 @@ class CuttingBoard(Counter):
class ServingWindow(Counter): class ServingWindow(Counter):
def __init__(self, pos, gamescore: GameScore): def __init__(self, pos, game_score: GameScore):
self.game_score = gamescore self.game_score = game_score
super().__init__(pos) super().__init__(pos)
def drop_off(self, item): def drop_off(self, item) -> HoldableItem | None:
reward = 5 reward = 5
# TODO define rewards
self.game_score.increment_score(reward) self.game_score.increment_score(reward)
return None
def can_score(self, item): def can_score(self, item):
if isinstance(item, Plate) and isinstance(item.holds, ProgressibleItem): if isinstance(item, Plate) and isinstance(item.holds, ProgressibleItem):
return item.holds.finished return item.holds.finished
def can_drop_off(self, item: HoldableItem): def can_drop_off(self, item: HoldableItem) -> bool:
return self.can_score(item) return self.can_score(item)
def pick_up(self): def pick_up(self):
...@@ -130,7 +133,7 @@ class ServingWindow(Counter): ...@@ -130,7 +133,7 @@ class ServingWindow(Counter):
class PlateReturn(Counter): class PlateReturn(Counter):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.occupied_by = Plate() self.occupied_by = [Plate()]
def pick_up(self): def pick_up(self):
"""Gets called upon a player performing the pickup action. Gives back a plate (possibly with ingredient. """Gets called upon a player performing the pickup action. Gives back a plate (possibly with ingredient.
...@@ -138,20 +141,28 @@ class PlateReturn(Counter): ...@@ -138,20 +141,28 @@ class PlateReturn(Counter):
Returns: A plate possibly with an ingredient on it. Returns: A plate possibly with an ingredient on it.
""" """
give_player = self.occupied_by give_player = self.occupied_by.pop()
self.occupied_by = Plate() if not self.occupied_by:
self.occupied_by.append(Plate())
return give_player return give_player
def drop_off(self, item: HoldableItem): def drop_off(self, item: HoldableItem) -> HoldableItem | None:
"""Takes the ingredient dropped of by the player. """Takes the ingredient dropped of by the player.
Args: Args:
item: The ingredient to be placed on the counter. item: The ingredient to be placed on the counter.
""" """
if item is Plate() and self.occupied_by is Plate(): if isinstance(item, Plate):
self.occupied_by = None if self.occupied_by[-1].holds:
return item
def can_drop_off(self, item: HoldableItem): 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. """Checks whether an ingredient by the player can be dropped of.
Args: Args:
...@@ -162,8 +173,8 @@ class PlateReturn(Counter): ...@@ -162,8 +173,8 @@ class PlateReturn(Counter):
""" """
# possibility to drop off empty plate on empty plate return # possibility to drop off empty plate on empty plate return
return ( return (
isinstance(self.occupied_by, Plate) and isinstance(item, Plate) isinstance(self.occupied_by[-1], Plate) and isinstance(item, Plate)
) or self.occupied_by.can_combine(item) ) or self.occupied_by[-1].can_combine(item)
def __repr__(self): def __repr__(self):
return "PlateReturn" return "PlateReturn"
...@@ -176,10 +187,10 @@ class TomatoDispenser(Counter): ...@@ -176,10 +187,10 @@ class TomatoDispenser(Counter):
def pick_up(self): def pick_up(self):
return Tomato() return Tomato()
def drop_off(self, item: HoldableItem): def drop_off(self, item: HoldableItem) -> HoldableItem | None:
return 0 return None
def can_drop_off(self, item: HoldableItem): def can_drop_off(self, item: HoldableItem) -> bool:
return False return False
def __repr__(self): def __repr__(self):
...@@ -190,12 +201,13 @@ class Trash(Counter): ...@@ -190,12 +201,13 @@ class Trash(Counter):
def pick_up(self): def pick_up(self):
pass pass
def drop_off(self, item: HoldableItem): def drop_off(self, item: HoldableItem) -> HoldableItem | None:
if isinstance(item, Plate): if isinstance(item, Plate):
item.holds = None 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 return True
def __repr__(self): def __repr__(self):
......
class HoldableItem: class HoldableItem:
"""Base class for game items which can be held by a player.""" """Base class for game items which can be held by a player."""
pass
def can_combine(self, other): def can_combine(self, other):
return False return False
...@@ -11,9 +9,9 @@ class HoldableItem: ...@@ -11,9 +9,9 @@ class HoldableItem:
class Plate(HoldableItem): class Plate(HoldableItem):
def __init__(self): def __init__(self, holds: HoldableItem = None):
self.clean = True self.clean = True
self.holds = None self.holds = holds
super().__init__() super().__init__()
......
...@@ -5,7 +5,6 @@ import numpy as np ...@@ -5,7 +5,6 @@ import numpy as np
import pygame import pygame
from overcooked_simulator import ROOT_DIR from overcooked_simulator import ROOT_DIR
from overcooked_simulator.game_items import Tomato, Plate
from overcooked_simulator.player import Player from overcooked_simulator.player import Player
from overcooked_simulator.pygame_gui.pygame_gui import PyGameGUI from overcooked_simulator.pygame_gui.pygame_gui import PyGameGUI
from overcooked_simulator.simulation_runner import Simulator from overcooked_simulator.simulation_runner import Simulator
...@@ -15,8 +14,8 @@ def main(): ...@@ -15,8 +14,8 @@ def main():
simulator = Simulator(Path(ROOT_DIR, "layouts", "basic.layout"), 600) simulator = Simulator(Path(ROOT_DIR, "layouts", "basic.layout"), 600)
player_one_name = "p1" player_one_name = "p1"
player_two_name = "p2" player_two_name = "p2"
simulator.register_player(Player(player_one_name, np.array([200, 200]))) simulator.register_player(Player(player_one_name, np.array([200.0, 200.0])))
simulator.register_player(Player(player_two_name, np.array([100, 200]))) simulator.register_player(Player(player_two_name, np.array([100.0, 200.0])))
# TODO maybe read the player names and keyboard keys from config file? # TODO maybe read the player names and keyboard keys from config file?
keys1 = [ keys1 = [
......
...@@ -6,6 +6,8 @@ if TYPE_CHECKING: ...@@ -6,6 +6,8 @@ if TYPE_CHECKING:
from overcooked_simulator.player import Player from overcooked_simulator.player import Player
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
import numpy.typing as npt
from scipy.spatial import distance_matrix from scipy.spatial import distance_matrix
from overcooked_simulator.counters import ( from overcooked_simulator.counters import (
Counter, Counter,
...@@ -55,7 +57,7 @@ class Environment: ...@@ -55,7 +57,7 @@ class Environment:
def __init__(self, layout_path): def __init__(self, layout_path):
self.players: dict[str, Player] = {} self.players: dict[str, Player] = {}
self.counter_side_length: float = 40 self.counter_side_length: int = 40
self.layout_path: Path = layout_path self.layout_path: Path = layout_path
self.game_score = GameScore() self.game_score = GameScore()
...@@ -132,7 +134,7 @@ class Environment: ...@@ -132,7 +134,7 @@ class Environment:
elif action.action == "keyup": elif action.action == "keyup":
player.perform_interact_hold_stop(counter) player.perform_interact_hold_stop(counter)
def get_closest_counter(self, point: np.ndarray): def get_closest_counter(self, point: npt.NDArray):
"""Determines the closest counter for a given 2d-coordinate point in the env. """Determines the closest counter for a given 2d-coordinate point in the env.
Args: Args:
...@@ -161,7 +163,7 @@ class Environment: ...@@ -161,7 +163,7 @@ class Environment:
facing_counter = self.get_closest_counter(facing_point) facing_counter = self.get_closest_counter(facing_point)
return facing_counter return facing_counter
def perform_movement(self, player: Player, move_vector: np.array): def perform_movement(self, player: Player, move_vector: npt.NDArray[int]):
"""Moves a player in the direction specified in the action.action. If the player collides with a """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. 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. (The extended code with the two ifs is for sliding movement at the counters, which feels a bit smoother.
...@@ -170,6 +172,8 @@ class Environment: ...@@ -170,6 +172,8 @@ class Environment:
The movement action is a unit 2d vector. The movement action is a unit 2d vector.
Detects collisions with other players and pushes them out of the way.
Args: Args:
player: The player to move. player: The player to move.
move_vector: The movement vector which is a unit-2d-vector of the movement direction move_vector: The movement vector which is a unit-2d-vector of the movement direction
...@@ -179,6 +183,18 @@ class Environment: ...@@ -179,6 +183,18 @@ class Environment:
step = move_vector * player.move_dist step = move_vector * player.move_dist
player.move(step) player.move(step)
if self.detect_collision(player): if self.detect_collision(player):
collided_players = self.get_collided_players(player)
for collided_player in collided_players:
pushing_vector = collided_player.pos - player.pos
if np.linalg.norm(pushing_vector) != 0:
pushing_vector = pushing_vector / np.linalg.norm(pushing_vector)
old_pos_other = collided_player.pos.copy()
collided_player.move(pushing_vector * (collided_player.move_dist / 2))
if self.detect_collision_counters(
collided_player
) or self.detect_collision_world_bounds(player):
collided_player.move_abs(old_pos_other)
player.move_abs(old_pos) player.move_abs(old_pos)
old_pos = player.pos.copy() old_pos = player.pos.copy()
...@@ -210,11 +226,29 @@ class Environment: ...@@ -210,11 +226,29 @@ class Environment:
Returns: True if the player is intersecting with any object in the environment. Returns: True if the player is intersecting with any object in the environment.
""" """
return ( return (
self.detect_player_collision(player) len(self.get_collided_players(player)) != 0
or self.detect_collision_counters(player) or self.detect_collision_counters(player)
or self.detect_collision_world_bounds(player) or self.detect_collision_world_bounds(player)
) )
def get_collided_players(self, player: Player) -> list[Player]:
"""Detects collisions between the queried player and other players. Returns the list of the collided players.
A player is modelled as a circle. Collision is detected if the distance between the players is smaller
than the sum of the radius's.
Args:
player: The player to check collisions with other players for.
Returns: The list of other players the player collides with.
"""
other_players = filter(lambda p: p.name != player.name, self.players.values())
def collide(p):
return np.linalg.norm(player.pos - p.pos) <= (player.radius + p.radius)
return list(filter(collide, other_players))
def detect_player_collision(self, player: Player): def detect_player_collision(self, player: Player):
"""Detects collisions between the queried player and other players. """Detects collisions between the queried player and other players.
A player is modelled as a circle. Collision is detected if the distance between the players is smaller A player is modelled as a circle. Collision is detected if the distance between the players is smaller
......
from typing import Optional from typing import Optional
import numpy as np import numpy as np
import numpy.typing as npt
from overcooked_simulator.counters import Counter, Trash from overcooked_simulator.counters import Counter
from overcooked_simulator.game_items import HoldableItem, Plate from overcooked_simulator.game_items import HoldableItem
class Player: class Player:
...@@ -13,17 +14,17 @@ class Player: ...@@ -13,17 +14,17 @@ class Player:
""" """
def __init__(self, name: str, pos: np.ndarray): def __init__(self, name: str, pos: npt.NDArray[float]):
self.name: str = name self.name: str = name
self.pos: np.ndarray = np.array(pos, dtype=float) self.pos: npt.NDArray[float] = np.array(pos, dtype=float)
self.holding: Optional[HoldableItem] = None self.holding: Optional[HoldableItem] = None
self.radius: int = 18 self.radius: int = 18
self.move_dist: int = 5 self.move_dist: int = 5
self.interaction_range: int = 60 self.interaction_range: int = 60
self.facing_direction: np.ndarray = np.array([0, 1]) self.facing_direction: npt.NDArray[float] = np.array([0, 1])
def move(self, movement: np.ndarray): def move(self, movement: npt.NDArray[float]):
"""Moves the player position by the given movement vector. """Moves the player position by the given movement vector.
A unit direction vector multiplied by move_dist is added to the player position. A unit direction vector multiplied by move_dist is added to the player position.
...@@ -34,7 +35,7 @@ class Player: ...@@ -34,7 +35,7 @@ class Player:
if np.linalg.norm(movement) != 0: if np.linalg.norm(movement) != 0:
self.turn(movement) self.turn(movement)
def move_abs(self, new_pos: np.ndarray): def move_abs(self, new_pos: npt.NDArray[float]):
"""Overwrites the player location by the new_pos 2d-vector. Absolute movement. """Overwrites the player location by the new_pos 2d-vector. Absolute movement.
Mostly needed for resetting the player after a collision. Mostly needed for resetting the player after a collision.
...@@ -43,7 +44,7 @@ class Player: ...@@ -43,7 +44,7 @@ class Player:
""" """
self.pos = new_pos self.pos = new_pos
def turn(self, direction: np.ndarray): def turn(self, direction: npt.NDArray[float]):
"""Turns the player in the given direction. Overwrites the facing_direction by a given 2d-vector. """Turns the player in the given direction. Overwrites the facing_direction by a given 2d-vector.
facing_direction is normalized to length 1. facing_direction is normalized to length 1.
...@@ -54,7 +55,7 @@ class Player: ...@@ -54,7 +55,7 @@ class Player:
self.facing_direction = direction / np.linalg.norm(direction) self.facing_direction = direction / np.linalg.norm(direction)
def can_reach(self, counter: Counter): def can_reach(self, counter: Counter):
"""Checks wether the player can reach the counter in question. Simple check if the distance is not larger """Checks whether the player can reach the counter in question. Simple check if the distance is not larger
than the player interaction range. than the player interaction range.
Args: Args:
...@@ -77,11 +78,7 @@ class Player: ...@@ -77,11 +78,7 @@ class Player:
self.holding = counter.pick_up() self.holding = counter.pick_up()
elif counter.can_drop_off(self.holding): elif counter.can_drop_off(self.holding):
if isinstance(counter, Trash) and isinstance(self.holding, Plate): self.holding = counter.drop_off(self.holding)
self.holding.holds = None
else:
counter.drop_off(self.holding)
self.holding = None
elif self.holding.can_combine(counter.occupied_by): elif self.holding.can_combine(counter.occupied_by):
returned_by_counter = counter.pick_up() returned_by_counter = counter.pick_up()
......
...@@ -12,7 +12,7 @@ from overcooked_simulator.counters import ( ...@@ -12,7 +12,7 @@ from overcooked_simulator.counters import (
PlateReturn, PlateReturn,
ServingWindow, ServingWindow,
) )
from overcooked_simulator.game_items import ProgressibleItem, Plate from overcooked_simulator.game_items import ProgressibleItem, Plate, HoldableItem
from overcooked_simulator.game_items import Tomato from overcooked_simulator.game_items import Tomato
from overcooked_simulator.overcooked_environment import Action from overcooked_simulator.overcooked_environment import Action
from overcooked_simulator.simulation_runner import Simulator from overcooked_simulator.simulation_runner import Simulator
...@@ -20,7 +20,7 @@ from overcooked_simulator.simulation_runner import Simulator ...@@ -20,7 +20,7 @@ from overcooked_simulator.simulation_runner import Simulator
WHITE = (255, 255, 255) WHITE = (255, 255, 255)
GREY = (190, 190, 190) GREY = (190, 190, 190)
BLACK = (0, 0, 0) BLACK = (0, 0, 0)
COUNTERCOLOR = (240, 240, 240) COUNTER_COLOR = (240, 240, 240)
LIGHTGREY = (220, 220, 220) LIGHTGREY = (220, 220, 220)
GREEN = (0, 255, 0) GREEN = (0, 255, 0)
RED = (255, 0, 0) RED = (255, 0, 0)
...@@ -33,6 +33,7 @@ PLATE_RETURN_COLOR = (170, 170, 240) ...@@ -33,6 +33,7 @@ PLATE_RETURN_COLOR = (170, 170, 240)
BOARD_COLOR = (239, 193, 151) BOARD_COLOR = (239, 193, 151)
<<<<<<< HEAD
def angle_between_vectors_old(v1, v2): def angle_between_vectors_old(v1, v2):
if np.linalg.norm(v1) != 0: if np.linalg.norm(v1) != 0:
v1 = v1 / np.linalg.norm(v1) v1 = v1 / np.linalg.norm(v1)
...@@ -53,6 +54,9 @@ def angle_between_vectors(v1, v2): ...@@ -53,6 +54,9 @@ def angle_between_vectors(v1, v2):
class PlayerKeyset: class PlayerKeyset:
=======
class PlayerKeySet:
>>>>>>> origin/34-can-put-tomatos-on-returned-plate
"""Set of keyboard keys for controlling a player. """Set of keyboard keys for controlling a player.
First four keys are for movement. Order: Down, Up, Left, Right. First four keys are for movement. Order: Down, Up, Left, Right.
5th key is for interacting with counters. 5th key is for interacting with counters.
...@@ -61,7 +65,7 @@ class PlayerKeyset: ...@@ -61,7 +65,7 @@ class PlayerKeyset:
""" """
def __init__(self, player_name: str, keys: list[pygame.key]): def __init__(self, player_name: str, keys: list[pygame.key]):
"""Creates a player keyset which contains information about which keyboard keys control the player. """Creates a player key set which contains information about which keyboard keys control the player.
Movement keys in the following order: Down, Up, Left, Right Movement keys in the following order: Down, Up, Left, Right
Args: Args:
...@@ -79,7 +83,7 @@ class PlayerKeyset: ...@@ -79,7 +83,7 @@ class PlayerKeyset:
class PyGameGUI: class PyGameGUI:
"""Visualisation of the overcooked environmnent and reading keyboard inputs using pygame.""" """Visualisation of the overcooked environment and reading keyboard inputs using pygame."""
def __init__( def __init__(
self, self,
...@@ -87,6 +91,7 @@ class PyGameGUI: ...@@ -87,6 +91,7 @@ class PyGameGUI:
player_names: list[str], player_names: list[str],
player_keys: list[pygame.key], player_keys: list[pygame.key],
): ):
self.screen = None
self.FPS = 60 self.FPS = 60
self.simulator = simulator self.simulator = simulator
self.counter_size = self.simulator.env.counter_side_length self.counter_size = self.simulator.env.counter_side_length
...@@ -99,10 +104,10 @@ class PyGameGUI: ...@@ -99,10 +104,10 @@ class PyGameGUI:
self.player_keys = player_keys self.player_keys = player_keys
assert len(self.player_names) == len( assert len(self.player_names) == len(
self.player_keys self.player_keys
), "Number of players and keysets should match." ), "Number of players and key sets should match."
self.player_keysets: list[PlayerKeyset] = [ self.player_key_sets: list[PlayerKeySet] = [
PlayerKeyset(player_name, keys) PlayerKeySet(player_name, keys)
for player_name, keys in zip(self.player_names, self.player_keys) for player_name, keys in zip(self.player_names, self.player_keys)
] ]
...@@ -121,13 +126,13 @@ class PyGameGUI: ...@@ -121,13 +126,13 @@ class PyGameGUI:
an action is sent in this function. an action is sent in this function.
""" """
keys = pygame.key.get_pressed() keys = pygame.key.get_pressed()
for player_idx, keyset in enumerate(self.player_keysets): for player_idx, key_set in enumerate(self.player_key_sets):
relevant_keys = [keys[k] for k in keyset.player_keys] relevant_keys = [keys[k] for k in key_set.player_keys]
if any(relevant_keys[:-2]): if any(relevant_keys[:-2]):
move_vec = np.zeros(2) move_vec = np.zeros(2)
for idx, pressed in enumerate(relevant_keys[:-2]): for idx, pressed in enumerate(relevant_keys[:-2]):
if pressed: if pressed:
move_vec += keyset.move_vectors[idx] move_vec += key_set.move_vectors[idx]
if np.linalg.norm(move_vec) != 0: if np.linalg.norm(move_vec) != 0:
move_vec = move_vec / np.linalg.norm(move_vec) move_vec = move_vec / np.linalg.norm(move_vec)
...@@ -142,17 +147,17 @@ class PyGameGUI: ...@@ -142,17 +147,17 @@ class PyGameGUI:
Args: Args:
event: Pygame event for extracting the key action. event: Pygame event for extracting the key action.
""" """
for keyset in self.player_keysets: for key_set in self.player_key_sets:
if event.key == keyset.pickup_key and event.type == pygame.KEYDOWN: if event.key == key_set.pickup_key and event.type == pygame.KEYDOWN:
action = Action(keyset.name, "pickup", "pickup") action = Action(key_set.name, "pickup", "pickup")
self.send_action(action) self.send_action(action)
if event.key == keyset.interact_key: if event.key == key_set.interact_key:
if event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN:
action = Action(keyset.name, "interact", "keydown") action = Action(key_set.name, "interact", "keydown")
self.send_action(action) self.send_action(action)
elif event.type == pygame.KEYUP: elif event.type == pygame.KEYUP:
action = Action(keyset.name, "interact", "keyup") action = Action(key_set.name, "interact", "keyup")
self.send_action(action) self.send_action(action)
def draw_background(self): def draw_background(self):
...@@ -164,7 +169,7 @@ class PyGameGUI: ...@@ -164,7 +169,7 @@ class PyGameGUI:
pygame.draw.rect(self.screen, BACKGROUND_LINES_COLOR, rect, 1) pygame.draw.rect(self.screen, BACKGROUND_LINES_COLOR, rect, 1)
def draw_players(self, state): def draw_players(self, state):
"""Visualizes the players as circles with a triangle for the facing diretion. """Visualizes the players as circles with a triangle for the facing direction.
If the player holds something in their hands, it is displayed If the player holds something in their hands, it is displayed
Args: Args:
...@@ -212,7 +217,7 @@ class PyGameGUI: ...@@ -212,7 +217,7 @@ class PyGameGUI:
holding_item_pos = player.pos + (20 * player.facing_direction) holding_item_pos = player.pos + (20 * player.facing_direction)
self.draw_item(holding_item_pos, player.holding) self.draw_item(holding_item_pos, player.holding)
def draw_item(self, pos, item): def draw_item(self, pos, item: HoldableItem):
"""Visualisation of an item at the specified position. On a counter or in the hands of the player.""" """Visualisation of an item at the specified position. On a counter or in the hands of the player."""
if isinstance(item, Tomato): if isinstance(item, Tomato):
if item.finished: if item.finished:
...@@ -229,7 +234,7 @@ class PyGameGUI: ...@@ -229,7 +234,7 @@ class PyGameGUI:
if isinstance(item, Plate): if isinstance(item, Plate):
image = pygame.image.load( image = pygame.image.load(
"overcooked_simulator/pygame_gui/images/plate.png" self.images_path / "plate.png"
).convert_alpha() # or .convert_alpha() ).convert_alpha() # or .convert_alpha()
rect = image.get_rect() rect = image.get_rect()
rect.center = pos rect.center = pos
...@@ -266,7 +271,7 @@ class PyGameGUI: ...@@ -266,7 +271,7 @@ class PyGameGUI:
self.counter_size, self.counter_size,
self.counter_size, self.counter_size,
) )
pygame.draw.rect(self.screen, COUNTERCOLOR, counter_rect_outline) pygame.draw.rect(self.screen, COUNTER_COLOR, counter_rect_outline)
if isinstance(counter, CuttingBoard): if isinstance(counter, CuttingBoard):
board_size = 30 board_size = 30
...@@ -320,7 +325,13 @@ class PyGameGUI: ...@@ -320,7 +325,13 @@ class PyGameGUI:
pygame.draw.rect(self.screen, YELLOW, board_rect) pygame.draw.rect(self.screen, YELLOW, board_rect)
if counter.occupied_by is not None: 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): def draw_counters(self, state):
"""Visualizes the counters in the environment. """Visualizes the counters in the environment.
...@@ -346,7 +357,7 @@ class PyGameGUI: ...@@ -346,7 +357,7 @@ class PyGameGUI:
pygame.display.flip() pygame.display.flip()
def start_pygame(self): def start_pygame(self):
"""Starts pygame and the gui loop. Each frame the gamestate is visualized and keyboard inputs are read.""" """Starts pygame and the gui loop. Each frame the game state is visualized and keyboard inputs are read."""
pygame.init() pygame.init()
pygame.font.init() pygame.font.init()
......
...@@ -6,23 +6,24 @@ from overcooked_simulator.player import Player ...@@ -6,23 +6,24 @@ from overcooked_simulator.player import Player
class Simulator(Thread): class Simulator(Thread):
"""Simulator main class which runs manages the environment and player inputs and gamestate outputs. """Simulator main class which runs manages the environment and player inputs and game state outputs.
Main Simulator class which runs the game environment. Players can be registered in the game. Main Simulator class which runs the game environment. Players can be registered in the game.
The simulator is run as its own thread. The simulator is run as its own thread.
Typical usage example: Typical usage example:
```python
sim = Simulator() sim = Simulator()
sim.register_player(Player("p1", [x,y])) sim.register_player(Player("p1", [x,y]))
sim.start() sim.start()
```
""" """
def __init__(self, env_layout_path, frequency: int): def __init__(self, env_layout_path, frequency: int):
self.finished: bool = False self.finished: bool = False
self.step_frequency: int = frequency self.step_frequency: int = frequency
self.prefered_sleeptime_ns: float = 1e9 / self.step_frequency self.preferred_sleep_time_ns: float = 1e9 / self.step_frequency
self.env: Environment = Environment(env_layout_path) self.env: Environment = Environment(env_layout_path)
super().__init__() super().__init__()
...@@ -40,19 +41,19 @@ class Simulator(Thread): ...@@ -40,19 +41,19 @@ class Simulator(Thread):
self.env.perform_action(action) self.env.perform_action(action)
def get_state(self): def get_state(self):
"""Get the current gamestate as python objects. """Get the current game state as python objects.
Returns: Returns:
The current state of the game. Currently as dict with lists of environment objects. The current state of the game. Currently, as dict with lists of environment objects.
""" """
return self.env.get_state() return self.env.get_state()
def get_state_json(self): def get_state_json(self):
"""Get the current gamestate in json-like dict. """Get the current game state in json-like dict.
Returns: Returns:
The gamestate encoded in a json style nested dict. The gamest ate encoded in a json style nested dict.
""" """
return self.env.get_state_json() return self.env.get_state_json()
...@@ -86,7 +87,7 @@ class Simulator(Thread): ...@@ -86,7 +87,7 @@ class Simulator(Thread):
self.step() self.step()
step_duration = time.time_ns() - step_start step_duration = time.time_ns() - step_start
time_to_sleep_ns = self.prefered_sleeptime_ns - ( time_to_sleep_ns = self.preferred_sleep_time_ns - (
step_duration + overslept_in_ns step_duration + overslept_in_ns
) )
......
...@@ -36,7 +36,7 @@ def test_player_registration(): ...@@ -36,7 +36,7 @@ def test_player_registration():
assert len(sim.env.players) == 2, "Wrong number of players" assert len(sim.env.players) == 2, "Wrong number of players"
p3 = Player("player2", np.array([100, 100])) p3 = Player("player2", np.array([100, 100]))
sim.register_player(p2) # same player name sim.register_player(p3) # same player name
assert len(sim.env.players) == 2, "Wrong number of players" assert len(sim.env.players) == 2, "Wrong number of players"
sim.start() sim.start()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment