diff --git a/games/src/ataxx/position.rs b/games/src/ataxx/position.rs index 4b4ba17..a4217ba 100644 --- a/games/src/ataxx/position.rs +++ b/games/src/ataxx/position.rs @@ -27,8 +27,8 @@ use crate::ataxx::{ Rank, Square, ColorParseError, Color }; -use crate::MoveList; -use crate::MoveStore; +use crate::interface::MoveList; +use crate::interface::MoveStore; use super::moves; diff --git a/games/src/bitboard.rs b/games/src/interface/bitboard.rs similarity index 77% rename from games/src/bitboard.rs rename to games/src/interface/bitboard.rs index bc58cee..70072f2 100644 --- a/games/src/bitboard.rs +++ b/games/src/interface/bitboard.rs @@ -1,6 +1,6 @@ -use std::fmt::Display; use std::ops::BitOr; -use std::str::FromStr; + +use super::{RepresentableType, Square}; pub trait BitBoard: Sized @@ -147,55 +147,3 @@ where ) } } - -pub trait Square: RepresentableType -where - Self::File: RepresentableType, - Self::Rank: RepresentableType, -{ - type File; - type Rank; - - fn new(file: Self::File, rank: Self::Rank) -> Self { - Self::unsafe_from(rank.into() * Self::File::N as u8 + file.into()) - } - - fn file(self) -> Self::File { - Self::File::unsafe_from(self.into() % Self::File::N as u8) - } - - fn rank(self) -> Self::Rank { - Self::Rank::unsafe_from(self.into() / Self::File::N as u8) - } - - fn north(self) -> Self { - Self::unsafe_from(self.into() + Self::File::N as u8) - } - - fn south(self) -> Self { - Self::unsafe_from(self.into() - Self::File::N as u8) - } - - fn east(self) -> Self { - Self::unsafe_from(self.into() + 1) - } - - fn west(self) -> Self { - Self::unsafe_from(self.into() - 1) - } -} - -/// RepresentableType is a basic trait which is implemented by enums with both a -/// binary and string representation and backed by an integer. -pub trait RepresentableType>: - Copy + Eq + FromStr + Display + From + Into -{ - /// N is the number of specializations of the enum. - const N: usize; - - /// unsafe_from unsafely converts the given number into Self. - fn unsafe_from>(number: T) -> Self { - debug_assert!(number.into() < Self::N); - unsafe { std::mem::transmute_copy(&number) } - } -} diff --git a/games/src/interface/mod.rs b/games/src/interface/mod.rs new file mode 100644 index 0000000..0f954b2 --- /dev/null +++ b/games/src/interface/mod.rs @@ -0,0 +1,29 @@ +use std::fmt::Display; +use std::str::FromStr; + +mod bitboard; +mod r#move; +mod piece; +mod position; +mod square; + +pub use bitboard::*; +pub use piece::*; +pub use position::*; +pub use r#move::*; +pub use square::*; + +/// RepresentableType is a basic trait which is implemented by enums with both a +/// binary and string representation and backed by an integer. +pub trait RepresentableType>: + Copy + Eq + FromStr + Display + From + Into +{ + /// N is the number of specializations of the enum. + const N: usize; + + /// unsafe_from unsafely converts the given number into Self. + fn unsafe_from>(number: T) -> Self { + debug_assert!(number.into() < Self::N); + unsafe { std::mem::transmute_copy(&number) } + } +} diff --git a/games/src/interface/move.rs b/games/src/interface/move.rs new file mode 100644 index 0000000..2895ad8 --- /dev/null +++ b/games/src/interface/move.rs @@ -0,0 +1,51 @@ +use std::fmt::Display; +use std::str::FromStr; + +use arrayvec::ArrayVec; + +/// The Move trait should be implemented the move representation of a game. +pub trait Move: FromStr + Display + From + Into { + /// NULL represents the null or the 'do nothing' move. + const NULL: Self; + /// MAX_IN_GAME is a suitably high maximum for the number of move in a game. + const MAX_IN_GAME: usize; + /// MAX_IN_POSITION is a suitably high maximum for the number of move in a + /// single, possibly unreachable position. + const MAX_IN_POSITION: usize; +} + +/// MoveStore is a trait implemented by types which are able to store moves +/// inside themselves and are thus usable in move-generation methods in +/// [Position] like [`Position::generate_moves_into`]. +pub trait MoveStore: Default { + /// push adds the given Move to the MoveStore. + fn push(&mut self, m: M); + + /// len returns the number of [Move]s stored in the MoveStore. + fn len(&self) -> usize; + + /// is_empty checks if no [Move]s are stored in the MoveStore. + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +/// MoveList is a basic implementation of [`MoveStore`] that is used to allow users +/// to utilize move-generation methods without having to implement a [MoveStore] by +/// themselves. It also has utility methods other than the [`MoveStore`] trait. +pub type MoveList = ArrayVec; + +// MoveStore implementation for MoveList. +impl MoveStore for MoveList { + fn push(&mut self, m: M) { + self.push(m); + } + + fn len(&self) -> usize { + self.len() + } + + fn is_empty(&self) -> bool { + self.is_empty() + } +} diff --git a/games/src/interface/piece.rs b/games/src/interface/piece.rs new file mode 100644 index 0000000..d1330bf --- /dev/null +++ b/games/src/interface/piece.rs @@ -0,0 +1,26 @@ +use std::ops::Not; + +use super::RepresentableType; + +/// The ColoredPiece trait should be implemented by the piece representation +/// (with color) for a game. +pub trait ColoredPiece: RepresentableType +where + Self::Piece: RepresentableType, + Self::Color: RepresentableType + Not, +{ + /// Piece is the piece representation for the game. + type Piece; + /// Color is the color representation for the game. + type Color; + + /// new creates a new ColoredPiece from the given Piece and Color. + fn new(piece: Self::Piece, color: Self::Color) -> Self { + Self::unsafe_from(color.into() * Self::Piece::N as u8 + piece.into()) + } + + /// piece returns the Piece part of the given ColoredPiece. + fn piece(&self) -> Self::Piece; + /// color returns the Color part of the given ColoredPiece. + fn color(&self) -> Self::Color; +} diff --git a/games/src/interface/position.rs b/games/src/interface/position.rs new file mode 100644 index 0000000..c30d108 --- /dev/null +++ b/games/src/interface/position.rs @@ -0,0 +1,81 @@ +use std::fmt::Display; +use std::ops::Index; +use std::str::FromStr; + +use super::{BitBoard, ColoredPiece, Move, MoveList, MoveStore}; + +/// Position is a generalized interface for board representations of a wide +/// range of games. It can be used to create game-agnostic software. Tetka +/// provides some of the popular board representations out of the box, but +/// custom ones can also be implemented by the library user. +pub trait Position: + FromStr // FEN parsing support. + + Display // Basic ascii display support. + + Index // Support for fetching various BitBoards. + + Index<::Piece> + + Index<::Color> +where + >::Output: Into, + ::Piece>>::Output: Into, + ::Color>>::Output: Into, + Self::BitBoard: BitBoard, + Self::ColoredPiece: ColoredPiece, + Self::Move: Move, +{ + /// The type for the bitboards used by this board representation. + type BitBoard; + + /// The type for the pieces (with color) used by this board representation. + type ColoredPiece; + + /// The type for one move in this board representation. + type Move; + + // Peeking, insertion, and removal of pieces from the board representation. + + /// insert puts the given piece at the given square. The implementation is + /// free to assume that the square is currently empty. + fn insert(&mut self, sq: ::Square, piece: ::Piece); + /// remove clears the given square, and returns the piece that was there. + fn remove(&mut self, sq: ::Square) -> Option<::Piece>; + /// at returns the piece that is in the given square. + fn at(&self, sq: ::Square) -> Option<::Piece>; + + // Game Result functions. + + /// winner returns the winning side in the current position. + fn winner(&self) -> Option<::Color>; + /// is_game_over returns a boolean representing if the game is over. + fn is_game_over(&self) -> bool { self.winner().is_some() } + + /// after_move returns the position after playing the given move on the + /// current position. The UPDATE_PERIPHERALS flag can be interpreted as + /// toggling the non-essential updated which are done by this function, like + /// the hash function for the position. + fn after_move(mov: Self::Move) -> Self; + + // Move Generation functions for the board representation. + + /// generate_moves_into generates all the moves in the current position into + /// the given move storage. The QUIET and NOISY flags toggles the generation + /// of reversible and irreversible moves respectively. + fn generate_moves_into< + const QUIET: bool, const NOISY: bool, + T: MoveStore + >(&self, movelist: &mut T); + /// generate_moves is similar to generate_moves_into, except that instead of + /// taking some storage as input it stores into a custom stack-based type. + fn generate_moves(&self) -> MoveList { + let mut movelist: MoveList = Default::default(); + self.generate_moves_into::(&mut movelist); + movelist + } + /// count_moves is similar to generate_moves, except instead of returning a + /// list of the available moves, it returns the number of available moves. + /// By default this is simply `generate_moves().len()`, but implementations + /// may take advantage of various optimizations counting as opposed to + /// storing the moves allows to provide a more efficient version. + fn count_moves(&self) -> usize { + self.generate_moves::().len() + } +} diff --git a/games/src/interface/square.rs b/games/src/interface/square.rs new file mode 100644 index 0000000..10a1f1e --- /dev/null +++ b/games/src/interface/square.rs @@ -0,0 +1,38 @@ +use super::RepresentableType; + +pub trait Square: RepresentableType +where + Self::File: RepresentableType, + Self::Rank: RepresentableType, +{ + type File; + type Rank; + + fn new(file: Self::File, rank: Self::Rank) -> Self { + Self::unsafe_from(rank.into() * Self::File::N as u8 + file.into()) + } + + fn file(self) -> Self::File { + Self::File::unsafe_from(self.into() % Self::File::N as u8) + } + + fn rank(self) -> Self::Rank { + Self::Rank::unsafe_from(self.into() / Self::File::N as u8) + } + + fn north(self) -> Self { + Self::unsafe_from(self.into() + Self::File::N as u8) + } + + fn south(self) -> Self { + Self::unsafe_from(self.into() - Self::File::N as u8) + } + + fn east(self) -> Self { + Self::unsafe_from(self.into() + 1) + } + + fn west(self) -> Self { + Self::unsafe_from(self.into() - 1) + } +} diff --git a/games/src/lib.rs b/games/src/lib.rs index 7c9747f..465668a 100644 --- a/games/src/lib.rs +++ b/games/src/lib.rs @@ -1,155 +1,2 @@ -use std::fmt::Display; -use std::ops::{Index, Not}; -use std::str::FromStr; - -use arrayvec::ArrayVec; - pub mod ataxx; -mod bitboard; -pub use bitboard::*; - -/// Position is a generalized interface for board representations of a wide -/// range of games. It can be used to create game-agnostic software. Tetka -/// provides some of the popular board representations out of the box, but -/// custom ones can also be implemented by the library user. -pub trait Position: - FromStr // FEN parsing support. - + Display // Basic ascii display support. - + Index // Support for fetching various BitBoards. - + Index<::Piece> - + Index<::Color> -where - >::Output: Into, - ::Piece>>::Output: Into, - ::Color>>::Output: Into, - Self::BitBoard: bitboard::BitBoard, - Self::ColoredPiece: ColoredPiece, - Self::Move: Move, -{ - /// The type for the bitboards used by this board representation. - type BitBoard; - - /// The type for the pieces (with color) used by this board representation. - type ColoredPiece; - - /// The type for one move in this board representation. - type Move; - - // Peeking, insertion, and removal of pieces from the board representation. - - /// insert puts the given piece at the given square. The implementation is - /// free to assume that the square is currently empty. - fn insert(&mut self, sq: ::Square, piece: ::Piece); - /// remove clears the given square, and returns the piece that was there. - fn remove(&mut self, sq: ::Square) -> Option<::Piece>; - /// at returns the piece that is in the given square. - fn at(&self, sq: ::Square) -> Option<::Piece>; - - // Game Result functions. - - /// winner returns the winning side in the current position. - fn winner(&self) -> Option<::Color>; - /// is_game_over returns a boolean representing if the game is over. - fn is_game_over(&self) -> bool { self.winner().is_some() } - - /// after_move returns the position after playing the given move on the - /// current position. The UPDATE_PERIPHERALS flag can be interpreted as - /// toggling the non-essential updated which are done by this function, like - /// the hash function for the position. - fn after_move(mov: Self::Move) -> Self; - - // Move Generation functions for the board representation. - - /// generate_moves_into generates all the moves in the current position into - /// the given move storage. The QUIET and NOISY flags toggles the generation - /// of reversible and irreversible moves respectively. - fn generate_moves_into< - const QUIET: bool, const NOISY: bool, - T: MoveStore - >(&self, movelist: &mut T); - /// generate_moves is similar to generate_moves_into, except that instead of - /// taking some storage as input it stores into a custom stack-based type. - fn generate_moves(&self) -> MoveList { - let mut movelist: MoveList = Default::default(); - self.generate_moves_into::(&mut movelist); - movelist - } - /// count_moves is similar to generate_moves, except instead of returning a - /// list of the available moves, it returns the number of available moves. - /// By default this is simply `generate_moves().len()`, but implementations - /// may take advantage of various optimizations counting as opposed to - /// storing the moves allows to provide a more efficient version. - fn count_moves(&self) -> usize { - self.generate_moves::().len() - } -} - -/// The Move trait should be implemented the move representation of a game. -pub trait Move: FromStr + Display + From + Into { - /// NULL represents the null or the 'do nothing' move. - const NULL: Self; - /// MAX_IN_GAME is a suitably high maximum for the number of move in a game. - const MAX_IN_GAME: usize; - /// MAX_IN_POSITION is a suitably high maximum for the number of move in a - /// single, possibly unreachable position. - const MAX_IN_POSITION: usize; -} - -/// The ColoredPiece trait should be implemented by the piece representation -/// (with color) for a game. -pub trait ColoredPiece: RepresentableType -where - Self::Piece: RepresentableType, - Self::Color: RepresentableType + Not, -{ - /// Piece is the piece representation for the game. - type Piece; - /// Color is the color representation for the game. - type Color; - - /// new creates a new ColoredPiece from the given Piece and Color. - fn new(piece: Self::Piece, color: Self::Color) -> Self { - Self::unsafe_from(color.into() * Self::Piece::N as u8 + piece.into()) - } - - /// piece returns the Piece part of the given ColoredPiece. - fn piece(&self) -> Self::Piece; - /// color returns the Color part of the given ColoredPiece. - fn color(&self) -> Self::Color; -} - -/// MoveStore is a trait implemented by types which are able to store moves -/// inside themselves and are thus usable in move-generation methods in -/// [Position] like [`Position::generate_moves_into`]. -pub trait MoveStore: Default { - /// push adds the given Move to the MoveStore. - fn push(&mut self, m: M); - - /// len returns the number of [Move]s stored in the MoveStore. - fn len(&self) -> usize; - - /// is_empty checks if no [Move]s are stored in the MoveStore. - fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -/// MoveList is a basic implementation of [`MoveStore`] that is used to allow users -/// to utilize move-generation methods without having to implement a [MoveStore] by -/// themselves. It also has utility methods other than the [`MoveStore`] trait. -pub type MoveList = ArrayVec; - -// MoveStore implementation for MoveList. -impl MoveStore for MoveList { - fn push(&mut self, m: M) { - self.push(m); - } - - fn len(&self) -> usize { - self.len() - } - - fn is_empty(&self) -> bool { - self.is_empty() - } -} +pub mod interface;