From fdb2804883deb31e3aeb15bbe588dcc9b7b76bd0 Mon Sep 17 00:00:00 2001 From: Mica White Date: Mon, 8 Dec 2025 19:56:48 -0500 Subject: Stuff --- engine/src/tablebase.rs | 372 ++++++++++++++++++++++++------------------------ 1 file changed, 186 insertions(+), 186 deletions(-) mode change 100644 => 100755 engine/src/tablebase.rs (limited to 'engine/src/tablebase.rs') diff --git a/engine/src/tablebase.rs b/engine/src/tablebase.rs old mode 100644 new mode 100755 index 87bf404..b56bea4 --- 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]>, -} - -#[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, - /// The tablebase author - author_name: Box, - /// 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 { - // magic is used to verify that the file is valid - let magic = reader.read_u32::()?; - if magic != MAGIC { - return Err(TablebaseFileError::MagicError); - } - - read_reserved_bytes::<2>(reader)?; - - let version = reader.read_u16::()?; - if version != SUPPORTED_VERSION { - return Err(TablebaseFileError::UnsupportedVersion(version)); - } - - let magic_factor = reader.read_u64::()?; - let entries_count = reader.read_u64::()?; - let table_length = reader.read_u64::()?; - - 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::()?; - 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(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, 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 { - 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]>, +} + +#[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, + /// The tablebase author + author_name: Box, + /// 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 { + // magic is used to verify that the file is valid + let magic = reader.read_u32::()?; + if magic != MAGIC { + return Err(TablebaseFileError::MagicError); + } + + read_reserved_bytes::<2>(reader)?; + + let version = reader.read_u16::()?; + if version != SUPPORTED_VERSION { + return Err(TablebaseFileError::UnsupportedVersion(version)); + } + + let magic_factor = reader.read_u64::()?; + let entries_count = reader.read_u64::()?; + let table_length = reader.read_u64::()?; + + 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::()?; + 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(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, 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 { + 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, + }) + } +} -- cgit v1.2.3