1use crate::*;
3
4#[cfg(not(feature = "std"))]
5extern crate alloc;
6
7#[cfg(not(feature = "std"))]
8use alloc::string::String;
9
10#[cfg(feature = "std")]
11use std::string::String;
12
13use core::fmt::*;
14
15crate::helpers::simple_enum! {
16 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
18 pub enum Piece {
19 Pawn,
21 Lance,
23 Knight,
25 Silver,
27 Bishop,
29 Rook,
31 Gold,
33 King,
35 Tokin,
37 PLance,
39 PKnight,
41 PSilver,
43 PBishop,
45 PRook
47 }
48}
49
50crate::helpers::simple_error! {
56 pub struct PieceParseError = "The value is not a valid Piece.";
58}
59
60impl Piece {
61 pub const HAND_NUM: usize = 7;
63
64 pub const MAX_HAND: [u8; Self::NUM] = [
66 18, 4, 4, 4, 2, 2, 4, 0, 0, 0, 0, 0, 0, 0,
74 ];
75
76 const PROMOTED: [Self; Self::NUM] = [
78 Piece::Tokin,
79 Piece::PLance,
80 Piece::PKnight,
81 Piece::PSilver,
82 Piece::PBishop,
83 Piece::PRook,
84 Piece::Gold,
85 Piece::King,
86 Piece::Tokin,
87 Piece::PLance,
88 Piece::PKnight,
89 Piece::PSilver,
90 Piece::PBishop,
91 Piece::PRook,
92 ];
93
94 const UNPROMOTED: [Self; Self::NUM] = [
96 Piece::Pawn,
97 Piece::Lance,
98 Piece::Knight,
99 Piece::Silver,
100 Piece::Bishop,
101 Piece::Rook,
102 Piece::Gold,
103 Piece::King,
104 Piece::Pawn,
105 Piece::Lance,
106 Piece::Knight,
107 Piece::Silver,
108 Piece::Bishop,
109 Piece::Rook,
110 ];
111
112 #[inline(always)]
114 pub const fn is_promoted(self) -> bool {
115 (self as usize) >= Self::Tokin as usize
116 }
117
118 #[inline(always)]
120 pub const fn is_unpromoted(self) -> bool {
121 (self as usize) < Self::Tokin as usize
122 }
123
124 #[inline(always)]
126 pub const fn is_promotable(self) -> bool {
127 (self as usize) < Self::Gold as usize
128 }
129
130 #[inline(always)]
142 pub const fn can_promote(self, color: Color, square: Square) -> bool {
143 if !self.is_promotable() {
144 false
145 } else {
146 let rank = square.rank() as usize;
147 match color {
148 Color::White => rank > 5,
149 Color::Black => rank < 3,
150 }
151 }
152 }
153
154 #[inline(always)]
177 pub const fn must_promote(self, color: Color, square: Square) -> bool {
178 let rank = square.rank() as usize;
179 if 1 < rank && rank < 7 {
180 return false;
181 }
182 match color {
183 Color::White => match self {
184 Self::Pawn | Self::Lance => rank == 8,
185 Self::Knight => rank >= 7,
186 _ => false,
187 },
188 Color::Black => match self {
189 Self::Pawn | Self::Lance => rank == 0,
190 Self::Knight => rank <= 1,
191 _ => false,
192 },
193 }
194 }
195
196 #[inline(always)]
203 pub const fn can_drop(self, color: Color, square: Square) -> bool {
204 !self.must_promote(color, square)
205 }
206
207 #[inline(always)]
211 pub const fn promote(self) -> Self {
212 Self::PROMOTED[self as usize]
213 }
214
215 #[inline(always)]
219 pub const fn unpromote(self) -> Self {
220 Self::UNPROMOTED[self as usize]
221 }
222
223 pub fn try_from_char(c: char) -> Option<(Self, Color)> {
224 match c {
225 'p' => Some((Self::Pawn, Color::White)),
226 'l' => Some((Self::Lance, Color::White)),
227 'n' => Some((Self::Knight, Color::White)),
228 's' => Some((Self::Silver, Color::White)),
229 'g' => Some((Self::Gold, Color::White)),
230 'r' => Some((Self::Rook, Color::White)),
231 'b' => Some((Self::Bishop, Color::White)),
232 'k' => Some((Self::King, Color::White)),
233 'P' => Some((Self::Pawn, Color::Black)),
234 'L' => Some((Self::Lance, Color::Black)),
235 'N' => Some((Self::Knight, Color::Black)),
236 'S' => Some((Self::Silver, Color::Black)),
237 'G' => Some((Self::Gold, Color::Black)),
238 'R' => Some((Self::Rook, Color::Black)),
239 'B' => Some((Self::Bishop, Color::Black)),
240 'K' => Some((Self::King, Color::Black)),
241 _ => None,
242 }
243 }
244
245 pub fn try_from_str(s: &str) -> Option<(Self, Color)> {
246 let mut chars = s.chars();
247 let first = chars.next()?; let second = chars.next(); match (first, second) {
251 ('+', Some(c)) => match c {
253 'p' => Some((Self::Tokin, Color::White)),
254 'l' => Some((Self::PLance, Color::White)),
255 'n' => Some((Self::PKnight, Color::White)),
256 's' => Some((Self::PSilver, Color::White)),
257 'b' => Some((Self::PBishop, Color::White)),
258 'r' => Some((Self::PRook, Color::White)),
259 'P' => Some((Self::Tokin, Color::Black)),
260 'L' => Some((Self::PLance, Color::Black)),
261 'N' => Some((Self::PKnight, Color::Black)),
262 'S' => Some((Self::PSilver, Color::Black)),
263 'B' => Some((Self::PBishop, Color::Black)),
264 'R' => Some((Self::PRook, Color::Black)),
265 _ => None,
266 },
267 (c, None) => match c {
269 'p' => Some((Self::Pawn, Color::White)),
270 'l' => Some((Self::Lance, Color::White)),
271 'n' => Some((Self::Knight, Color::White)),
272 's' => Some((Self::Silver, Color::White)),
273 'g' => Some((Self::Gold, Color::White)),
274 'r' => Some((Self::Rook, Color::White)),
275 'b' => Some((Self::Bishop, Color::White)),
276 'k' => Some((Self::King, Color::White)),
277 'P' => Some((Self::Pawn, Color::Black)),
278 'L' => Some((Self::Lance, Color::Black)),
279 'N' => Some((Self::Knight, Color::Black)),
280 'S' => Some((Self::Silver, Color::Black)),
281 'G' => Some((Self::Gold, Color::Black)),
282 'R' => Some((Self::Rook, Color::Black)),
283 'B' => Some((Self::Bishop, Color::Black)),
284 'K' => Some((Self::King, Color::Black)),
285 _ => None,
286 },
287 _ => None,
289 }
290 }
291
292 pub fn to_str(self, color: Color) -> String {
293 let s: &str = match self {
294 Self::Pawn => "p",
295 Self::Lance => "l",
296 Self::Knight => "n",
297 Self::Silver => "s",
298 Self::Bishop => "b",
299 Self::Rook => "r",
300 Self::Gold => "g",
301 Self::King => "k",
302 Self::Tokin => "+p",
303 Self::PLance => "+l",
304 Self::PKnight => "+n",
305 Self::PSilver => "+s",
306 Self::PBishop => "+b",
307 Self::PRook => "+r",
308 };
309
310 if color == Color::Black {
311 s.to_uppercase()
312 } else {
313 String::from(s)
314 }
315 }
316}
317
318impl core::str::FromStr for Piece {
319 type Err = PieceParseError;
320
321 fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
322 Piece::try_from_str(s)
323 .map(|(piece, _color)| piece) .ok_or(PieceParseError) }
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
329pub struct ColoredPiece {
330 pub piece: Piece,
331 pub color: Color,
332}
333
334impl core::str::FromStr for ColoredPiece {
335 type Err = PieceParseError;
336
337 fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
338 Piece::try_from_str(s)
339 .map(|(piece, color)| ColoredPiece { piece, color })
340 .ok_or(PieceParseError)
341 }
342}
343
344impl core::fmt::Display for ColoredPiece {
345 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
346 write!(f, "{}", self.piece.to_str(self.color))
347 }
348}