inteligentna_smieciarka/src/simulation.py
2023-03-27 11:15:11 +02:00

266 lines
8.8 KiB
Python

from pathlib import Path
from random import randint
import pygame as pg
from agent import Agent
ROAD_SPRITE = pg.Vector2(0, 0)
HOUSE_WITHOUT_TRASH_SPRITE = pg.Vector2(1, 0)
HOUSE_WITH_TRASH_SPRITE = pg.Vector2(1, 1)
HOUSE_SPRITES = {0: HOUSE_WITHOUT_TRASH_SPRITE,
1: HOUSE_WITH_TRASH_SPRITE}
TRUCK_SPRITE = pg.Vector2(2, 0)
PAPER_DUMP_SPRITE = pg.Vector2(3, 0)
PLASTIC_DUMP_SPRITE = pg.Vector2(3, 1)
GLASS_DUMP_SPRITE = pg.Vector2(3, 2)
MIXED_DUMP_SPRITE = pg.Vector2(3, 3)
DUMP_SPRITES = {'paper': PAPER_DUMP_SPRITE,
'glass': GLASS_DUMP_SPRITE,
'plastic': PLASTIC_DUMP_SPRITE,
'mixed': MIXED_DUMP_SPRITE}
TRASH_TYPES = ['paper', 'plastic', 'glass', 'mixed']
class Entity:
def __init__(self, state, position):
self.state = state
self.position = position
class TruckEntity(Entity):
def __init__(self, state, position):
super().__init__(state, position)
self.tile = TRUCK_SPRITE
self.entity_type = 'truck'
self.fullness = {'paper': 0,
'glass': 0,
'plastic': 0,
'mixed': 0}
def move(self, move_vector):
proposed_pos = self.position + move_vector
move_valid = True
if proposed_pos not in self.state.roads_pos \
and proposed_pos not in self.state.houses_pos \
and proposed_pos not in self.state.dumps_pos:
move_valid = False
if proposed_pos.x < 0 or proposed_pos.x >= self.state.world_limits.x:
move_valid = False
if proposed_pos.y < 0 or proposed_pos.y >= self.state.world_limits.y:
move_valid = False
if move_valid:
self.position = proposed_pos
class Bin:
def __init__(self, size, trash_type):
self.size = size
self.trash_type = trash_type
self.fullness = randint(0, 100)
def __str__(self):
return self.trash_type + " bin"
class HouseEntity(Entity):
def __init__(self, state, position):
super().__init__(state, position)
self.tile = HOUSE_SPRITES[0]
self.entity_type = 'house'
self.bins = [Bin(randint(1, 2), trash_type) for trash_type in TRASH_TYPES]
class DumpEntity(Entity):
def __init__(self, state, position, trash_type):
super().__init__(state, position)
self.tile = DUMP_SPRITES[trash_type]
self.entity_type = 'dump'
self.trash_type = trash_type
class SimulationState:
def __init__(self):
self.roads_pos = []
self.houses_pos = []
self.dumps_pos = []
self.entities = []
# stworzenie mapy i jednostek na podstawie pliku txt
map_path = Path("../res/map.txt")
with open(map_path, "r") as map_file:
map_data = map_file.readlines()
max_x = len(map_data[0].replace('\n', ''))
max_y = len(map_data)
for y in range(0, max_y):
for x in range(0, max_x):
tile = map_data[y].replace('\n', '')[x]
if tile == "O":
self.truck_origin = pg.Vector2(x, y)
self.roads_pos.append(pg.Vector2(x, y))
if tile == "R":
self.roads_pos.append(pg.Vector2(x, y))
if tile == "H":
self.houses_pos.append(pg.Vector2(x, y))
self.entities.append(HouseEntity(self, pg.Vector2(x, y)))
if tile == "M":
self.dumps_pos.append(pg.Vector2(x, y))
self.paper_dump_pos = pg.Vector2(x, y)
self.entities.append(DumpEntity(self, pg.Vector2(x, y), 'paper'))
if tile == "P":
self.dumps_pos.append(pg.Vector2(x, y))
self.plastic_dump_pos = pg.Vector2(x, y)
self.entities.append(DumpEntity(self, pg.Vector2(x, y), 'plastic'))
if tile == "S":
self.dumps_pos.append(pg.Vector2(x, y))
self.glass_dump_pos = pg.Vector2(x, y)
self.entities.append(DumpEntity(self, pg.Vector2(x, y), 'glass'))
if tile == "Z":
self.dumps_pos.append(pg.Vector2(x, y))
self.mixed_dump_pos = pg.Vector2(x, y)
self.entities.append(DumpEntity(self, pg.Vector2(x, y), 'mixed'))
self.entities.append(TruckEntity(self, self.truck_origin))
self.world_limits = pg.Vector2(max_x, max_y)
def update(self, move_agent):
for entity in self.entities:
if entity.entity_type == 'truck':
entity.move(move_agent)
class Layer:
def __init__(self, sim, texture_file):
self.sim = sim
self.texture_atlas = pg.image.load(texture_file)
def renderTile(self, surface, position, tile):
# pozycja na ekranie
sprite_pos = position.elementwise() * self.sim.cell_size
# tekstura
pos_in_atlas = tile.elementwise() * self.sim.cell_size
texture = pg.Rect(int(pos_in_atlas.x),
int(pos_in_atlas.y),
self.sim.cell_size.x,
self.sim.cell_size.y)
# render
surface.blit(self.texture_atlas, sprite_pos, texture)
def render(self, surface):
raise NotImplementedError()
class EntityLayer(Layer):
def __init__(self, sim, texture_file, simulation_state, entities):
super().__init__(sim, texture_file)
self.simulation_state = simulation_state
self.entities = entities
def render(self, surface):
for entity in self.entities:
self.renderTile(surface, entity.position, entity.tile)
class StructureLayer(Layer):
def __init__(self, sim, texture_file, simulation_state, structures_pos, tile):
super().__init__(sim, texture_file)
self.simulation_state = simulation_state
self.structures_pos = structures_pos
self.tile = tile
def render(self, surface):
for position in self.structures_pos:
self.renderTile(surface, position, self.tile)
class Interface:
def __init__(self):
pg.init()
# stan symulacji
self.state = SimulationState()
self.move_truck = pg.Vector2(0, 0)
# autonomiczny agent
self.agent = Agent(self)
# rendering
self.cell_size = pg.Vector2(64, 64)
texture_file = Path("../res/tiles.png")
self.layers = [StructureLayer(self, texture_file, self.state, self.state.roads_pos, ROAD_SPRITE),
EntityLayer(self, texture_file, self.state, self.state.entities)]
# okno
pg.display.set_caption("Inteligentna śmieciarka")
window_size = self.state.world_limits.elementwise() * self.cell_size
self.window = pg.display.set_mode((int(window_size.x), int(window_size.y)))
self.state.world_limits.elementwise() * self.cell_size
# dla pętli
self.clock = pg.time.Clock()
self.run_simulation = True
self.debug_mode = False
def processUserInput(self):
self.move_truck = pg.Vector2(0, 0)
for event in pg.event.get():
if event.type == pg.QUIT:
self.run_simulation = False
break
elif event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.run_simulation = False
break
if event.key == pg.K_BACKQUOTE:
self.debug_mode = not self.debug_mode
if self.debug_mode:
if event.key == pg.K_RIGHT:
self.move_truck.x = 1
if event.key == pg.K_LEFT:
self.move_truck.x = -1
if event.key == pg.K_DOWN:
self.move_truck.y = 1
if event.key == pg.K_UP:
self.move_truck.y = -1
if event.key == pg.K_d:
self.agent.discover()
def processAgentInput(self):
self.move_truck = self.agent.decide_move()
def update(self):
self.state.update(self.move_truck)
self.agent.update()
def render(self):
if not self.debug_mode:
self.window.fill((89, 183, 53))
else:
self.window.fill((180, 32, 42))
for layer in self.layers:
layer.render(self.window)
pg.display.update()
def loop(self):
while self.run_simulation:
self.processUserInput()
if not self.debug_mode:
self.processAgentInput()
self.update()
self.render()
self.clock.tick(24)
pg.quit()