1use crate::{BitSet, SolverEvent};
2
3use std::{iter::FusedIterator, str::FromStr};
4
5#[derive(Default, Clone)]
6pub struct Game {
7 pub taking_all: [u64; 4], pub taking: Vec<u8>,
9 pub breaking: Vec<u8>,
10 }
13
14impl Game {
15 #[inline] fn parse_octal(c: u8) -> Option<u8> {
16 (b'0' <= c && c <= b'8').then(|| c - b'0')
17 }
18
19 pub fn from_ascii(mut s: &[u8]) -> Option<Game> {
20 let mut result = Self::default();
21 if s.starts_with(b"4.") || s.starts_with(b"4,") || s.starts_with(b"4d") {
22 result.breaking.push(0);
23 s = &s[2..];
24 } else if s.starts_with(b"0.") || s.starts_with(b"0,") || s.starts_with(b"0d") {
25 s = &s[2..];
26 } else if s.starts_with(b".") || s.starts_with(b",") || s.starts_with(b"d") {
27 s = &s[1..];
28 }
29 let mut position = 0u8;
30 for c in s {
31 position = position.checked_add(1)?;
32 let oct = Self::parse_octal(*c)?;
33 if oct & 1 != 0 { result.taking_all.add_nimber(position as u16) }
34 if oct & 2 != 0 { result.taking.push(position) }
35 if oct & 4 != 0 { result.breaking.push(position) }
36 }
37 Some(result)
38 }
39
40 #[inline] pub fn can_take_all(&self, n: usize) -> bool {
41 self.taking_all.try_get_bit(n).unwrap_or(false)
42 }
43
44 pub(crate) fn consider_taking<S: SolverEvent>(&self, nimbers: &[u16], option_nimbers: &mut [u64; 1<<(16-6)], stats: &mut S) {
45 let n = nimbers.len();
46 if self.can_take_all(n) { option_nimbers.add_nimber(0) }
47 for t in &self.taking {
48 let t = *t as usize;
49 if t >= n { break }
50 option_nimbers.add_nimber(nimbers[n-t]);
51 stats.take_option();
52 }
53 }
54
55 #[inline] pub fn breaking_moves(&self, n: usize) -> BreakingMoveIterator<std::iter::Copied<std::slice::Iter<'_, u8>>> {
56 BreakingMoveIterator::for_slice(n, self.breaking.as_slice())
58 }
59
60 pub fn rules(&self) -> [u8; 256] {
62 let mut result = [0; 256];
63 for a in 0..256 {
64 if self.taking_all.get_bit(a) {
65 result[a] |= 1;
66 }
67 }
68 for t in &self.taking { result[*t as usize] |= 2; }
69 for b in &self.breaking { result[*b as usize] |= 4; }
70 result
71 }
72
73 pub fn to_ascii(&self, separator: u8) -> Vec<u8> {
75 let rules = self.rules();
76 let mut number_of_rules = 0;
77 for (i, r) in rules.iter().enumerate() {
78 if *r != 0 { number_of_rules = i; }
79 }
80 number_of_rules += 1;
81 let mut result = Vec::with_capacity(number_of_rules);
82 result.push(rules[0] + b'0');
83 result.push(separator);
84 for r in 1..number_of_rules {
85 result.push(rules[r] + b'0');
86 }
87 result
88 }
89
90 pub fn taking_iters(&self, position: usize) -> usize {
93 self.taking.iter().map(|t| position.saturating_sub(*t as usize)).sum()
94 }
95
96 pub fn breaking_naive_iters(&self, position: usize) -> usize {
99 self.breaking.iter().map(|b| {
100 let b = *b as usize;
101 if position < b + 2 { 0 } else
102 if position & 1 != b & 1 { let k = (position - b - 1) / 2; k*k+k } else { let hd = (position - b) / 2; let k = hd-1; k*k+k + hd } }).sum()
105 }
106
107 pub fn naive_iters(&self, position: usize) -> usize {
110 self.taking_iters(position) + self.breaking_naive_iters(position)
111 }
112
113 pub fn period(&self, nimbers: &[u16]) -> Option<(usize, usize)> {
118 let mut max_to_take =
119 (self.taking_all.msb_index().unwrap_or(0) as u8)
120 .max(*self.taking.last().unwrap_or(&0));
121
122 let log2mult = if let Some(b) = self.breaking.last() { max_to_take = max_to_take.max(*b);
124 1 } else { 0 }; let len = nimbers.len();
128 for period in 1 ..= (len >> log2mult) {
129 let mut preperiod = len - period;
130 while preperiod > 0 && nimbers[preperiod-1] == nimbers[preperiod-1+period] {
131 preperiod -= 1;
132 }
133 if len >= ((preperiod + period) << log2mult) + max_to_take as usize {
135 return Some((preperiod, period));
136 }
137 }
138 None
139 }
140}
141
142impl FromStr for Game {
143 type Err = &'static str;
144
145 fn from_str(s: &str) -> Result<Self, Self::Err> {
146 Self::from_ascii(s.as_bytes()).ok_or("game description must be in format [X.]OOO... where X is 0 or 4 and (up to 255) Os are octal digits")
147 }
148}
149
150impl ToString for Game {
151 fn to_string(&self) -> String {
152 unsafe{ String::from_utf8_unchecked(self.to_ascii(b'.')) }
153 }
154}
155
156pub struct BreakingMoveIterator<I> {
163 current: I,
164 n: usize,
165 after_take: usize,
166 last_i: usize,
167 i: usize
168}
169
170impl<I> BreakingMoveIterator<I> {
171 #[inline] pub fn for_iter(n: usize, to_take_iterator: I) -> Self {
172 Self { current: to_take_iterator, n, after_take: 0, last_i: 0, i: 0 }
173 }
174}
175
176impl<'a, IU: Into<usize> + Copy> BreakingMoveIterator<std::iter::Copied<std::slice::Iter<'a, IU>>> {
177 #[inline] pub fn for_slice(n: usize, slice: &'a [IU]) -> Self {
178 Self::for_iter(n, slice.iter().copied())
179 }
180}
181
182impl<IU: Into<usize>, I: Iterator<Item = IU>> Iterator for BreakingMoveIterator<I> {
183 type Item = (usize, usize);
184
185 fn next(&mut self) -> Option<Self::Item> {
186 if self.i == self.last_i {
187 let b = self.current.next()?.into();
188 if b+1 >= self.n { return None; } self.after_take = self.n - b; self.last_i = self.after_take / 2; self.i = 1;
192 } else {
193 self.i += 1;
194 }
195 Some((self.i, self.after_take - self.i))
196 }
197}
198
199impl<IU: Into<usize>, I: Iterator<Item = IU> + FusedIterator> FusedIterator for BreakingMoveIterator<I>{}
200
201
202#[cfg(test)]
203pub(crate) mod tests {
204 use super::*;
205
206 #[test]
207 fn test_4d() {
208 let g = Game::from_str("4.").unwrap();
209 assert_eq!(g.breaking_moves(0).collect::<Vec<_>>(), vec![]);
210 assert_eq!(g.breaking_moves(1).collect::<Vec<_>>(), vec![]);
211 assert_eq!(g.breaking_moves(2).collect::<Vec<_>>(), vec![(1, 1)]);
212 assert_eq!(g.breaking_moves(3).collect::<Vec<_>>(), vec![(1, 2)]);
213 assert_eq!(g.breaking_moves(4).collect::<Vec<_>>(), vec![(1, 3), (2, 2)]);
214 assert_eq!(g.breaking_moves(5).collect::<Vec<_>>(), vec![(1, 4), (2, 3)]);
215 }
216
217 #[test]
218 fn test_0d44() {
219 let g = Game::from_str("0.44").unwrap();
220 assert_eq!(g.breaking_moves(0).collect::<Vec<_>>(), vec![]);
221 assert_eq!(g.breaking_moves(1).collect::<Vec<_>>(), vec![]);
222 assert_eq!(g.breaking_moves(2).collect::<Vec<_>>(), vec![]);
223 assert_eq!(g.breaking_moves(3).collect::<Vec<_>>(), vec![(1, 1)]);
224 assert_eq!(g.breaking_moves(4).collect::<Vec<_>>(), vec![(1, 2), (1, 1)]);
225 assert_eq!(g.breaking_moves(5).collect::<Vec<_>>(), vec![(1, 3), (2, 2), (1, 2)]);
226 }
227}