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}