Spaces:
Sleeping
Sleeping
| use serde::{Deserialize, Serialize}; | |
| const HEART_COLOR_COUNT: usize = 7; | |
| pub struct HeartBoard(pub u64); | |
| impl HeartBoard { | |
| pub const MASK: u64 = 0xFF; // 8 bits per color | |
| pub fn get_color_count(&self, color: usize) -> u8 { | |
| if color >= HEART_COLOR_COUNT { return 0; } | |
| ((self.0 >> (color * 8)) & Self::MASK) as u8 | |
| } | |
| pub fn set_color_count(&mut self, color: usize, val: u8) { | |
| if color >= HEART_COLOR_COUNT { return; } | |
| let mask = Self::MASK << (color * 8); | |
| self.0 = (self.0 & !mask) | ((val as u64) << (color * 8)); | |
| } | |
| pub fn add_to_color(&mut self, color: usize, val: i32) { | |
| if color >= HEART_COLOR_COUNT { return; } | |
| let current = self.get_color_count(color) as i32; | |
| self.set_color_count(color, (current + val).max(0).min(255) as u8); | |
| } | |
| pub fn add_heart(&mut self, color: usize) { | |
| self.add_to_color(color, 1); | |
| } | |
| pub fn get_total_count(&self) -> u32 { | |
| let mut total = 0; | |
| for i in 0..7 { | |
| total += self.get_color_count(i) as u32; | |
| } | |
| total | |
| } | |
| pub fn from_array(arr: &[u8; 7]) -> Self { | |
| let mut val = 0u64; | |
| for i in 0..7 { | |
| val |= (arr[i] as u64) << (i * 8); | |
| } | |
| Self(val) | |
| } | |
| pub fn from_array_u32(arr: &[u32; 7]) -> Self { | |
| let mut val = 0u64; | |
| for i in 0..7 { | |
| val |= (arr[i].min(255) as u64) << (i * 8); | |
| } | |
| Self(val) | |
| } | |
| pub fn to_array(&self) -> [u8; 7] { | |
| let mut arr = [0u8; 7]; | |
| for i in 0..7 { | |
| arr[i] = ((self.0 >> (i * 8)) & Self::MASK) as u8; | |
| } | |
| arr | |
| } | |
| pub fn to_array_u32(&self) -> [u32; 7] { | |
| let mut arr = [0u32; 7]; | |
| for i in 0..7 { | |
| arr[i] = ((self.0 >> (i * 8)) & Self::MASK) as u32; | |
| } | |
| arr | |
| } | |
| /// Returns a bitmask of colors present (0-6). | |
| /// Used for O(1) intersection check with color filters. | |
| pub fn get_color_mask(&self) -> u8 { | |
| let mut mask = 0u8; | |
| for i in 0..7 { | |
| if ((self.0 >> (i * 8)) & Self::MASK) != 0 { | |
| mask |= 1 << i; | |
| } | |
| } | |
| mask | |
| } | |
| pub fn add(&mut self, other: Self) { | |
| // Simple addition per segment. | |
| // Note: This relies on segments not overflowing 255. | |
| // For safety, we can mask or use saturating logic, but for raw MCTS, speed is king. | |
| self.0 += other.0; | |
| } | |
| pub fn satisfies(&self, req: Self) -> bool { | |
| let mut pool = self.to_array(); | |
| let need = req.to_array(); | |
| let mut wildcards = pool[6] as i32; | |
| let mut satisfied = 0; | |
| let mut total_req = 0; | |
| for i in 0..6 { | |
| let n = need[i] as i32; | |
| let h = pool[i] as i32; | |
| total_req += n; | |
| if h >= n { | |
| satisfied += n; | |
| pool[i] -= n as u8; | |
| } else { | |
| satisfied += h; | |
| pool[i] = 0; | |
| let deficit = n - h; | |
| let used_wild = wildcards.min(deficit); | |
| satisfied += used_wild; | |
| wildcards -= used_wild; | |
| } | |
| } | |
| let mut any_need = need[6] as i32; | |
| total_req += any_need; | |
| let used_wild = wildcards.min(any_need); | |
| satisfied += used_wild; | |
| any_need -= used_wild; | |
| if any_need > 0 { | |
| for i in 0..6 { | |
| let used = (pool[i] as i32).min(any_need); | |
| satisfied += used; | |
| any_need -= used; | |
| if any_need <= 0 { | |
| break; | |
| } | |
| } | |
| } | |
| satisfied >= total_req | |
| } | |
| } | |
| pub fn process_hearts(pool: &mut [u32; 7], need: &[u32; 7]) -> (u32, u32) { | |
| let mut p8 = [0u8; 7]; | |
| let mut n8 = [0u8; 7]; | |
| for i in 0..7 { | |
| p8[i] = pool[i].min(255) as u8; | |
| n8[i] = need[i].min(255) as u8; | |
| } | |
| let mut satisfied = 0; | |
| let mut wildcards = p8[6] as i32; | |
| let mut total_req = 0; | |
| for i in 0..6 { | |
| let n = n8[i] as i32; | |
| let h = p8[i] as i32; | |
| total_req += n; | |
| if h >= n { | |
| satisfied += n; | |
| p8[i] -= n as u8; | |
| } else { | |
| satisfied += h; | |
| p8[i] = 0; | |
| let deficit = n - h; | |
| let used_wild = wildcards.min(deficit); | |
| satisfied += used_wild; | |
| wildcards -= used_wild; | |
| } | |
| } | |
| let mut any_need = n8[6] as i32; | |
| total_req += any_need; | |
| let used_wild = wildcards.min(any_need); | |
| satisfied += used_wild; | |
| any_need -= used_wild; | |
| if any_need > 0 { | |
| for i in 0..6 { | |
| let used = (p8[i] as i32).min(any_need); | |
| satisfied += used; | |
| p8[i] -= used as u8; | |
| any_need -= used; | |
| if any_need <= 0 { | |
| break; | |
| } | |
| } | |
| } | |
| pool[6] = wildcards.max(0) as u32; | |
| for i in 0..6 { | |
| pool[i] = p8[i] as u32; | |
| } | |
| (satisfied as u32, total_req as u32) | |
| } | |