1#![crate_name = "chess_huffman"]
2
3mod codes;
4mod pgn;
5mod psqt;
6mod ranking;
7#[cfg(test)]
8mod tests;
9
10use bitm::BitAccess;
11use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
12use codes::Book;
13use minimum_redundancy::{Decoder, DecodingResult};
14use shakmaty::san::{ParseSanError, SanError};
15use shakmaty::{Chess, Move, PlayError, Position};
16use std::fmt;
17use std::io::Cursor;
18
19pub type EncodeResult<T> = Result<T, GameEncodeError>;
21pub type DecodeResult<T> = Result<T, GameDecodeError>;
23
24#[derive(Debug)]
26pub struct GameEncodeError {
27 pub kind: GameEncodeErrorKind,
29 pub explanation: String,
31}
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
35pub enum GameEncodeErrorKind {
36 IoError,
38 HuffmanEncodeError,
40 SanError,
42 ParseSanError,
44 IllegalMove,
46}
47
48impl std::error::Error for GameEncodeError {}
49
50impl fmt::Display for GameEncodeError {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "Failed to encode chess game: {}", &self.explanation)
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct GameDecodeError {}
59
60impl std::error::Error for GameDecodeError {}
61
62impl fmt::Display for GameDecodeError {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(f, "Cannot decode invalid bit vector")
65 }
66}
67
68impl From<std::io::Error> for GameEncodeError {
69 fn from(inner: std::io::Error) -> Self {
70 Self {
71 kind: GameEncodeErrorKind::IoError,
72 explanation: format!("I/O Error: {inner}"),
73 }
74 }
75}
76
77impl From<SanError> for GameEncodeError {
78 fn from(inner: SanError) -> Self {
79 Self {
80 kind: GameEncodeErrorKind::SanError,
81 explanation: format!("Illegal or ambiguous SAN: {inner}"),
82 }
83 }
84}
85
86impl From<ParseSanError> for GameEncodeError {
87 fn from(inner: ParseSanError) -> Self {
88 Self {
89 kind: GameEncodeErrorKind::SanError,
90 explanation: format!("Unable to parse SAN: {inner}"),
91 }
92 }
93}
94
95impl From<PlayError<Chess>> for GameDecodeError {
96 fn from(_: PlayError<Chess>) -> Self {
97 Self {}
98 }
99}
100
101#[derive(Debug, Eq, Hash, PartialEq, Clone)]
103pub struct EncodedGame {
104 pub inner: Vec<u64>,
105 pub bit_index: usize,
106}
107
108#[derive(Debug, Copy, Clone, PartialEq, Eq)]
110pub enum EncodedGameConstructionError {
111 EmptyBytes,
112 InvalidBytes,
113}
114
115impl std::error::Error for EncodedGameConstructionError {}
116
117impl fmt::Display for EncodedGameConstructionError {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(
120 f,
121 "{}",
122 match self {
123 EncodedGameConstructionError::EmptyBytes =>
124 "Cannot construct EncodedGame from empty byte slice",
125 EncodedGameConstructionError::InvalidBytes =>
126 "Invalid bytes for constructing EncodedGame",
127 }
128 )
129 }
130}
131
132impl EncodedGame {
133 #[must_use]
135 pub fn to_bytes(&self) -> Vec<u8> {
136 let byte_count = if self.bit_index % 8 == 0 {
137 self.bit_index / 8
138 } else {
139 self.bit_index / 8 + 1
140 };
141 #[allow(clippy::cast_possible_truncation)]
142 let m = (self.bit_index % 64) as u8;
143 let padding = if m == 0 { 0 } else { 64 - m };
144
145 let mut wrt = Vec::with_capacity(self.inner.len() * 8 + 1);
146 for x in &self.inner {
147 wrt.write_u64::<LittleEndian>(*x).unwrap();
148 }
149 wrt.truncate(byte_count);
150 wrt.push(padding);
151
152 wrt
153 }
154
155 #[must_use]
160 pub fn from_bytes(bytes: &[u8]) -> Result<Self, EncodedGameConstructionError> {
161 if bytes.is_empty() {
162 return Err(EncodedGameConstructionError::EmptyBytes);
163 }
164
165 let total_len_minus_one = bytes.len() - 1;
166
167 let padding = (bytes[total_len_minus_one] & 0b0011_1111) as usize;
171
172 if padding != 0 && total_len_minus_one == 0 {
173 return Err(EncodedGameConstructionError::InvalidBytes);
174 }
175
176 let padding_bytes = padding / 8;
177 let bit_index = total_len_minus_one * 8 - (padding % 8);
178 let content_slice = &bytes[0..total_len_minus_one];
179 let full_slice = if padding_bytes != 0 {
180 &[content_slice, &vec![0; padding_bytes]].concat()
181 } else {
182 content_slice
183 };
184 let mut rdr = Cursor::new(full_slice);
185 let mut buffer = vec![];
186 while let Ok(x) = rdr.read_u64::<LittleEndian>() {
187 buffer.push(x);
188 }
189
190 if bit_index > buffer.len() * 64 {
191 return Err(EncodedGameConstructionError::InvalidBytes);
192 }
193
194 Ok(EncodedGame {
195 inner: buffer,
196 bit_index,
197 })
198 }
199
200 fn new() -> Self {
201 Self {
202 inner: vec![0; 256 / 64],
203 bit_index: 0,
204 }
205 }
206}
207
208pub fn encode_game(moves: &[Move]) -> EncodeResult<EncodedGame> {
248 let mut encoder = MoveByMoveEncoder::new();
249 for &m in moves {
250 encoder.add_move(m)?;
251 }
252 Ok(encoder.result)
253}
254
255pub fn encode_pgn<T: AsRef<[u8]>>(pgn: T) -> EncodeResult<EncodedGame> {
278 let mut reader = pgn_reader::Reader::new(Cursor::new(pgn.as_ref()));
279
280 let mut encoder = pgn::Encoder::new();
281 let bits = reader
282 .read_game(&mut encoder)?
283 .unwrap_or_else(|| Ok(EncodedGame::new()))?;
284 Ok(bits)
285}
286
287pub fn encode_pgn_file<P: AsRef<std::path::Path>>(path: P) -> EncodeResult<EncodedGame> {
298 let file = std::fs::File::open(path)?;
299 let mut reader = pgn_reader::Reader::new(file);
300
301 let mut encoder = pgn::Encoder::new();
302 let bits = reader
303 .read_game(&mut encoder)?
304 .unwrap_or_else(|| Ok(EncodedGame::new()))?;
305 Ok(bits)
306}
307
308pub fn decode_game(encoded: &EncodedGame) -> DecodeResult<(Vec<Move>, Vec<Chess>)> {
331 let mut moves = vec![];
332 let mut positions = vec![];
333
334 let decoder = MoveByMoveDecoder::new(encoded);
335 for d in decoder.into_iter_moves_and_positions() {
336 let (m, pos) = d?;
337 moves.push(m);
338 positions.push(pos);
339 }
340
341 Ok((moves, positions))
342}
343
344pub struct MoveByMoveDecoder<'a> {
367 bit_iter: bitm::BitIterator<'a>,
368 huff_decoder: Decoder<'a, u8>,
369 pos: Chess,
370}
371
372impl<'a> MoveByMoveDecoder<'a> {
373 #[must_use]
375 pub fn new(encoded: &'a EncodedGame) -> Self {
376 let huff_decoder = codes::get_decoder();
377 let bit_iter = encoded.inner.bit_in_range_iter(0..encoded.bit_index);
378 Self {
379 bit_iter,
380 huff_decoder,
381 pos: Chess::default(),
382 }
383 }
384}
385
386impl MoveByMoveDecoder<'_> {
387 pub fn next_move(&mut self) -> Option<DecodeResult<Move>> {
389 match self.huff_decoder.decode_next(&mut self.bit_iter) {
390 DecodingResult::Value(rank) => {
391 let m =
392 ranking::nth_from_position(*rank as usize, &self.pos).ok_or(GameDecodeError {});
393 match m {
394 Ok(m) => {
395 self.pos.play_unchecked(m);
396
397 Some(Ok(m))
398 }
399 Err(e) => Some(Err(e)),
400 }
401 }
402 DecodingResult::Invalid => {
403 Some(Err(GameDecodeError {}))
404 }
406 DecodingResult::Incomplete => {
407 if self.huff_decoder.consumed_fragments() == 0 {
408 None
409 } else {
410 Some(Err(GameDecodeError {}))
411 }
412 }
413 }
414 }
415
416 pub fn next_position(&mut self) -> Option<DecodeResult<&Chess>> {
418 if let Some(move_result) = self.next_move() {
419 match move_result {
420 Ok(_) => Some(Ok(&self.pos)),
421 Err(e) => Some(Err(e)),
422 }
423 } else {
424 None
425 }
426 }
427
428 pub fn next_move_and_position(&mut self) -> Option<DecodeResult<(Move, &Chess)>> {
430 if let Some(move_result) = self.next_move() {
431 match move_result {
432 Ok(m) => Some(Ok((m, &self.pos))),
433 Err(e) => Some(Err(e)),
434 }
435 } else {
436 None
437 }
438 }
439
440 pub fn into_iter_moves(self) -> impl Iterator<Item = DecodeResult<Move>> {
444 struct MoveIter<'a> {
445 decoder: MoveByMoveDecoder<'a>,
446 error: bool,
447 }
448 impl Iterator for MoveIter<'_> {
449 type Item = DecodeResult<Move>;
450
451 fn next(&mut self) -> Option<Self::Item> {
452 if self.error {
453 return Some(Err(GameDecodeError {}));
454 }
455
456 let item = self.decoder.next_move();
457 if let Some(Err(_)) = item {
458 self.error = true;
459 }
460 item
461 }
462 }
463
464 MoveIter {
465 decoder: self,
466 error: false,
467 }
468 }
469
470 pub fn into_iter_positions(self) -> impl Iterator<Item = DecodeResult<Chess>> {
475 struct PosIter<'a> {
476 decoder: MoveByMoveDecoder<'a>,
477 error: bool,
478 }
479 impl Iterator for PosIter<'_> {
480 type Item = DecodeResult<Chess>;
481
482 fn next(&mut self) -> Option<Self::Item> {
483 if self.error {
484 return Some(Err(GameDecodeError {}));
485 }
486
487 let item = self.decoder.next_position().map(|r| r.cloned());
488 if let Some(Err(_)) = item {
489 self.error = true;
490 }
491 item
492 }
493 }
494
495 PosIter {
496 decoder: self,
497 error: false,
498 }
499 }
500
501 pub fn into_iter_moves_and_positions(
506 self,
507 ) -> impl Iterator<Item = DecodeResult<(Move, Chess)>> {
508 struct MovePosIter<'a> {
509 decoder: MoveByMoveDecoder<'a>,
510 error: bool,
511 }
512 impl Iterator for MovePosIter<'_> {
513 type Item = DecodeResult<(Move, Chess)>;
514
515 fn next(&mut self) -> Option<Self::Item> {
516 if self.error {
517 return Some(Err(GameDecodeError {}));
518 }
519
520 let item = self
521 .decoder
522 .next_move_and_position()
523 .map(|r| r.map(|(m, p)| (m, p.clone())));
524 if let Some(Err(_)) = item {
525 self.error = true;
526 }
527 item
528 }
529 }
530
531 MovePosIter {
532 decoder: self,
533 error: false,
534 }
535 }
536}
537
538pub struct MoveByMoveEncoder<'a> {
557 book: &'a Book,
558 pub pos: Chess,
560 pub result: EncodedGame,
562}
563
564impl MoveByMoveEncoder<'_> {
565 #[must_use]
567 pub fn new() -> Self {
568 let book = &*codes::BOOK_FROM_LICHESS_WEIGHTS;
569 Self {
570 book,
571 pos: Chess::default(),
572 result: EncodedGame::new(),
573 }
574 }
575
576 pub fn add_move(&mut self, m: Move) -> EncodeResult<()> {
599 match ranking::move_rank(&self.pos, m) {
600 Some(rank) => {
601 if rank > 255 {
602 return Err(GameEncodeError {
603 kind: GameEncodeErrorKind::HuffmanEncodeError,
604 explanation: String::from(
605 "Too many possible valid moves - is this a valid chess position?",
606 ),
607 });
608 }
609 #[allow(clippy::cast_possible_truncation)]
610 self.book.encode(&mut self.result, rank as u8);
611 self.pos.play_unchecked(m);
612 }
613 None => {
614 return Err(GameEncodeError {
615 kind: GameEncodeErrorKind::IllegalMove,
616 explanation: format!("Illegal move {m}"),
617 });
618 }
619 }
620
621 Ok(())
622 }
623
624 pub fn clear(&mut self) {
626 self.pos = Chess::default();
627 self.result = EncodedGame::new();
628 }
629}
630
631impl Default for MoveByMoveEncoder<'_> {
632 fn default() -> Self {
633 Self::new()
634 }
635}