diff --git a/.gitignore b/.gitignore index 7e99e36..e62d64d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -*.pyc \ No newline at end of file +*.pyc +*.json +*.txt \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a3c0b4a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Viron"] + path = Viron + url = https://www.github.com/Preponderous-Software/Viron diff --git a/Viron b/Viron new file mode 160000 index 0000000..d9809d5 --- /dev/null +++ b/Viron @@ -0,0 +1 @@ +Subproject commit d9809d5632aa341f4f340f741158465022c2ff87 diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..92f6afc --- /dev/null +++ b/__init__.py @@ -0,0 +1,15 @@ +import os +import importlib + +package_dir = os.path.dirname(__file__) + +for root, dirs, files in os.walk(package_dir): + for file in files: + if file.endswith('.py') and file != '__init__.py': + rel_dir = os.path.relpath(root, package_dir) + module_name = file[:-3] + if rel_dir == '.': + import_path = f".{module_name}" + else: + import_path = "." + ".".join(rel_dir.split(os.sep)) + f".{module_name}" + importlib.import_module(import_path, package=__package__) \ No newline at end of file diff --git a/create_environments.bat b/create_environments.bat new file mode 100644 index 0000000..2e95795 --- /dev/null +++ b/create_environments.bat @@ -0,0 +1,42 @@ +# Script to run `python main.py` a number of times with incrementing sizes + +echo off + +setlocal enabledelayedexpansion +set "script=main.py" +REM Check if a max size argument was provided, otherwise use default +if "%~1"=="" ( + echo No max size provided, using default value. + set "max_size=100" +) else ( + echo Max size provided: %~1 + set "max_size=%~1" +) +REM Delete environments.json before starting +echo Deleting environments.json if it exists... +if exist environments.json ( + echo environments.json found, deleting... + del environments.json +) else ( + echo environments.json not found, nothing to delete. +) + +set "size_of_next_env=1" +set "increment=1" +set "output_file=output.txt" +set "python_executable=python" +set "log_file=log.txt" +set "error_log_file=error_log.txt" + +:loop +if !size_of_next_env! leq !max_size! ( + @echo Starting environment with size !size_of_next_env! + %python_executable% %script% !size_of_next_env! --exit-after-create >> %output_file% 2>> %error_log_file% + if errorlevel 1 ( + echo Error occurred while running with size !size_of_next_env!. + ) else ( + echo Environment created successfully with size !size_of_next_env!. + ) + set /a size_of_next_env+=%increment% + goto loop +) \ No newline at end of file diff --git a/down.bat b/down.bat new file mode 100644 index 0000000..ba19c87 --- /dev/null +++ b/down.bat @@ -0,0 +1,6 @@ +# This script is used to stop the Docker containers defined in the Docker Compose file. + +docker compose -f .\viron\compose.yml down --remove-orphans --volumes +REM The --remove-orphans flag removes containers for services not defined in the Compose file. +REM The --volumes flag removes the volumes associated with the containers, ensuring a clean shutdown. +REM This ensures that all resources are cleaned up when the containers are stopped. diff --git a/entity.py b/entity.py deleted file mode 100644 index 4f2c81d..0000000 --- a/entity.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2022 Preponderous Software -# MIT License -import datetime -import uuid - - -# @author Daniel McCoy Stephenson -# @since July 1st, 2022 -class Entity(object): - - def __init__(self, name, energy, living, edibleEntityTypes): - self.id = uuid.uuid4() - self.name = name - self.energy = energy - self.living = living - self.edibleEntityTypes = edibleEntityTypes - self.targetEnergy = energy - self.creationDate = datetime.datetime.now() - self.environmentID = -1 - self.gridID = -1 - self.locationID = -1 - - def getID(self): - return self.id - - def getName(self): - return self.name - - def getEnvironmentID(self): - return self.environmentID - - def getCreationDate(self): - return self.creationDate - - def getEnvironmentID(self): - return self.environmentID - - def getGridID(self): - return self.gridID - - def getLocationID(self): - return self.locationID - - def setID(self, id): - self.id = id - - def setName(self, name): - self.name = name - - def setEnvironmentID(self, environmentID): - self.environmentID = environmentID - - def setCreationDate(self, creationDate): - self.creationDate = creationDate - - def setGridID(self, gridID): - self.gridID = gridID - - def setLocationID(self, locationID): - self.locationID = locationID - - def printInfo(self): - print("--------------") - print(self.name) - print("--------------") - print("ID: ", self.getID()) - print("Creation Date: ", self.getCreationDate()) - print("Environment ID: ", self.getEnvironmentID()) - print("Grid ID: ", self.getGridID()) - print("Location ID: ", self.getLocationID()) - print("\n") - - def getEnergy(self): - return self.energy - - def addEnergy(self, amount): - self.energy += amount - - def removeEnergy(self, amount): - self.energy -= amount - - def needsEnergy(self): - return self.energy < self.targetEnergy - - def canEat(self, entity): - for entityType in self.edibleEntityTypes: - if type(entity) is entityType: - return True - return False - - def isLiving(self): - return self.living \ No newline at end of file diff --git a/environment.py b/environment.py deleted file mode 100644 index cfd4920..0000000 --- a/environment.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2022 Preponderous Software -# MIT License -import datetime -import uuid -from entity import Entity -from grid import Grid - - -# @author Daniel McCoy Stephenson -# @since July 1st, 2022 -class Environment(object): - - def __init__(self, name, size): - self.id = uuid.uuid4() - self.name = name - self.grid = Grid(size, size) - self.creationDate = datetime.datetime.now() - - def getID(self): - return self.id - - def getName(self): - return self.name - - def getCreationDate(self): - return self.creationDate - - def getGrid(self): - return self.grid - - def setID(self, id): - self.id = id - - def setName(self, name): - self.name = name - - def setGrid(self, grid): - self.grid = grid - - def addEntity(self, entity: Entity): - entity.setEnvironmentID(self.getID()) - self.grid.addEntity(entity) - - def addEntityToLocation(self, entity: Entity, location): - entity.setEnvironmentID(self.getID()) - self.grid.addEntityToLocation(entity, location) - - def removeEntity(self, entity: Entity): - self.grid.removeEntity(entity) - - def isEntityPresent(self, entity: Entity): - return self.grid.isEntityPresent(entity) - - def getNumEntities(self): - return self.getGrid().getNumEntities() - - def printInfo(self): - print("--------------") - print(self.name) - print("--------------") - print("Num entities: ", self.getNumEntities()) - print("Num locations: ", self.getGrid().getSize()) - print("Creation Date: ", self.getCreationDate()) - print("ID: ", self.getID()) - print("Grid ID: ", self.getGrid().getID()) - print("\n") \ No newline at end of file diff --git a/grid.py b/grid.py deleted file mode 100644 index efa5bc4..0000000 --- a/grid.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2022 Preponderous Software -# MIT License - - -# @author Daniel McCoy Stephenson -# @since July 1st, 2022 -import random -import uuid - -from entity import Entity -from location import Location - - -class Grid(object): - - def __init__(self, columns, rows): - self.id = uuid.uuid4() - self.columns = columns - self.rows = rows - self.locations = [] - self.generateLocations() - - def getID(self): - return self.id - - def getColumns(self): - return self.columns - - def getRows(self): - return self.rows - - def getLocations(self): - return self.locations - - def getFirstLocation(self): - return self.locations[0] - - def getSize(self): - return len(self.locations) - - def getLocations(self): - return self.locations - - def getNumEntities(self): - count = 0 - for location in self.locations: - count += location.getNumEntities() - return count - - def setID(self, id): - self.id = id - - def setColumns(self, columns): - self.columns = columns - - def setRows(self, rows): - self.rows = rows - - def setLocations(self, locations): - self.locations = locations - - def addLocation(self, location: Location): - self.locationss.append(location) - - def removeLocation(self, location: Location): - self.locations.remove(location) - - def addEntity(self, entity: Entity): - entity.setGridID(self.getID()) - self.getRandomLocation().addEntity(entity) - - def addEntityToLocation(self, entity: Entity, location): - entity.setGridID(self.getID) - self.getLocation(location.getID()).addEntity(entity) - - def removeEntity(self, entity: Entity): - for location in self.grid.getLocations(): - if location.isEntityPresent(entity): - location.removeEntity(entity) - return - - def isEntityPresent(self, entity: Entity): - for location in self.grid.getLocations(): - if location.isEntityPresent(entity): - return True - - def generateLocations(self): - for x in range(self.getColumns()): - for y in range(self.getRows()): - location = Location(x, y) - self.locations.append(location) - - def getLocation(self, id): - for location in self.locations: - if location.getID() == id: - return location - return -1 - - def getRandomLocation(self): - index = random.randrange(0, len(self.locations)) - return self.locations[index] - - def getLocationByCoordinates(self, x, y): - for location in self.locations: - if location.getX() == x and location.getY() == y: - return location - return -1 - - def getUp(self, location: Location): - return self.getLocationByCoordinates(location.getX(), location.getY() - 1) - - def getRight(self, location: Location): - return self.getLocationByCoordinates(location.getX() + 1, location.getY()) - - def getDown(self, location: Location): - return self.getLocationByCoordinates(location.getX(), location.getY() + 1) - - def getLeft(self, location: Location): - return self.getLocationByCoordinates(location.getX() - 1, location.getY()) diff --git a/location.py b/location.py deleted file mode 100644 index c9544fc..0000000 --- a/location.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2022 Preponderous Software -# MIT License - - -# @author Daniel McCoy Stephenson -# @since July 1st, 2022 -import uuid - -from entity import Entity - - -class Location(object): - - def __init__(self, x, y): - self.id = uuid.uuid4() - self.x = x - self.y = y - self.entities = [] - - def getID(self): - return self.id - - def getX(self): - return self.x - - def getY(self): - return self.y - - def getNumEntities(self): - return len(self.entities) - - def addEntity(self, entity: Entity): - if not self.isEntityPresent(entity): - self.entities.append(entity) - entity.setLocationID(self.getID()) - else: - print("Warning: An entity was already present when attempting to add it to a location.") - - def removeEntity(self, entity: Entity): - if self.isEntityPresent(entity): - self.entities.remove(entity) - else: - print("Warning: An entity was not present when attempting to remove it from a location.") - - def isEntityPresent(self, entity: Entity): - return entity in self.entities - - def getEntities(self): - return self.entities \ No newline at end of file diff --git a/main.py b/main.py index f716caf..5814952 100644 --- a/main.py +++ b/main.py @@ -1,35 +1,112 @@ from operator import truediv import random import pygame -from environment import Environment +from Viron.src.main.python.preponderous.viron.models.location import Location +from Viron.src.main.python.preponderous.viron.services.environmentService import EnvironmentService +from Viron.src.main.python.preponderous.viron.services.locationService import LocationService from graphik import Graphik +import os +import json +import sys +import time black = (0,0,0) white = (255,255,255) -displayWidth = 600 -displayHeight = 600 +displayWidth = 800 +displayHeight = 800 -gridSize = 50 +def log(message): + print(message) -def drawEnvironment(environment, graphik, locationWidth, locationHeight): - for location in environment.getGrid().getLocations(): +numGrids = 1 +if len(sys.argv) > 1: + try: + gridSize = int(sys.argv[1]) + except ValueError: + log("Invalid grid size argument, using default of 50.") + gridSize = 50 +else: + gridSize = 50 + +url = "http://localhost" +port = 9999 + +locationService = LocationService(url, port) +environmentService = EnvironmentService(url, port) +exit_after_create = False +if len(sys.argv) > 2 and sys.argv[2] == "--exit-after-create": + exit_after_create = True +def drawEnvironment(locations, graphik, locationWidth, locationHeight): + for location in locations: + location = Location(location_id=location['locationId'], x=location['x'], y=location['y']) red = random.randrange(50, 200) green = random.randrange(50, 200) blue = random.randrange(50, 200) - graphik.drawRectangle(location.getX() * locationWidth, location.getY() * locationHeight, locationWidth, locationHeight, (red,green,blue)) + graphik.drawRectangle(location.get_x() * locationWidth, location.get_y() * locationHeight, locationWidth, locationHeight, (red,green,blue)) def main(): pygame.init() gameDisplay = pygame.display.set_mode((displayWidth, displayHeight)) graphik = Graphik(gameDisplay) pygame.display.set_caption("Visualizing Environment With Random Colors") + + env_file = "environments.json" + environments = {} + + # Load existing environments if file exists + if os.path.exists(env_file): + log("Environments file exists, loading...") + with open(env_file, "r") as f: + environments = json.load(f) - environment = Environment("Test", gridSize) + # Create a unique key for the environment based on grid size and numGrids + env_key = f"{numGrids}x{gridSize}" + + if env_key in environments: + graphik.drawText("Loading existing environment, please wait...", displayWidth/2, displayHeight/2, 20, "white") + env_id = environments[env_key]["environment_id"] + try: + environment = environmentService.get_environment_by_id(env_id) + log(f"Loaded existing environment with id {env_id} and size {gridSize}x{gridSize} with {numGrids} grid(s).") + except Exception as e: + log(f"Error loading existing environment: {e}") + graphik.drawText("Error loading environment, please check logs.", displayWidth/2, displayHeight/2 + 30, 20, "red") + pygame.display.update() + time.sleep(2) + pygame.quit() + return + else: + graphik.drawText("Creating environment, please wait...", 400, 400, 20,"white") + pygame.display.update() + log("Creating environment with " + str(numGrids) + " grid(s) of size " + str(gridSize) + "x" + str(gridSize)) + start_time = time.time() + environment = environmentService.create_environment("Test", numGrids, gridSize) + end_time = time.time() + environments[env_key] = { + "environment_id": environment.getEnvironmentId(), + "grid_size": gridSize, + "num_grids": numGrids, + "creation_time_seconds": end_time - start_time + } + with open(env_file, "w") as f: + json.dump(environments, f, indent=2) + log(f"Created new environment with id {environment.getEnvironmentId()} in {end_time - start_time:.2f} seconds.") + + if exit_after_create: + log("Exiting after environment creation.") + locations = locationService.get_locations_in_environment(environment.getEnvironmentId()) + drawEnvironment(locations, graphik, displayWidth/gridSize, displayHeight/gridSize) + pygame.display.update() + time.sleep(2) + pygame.quit() + return - locationWidth = displayWidth/environment.getGrid().getRows() - locationHeight = displayHeight/environment.getGrid().getColumns() + locationWidth = displayWidth/gridSize + locationHeight = displayHeight/gridSize + + locationsCache = {} running = True @@ -38,9 +115,13 @@ def main(): if event.type == pygame.QUIT: pygame.quit() quit() + + if locationsCache == {}: + log("Fetching locations from service...") + locationsCache = locationService.get_locations_in_environment(environment.getEnvironmentId()) gameDisplay.fill(white) - drawEnvironment(environment, graphik, locationWidth, locationHeight) + drawEnvironment(locationsCache, graphik, locationWidth, locationHeight) pygame.display.update() main() \ No newline at end of file diff --git a/up.bat b/up.bat new file mode 100644 index 0000000..3c3ddd2 --- /dev/null +++ b/up.bat @@ -0,0 +1,5 @@ +# This script is used to start the Docker containers defined in the Docker Compose file. + +docker compose -f .\viron\compose.yml up -d --build +REM The -d flag runs the containers in detached mode, allowing them to run in the background. +REM The --build flag forces a rebuild of the images before starting the containers.