Echoes of Eden is a civilization simulation. The simulation starts with a small, stable village in the middle of a forest. Each person in the village has a set of tasks they can do, including gathering resources, building structures, having a family, and exploring. They are also aware of various personal metrics such as health, hunger, and family status. Villagers choose what tasks to do based on these personal metrics, and by using various epsilon-greedy algorithms.
The simulation uses a 2D array to map the village and keep track of where each villager is. As villagers move around the map, they can remember which structures they have passed. They use this memory to know where to find things later and determine what tasks need to be done. Villagers also have an inventory that enforces a limit on how many resources they can carry at a time.
After a set amount of iterations (mimicking some amount of years), the simulation will finish and print graphs and statistics about the village. The purpose of these is for use in analyzing how successful the simulation's decision-making algorithms are at keeping the village alive and thriving.
This program uses poetry to manage dependencies. Before running the program, install poetry. Then install the program's
dependencies using poetry shell and poetry install.
All settings used throughout the program are stored in example.dev_settings.yaml (the settings used during
development) and example.prod_settings.yaml (the settings used during production). Before running the program, change
these file names to dev_settings.yaml and prod_settings.yaml.
Then, to run the simulation, run PYTHONPATH=$(pwd) python3 src/main.py. The simulation will take some time to complete.
You will get the simulation results plotted as output of the program.
A note on run time: the simulation spends a lot of time computing each villager's vision as they move. One way to run
the program faster is to change how often villagers look around. This can be done in mover.py, in the towards()
method. The modulo in towards() (see below) is telling villagers to look around every four steps. You can increase
this number if you want villagers to look around less often. (Caution: if villagers look around too infrequently, they
may not remember the structures they passed at all.)
if step % 4 == 0:
self._memories.combine(self._vision.look_around())
The project directories are organized like so:
src
logger.py: logs messages to the console about what is happening in the simulation as it runsmain.py: runs the simulationsettings.py: loads settings from the settings.yaml file into a global 'settings' variable- simulation
simulation.py: the actual simulation: stores people, iterations, and grid, and contains methods for creating disasters and running the simulation- grid
disjoint_set.py: used to group work structures to ensure they have the same yield function, i.e., groves of trees have the same wood yield.grid.py: a 2D array for mapping the simulation spatially, including locations of structures and peoplegrid_disaster_generator.py: generates grid-related disasters like mine disaster, stolen resources, or forest firegrid_generator.py: generates a unique grid each time, with a small starting village and surrounding forestlocation.py: handles logic about a specific location, such as travel time to another place, or determining what's nearbystructure_generator.py: generates structures within the gridtemperature.py: manages the temperature for every day of the year; temperature is taken from a normal distribution, using a mean from a sin wave that spans the year- structure
structure.py: an abstract class, handles location/placement of structure on gridstructure_factory.py: manages the creation of structuresstructure_type.py: an enum used in the code to identify structure types- store
barn.py: storage for food, wood and stonehome.py: storage for a person's foodstore.py: an abstract class, handles the implementation for all storage types
- work
farm.py: yields foodmine.py: yields stonetree.py: yields woodwork.py: an abstract class, handles the implementation for all work types- construction
construction.py: an abstract class, handles the implementation for all construction typesconstruction_barn.py: when completed abarnwill take its placeconstruction_farm.py: when completed afarmwill take its placeconstruction_home.py: when completed ahomewill take its placeconstruction_mine.py: when completed aminewill take its place
- people
home_manager.py: handles all logic for people swapping homespeople.py: handles all logic for the general population or peoplepeople_disaster_generator.py: creates disasters that directly involve people: divorce, sickness, craving, death, forget tasks, sleepwalk, baby boompeople_generator.py: creates each person, assigning an age, name, and location spawned.- person
backpack.py: handles all logic for a person's inventorymemories.py: handles all logic for a person's memoryperson.py: handles all logic for a person's actionsthinker.py: handles all logic for adding tasks and adjusting task priorities for a person- movement
move_result.py: defines class MoveResult for storing if a call to move_to succeeded and resulted in a structuremover.py: handles all lower-level logic for moving a person around the gridnavigator.py: handles all higher-level logic for moving a person around the gridvision.py: handles all logic for what a person can see around them as they move around the grid
- scheduler
scheduler.py: handles all logic for which task should be executed at what time based on priority values- task
eat.py: represents task of eatingexplore.py: represents task of exploring the gridfind_home.py: represents task of finding a homefind_spouse: represents task of finding a spousetask.py: an abstract class, with abstract methods for each task type to overridetask_factory.py: manages the creation of taskstask_type.py: an enum used in the code to identify task typestransport.py: represents task of transporting items to a location- construction
build.py: an abstract class, handles logic of building at an already declared construction site for all structure typesbuild_barn.py: represents task of building a barn at the already declared construction sitebuild_farm.py: represents task of building a farm at the already declared construction sitebuild_home.py: represents task of building a home at the already declared construction sitebuild_mine.py: represents task of building a mine at the already declared construction site
- start_construction
start_barn_construction.py: represents task of starting construction site for barnstart_construction.py: an abstract class, handles logic of starting construction for all structure typesstart_farm_construction.py: represents task of starting construction site for farmstart_home_construction.py: represents task of starting construction site for homestart_mine_construction.py: represents task of starting construction site for mine
- work
chop_tree.py: represents task of chopping treework.py: an abstract class, handles logic of all types of work actionswork_farm.py: represents task of working farmwork_mine.py: represents task of working mine
- visualization
visualizer.py: manages and displays town grid and simulation stats using grid and state plotters- plotter
grid_plotter.py: generates and visualizes random grid snapshots with terrain types, displaying them as a slideshowstate_plotter.py: aggregates and visualizes simulation states (e.g., grid, people, resources) over time through line plots.
- state
grid_disaster_state.py: Initialize grid disaster attributes based on countsgrid_state.py: establishes counts of grid values (barns, homes, mines, constructions, ...)people_disaster_state.py: Initialize people disaster attributes based on countspeople_state.py: establishes averages for people values (hunger, health, etc.)resource_state.py: calculates/stores resource data for barnsstate.py: generating and formatting state datatask_state.py: calculates/stores task data for
- A more comprehensive tool that checks for errors, enforces a coding standard, and looks for code smells.
poetry run pylint src/**/*.py
- A static type checker for Python that can help catch type errors.
poetry run mypy src/**/*.py
- An opinionated code formatter that enforces a consistent style.
poetry run black src/**/*.pypoetry run isort src/**/*.pypoetry run autoflake --in-place --remove-unused-variables src/**/*.py
This project uses the MIT license. (See the 'LICENSE' file in this directory.)