Skip to main content

secondbest/board/
types.rs

1//! Types and enums for the game board.
2//!
3//! This module defines the core types used to represent the game board, including:
4//! - `Position`: Represents positions on the circular board
5//! - `Color`: Represents piece colors (Black and White)
6//! - `Action`: Represents possible game actions (Put and Move)
7//! - `PositionIter`: Iterator for board positions
8//!
9//! These types provide a high-level representation of the game elements, built on top
10//! of the efficient bit-based implementation in the `bitboard` module.
11
12/// Represents a position on the game board.
13///
14/// The board consists of 8 positions arranged in a circle, named after cardinal and intercardinal directions.
15/// Each position is represented by a specific bit pattern for efficient bit-based operations.
16///
17/// # Examples
18///
19/// ```
20/// use secondbest::board::Position;
21///
22/// // Using a specific position
23/// let north_position = Position::N;
24///
25/// // Iterating through all positions
26/// for position in Position::iter() {
27///     println!("Position: {:?}", position);
28/// }
29/// ```
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31#[repr(u32)]
32pub enum Position {
33    /// North position (bit pattern: 0x0000_0008)
34    N = 0x0000_0008,
35    /// Northeast position (bit pattern: 0x0000_0080)
36    NE = 0x0000_0080,
37    /// East position (bit pattern: 0x0000_0800)
38    E = 0x0000_0800,
39    /// Southeast position (bit pattern: 0x0000_8000)
40    SE = 0x0000_8000,
41    /// South position (bit pattern: 0x0008_0000)
42    S = 0x0008_0000,
43    /// Southwest position (bit pattern: 0x0080_0000)
44    SW = 0x0080_0000,
45    /// West position (bit pattern: 0x0800_0000)
46    W = 0x0800_0000,
47    /// Northwest position (bit pattern: 0x8000_0000)
48    NW = 0x8000_0000,
49}
50
51impl Position {
52    /// Creates a Position from a u32 value without checking if it's valid.
53    ///
54    /// # Safety
55    ///
56    /// This function is unsafe because it does not verify that the input value
57    /// represents a valid Position. The caller must ensure that the value is one
58    /// of the valid bit patterns defined in the Position enum.
59    pub(crate) unsafe fn from_u32_unchecked(value: u32) -> Self {
60        unsafe { std::mem::transmute(value) }
61    }
62
63    /// Returns an iterator over all positions on the board.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use secondbest::board::Position;
69    ///
70    /// // Iterate through all positions
71    /// for position in Position::iter() {
72    ///     println!("Position: {:?}", position);
73    /// }
74    /// ```
75    pub fn iter() -> PositionIter {
76        PositionIter::new()
77    }
78}
79
80impl From<Position> for u32 {
81    fn from(value: Position) -> Self {
82        value as u32
83    }
84}
85
86impl TryFrom<u32> for Position {
87    type Error = crate::error::Error;
88    fn try_from(value: u32) -> Result<Self, Self::Error> {
89        // Check if the value has bits in valid position bit patterns (non-zero when ANDed with 0x8888_8888)
90        // and has exactly one bit set (valid positions have only one bit set)
91        if (value & 0x8888_8888) != 0 && value.count_ones() == 1 {
92            Ok(unsafe { Position::from_u32_unchecked(value) })
93        } else {
94            Err(crate::error::Error::Position(value))
95        }
96    }
97}
98
99impl std::fmt::Display for Position {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        use Position::*;
102        let s = match self {
103            N => "N",
104            NE => "NE",
105            E => "E",
106            SE => "SE",
107            S => "S",
108            SW => "SW",
109            W => "W",
110            NW => "NW",
111        };
112        write!(f, "{}", s)
113    }
114}
115
116/// An iterator that yields all positions on the board in sequence.
117///
118/// This iterator returns positions in the order: N, NE, E, SE, S, SW, W, NW.
119#[derive(Debug)]
120pub struct PositionIter {
121    value: u32,
122}
123
124impl PositionIter {
125    /// Creates a new position iterator.
126    fn new() -> Self {
127        Self { value: 0x0000_0008 }
128    }
129}
130
131impl Iterator for PositionIter {
132    type Item = Position;
133    fn next(&mut self) -> Option<Self::Item> {
134        if self.value == 0 {
135            None
136        } else {
137            let pos = unsafe { Position::from_u32_unchecked(self.value) };
138            self.value <<= 4;
139            Some(pos)
140        }
141    }
142}
143
144/// Represents a player's color in the game.
145///
146/// There are two colors in the game:
147/// - `B` for Black
148/// - `W` for White
149///
150/// # Examples
151///
152/// ```
153/// use secondbest::board::Color;
154///
155/// let black = Color::B;
156/// let white = Color::W;
157///
158/// // Get the opposite color
159/// let opposite_of_black = black.opposite();
160/// assert_eq!(opposite_of_black, Color::W);
161/// ```
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
163pub enum Color {
164    /// Black piece
165    B,
166    /// White piece
167    W,
168}
169
170impl Color {
171    /// Returns the opposite color.
172    ///
173    /// # Returns
174    /// * `Color::W` if called on `Color::B`
175    /// * `Color::B` if called on `Color::W`
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use secondbest::board::Color;
181    ///
182    /// assert_eq!(Color::B.opposite(), Color::W);
183    /// assert_eq!(Color::W.opposite(), Color::B);
184    /// ```
185    pub fn opposite(self) -> Self {
186        use Color::*;
187        match self {
188            B => W,
189            W => B,
190        }
191    }
192}
193
194impl From<bool> for Color {
195    fn from(value: bool) -> Self {
196        if value { Color::B } else { Color::W }
197    }
198}
199
200impl From<Color> for bool {
201    fn from(value: Color) -> Self {
202        matches!(value, Color::B)
203    }
204}
205
206impl std::fmt::Display for Color {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        use Color::*;
209        let s = match self {
210            B => "Black",
211            W => "White",
212        };
213        write!(f, "{}", s)
214    }
215}
216
217/// Represents an action that a player can perform in the game.
218///
219/// Actions can be either placing a new piece on the board (`Put`) or
220/// moving an existing piece from one position to another (`Move`).
221///
222/// # Examples
223///
224/// ```
225/// use secondbest::board::{Action, Position, Color};
226///
227/// // Creating a Put action (placing a black piece at position N)
228/// let put_action = Action::Put(Position::N, Color::B);
229///
230/// // Creating a Move action (moving a piece from position N to position E)
231/// let move_action = Action::Move(Position::N, Position::E);
232/// ```
233#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
234pub enum Action {
235    /// Represents placing a new piece of the specified color at the given position.
236    Put(Position, Color),
237    /// Represents moving a piece from one position to another.
238    Move(Position, Position),
239}
240
241impl std::fmt::Display for Action {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        match self {
244            Action::Put(pos, color) => write!(f, "Put({}, {})", pos, color),
245            Action::Move(from, to) => write!(f, "Move({}, {})", from, to),
246        }
247    }
248}