commit 712bef23379d895c5ca309a573095cbdba594542 Author: Nekkowe! Date: Sat Sep 30 16:53:32 2023 +0200 rendering & simulation steps diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..441378c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: main.py", + "type": "python", + "request": "launch", + //"program": "${file}", + "program": "main.py", + "console": "integratedTerminal", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..0857b92 --- /dev/null +++ b/main.py @@ -0,0 +1,29 @@ +from sim import Environment, CAMSReverseAndSidestepAgent, Direction +import time +import matplotlib.pyplot as plt +import numpy as np + +STEPS = 1000 +FRAME_DELAY = 1/60 + + +# Fixing random state for reproducibility +np.random.seed(12345) + +env = Environment((40, 30)) +first_agent = CAMSReverseAndSidestepAgent( + environment=env, + position=(20,15), + initial_direction=Direction.NORTH +) + +fig, ax = plt.subplots() +im = ax.imshow(env.render(), aspect="equal", origin="lower") +fig.show() + +for i in range(1, STEPS+1): + print(i) + plt.pause(FRAME_DELAY) + env.step() + im.set_data(env.render()) + fig.canvas.draw() \ No newline at end of file diff --git a/sim.py b/sim.py new file mode 100644 index 0000000..d78b618 --- /dev/null +++ b/sim.py @@ -0,0 +1,143 @@ +import numpy as np +from enum import Enum, auto +import matplotlib.pyplot as plt + +class Direction(Enum): + NORTH = (0, 1) + SOUTH = (0, -1) + WEST = (-1, 0) + EAST = (1, 0) + +class CellState(Enum): + OPEN = auto() + WALL = auto() + +class Colours(Enum): + WALL = [30,30,30] + STOCKED = [200,100,0] + DEPLETED = [0,50,50] + UNDEFINED = [255,0,255] + AGENT = [0,255,0] + +class Cell(): + def __init__(self, state, resources): + self.state = state + self.resources = resources + + def get_colour(self): + if self.state is CellState.WALL: + return Colours.WALL + elif self.state is CellState.OPEN: + if self.resources > 0: + return Colours.STOCKED + elif self.resources == 0: + return Colours.DEPLETED + else: + return Colours.UNDEFINED + else: + return Colours.UNDEFINED + +class Environment: + def __init__(self, shape): + self.shape = shape + self.agents = [] + + self.resource_map = np.round( + np.random.normal(loc=50, scale=10, size=shape) + ) + + cols, rows = shape + self.cells = [[] for _ in range(rows)] + for y in range(rows): + for x in range(cols): + if (x == 0 or y == 0 or x+1==cols or y+1==rows): + self.cells[y].append(Cell(CellState.WALL, 0)) + else: + self.cells[y].append(Cell(CellState.OPEN, self.resource_map[x,y])) + + def step(self): + for agent in self.agents: + agent.step() + + def render(self): + cols, rows = self.shape + pixel_data = [[] for _ in range(rows)] + + for y in range(self.shape[1]): + for x in range(self.shape[0]): + #print((x, y, self.cell((x,y)).state, self.cell((x,y)).get_colour().value)) + pixel_data[y].append(self.cell((x,y)).get_colour().value) + + for agent in self.agents: + x, y = agent.position + pixel_data[y][x] = Colours.AGENT.value + + return np.array(pixel_data) + + def cell(self, position): + x, y = tuple(position) + return self.cells[y][x] + + def is_wall(self, position): + x, y = tuple(position) + return self.cells[y][x].state == CellState.WALL + + def register_agent(self, agent): + self.agents.append(agent) + + def unregister_agent(self, agent): + self.agents.remove(agent) + +class Agent: + def __init__(self, environment, position): + self.position = np.array(position) + self.environment = environment + self.environment.register_agent(self) + + def step(self): + pass + + def move(self, direction, respect_walls=True): + new_position = self.position + direction.value + if respect_walls and self.environment.is_wall(new_position): + return False + else: + self.position = new_position + return True + + def die(self): + self.environment.unregister_agent(self) + +class DirectionalAgent(Agent): + def __init__(self, environment, position, initial_direction): + super().__init__(environment, position) + self.direction = initial_direction + + def get_cell_in_front(self): + position_in_front = self.position + self.direction.value + return self.environment.cell(position_in_front) + + def wall_in_front(self): + position_in_front = self.position + self.direction.value + return self.environment.is_wall(position_in_front) + + def turn_left(self): + raise NotImplementedError + + def turn_right(self): + raise NotImplementedError + + def reverse_direction(self): + new_vector = np.array(self.direction.value) * -1 + self.direction = Direction(tuple(new_vector)) + +class CAMSReverseAndSidestepAgent(DirectionalAgent): + def __init__(self, environment, position, initial_direction, required_resources = 100): + super().__init__(environment, position, initial_direction) + self.resources = 0 + self.required_resources = required_resources + + def step(self): + if self.wall_in_front(): + self.reverse_direction() + self.move(self.direction)