Skip to content
Snippets Groups Projects
overcooked_environment.py 5.44 KiB
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from overcooked_simulator.player import Player
from pathlib import Path
import numpy as np


class Counter:
    """Simple class for a counter at a specified position (center of counter). Can hold things on top.
    """
    def __init__(self, pos: np.array):
        self.pos = pos
        self.occupied_by = None

    def __repr__(self):
        return f"Counter(pos:{str(self.pos)},holds:{self.occupied_by})"


class Action:
    """Action class, specifies player, action type and action itself.
    """
    def __init__(self, player, act_type, action):
        self.player = player
        self.act_type = act_type
        assert self.act_type in ["movement", "pickup", "interact"], "Unknown action type"
        self.action = action


class Environment:
    """Environment class which handles the game logic for the overcooked-inspired environment.

        Handles player movement, collision-detection, counters, cooking processes, recipes, incoming orders, time.
        """
    def __init__(self):
        self.players = {}
        self.counter_side_length = 40
        self.layout_path = Path("overcooked_simulator/layouts/basic.layout")
        self.counters = self.create_counters(self.layout_path)
        self.score = 0

    def create_counters(self, layout_file: Path):
        """Creates layout of kitchen counters in the environment based on layout file.
        Counters are arranged in a fixed size grid starting at [0,0]. The center of the first counter is at
        [counter_size/2, counter_size/2], counters are directly next to each other (of no empty space is specified
        in layout).

        Args:
            layout_file: Path to the layout file.
        """
        current_y = self.counter_side_length / 2
        counters = []

        with open(layout_file, "r") as layout_file:
            lines = layout_file.readlines()
        for line in lines:
            line = line.replace("\n", "").replace(" ", "")  # remove newline char
            current_x = self.counter_side_length / 2
            for character in line:
                character = character.capitalize()
                if character == "C":
                    counter = Counter(np.array([current_x, current_y]))
                    counters.append(counter)
                    current_x += self.counter_side_length
                elif character == "E":
                    pass

            current_y += self.counter_side_length
        return counters

    def perform_action(self, action: Action):
        """Performs an action of a player in the environment. Maps different types of action inputs to the
        correct execution of the players.
        Possible action types are movement, pickup and interact actions.

        Args:
            action: The action to be performed
        """
        assert action.player in self.players.keys(), "Unknown player."

        player = self.players[action.player]
        if action.act_type == "movement":
            self.perform_movement(player, action.action)
        elif action.act_type == "pickup":
            self.perform_pickup(player)
        elif action.act_type == "interact":
            self.perform_interact(player)

    def get_closest_counter(self, player: Player):
        """Determines the closest counter in the environment of a player.

        Args:
            player: The player for which to find the closest counter

        Returns: The closest counter for the given player.
        """
        pass

    def perform_pickup(self, player: Player):
        """Performs the game action corresponding to picking up an item

        Args:
            player: The player which performs the pickup action.

        Returns: TODO?

        """
        pass

    def perform_interact(self, player: Player):
        """Performs the game action corresponding to interacting with a counter or other object.

        Args:
            player: The player which performs the interaction.

        Returns: TODO?
        """
        pass

    def perform_movement(self, player: Player, action):
        """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.

        Args:
            player: The player to move.
            action: The Action which now contains a unit-2d-vector of the movement direction
        """
        pass

    def detect_collision(self, player: Player):
        """Detect collisions between the player and other players or counters.

        Args:
            player: The player for which to check collisions.

        Returns: True if the player is intersecting with any object in the environment.

        """
        pass

    def step(self):
        """Performs a step of the environment. Affects time based events such as cooking or cutting things, orders
        and timelimits.
        """
        pass

    def get_state(self):
        """Get the current state of the game environment. The state here is accessible by the current python objects.

        Returns: Dict of lists of the current relevant game objects.

        """
        return {"players": self.players,
                "counters": self.counters,
                "score": self.score}

    def get_state_json(self):
        """Get the current state of the game environment as a json-like nested dictionary.

        Returns: Json-like string of the current game state.

        """
        pass