diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 3fbd82d..c33b7c7 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +use std::default; + use crate::objective::Objective; mod card; @@ -15,7 +17,7 @@ struct Shop { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Icon { +pub enum Icon { Circle, Square, Triangle, @@ -47,9 +49,34 @@ struct Board { objectives: [CriteriaCard; 4], } +#[derive(Debug, Clone, Copy, Default)] +struct PointThresholds(u32); + +impl PointThresholds { + const BITFIELD_SIZE: usize = 4; + pub fn len(&self) -> usize { + (self.0.ilog2() as usize).div_ceil(Self::BITFIELD_SIZE) + } + pub fn new(points: &[u8]) -> Self { + let mut result = Self(0); + for (index, p) in points.iter().enumerate() { + result.set(index, *p); + } + result + } + fn set(&mut self, index: usize, value: u8) { + debug_assert!(value < (1 << Self::BITFIELD_SIZE)); + self.0 |= u32::from(value) << (index * Self::BITFIELD_SIZE); + } + pub fn get(&self, index: usize) -> u8 { + let bitmask = (1u8 << Self::BITFIELD_SIZE) - 1; + (self.0 >> (index * Self::BITFIELD_SIZE)) as u8 & bitmask + } +} + #[derive(Debug)] struct CriteriaCard { objective: Objective, // Points depending on how often the criteria was met - points: Vec, + points: PointThresholds, } diff --git a/canvas/src/objective.rs b/canvas/src/objective.rs index 2ac95ac..31bf866 100644 --- a/canvas/src/objective.rs +++ b/canvas/src/objective.rs @@ -5,9 +5,9 @@ use crate::{Icon, Painting, icon_bitfield::IconBitfield}; pub enum Objective { AllFields, /// Must use every icon in the vector at least once - IconsUsed(Vec), + IconsUsed(IconSet), /// The count of all icons in the vec `.0` combined is exactly `.1` - ExactlyNIcons((Vec, u8)), + ExactlyNIcons((IconSet, u8)), /// These icons are in fields exactly next to each other /// One icon can only be the neighbor of one other icon Neighbors(Vec<(Icon, Icon)>), @@ -15,6 +15,58 @@ pub enum Objective { DistantNeighbors(Vec<(Icon, Icon)>), NInARow(Icon, u8), } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IconSet(u8); +const ICON_SET_ORDER: [Icon; 4] = [Icon::Circle, Icon::Triangle, Icon::Square, Icon::Color]; + +pub struct IconSetIterator { + icon_set: IconSet, + index: u8, +} + +impl From<&[Icon]> for IconSet { + fn from(value: &[Icon]) -> Self { + let v = value + .iter() + .map(|x| 1 << ICON_SET_ORDER.iter().position(|v| x == v).unwrap()) + .fold(0, |a, b| a | b); + Self(v) + } +} + +impl IconSet { + pub fn len(&self) -> usize { + self.0.count_ones() as usize + } +} + +impl Iterator for IconSetIterator { + type Item = Icon; + + fn next(&mut self) -> Option { + while self.index < 4 { + let old_index = self.index; + self.index += 1; + + if self.icon_set.0 & (1 << old_index) != 0 { + return Some(*ICON_SET_ORDER.get(old_index as usize).unwrap()); + } + } + None + } +} + +impl IntoIterator for IconSet { + type Item = Icon; + type IntoIter = IconSetIterator; + + fn into_iter(self) -> Self::IntoIter { + IconSetIterator { + icon_set: self, + index: 0, + } + } +} impl Objective { pub fn count_matches(&self, painting: &Painting) -> u8 { @@ -28,7 +80,7 @@ impl Objective { } Objective::IconsUsed(icons) => { let bits = IconBitfield::from_painting(painting); - if icons.iter().all(|i| bits.get_icon_bits(*i) > 0) { + if icons.into_iter().all(|i| bits.get_icon_bits(i) > 0) { 1 } else { 0 @@ -38,8 +90,8 @@ impl Objective { let bits = IconBitfield::from_painting(painting); if icons - .iter() - .map(|i| bits.get_icon_bits(*i).count_ones()) + .into_iter() + .map(|i| bits.get_icon_bits(i).count_ones()) .sum::() as u8 == *count { @@ -70,7 +122,10 @@ impl Objective { #[cfg(test)] mod tests { - use crate::{Card, Painting, objective::Objective}; + use crate::{ + Card, Icon, Painting, + objective::{IconSet, Objective}, + }; #[test] fn test_all_fields() { @@ -89,18 +144,37 @@ mod tests { Card::from_dbg_string("ll..."), Card::from_dbg_string("..t.."), ]; - assert_eq!( - Objective::NInARow(crate::Icon::Color, 2).count_matches(&v), - 2 - ); + assert_eq!(Objective::NInARow(Icon::Color, 2).count_matches(&v), 2); let tricky_two: Painting = [ Card::from_dbg_string("...ll"), Card::from_dbg_string("tl..."), Card::from_dbg_string("..lc."), ]; assert_eq!( - Objective::NInARow(crate::Icon::Color, 2).count_matches(&tricky_two), + Objective::NInARow(Icon::Color, 2).count_matches(&tricky_two), 2 ); } + + #[test] + fn test_icons_used() { + let v: Painting = [ + Card::from_dbg_string("...ll"), + Card::from_dbg_string("ll..."), + Card::from_dbg_string("..t.."), + ]; + + assert_eq!( + Objective::IconsUsed(IconSet::from([Icon::Color].as_slice())).count_matches(&v), + 1 + ); + assert_eq!( + Objective::IconsUsed(IconSet::from([Icon::Triangle].as_slice())).count_matches(&v), + 1 + ); + assert_eq!( + Objective::IconsUsed(IconSet::from([Icon::Circle].as_slice())).count_matches(&v), + 0 + ); + } }