1use crate::{
8 board::Role,
9 square::{File, Square},
10};
11
12use super::{ByteInput as Input, prelude::*};
13
14#[derive(Clone, Copy, Debug)]
15pub struct Header {
16 pub first_game: u16,
17 pub len: u32,
18 pub accum: u32,
19}
20
21pub fn header(input: &mut Input<'_>) -> ModalResult<Header> {
23 let first_game = be_u16.parse_next(input)?;
24 let len = be_u32.parse_next(input)?;
25 let accum = be_u32.parse_next(input)?;
26 let zero1 = be_u32.parse_next(input)?;
27 let len2 = be_u32.parse_next(input)?;
28 let zero2 = be_u32.parse_next(input)?;
29
30 assert_eq!(len, len2);
31 assert_eq!(zero1, 0);
32 assert_eq!(zero2, 0);
33
34 Ok(Header { first_game, len, accum })
35}
36
37#[derive(Clone, Copy, Debug)]
38pub struct Info {
39 pub len: u32,
40 pub not_initial: bool,
41 pub not_encoded: bool,
42 pub special_encoding: bool,
43 pub freestyle: bool,
44}
45
46pub fn info(input: &mut Input<'_>) -> ModalResult<Info> {
47 let info = u8.parse_next(input)?;
48 let len = be_u24.parse_next(input)?;
49 let not_initial = info & 0x40 != 0;
50 let not_encoded = info & 0x80 != 0;
51 let special_encoding = info & 4 != 0;
52 let freestyle = info & 0xA > 0;
53 Ok(Info { len, not_initial, not_encoded, special_encoding, freestyle })
55}
56#[derive(Clone, Copy, Debug)]
57pub struct Game {
58 pub info: Info,
59}
60
61pub fn game(input: &mut Input<'_>) -> ModalResult<Game> {
62 let info = info.parse_next(input)?;
63 if info.not_initial {
64 unimplemented!();
65 }
66 Ok(Game { info })
70}
71
72#[test]
73fn example() {
74 let input = include_bytes!("../../examples/twic1616.cbh");
75 let headers = super::cbh::headers.parse(input).expect("can pass CBH file");
76
77 let input = include_bytes!("../../examples/twic1616.cbg");
78 let header = header.parse_next(&mut input.as_slice()).expect("can parse CBG header");
79 assert_eq!(input.len(), header.len as usize);
80 println!("{header:?}");
81
82 for (i, record) in headers.records.iter().enumerate() {
83 let mut input = &input[record.game_offset..];
84 let game = game.parse_next(&mut input).unwrap();
85 println!(":: {i}: {game:?}");
86 }
87 }
89
90fn permute(x: u8, count: usize) -> u8 {
93 let index = x.wrapping_sub(count as u8) as usize;
94 PERMUTE[index]
95}
96
97#[derive(Clone, Copy, Debug)]
99pub enum Token {
100 Pop,
101 Push,
102 Move(Move),
103 Skip,
104}
105
106#[derive(Clone, Copy, Debug)]
107pub enum Move {
108 Null,
109 King(i8, i8),
111 Castle(i8),
113 Queen(u8, u8, u8),
115 Rook(u8, u8, u8),
117 Bishop(u8, u8, u8),
119 Knight(u8, i8, i8),
121 Pawn(File, PawnMove),
123 FromTo(Square, Square, Role),
133}
134
135#[repr(u8)]
136#[derive(Clone, Copy, Debug)]
137pub enum PawnMove {
138 One,
139 Two,
140 Left,
142 Right,
143}
144
145pub fn token<'a>(count: &mut usize) -> impl FnMut(&mut Input<'a>) -> ModalResult<Token> {
147 use Move::{Castle, King, Null, Pawn};
148
149 move |input: &mut Input<'_>| {
150 if input.is_empty() {
154 return Ok(Token::Pop);
155 }
156
157 let byte = permute(u8.parse_next(input)?, *count);
158
159 let play = match byte {
160 0x00 => Null,
162
163 0x01 => King(0, 1),
165 0x02 => King(1, 1),
166 0x03 => King(1, 0),
167 0x04 => King(1, -1),
168 0x05 => King(0, -1),
169 0x06 => King(-1, -1),
170 0x07 => King(-1, 0),
171 0x08 => King(-1, 1),
172 0x09 => Castle(2),
173 0x0a => Castle(-2),
174
175 queen @ 0x0b..=0x26 => queen_move(0, queen - 0x0b),
177 queen @ 0x8f..=0xaa => queen_move(1, queen - 0x8f),
178 queen @ 0xab..=0xc6 => queen_move(2, queen - 0xab),
179
180 rook @ 0x27..=0x34 => rook_move(0, rook - 0x27),
182 rook @ 0x35..=0x42 => rook_move(1, rook - 0x35),
183 rook @ 0xc7..=0xd4 => rook_move(2, rook - 0xc7),
184
185 bishop @ 0x43..=0x50 => bishop_move(0, bishop - 0x43),
187 bishop @ 0x51..=0x5e => bishop_move(1, bishop - 0x51),
188 bishop @ 0xd5..=0xe2 => bishop_move(2, bishop - 0xd5),
189
190 knight @ 0x5f..=0x66 => knight_move(0, knight - 0x5f),
192 knight @ 0x67..=0x6e => knight_move(1, knight - 0x67),
193 knight @ 0xe3..=0xea => knight_move(2, knight - 0xea),
194
195 pawn @ 0x6f..=0x8e => {
197 use PawnMove::*;
198
199 let offset = pawn - 0x6f;
200 let file = File::panicky_from_index(offset / 8);
201 match offset % 4 {
202 0 => Pawn(file, One),
203 1 => Pawn(file, Two),
204 2 => Pawn(file, Right),
205 3 => Pawn(file, Left),
206 _ => unreachable!(),
207 }
208 }
209
210 0xeb => {
212 use Role::*;
213
214 let hi = permute(u8.parse_next(input)?, *count) as u16;
215 let lo = permute(u8.parse_next(input)?, *count) as u16;
216 let word = (hi << 8) | lo;
217
218 let from = word & 63;
219 let to = (word >> 6) & 63;
220
221 let promoted = match (word >> 12) & 3 {
222 0 => Queen,
223 1 => Rook,
224 2 => Bishop,
225 3 => Knight,
226 _ => unreachable!(),
227 };
228
229 Move::FromTo(
230 Square::panicky_from_index(from as u8),
231 Square::panicky_from_index(to as u8),
232 promoted,
233 )
234 }
235
236 0xec..=0xfd => return Ok(Token::Skip),
238
239 0xfe => return Ok(Token::Push),
241
242 0xff => return Ok(Token::Pop),
244 };
245
246 *count += 1;
247 Ok(Token::Move(play))
248 }
249}
250
251fn queen_move(i: u8, queen: u8) -> Move {
252 use Move::Queen;
253
254 match queen {
255 y @ 0x00..=0x06 => Queen(i, 0, y + 1), x @ 0x07..=0x14 => Queen(i, x - 6, 0), d @ 0x15..=0x1b => Queen(i, d - 0x14, d - 0x14), e @ 0x1c..=0x22 => Queen(i, e - 0x1b, 0x1c + 7 - e), _ => unreachable!(),
260 }
261}
262
263fn rook_move(i: u8, rook: u8) -> Move {
264 use Move::Rook;
265
266 match rook {
267 x @ 0x0..=0x6 => Rook(i, 0, x + 1), y @ 0x7..=0xd => Rook(i, y - 6, 0), _ => unreachable!(),
270 }
271}
272
273fn bishop_move(i: u8, bishop: u8) -> Move {
274 use Move::Bishop;
275
276 match bishop {
277 d @ 0x0..=0x6 => Bishop(i, d + 1, d + 1), e @ 0x7..=0xd => Bishop(i, e - 6, 14 - e), _ => unreachable!(),
280 }
281}
282
283fn knight_move(i: u8, knight: u8) -> Move {
284 use Move::Knight;
285
286 match knight {
287 0 => Knight(i, 2, 1),
288 1 => Knight(i, 1, 2),
289 2 => Knight(i, -1, 2),
290 3 => Knight(i, -2, 1),
291 4 => Knight(i, -2, -1),
292 5 => Knight(i, -1, -2),
293 6 => Knight(i, 1, -2),
294 7 => Knight(i, 2, -1),
295 _ => unreachable!(),
296 }
297}
298
299pub const PERMUTE: [u8; 256] = [
300 0xa2, 0x95, 0x43, 0xf5, 0xc1, 0x3d, 0x4a, 0x6c, 0x53, 0x83, 0xcc, 0x7c, 0xff, 0xae, 0x68, 0xad, 0xd1, 0x92, 0x8b, 0x8d, 0x35, 0x81, 0x5e, 0x74, 0x26, 0x8e, 0xab, 0xca, 0xfd, 0x9a, 0xf3, 0xa0, 0xa5, 0x15, 0xfc, 0xb1, 0x1e, 0xed, 0x30, 0xea, 0x22, 0xeb, 0xa7, 0xcd, 0x4e, 0x6f, 0x2e, 0x24, 0x32, 0x94, 0x41, 0x8c, 0x6e, 0x58, 0x82, 0x50, 0xbb, 0x02, 0x8a, 0xd8, 0xfa, 0x60, 0xde, 0x52, 0xba, 0x46, 0xac, 0x29, 0x9d, 0xd7, 0xdf, 0x08, 0x21, 0x01, 0x66, 0xa3, 0xf1, 0x19, 0x27, 0xb5, 0x91, 0xd5, 0x42, 0x0e, 0xb4, 0x4c, 0xd9, 0x18, 0x5f, 0xbc, 0x25, 0xa6, 0x96, 0x04, 0x56, 0x6a, 0xaa, 0x33, 0x1c, 0x2b, 0x73, 0xf0, 0xdd, 0xa4, 0x37, 0xd3, 0xc5, 0x10, 0xbf, 0x5a, 0x23, 0x34, 0x75, 0x5b, 0xb8, 0x55, 0xd2, 0x6b, 0x09, 0x3a, 0x57, 0x12, 0xb3, 0x77, 0x48, 0x85, 0x9b, 0x0f, 0x9e, 0xc7, 0xc8, 0xa1, 0x7f, 0x7a, 0xc0, 0xbd, 0x31, 0x6d, 0xf6, 0x3e, 0xc3, 0x11, 0x71, 0xce, 0x7d, 0xda, 0xa8, 0x54, 0x90, 0x97, 0x1f, 0x44, 0x40, 0x16, 0xc9, 0xe3, 0x2c, 0xcb, 0x84, 0xec, 0x9f, 0x3f, 0x5c, 0xe6, 0x76, 0x0b, 0x3c, 0x20, 0xb7, 0x36, 0x00, 0xdc, 0xe7, 0xf9, 0x4f, 0xf7, 0xaf, 0x06, 0x07, 0xe0, 0x1a, 0x0a, 0xa9, 0x4b, 0x0c, 0xd6, 0x63, 0x87, 0x89, 0x1d, 0x13, 0x1b, 0xe4, 0x70, 0x05, 0x47, 0x67, 0x7b, 0x2f, 0xee, 0xe2, 0xe8, 0x98, 0x0d, 0xef, 0xcf, 0xc4, 0xf4, 0xfb, 0xb0, 0x17, 0x99, 0x64, 0xf2, 0xd4, 0x2a, 0x03, 0x4d, 0x78, 0xc6, 0xfe, 0x65, 0x86, 0x88, 0x79, 0x45, 0x3b, 0xe5, 0x49, 0x8f, 0x2d, 0xb9, 0xbe, 0x62, 0x93, 0x14, 0xe9, 0xd0, 0x38, 0x9c, 0xb2, 0xc2, 0x59, 0x5d, 0xb6, 0x72, 0x51, 0xf8, 0x28, 0x7e, 0x61, 0x39, 0xe1, 0xdb, 0x69, 0x80, ];