use super::static_shape::{STATIC_SHAPES, StaticShape};
const RECENT_SHAPES: usize = 4;
#[derive(Debug)]
pub(in crate::wire::binary) struct ShapeCache {
pub(in crate::wire::binary) known: [StaticShape; STATIC_SHAPES],
pub(in crate::wire::binary) picked: Vec<&'static str>,
pub(in crate::wire::binary) found: Vec<Result<usize, Option<u32>>>,
pub(in crate::wire::binary) numbers: Vec<usize>,
recent: [u32; RECENT_SHAPES],
recent_len: usize,
}
impl Default for ShapeCache {
fn default() -> Self {
ShapeCache {
known: [StaticShape::default(); STATIC_SHAPES],
picked: Vec::new(),
found: Vec::new(),
numbers: Vec::new(),
recent: [0; RECENT_SHAPES],
recent_len: 0,
}
}
}
impl ShapeCache {
pub(in crate::wire::binary) fn clear(&mut self) {
self.known = [StaticShape::default(); STATIC_SHAPES];
self.recent_len = 0;
}
pub(in crate::wire::binary) fn find_recent(
&mut self,
mut is: impl FnMut(usize) -> bool,
) -> Option<usize> {
let at = (0..self.recent_len).find(|&at| is(self.recent[at] as usize))?;
let shape = self.recent[at];
self.recent.copy_within(0..at, 1);
self.recent[0] = shape;
Some(shape as usize)
}
pub(in crate::wire::binary) fn note(&mut self, shape: usize) {
let Ok(shape) = u32::try_from(shape) else {
return;
};
let held = &self.recent[..self.recent_len];
let at = if let Some(at) = held.iter().position(|&s| s == shape) {
at
} else {
self.recent_len = (self.recent_len + 1).min(RECENT_SHAPES);
self.recent_len - 1
};
self.recent.copy_within(0..at, 1);
self.recent[0] = shape;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_latest_shapes_are_kept_latest_first() {
let mut c = ShapeCache::default();
assert_eq!(c.find_recent(|_| true), None);
for shape in 0..6 {
c.note(shape);
}
assert_eq!(c.find_recent(|s| s == 1), None);
assert_eq!(c.find_recent(|s| s == 3), Some(3));
c.note(2);
let mut seen = Vec::new();
c.find_recent(|s| {
seen.push(s);
false
});
assert_eq!(seen, [2, 3, 5, 4]);
c.clear();
assert_eq!(c.find_recent(|_| true), None);
}
#[test]
fn a_shape_noted_twice_is_kept_once() {
let mut c = ShapeCache::default();
c.note(7);
c.note(7);
let mut seen = Vec::new();
c.find_recent(|s| {
seen.push(s);
false
});
assert_eq!(seen, [7]);
}
}