tri_grid_sim 0.1.1

Deterministic tick-based simulation on a 2D grid with directional triangles
Documentation
  • Coverage
  • 83.46%
    111 out of 133 items documented24 out of 50 items with examples
  • Size
  • Source code size: 134.09 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 15.42 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 21s Average build duration of successful builds.
  • all releases: 21s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • YelenaTor

tri_grid_sim

A deterministic, tick-based simulation library for 2D grid worlds with directional triangles.

Overview

tri_grid_sim provides the core simulation engine for games and simulations that use a rectangular grid where each tile is subdivided into four directional triangles (North, East, South, West). This structure enables rich directional gameplay mechanics while maintaining a simple underlying grid topology.

Key Features

  • Deterministic simulation: All operations produce identical results given the same inputs, making the engine suitable for replays, networking, and debugging.
  • Double-buffered state: The simulation uses two state buffers to ensure consistent reads during tick updates.
  • Fixed-point arithmetic: Uses parts-per-million (PPM) integers instead of floats to guarantee cross-platform determinism.
  • Extensible entity system: Open ID patterns (GroupKind(u32), StructureKind(u32), etc.) allow users to define their own entity types without modifying the library.
  • Trait-based behaviors: Custom game logic is injected via behavior traits rather than hardcoded into the engine.

Architecture

The simulation is organized around these core concepts:

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     │
└───────────┴───────────┘
  (0,0)       (1,0)
  • [Grid]: Defines the world dimensions and provides coordinate/index conversion.
  • [Direction]: The four cardinal directions (North, East, South, West).
  • Each tile contains 4 triangles, one per direction.

Terrain Layer

  • [TerrainMap]: Stores per-tile terrain data and shared edge data.
  • [TerrainTile]: Properties like terrain kind, survivability, and danger modifiers.
  • [TerrainEdge]: Shared edges between adjacent tiles with passability and movement cost.
  • [TerrainGenerator]: Trait for procedurally generating terrain from a seed.

Resource Layer

  • [ResourceMap]: Sparse storage of resources per triangle.
  • [ResourceKind]: Open ID for user-defined resource types.
  • [ResourceGenerator]: Trait for procedurally generating initial resources.

Entity Layer

  • [Group]: Mobile entities (units, characters) with position and direction.
  • [Structure]: Stationary entities (buildings) with inventory and parameters.
  • Both use open ID patterns for extensibility.

Simulation Loop

  • [World]: The main simulation container holding all state.
  • [WorldState]: Per-tick mutable state (danger values, resources, inventories).
  • [WorldBehavior]: Trait for injecting custom logic into the tick loop.

Quick Start

use tri_grid_sim::{Direction, SimConfig, World};

// Create a 10x10 world with default configuration
let cfg = SimConfig::default();
let mut world = World::new(10, 10, cfg).expect("valid dimensions");

// Set some initial danger in a triangle
world.set_danger(5, 5, Direction::North, 1000);

// Run simulation ticks
for _ in 0..10 {
    world.tick();
}

// Query the resulting state
let danger = world.danger(5, 5, Direction::North);
println!("Danger after 10 ticks: {}", danger);

Custom Behaviors

Inject game-specific logic by implementing behavior traits:

use tri_grid_sim::{WorldBehavior, WorldCtx, WorldState};

struct MyDangerSource {
    x: u32,
    y: u32,
    amount: i32,
}

impl WorldBehavior for MyDangerSource {
    fn apply(&self, ctx: &WorldCtx<'_>, next: &mut WorldState) {
        // Add danger to the North triangle each tick
        let idx = ctx.grid().idx(self.x, self.y);
        next.danger[idx][0] += self.amount; // 0 = North
    }
}

Feature Flags

  • raiders: Enables example RAIDERS group kind and RaidersBehavior.
  • structures_example: Enables example SETTLEMENT structure kind and SettlementBehavior.

These features demonstrate how to build on the engine but are not required for normal use.

Coordinate System

The grid uses a top-left origin coordinate system:

  • (0, 0) is the top-left corner
  • x increases rightward
  • y increases downward
  • Direction offsets: North=(0,-1), East=(1,0), South=(0,1), West=(-1,0)

Determinism Guarantees

The simulation guarantees determinism through:

  1. Fixed iteration order: Tiles are processed in ascending index order; directions in Direction::ALL order (N, E, S, W).
  2. Integer arithmetic: All calculations use i32/i64 with PPM scaling.
  3. Seeded generation: Terrain and resource generators accept explicit seeds.
  4. No floating-point: Avoids platform-dependent float behavior.

This means: same initial state + same inputs = identical results across platforms.