A scalable, modular ECS-based world simulation framework โ people, cities, relationships, and more
Project description
๐ pykworldsim
A scalable, modular Entity-Component-System (ECS) world simulation framework.
Simulate people, relationships, cities, jobs, goals, and events โ or anything you design.
โจ Features
| Feature | Description |
|---|---|
| โก Pure ECS | Entities, components, and systems cleanly separated |
| ๐ค Domain model | Built-in Person, Location, Job, Goal, Relationship, Event components |
| ๐ Deterministic | Seed-based reproducibility across runs |
| โฏ๏ธ Pausable loop | Pause / resume the simulation mid-run |
| ๐พ Save / Load | Full JSON world state serialisation |
| ๐ Plugin system | Register custom systems dynamically by class or import path |
| โ๏ธ YAML/JSON config | Define entire worlds declaratively |
| ๐ Rich CLI | pykworldsim run config.yaml with coloured output |
| ๐งช Fully tested | 60+ pytest test cases covering all layers |
| ๐ฆ pip-installable | Standard pyproject.toml, PEP 561 typed |
๐ฆ Installation
# From PyPI
pip install pykworldsim
# From source (with dev extras)
git clone https://github.com/ka1rav6/pykworldsim
cd pykworldsim
pip install -e ".[dev]"
๐ Quick Start
Physics simulation
from pykworldsim import World, Simulation, Position, Velocity, MovementSystem
from pykworldsim.core.systems.physics import PhysicsSystem
world = World(name="demo")
world.register_system(PhysicsSystem(gravity=9.81, bounds=(0, 0, 100, 100)))
world.register_system(MovementSystem())
e = world.create_entity()
world.add_component(e, Position(x=10.0, y=0.0))
world.add_component(e, Velocity(dx=2.0, dy=0.0))
sim = Simulation(world, seed=42)
sim.run(steps=100, dt=0.1)
pos = world.get_component(e, Position)
print(f"Final: ({pos.x:.2f}, {pos.y:.2f})")
Social simulation
from pykworldsim import (
World, Simulation,
Person, Job, Goal, Relationship,
SocialSystem, EventSystem,
)
from pykworldsim.core.components.event import Event
world = World(name="society")
world.register_system(SocialSystem())
world.register_system(EventSystem())
alice = world.create_entity()
world.add_component(alice, Person(name="Alice", age=28.0, happiness=65.0))
world.add_component(alice, Job(title="Engineer", salary=8000.0, satisfaction=75.0))
world.add_component(alice, Goal(description="Get promoted", priority=9.0))
# Schedule a birthday event at tick 5
ev = world.create_entity()
world.add_component(ev, Person(name="_holder"))
world.add_component(ev, Event(name="Birthday", event_type="social", tick_scheduled=5))
sim = Simulation(world, seed=2024)
sim.run(steps=20, dt=1.0)
person = world.get_component(alice, Person)
print(f"Alice: age={person.age:.1f}, happiness={person.happiness:.1f}")
Original repo API (backward compatible)
from pykworldsim import World
from pykworldsim import Person
# Original-style usage still works
world = World()
alice = world.create_entity()
world.add_component(alice, Person(name="Alice"))
world.Simulate() # alias for world.update()
report = world.generate_report()
print(report)
๐ฅ๏ธ CLI Usage
# Run a simulation from a YAML config
pykworldsim run examples/config.yaml
# Enable debug logging and save final state
pykworldsim run examples/config.yaml --verbose --save output.json
# Inspect a saved world
pykworldsim inspect output.json
# Print full JSON report
pykworldsim run examples/config.yaml --report
# Load a custom plugin system
pykworldsim run config.yaml --plugin mypackage.systems.MySystem
# List registered plugins
pykworldsim plugins
โ๏ธ YAML / JSON Config Format
name: my-world
log_level: INFO
simulation:
steps: 100
dt: 0.1
seed: 42
systems:
- type: MovementSystem
- type: SocialSystem
params:
age_rate: 0.05
energy_decay: 0.3
entities:
- position: {x: 0.0, y: 0.0}
velocity: {dx: 5.0, dy: 2.0}
- person: {name: Alice, age: 28.0, happiness: 65.0}
job: {title: Engineer, salary: 8000.0, satisfaction: 75.0}
goal: {description: "Get promoted", priority: 9.0}
๐ Plugin System
Register a custom system at runtime โ no subclassing of a framework class needed, just subclass BaseSystem:
from pykworldsim.core.systems.base_system import BaseSystem
from pykworldsim.core.components.position import Position
from pykworldsim.plugins import PluginRegistry
class BoundaryWrapSystem(BaseSystem):
def __init__(self, width=100.0, height=100.0):
super().__init__()
self.width = width
self.height = height
def update(self, dt: float) -> None:
for entity, (pos,) in self.world.get_components(Position):
pos.x %= self.width
pos.y %= self.height
# Register by class
PluginRegistry.register("BoundaryWrapSystem", BoundaryWrapSystem)
# Or register by import path (useful for CLI --plugin flag)
PluginRegistry.register_from_path("mypackage.systems.BoundaryWrapSystem")
๐พ Save & Load
# Save world state
world.save("checkpoint.json")
# Restore world state
from pykworldsim import World
world = World.load("checkpoint.json")
๐๏ธ Architecture
pykworldsim/
โโโ core/
โ โโโ entity.py Thread-safe entity ID allocation
โ โโโ world.py ECS container: entities + components + systems
โ โโโ simulation.py Deterministic, pausable simulation loop
โ โโโ components/
โ โ โโโ position.py 2-D spatial coordinate
โ โ โโโ velocity.py 2-D velocity vector
โ โ โโโ person.py Individual agent (age, health, happiness, energy)
โ โ โโโ location.py Named place / city
โ โ โโโ relationship.py Directed bond between entities
โ โ โโโ job.py Occupation / role
โ โ โโโ goal.py Motivation / objective
โ โ โโโ event.py Scheduled time-based occurrence
โ โโโ systems/
โ โ โโโ base_system.py Abstract base class
โ โ โโโ movement.py pos += vel ร dt
โ โ โโโ physics.py Gravity + boundary bouncing
โ โ โโโ social.py Ageing, energy, happiness, goal progress
โ โ โโโ event.py Event scheduling and dispatch
โ โโโ config/
โ โโโ loader.py YAML / JSON โ World + Simulation
โโโ plugins/
โ โโโ registry.py Dynamic system registration
โโโ utils/
โ โโโ logging.py Logging configuration helper
โโโ cli.py Typer CLI with Rich output
ECS pattern:
| Concept | Role |
|---|---|
| Entity | Opaque integer ID โ carries no data |
| Component | Plain dataclass โ data only, no logic |
| System | Logic only โ queries entities by component, mutates them |
๐งช Running Tests
pip install -e ".[dev]"
pytest # all tests
pytest --cov=pykworldsim # with coverage report
pytest tests/test_simulation.py -v # single module
pytest -k "test_physics" # filter by name
Example usage
from pykworldsim.core.world import World
from pykworldsim.core.simulation import Simulation
from pykworldsim.systems.physics import MovementSystem
from pykworldsim.core.entity import Entity
# Create world
world = World(size=100)
# Create entity
player = Entity()
player.add_component("position", {"x": 0, "y": 0})
player.add_component("velocity", {"dx": 1, "dy": 1})
world.add_entity(player)
# Add system
world.add_system(MovementSystem())
# Run simulation
sim = Simulation(world)
for step in range(10):
sim.step(dt=1.0)
# Output
for entity in world.entities:
pos = entity.get_component("position")
print(pos)
๐ License
MIT ยฉ Kairav Dutta
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pykworldsim-2.0.0.tar.gz.
File metadata
- Download URL: pykworldsim-2.0.0.tar.gz
- Upload date:
- Size: 32.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
465faa33564e2e695ea5b3316dc4694653cd4bf8f9a8a712e899dece3e0de522
|
|
| MD5 |
8e7ceafeadbcfa4a54a2ff77b79b9012
|
|
| BLAKE2b-256 |
53d0674798448506bf6b8ea3619b29052220bb00f2c476c290540b2240cdb554
|
File details
Details for the file pykworldsim-2.0.0-py3-none-any.whl.
File metadata
- Download URL: pykworldsim-2.0.0-py3-none-any.whl
- Upload date:
- Size: 32.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c4beae96a6e9cb95cc2d590c2806d9fba4d474137560f689d46c549bbaacf2f
|
|
| MD5 |
065eed81cd3e390edd78f0970dc5dfff
|
|
| BLAKE2b-256 |
001dd8093e1b06f3d131934355c7f6618a3f19e84674d1d583938bca64593fe0
|