A deterministic, tick-based simulation library for 2D grid worlds with directional triangles.
tri_grid is a Rust workspace providing a simulation engine where each tile in a rectangular grid is subdivided into four directional triangles (North, East, South, West). This structure enables rich directional gameplay mechanics while maintaining a simple underlying grid topology.
┌───────────┬───────────┐
│ N │ N │
│ W ● E │ W ● E │ ← Each tile has 4 triangles
│ S │ S │ pointing to cardinal directions
├───────────┼───────────┤
│ N │ N │
│ W ● E │ W ● E │
│ S │ S │
└───────────┴───────────┘
- Deterministic simulation — All operations produce identical results given the same inputs, perfect for replays, networking, and debugging
- Double-buffered state — Consistent reads during tick updates
- Fixed-point arithmetic — PPM (parts-per-million) integers avoid floating-point non-determinism
- Extensible entity system — Open ID patterns let you define custom types without modifying the library
- Trait-based behaviors — Inject custom game logic via behavior traits
| Crate | Description |
|---|---|
tri_grid_sim |
Core simulation engine |
tri_grid_render |
Renderer-agnostic visualization helpers |
tri_grid_cli |
Demo/example harness (not published) |
Add to your Cargo.toml:
[dependencies]
tri_grid_sim = "0.1"
tri_grid_render = "0.1" # Optional, for rendering helpersuse tri_grid_sim::{Direction, SimConfig, World};
fn main() -> tri_grid_sim::Result<()> {
// Create a 10x10 world with default configuration
let cfg = SimConfig::default();
let mut world = World::new(10, 10, cfg)?;
// Set initial danger in a triangle
world.set_danger(5, 5, Direction::North, 1000);
// Run simulation ticks
for _ in 0..100 {
world.tick();
}
// Query results
println!("Danger after 100 ticks: {}", world.danger(5, 5, Direction::North));
Ok(())
}Inject game-specific logic by implementing behavior traits:
use tri_grid_sim::{WorldBehavior, WorldCtx, WorldState};
struct DangerSource {
x: u32,
y: u32,
amount: i32,
}
impl WorldBehavior for DangerSource {
fn apply(&self, ctx: &WorldCtx<'_>, next: &mut WorldState) {
let idx = ctx.grid().idx(self.x, self.y);
next.danger[idx][0] += self.amount; // Add to North triangle
}
}use tri_grid_sim::{Direction, ResourceKind, StructureKind, SimConfig, World};
// Define your resource and structure types
const FOOD: ResourceKind = ResourceKind(1);
const FARM: StructureKind = StructureKind(1);
fn main() -> tri_grid_sim::Result<()> {
let mut world = World::new(10, 10, SimConfig::default())?;
// Place resources on triangles
world.set_resource(5, 5, Direction::North, FOOD, 100);
// Spawn a structure with inventory
let farm_id = world.spawn_structure(FARM, 5, 5);
world.set_inventory(farm_id, FOOD, 50);
// Query state
println!("Food on triangle: {}", world.get_resource(5, 5, Direction::North, FOOD));
println!("Food in farm: {}", world.get_inventory(farm_id, FOOD));
Ok(())
}- Grid — Defines world dimensions, coordinate/index conversion
- Direction — North, East, South, West for triangle identification
- TerrainMap — Per-tile terrain data with shared edges between tiles
- ResourceMap — Sparse per-triangle resource storage
- World — Main simulation container with double-buffered state
- WorldBehavior — Trait for custom tick logic
Top-left origin:
(0, 0)is the top-left cornerxincreases rightwardyincreases downward
- Fixed iteration order — Tiles processed in ascending index order
- Integer arithmetic — All calculations use
i32/i64with PPM scaling - Seeded generation — Terrain and resource generators accept explicit seeds
- No floating-point — Avoids platform-dependent float behavior
Run the basic simulation example:
cargo run --example basic_simulation -p tri_grid_simraiders— Example raider group kind and behaviorstructures_example— Example settlement structure kind and behavior
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.