1use crate::bitboard::Bitboard;
2use crate::constants::{FLAG_CANON, VERSION};
3use crate::qfen::{bb_from_qfen, bb_to_qfen};
4use crate::symmetry::SymmetryHandler;
5
6#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
8pub struct State {
9 pub bb: Bitboard,
10}
11
12impl State {
13 pub fn new(bb: Bitboard) -> Self {
14 Self { bb }
15 }
16
17 pub fn empty() -> Self {
18 Self {
19 bb: Bitboard::EMPTY,
20 }
21 }
22
23 pub fn pack(&self, flags: u8) -> [u8; 18] {
26 let mut buf = [0u8; 18];
27 buf[0] = VERSION;
28 buf[1] = flags;
29 let bb_bytes = self.bb.to_le_bytes();
30 buf[2..18].copy_from_slice(&bb_bytes);
31 buf
32 }
33
34 pub fn unpack(data: &[u8]) -> Result<Self, String> {
35 if data.len() < 18 {
36 return Err(format!(
37 "Buffer too small: need 18 bytes, got {}",
38 data.len()
39 ));
40 }
41 if data[0] != VERSION {
42 return Err(format!("Unsupported version {}", data[0]));
43 }
44 let mut bb_buf = [0u8; 16];
45 bb_buf.copy_from_slice(&data[2..18]);
46 Ok(Self {
47 bb: Bitboard::from_le_bytes(&bb_buf),
48 })
49 }
50
51 pub fn to_qfen(&self) -> String {
54 bb_to_qfen(&self.bb)
55 }
56
57 pub fn from_qfen(qfen: &str) -> Result<Self, String> {
58 bb_from_qfen(qfen).map(|bb| Self { bb })
59 }
60
61 pub fn canonical_payload(&self) -> [u8; 16] {
64 SymmetryHandler::canonical_payload(&self.bb)
65 }
66
67 pub fn canonical_key(&self) -> [u8; 18] {
68 let mut key = [0u8; 18];
69 key[0] = VERSION;
70 key[1] = FLAG_CANON;
71 key[2..18].copy_from_slice(&self.canonical_payload());
72 key
73 }
74
75 pub fn symmetry_count(&self) -> usize {
76 SymmetryHandler::orbit_size(&self.bb)
77 }
78}
79
80impl Default for State {
81 fn default() -> Self {
82 Self::empty()
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn pack_unpack_roundtrip() {
92 let st = State::from_qfen("A.bC/..../d..B/...a").unwrap();
93 let buf = st.pack(0);
94 let st2 = State::unpack(&buf).unwrap();
95 assert_eq!(st, st2);
96 }
97
98 #[test]
99 fn canonical_key_starts_with_version_flag() {
100 let key = State::empty().canonical_key();
101 assert_eq!(key[0], VERSION);
102 assert_eq!(key[1], FLAG_CANON);
103 }
104
105 #[test]
106 fn qfen_roundtrip() {
107 let qfen = "AbCd/..../..../....";
108 let st = State::from_qfen(qfen).unwrap();
109 assert_eq!(st.to_qfen(), qfen);
110 }
111}