Skip to main content

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    pub fn next() -> Self {
64        Self(SPACE_INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed))
65    }
66}
67
68impl fmt::Display for SpaceInstanceId {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "{}", self.0)
71    }
72}
73
74/// Monotonically increasing tick counter.
75///
76/// Incremented each time the simulation advances one step.
77#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
78pub struct TickId(pub u64);
79
80impl fmt::Display for TickId {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "{}", self.0)
83    }
84}
85
86impl From<u64> for TickId {
87    fn from(v: u64) -> Self {
88        Self(v)
89    }
90}
91
92/// Tracks arena generation for snapshot identity.
93///
94/// Incremented each time a new snapshot is published, enabling
95/// ObsPlan invalidation detection.
96#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
97pub struct WorldGenerationId(pub u64);
98
99impl fmt::Display for WorldGenerationId {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "{}", self.0)
102    }
103}
104
105impl From<u64> for WorldGenerationId {
106    fn from(v: u64) -> Self {
107        Self(v)
108    }
109}
110
111/// Tracks the version of global simulation parameters.
112///
113/// Incremented when any `SetParameter` or `SetParameterBatch` command
114/// is applied, enabling stale-parameter detection.
115#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
116pub struct ParameterVersion(pub u64);
117
118impl fmt::Display for ParameterVersion {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        write!(f, "{}", self.0)
121    }
122}
123
124impl From<u64> for ParameterVersion {
125    fn from(v: u64) -> Self {
126        Self(v)
127    }
128}
129
130/// Key for a global simulation parameter (e.g., learning rate, reward scale).
131///
132/// Parameters are registered at world creation; invalid keys are rejected
133/// at ingress.
134#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
135pub struct ParameterKey(pub u32);
136
137impl fmt::Display for ParameterKey {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        write!(f, "{}", self.0)
140    }
141}
142
143impl From<u32> for ParameterKey {
144    fn from(v: u32) -> Self {
145        Self(v)
146    }
147}
148
149/// A coordinate in simulation space.
150///
151/// Uses `SmallVec<[i32; 4]>` to avoid heap allocation for spaces
152/// up to 4 dimensions, covering all v1 topologies (1D, 2D, hex).
153/// Higher-dimensional spaces spill to the heap transparently.
154pub type Coord = SmallVec<[i32; 4]>;