weiss_core/env/
debug_events.rs

1use crate::replay::ReplayEvent;
2
3use super::GameEnv;
4
5/// Fixed-capacity ring storing recent replay events for debug inspection.
6pub(crate) struct EventRing {
7    capacity: usize,
8    events: Vec<ReplayEvent>,
9    next: usize,
10    full: bool,
11}
12
13impl EventRing {
14    /// Create a ring with `capacity` events.
15    pub(crate) fn new(capacity: usize) -> Self {
16        let mut events = Vec::with_capacity(capacity);
17        events.reserve(capacity);
18        Self {
19            capacity,
20            events,
21            next: 0,
22            full: false,
23        }
24    }
25
26    /// Clear all buffered events.
27    pub(crate) fn clear(&mut self) {
28        self.events.clear();
29        self.next = 0;
30        self.full = false;
31    }
32
33    /// Push one event, evicting the oldest when full.
34    pub(crate) fn push(&mut self, event: ReplayEvent) {
35        if self.capacity == 0 {
36            return;
37        }
38        if self.events.len() < self.capacity {
39            self.events.push(event);
40            if self.events.len() == self.capacity {
41                self.full = true;
42                self.next = 0;
43            }
44        } else {
45            self.events[self.next] = event;
46            self.next = (self.next + 1) % self.capacity;
47        }
48    }
49
50    /// Number of currently stored events.
51    pub(crate) fn len(&self) -> usize {
52        if self.capacity == 0 {
53            0
54        } else if self.full {
55            self.capacity
56        } else {
57            self.events.len()
58        }
59    }
60
61    /// Copy a chronological snapshot as numeric event codes into `out`.
62    ///
63    /// Returns the number of written entries (remaining slots are zero-filled).
64    pub(crate) fn snapshot_codes<F: Fn(&ReplayEvent) -> u32>(
65        &self,
66        out: &mut [u32],
67        code_fn: F,
68    ) -> usize {
69        let len = self.len();
70        if len == 0 {
71            for slot in out.iter_mut() {
72                *slot = 0;
73            }
74            return 0;
75        }
76        let cap = self.capacity;
77        for (i, slot) in out.iter_mut().enumerate() {
78            if i >= len {
79                *slot = 0;
80                continue;
81            }
82            let idx = if self.full { (self.next + i) % cap } else { i };
83            *slot = code_fn(&self.events[idx]);
84        }
85        len
86    }
87
88    /// Clone a chronological snapshot of stored events.
89    pub(crate) fn snapshot_events(&self) -> Vec<ReplayEvent> {
90        let len = self.len();
91        if len == 0 {
92            return Vec::new();
93        }
94        let cap = self.capacity;
95        let mut out = Vec::with_capacity(len);
96        for i in 0..len {
97            let idx = if self.full { (self.next + i) % cap } else { i };
98            out.push(self.events[idx].clone());
99        }
100        out
101    }
102}
103
104/// Stable numeric code for each canonical replay event variant.
105pub(crate) fn event_code(event: &ReplayEvent) -> u32 {
106    use crate::events::Event;
107    match event {
108        Event::Draw { .. } => 1,
109        Event::Damage { .. } => 2,
110        Event::DamageCancel { .. } => 3,
111        Event::DamageIntent { .. } => 4,
112        Event::DamageModifierApplied { .. } => 5,
113        Event::DamageModified { .. } => 6,
114        Event::DamageCommitted { .. } => 7,
115        Event::ReversalCommitted { .. } => 8,
116        Event::Reveal { .. } => 9,
117        Event::TriggerQueued { .. } => 10,
118        Event::TriggerGrouped { .. } => 11,
119        Event::TriggerResolved { .. } => 12,
120        Event::TriggerCanceled { .. } => 13,
121        Event::TimingWindowEntered { .. } => 14,
122        Event::PriorityGranted { .. } => 15,
123        Event::PriorityPassed { .. } => 16,
124        Event::StackGroupPresented { .. } => 17,
125        Event::StackOrderChosen { .. } => 18,
126        Event::StackPushed { .. } => 19,
127        Event::StackResolved { .. } => 20,
128        Event::AutoResolveCapExceeded { .. } => 21,
129        Event::WindowAdvanced { .. } => 22,
130        Event::ChoicePresented { .. } => 23,
131        Event::ChoicePageChanged { .. } => 24,
132        Event::ChoiceMade { .. } => 25,
133        Event::ChoiceAutopicked { .. } => 26,
134        Event::ChoiceSkipped { .. } => 27,
135        Event::ZoneMove { .. } => 28,
136        Event::ControlChanged { .. } => 29,
137        Event::ModifierAdded { .. } => 30,
138        Event::ModifierRemoved { .. } => 31,
139        Event::Concede { .. } => 32,
140        Event::Play { .. } => 33,
141        Event::PlayEvent { .. } => 34,
142        Event::PlayClimax { .. } => 35,
143        Event::Trigger { .. } => 36,
144        Event::Attack { .. } => 37,
145        Event::AttackType { .. } => 38,
146        Event::Counter { .. } => 39,
147        Event::Clock { .. } => 40,
148        Event::Shuffle { .. } => 41,
149        Event::Refresh { .. } => 42,
150        Event::RefreshPenalty { .. } => 43,
151        Event::LevelUpChoice { .. } => 44,
152        Event::Encore { .. } => 45,
153        Event::Stand { .. } => 46,
154        Event::EndTurn { .. } => 47,
155        Event::Terminal { .. } => 48,
156    }
157}
158
159impl GameEnv {
160    /// Snapshot debug event ring as compact numeric codes.
161    pub fn debug_event_ring_codes(&self, viewer: u8, out: &mut [u32]) -> u16 {
162        let Some(rings) = self.debug_event_ring.as_ref() else {
163            for slot in out.iter_mut() {
164                *slot = 0;
165            }
166            return 0;
167        };
168        let ring = &rings[viewer as usize % 2];
169        let count = ring.snapshot_codes(out, event_code);
170        count as u16
171    }
172
173    /// Snapshot debug event ring as full event records.
174    pub fn debug_event_ring_snapshot(&self, viewer: u8) -> Vec<ReplayEvent> {
175        let Some(rings) = self.debug_event_ring.as_ref() else {
176            return Vec::new();
177        };
178        rings[viewer as usize % 2].snapshot_events()
179    }
180}