Skip to main content

chess/formats/chessbase/
game.rs

1//! Chessbase game file format (CBG)
2
3use core::num::NonZeroU32;
4
5use crate::{
6    Player,
7    board::{PlayerTable, Role, RoleTable},
8    formats::prelude::Framed,
9    position::SideTable,
10    square::{File, Square},
11};
12
13pub mod convert;
14pub mod parse;
15
16pub use parse::{PERMUTE, game, game_with_info, header, info, mode, position, tokens};
17
18/// ChessBase game filename suffix.
19pub const SUFFIX: &str = "cbg";
20
21#[derive(Clone, Debug)]
22pub struct Game {
23    pub position: Position,
24    pub tokens: Vec<Token>,
25}
26
27#[derive(Clone, Debug)]
28pub struct Position {
29    pub pieces: Pieces,
30    pub turn: Player,
31    pub castles: Castles,
32    pub en_passant: Option<File>,
33    pub round: NonZeroU32,
34}
35
36/// The tokens that moves are encoded as.
37#[derive(Clone, Copy, Debug)]
38pub enum Token {
39    Pop,
40    Push,
41    Move(Move),
42    Skip,
43}
44
45#[derive(Clone, Copy, Debug)]
46pub enum Move {
47    Null,
48    // (x, y) moves: -1 <= x,y <= 1
49    King(i8, i8),
50    // x move; x = 2 or x = -2
51    Castle(i8),
52    // queen index 0, 1, or 2, and (x, y) moves: 1 <= x,y < 8
53    Queen(u8, u8, u8),
54    // rook index 0, 1, or 2, and (x, y) moves: 1 <= x,y < 8
55    Rook(u8, u8, u8),
56    // bishop index 0, 1, or 2, and (x, y) moves: 1 <= x,y < 8
57    Bishop(u8, u8, u8),
58    // knight index 0, 1, or 2, and (x, y) moves: -2 <= x,y < 2
59    Knight(u8, i8, i8),
60    // any file
61    Pawn(File, PawnMove),
62    // (from, to, role) squares and valid promotion Role
63    //
64    // It seems that "a fourth piece never promotes, and its movements
65    // are always captured with multiple bytes".
66    //
67    // Only if the move is a pawn move that promotes, is the role valid.
68    //
69    // Any move could be encoded (UCI-style) with this (from, to) method,
70    // but typically it's only done for moving "fourth pieces" or pawns.
71    FromTo(Square, Square, Role),
72}
73
74#[repr(u8)]
75#[derive(Clone, Copy, Debug)]
76pub enum PawnMove {
77    One,
78    Two,
79    // captures, left/right from the perspective of the player
80    Left,
81    Right,
82}
83
84pub type Castles = PlayerTable<SideTable<bool>>;
85
86/// Ten slots cover two original pieces plus eight pawns promoted to the same role.
87pub type Pieces = PlayerTable<RoleTable<[Option<Square>; 10]>>;
88
89impl Pieces {
90    pub fn standard() -> Self {
91        use Player::*;
92        use Role::*;
93        use Square::*;
94
95        let mut pieces = Self::empty();
96        pieces[White][King][0] = Some(E1);
97        pieces[White][Queen][0] = Some(D1);
98        pieces[White][Rook][..2].copy_from_slice(&[Some(A1), Some(H1)]);
99        pieces[White][Bishop][..2].copy_from_slice(&[Some(C1), Some(F1)]);
100        pieces[White][Knight][..2].copy_from_slice(&[Some(B1), Some(G1)]);
101        pieces[White][Pawn][..8].copy_from_slice(&[
102            Some(A2),
103            Some(B2),
104            Some(C2),
105            Some(D2),
106            Some(E2),
107            Some(F2),
108            Some(G2),
109            Some(H2),
110        ]);
111        pieces[Black][King][0] = Some(E8);
112        pieces[Black][Queen][0] = Some(D8);
113        pieces[Black][Rook][..2].copy_from_slice(&[Some(A8), Some(H8)]);
114        pieces[Black][Bishop][..2].copy_from_slice(&[Some(C8), Some(F8)]);
115        pieces[Black][Knight][..2].copy_from_slice(&[Some(B8), Some(G8)]);
116        pieces[Black][Pawn][..8].copy_from_slice(&[
117            Some(A7),
118            Some(B7),
119            Some(C7),
120            Some(D7),
121            Some(E7),
122            Some(F7),
123            Some(G7),
124            Some(H7),
125        ]);
126        pieces
127    }
128}
129
130#[derive(Clone, Copy, Debug)]
131pub struct Header {
132    pub first_game: u16,
133    pub len: u32,
134}
135
136impl Header {
137    pub const LEN: usize = 26;
138}
139
140#[derive(Clone, Copy, Debug)]
141pub struct Info {
142    /// Length of the game body following this information.
143    pub len: usize,
144    pub mode: Mode,
145}
146
147impl Info {
148    pub const LEN: usize = 4;
149}
150
151impl Framed for Info {
152    const LEN: usize = Info::LEN;
153
154    fn total(&self) -> usize {
155        Self::LEN + self.len
156    }
157}
158
159#[repr(transparent)]
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub struct Mode(pub u8);
162
163impl Mode {
164    #[inline]
165    pub const fn is_freestyle(self) -> bool {
166        // The individual meanings of bits one and three are unknown.
167        self.0 & ((1 << 1) | (1 << 3)) != 0
168    }
169
170    #[inline]
171    pub const fn has_special_encoding(self) -> bool {
172        self.0 & (1 << 2) != 0
173    }
174
175    #[inline]
176    pub const fn has_setup(self) -> bool {
177        self.0 & (1 << 6) != 0
178    }
179
180    #[inline]
181    pub const fn is_undecodable(self) -> bool {
182        self.0 & (1 << 7) != 0
183    }
184}