murk_core/
id.rs

1//! Strongly-typed identifiers and the [`Coord`] type alias.
2
3use smallvec::SmallVec;
4use std::fmt;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7/// Identifies a field within a simulation world.
8///
9/// Fields are registered at world creation and assigned sequential IDs.
10/// `FieldId(n)` corresponds to the n-th field in the world configuration.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct FieldId(pub u32);
13
14impl fmt::Display for FieldId {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(f, "{}", self.0)
17    }
18}
19
20impl From<u32> for FieldId {
21    fn from(v: u32) -> Self {
22        Self(v)
23    }
24}
25
26/// Identifies a space (spatial topology) within a simulation world.
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
28pub struct SpaceId(pub u32);
29
30impl fmt::Display for SpaceId {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        write!(f, "{}", self.0)
33    }
34}
35
36impl From<u32> for SpaceId {
37    fn from(v: u32) -> Self {
38        Self(v)
39    }
40}
41
42/// Counter for unique [`SpaceInstanceId`] allocation.
43static SPACE_INSTANCE_COUNTER: AtomicU64 = AtomicU64::new(1);
44
45/// Unique per-instance identifier for a `Space` object.
46///
47/// Allocated from a monotonic atomic counter via [`SpaceInstanceId::next`].
48/// Two distinct space instances always have different IDs, even if they
49/// have identical topology. Used by observation plan caching to avoid
50/// ABA reuse when a space is dropped and a new one is allocated at the
51/// same address.
52///
53/// Cloning a space preserves its instance ID, which is correct because
54/// immutable spaces with the same ID have the same topology.
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
56pub struct SpaceInstanceId(u64);
57
58impl SpaceInstanceId {
59    /// Allocate a fresh, unique instance ID.
60    ///
61    /// Each call returns a new ID that has never been returned before
62    /// within this process. Thread-safe.
63    #[must_use]
64    pub fn next() -> Self {
65        Self(SPACE_INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed))
66    }
67}
68
69impl fmt::Display for SpaceInstanceId {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(f, "{}", self.0)
72    }
73}
74
75/// Monotonically increasing tick counter.
76///
77/// Incremented each time the simulation advances one step.
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
79pub struct TickId(pub u64);
80
81impl fmt::Display for TickId {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(f, "{}", self.0)
84    }
85}
86
87impl From<u64> for TickId {
88    fn from(v: u64) -> Self {
89        Self(v)
90    }
91}
92
93/// Tracks arena generation for snapshot identity.
94///
95/// Incremented each time a new snapshot is published, enabling
96/// ObsPlan invalidation detection.
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
98pub struct WorldGenerationId(pub u64);
99
100impl fmt::Display for WorldGenerationId {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        write!(f, "{}", self.0)
103    }
104}
105
106impl From<u64> for WorldGenerationId {
107    fn from(v: u64) -> Self {
108        Self(v)
109    }
110}
111
112/// Tracks the version of global simulation parameters.
113///
114/// Incremented when any `SetParameter` or `SetParameterBatch` command
115/// is applied, enabling stale-parameter detection.
116#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
117pub struct ParameterVersion(pub u64);
118
119impl fmt::Display for ParameterVersion {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(f, "{}", self.0)
122    }
123}
124
125impl From<u64> for ParameterVersion {
126    fn from(v: u64) -> Self {
127        Self(v)
128    }
129}
130
131/// Key for a global simulation parameter (e.g., learning rate, reward scale).
132///
133/// Parameters are registered at world creation; invalid keys are rejected
134/// at ingress.
135#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
136pub struct ParameterKey(pub u32);
137
138impl fmt::Display for ParameterKey {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        write!(f, "{}", self.0)
141    }
142}
143
144impl From<u32> for ParameterKey {
145    fn from(v: u32) -> Self {
146        Self(v)
147    }
148}
149
150/// A coordinate in simulation space.
151///
152/// Uses `SmallVec<[i32; 4]>` to avoid heap allocation for spaces
153/// up to 4 dimensions, covering all v1 topologies (1D, 2D, hex).
154/// Higher-dimensional spaces spill to the heap transparently.
155pub type Coord = SmallVec<[i32; 4]>;