diff options
| author | Mica White <botahamec@outlook.com> | 2025-12-08 19:56:48 -0500 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2025-12-08 19:56:48 -0500 |
| commit | fdb2804883deb31e3aeb15bbe588dcc9b7b76bd0 (patch) | |
| tree | a4c51cd88664cab7b6673dcd3b425fb7a0202a38 /engine/src/tablebase.rs | |
| parent | 93461aa9399d981db7d8611b3eb166636de4d971 (diff) | |
Diffstat (limited to 'engine/src/tablebase.rs')
| -rwxr-xr-x[-rw-r--r--] | engine/src/tablebase.rs | 372 |
1 files changed, 186 insertions, 186 deletions
diff --git a/engine/src/tablebase.rs b/engine/src/tablebase.rs index 87bf404..b56bea4 100644..100755 --- a/engine/src/tablebase.rs +++ b/engine/src/tablebase.rs @@ -1,186 +1,186 @@ -use std::{io, string::FromUtf8Error}; - -use byteorder::{BigEndian, ReadBytesExt}; -use model::{CheckersBitBoard, PieceColor}; -use thiserror::Error; - -const MAGIC: u32 = u32::from_be_bytes(*b".amp"); -const SUPPORTED_VERSION: u16 = 0; -const MAX_TABLE_LENGTH: u64 = 5_000_000_000; - -#[derive(Debug, Clone, PartialEq)] -pub struct Tablebase { - header: FileHeader, - entries: Box<[Option<TablebaseEntry>]>, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -struct FileHeader { - /// The version of Ampere Tablebase Format being used - version: u16, - /// The magic number multiplied by board hash values - magic_factor: u64, - /// The number of entries in the tablebase - entries_count: u64, - /// The length of the table needed in-memory - table_length: u64, - /// The type of game the tablebase is for - game_type: GameType, - /// The name of the tablebase - tablebase_name: Box<str>, - /// The tablebase author - author_name: Box<str>, - /// The Unix timestamp indicating when the tablebase was created - publication_time: u64, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct GameType { - /// The type of game being played - game_type: Game, - /// The color that makes the first move - start_color: PieceColor, - /// The width of the board - board_width: u8, - /// The height of the board - board_height: u8, - /// The move notation - notation: MoveNotation, - /// True if the bottom-left square is a playing square - invert_flag: bool, -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Game { - EnglishDraughts = 21, -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum MoveNotation { - /// Standard Chess Notation, like e5 - Standard = 0, - /// Alpha-numeric square representation, like e7-e5 - Alpha = 1, - /// Numeric square representation, like 11-12 - Numeric = 2, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -struct TablebaseEntry { - board: CheckersBitBoard, - evaluation: f32, - depth: u8, -} - -#[derive(Debug, Error)] -enum TablebaseFileError { - #[error("Invalid tablebase: the magic header field was incorrect")] - MagicError, - #[error("This version of the tablebase format is unsupported. Only {SUPPORTED_VERSION} is supported")] - UnsupportedVersion(u16), - #[error("The table is too large. The length of the table is {} entries, but the max is only {}", .found, .max)] - TableTooLarge { found: u64, max: u64 }, - #[error("The game type for this tablebase is unsupported. Only standard American Checkers is supported")] - UnsupportedGameType(u8), - #[error("A string was not valid UTF-8: {}", .0)] - InvalidString(#[from] FromUtf8Error), - #[error(transparent)] - IoError(#[from] io::Error), -} - -fn read_header(reader: &mut impl ReadBytesExt) -> Result<FileHeader, TablebaseFileError> { - // magic is used to verify that the file is valid - let magic = reader.read_u32::<BigEndian>()?; - if magic != MAGIC { - return Err(TablebaseFileError::MagicError); - } - - read_reserved_bytes::<2>(reader)?; - - let version = reader.read_u16::<BigEndian>()?; - if version != SUPPORTED_VERSION { - return Err(TablebaseFileError::UnsupportedVersion(version)); - } - - let magic_factor = reader.read_u64::<BigEndian>()?; - let entries_count = reader.read_u64::<BigEndian>()?; - let table_length = reader.read_u64::<BigEndian>()?; - - if table_length > MAX_TABLE_LENGTH { - return Err(TablebaseFileError::TableTooLarge { - found: table_length, - max: MAX_TABLE_LENGTH, - }); - } - - let game_type = read_game_type(reader)?; - let publication_time = reader.read_u64::<BigEndian>()?; - let tablebase_name_len = reader.read_u8()?; - let author_name_len = reader.read_u8()?; - let _ = read_reserved_bytes::<14>(reader); - - let tablebase_name = read_string(reader, tablebase_name_len)?; - let author_name = read_string(reader, author_name_len)?; - - Ok(FileHeader { - version, - magic_factor, - entries_count, - table_length, - game_type, - publication_time, - tablebase_name, - author_name, - }) -} - -fn read_reserved_bytes<const NUM_BYTES: usize>(reader: &mut impl ReadBytesExt) -> io::Result<()> { - reader.read_exact([0; NUM_BYTES].as_mut_slice())?; - Ok(()) -} - -#[derive(Debug, Error)] -enum ReadStringError { - #[error(transparent)] - InvalidUtf8(#[from] FromUtf8Error), - #[error(transparent)] - IoError(#[from] io::Error), -} - -fn read_string(reader: &mut impl ReadBytesExt, len: u8) -> Result<Box<str>, TablebaseFileError> { - let mut buffer = vec![0; len as usize]; - reader.read_exact(&mut buffer)?; - Ok(String::from_utf8(buffer)?.into_boxed_str()) -} - -fn read_game_type(reader: &mut impl ReadBytesExt) -> Result<GameType, TablebaseFileError> { - read_reserved_bytes::<1>(reader)?; - let game_type = reader.read_u8()?; - let start_color = reader.read_u8()?; - let board_width = reader.read_u8()?; - let board_height = reader.read_u8()?; - let invert_flag = reader.read_u8()?; - let notation = reader.read_u8()?; - read_reserved_bytes::<1>(reader)?; - - if game_type != 21 - || start_color != 1 - || board_width != 8 - || board_height != 8 - || invert_flag != 1 - || notation != 2 - { - Err(TablebaseFileError::UnsupportedGameType(game_type)) - } else { - Ok(GameType { - game_type: Game::EnglishDraughts, - start_color: PieceColor::Dark, - board_width: 8, - board_height: 8, - notation: MoveNotation::Numeric, - invert_flag: true, - }) - } -} +use std::{io, string::FromUtf8Error};
+
+use byteorder::{BigEndian, ReadBytesExt};
+use model::{CheckersBitBoard, PieceColor};
+use thiserror::Error;
+
+const MAGIC: u32 = u32::from_be_bytes(*b".amp");
+const SUPPORTED_VERSION: u16 = 0;
+const MAX_TABLE_LENGTH: u64 = 5_000_000_000;
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct Tablebase {
+ header: FileHeader,
+ entries: Box<[Option<TablebaseEntry>]>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct FileHeader {
+ /// The version of Ampere Tablebase Format being used
+ version: u16,
+ /// The magic number multiplied by board hash values
+ magic_factor: u64,
+ /// The number of entries in the tablebase
+ entries_count: u64,
+ /// The length of the table needed in-memory
+ table_length: u64,
+ /// The type of game the tablebase is for
+ game_type: GameType,
+ /// The name of the tablebase
+ tablebase_name: Box<str>,
+ /// The tablebase author
+ author_name: Box<str>,
+ /// The Unix timestamp indicating when the tablebase was created
+ publication_time: u64,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+struct GameType {
+ /// The type of game being played
+ game_type: Game,
+ /// The color that makes the first move
+ start_color: PieceColor,
+ /// The width of the board
+ board_width: u8,
+ /// The height of the board
+ board_height: u8,
+ /// The move notation
+ notation: MoveNotation,
+ /// True if the bottom-left square is a playing square
+ invert_flag: bool,
+}
+
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum Game {
+ EnglishDraughts = 21,
+}
+
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum MoveNotation {
+ /// Standard Chess Notation, like e5
+ Standard = 0,
+ /// Alpha-numeric square representation, like e7-e5
+ Alpha = 1,
+ /// Numeric square representation, like 11-12
+ Numeric = 2,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+struct TablebaseEntry {
+ board: CheckersBitBoard,
+ evaluation: f32,
+ depth: u8,
+}
+
+#[derive(Debug, Error)]
+enum TablebaseFileError {
+ #[error("Invalid tablebase: the magic header field was incorrect")]
+ MagicError,
+ #[error("This version of the tablebase format is unsupported. Only {SUPPORTED_VERSION} is supported")]
+ UnsupportedVersion(u16),
+ #[error("The table is too large. The length of the table is {} entries, but the max is only {}", .found, .max)]
+ TableTooLarge { found: u64, max: u64 },
+ #[error("The game type for this tablebase is unsupported. Only standard American Checkers is supported")]
+ UnsupportedGameType(u8),
+ #[error("A string was not valid UTF-8: {}", .0)]
+ InvalidString(#[from] FromUtf8Error),
+ #[error(transparent)]
+ IoError(#[from] io::Error),
+}
+
+fn read_header(reader: &mut impl ReadBytesExt) -> Result<FileHeader, TablebaseFileError> {
+ // magic is used to verify that the file is valid
+ let magic = reader.read_u32::<BigEndian>()?;
+ if magic != MAGIC {
+ return Err(TablebaseFileError::MagicError);
+ }
+
+ read_reserved_bytes::<2>(reader)?;
+
+ let version = reader.read_u16::<BigEndian>()?;
+ if version != SUPPORTED_VERSION {
+ return Err(TablebaseFileError::UnsupportedVersion(version));
+ }
+
+ let magic_factor = reader.read_u64::<BigEndian>()?;
+ let entries_count = reader.read_u64::<BigEndian>()?;
+ let table_length = reader.read_u64::<BigEndian>()?;
+
+ if table_length > MAX_TABLE_LENGTH {
+ return Err(TablebaseFileError::TableTooLarge {
+ found: table_length,
+ max: MAX_TABLE_LENGTH,
+ });
+ }
+
+ let game_type = read_game_type(reader)?;
+ let publication_time = reader.read_u64::<BigEndian>()?;
+ let tablebase_name_len = reader.read_u8()?;
+ let author_name_len = reader.read_u8()?;
+ let _ = read_reserved_bytes::<14>(reader);
+
+ let tablebase_name = read_string(reader, tablebase_name_len)?;
+ let author_name = read_string(reader, author_name_len)?;
+
+ Ok(FileHeader {
+ version,
+ magic_factor,
+ entries_count,
+ table_length,
+ game_type,
+ publication_time,
+ tablebase_name,
+ author_name,
+ })
+}
+
+fn read_reserved_bytes<const NUM_BYTES: usize>(reader: &mut impl ReadBytesExt) -> io::Result<()> {
+ reader.read_exact([0; NUM_BYTES].as_mut_slice())?;
+ Ok(())
+}
+
+#[derive(Debug, Error)]
+enum ReadStringError {
+ #[error(transparent)]
+ InvalidUtf8(#[from] FromUtf8Error),
+ #[error(transparent)]
+ IoError(#[from] io::Error),
+}
+
+fn read_string(reader: &mut impl ReadBytesExt, len: u8) -> Result<Box<str>, TablebaseFileError> {
+ let mut buffer = vec![0; len as usize];
+ reader.read_exact(&mut buffer)?;
+ Ok(String::from_utf8(buffer)?.into_boxed_str())
+}
+
+fn read_game_type(reader: &mut impl ReadBytesExt) -> Result<GameType, TablebaseFileError> {
+ read_reserved_bytes::<1>(reader)?;
+ let game_type = reader.read_u8()?;
+ let start_color = reader.read_u8()?;
+ let board_width = reader.read_u8()?;
+ let board_height = reader.read_u8()?;
+ let invert_flag = reader.read_u8()?;
+ let notation = reader.read_u8()?;
+ read_reserved_bytes::<1>(reader)?;
+
+ if game_type != 21
+ || start_color != 1
+ || board_width != 8
+ || board_height != 8
+ || invert_flag != 1
+ || notation != 2
+ {
+ Err(TablebaseFileError::UnsupportedGameType(game_type))
+ } else {
+ Ok(GameType {
+ game_type: Game::EnglishDraughts,
+ start_color: PieceColor::Dark,
+ board_width: 8,
+ board_height: 8,
+ notation: MoveNotation::Numeric,
+ invert_flag: true,
+ })
+ }
+}
|
