1use super::Color;
2use alloc::{
3 string::{String, ToString},
4 vec::Vec,
5};
6
7pub const A1: Position = Position::new(0, 0);
8pub const A2: Position = Position::new(1, 0);
9pub const A3: Position = Position::new(2, 0);
10pub const A4: Position = Position::new(3, 0);
11pub const A5: Position = Position::new(4, 0);
12pub const A6: Position = Position::new(5, 0);
13pub const A7: Position = Position::new(6, 0);
14pub const A8: Position = Position::new(7, 0);
15
16pub const B1: Position = Position::new(0, 1);
17pub const B2: Position = Position::new(1, 1);
18pub const B3: Position = Position::new(2, 1);
19pub const B4: Position = Position::new(3, 1);
20pub const B5: Position = Position::new(4, 1);
21pub const B6: Position = Position::new(5, 1);
22pub const B7: Position = Position::new(6, 1);
23pub const B8: Position = Position::new(7, 1);
24
25pub const C1: Position = Position::new(0, 2);
26pub const C2: Position = Position::new(1, 2);
27pub const C3: Position = Position::new(2, 2);
28pub const C4: Position = Position::new(3, 2);
29pub const C5: Position = Position::new(4, 2);
30pub const C6: Position = Position::new(5, 2);
31pub const C7: Position = Position::new(6, 2);
32pub const C8: Position = Position::new(7, 2);
33
34pub const D1: Position = Position::new(0, 3);
35pub const D2: Position = Position::new(1, 3);
36pub const D3: Position = Position::new(2, 3);
37pub const D4: Position = Position::new(3, 3);
38pub const D5: Position = Position::new(4, 3);
39pub const D6: Position = Position::new(5, 3);
40pub const D7: Position = Position::new(6, 3);
41pub const D8: Position = Position::new(7, 3);
42
43pub const E1: Position = Position::new(0, 4);
44pub const E2: Position = Position::new(1, 4);
45pub const E3: Position = Position::new(2, 4);
46pub const E4: Position = Position::new(3, 4);
47pub const E5: Position = Position::new(4, 4);
48pub const E6: Position = Position::new(5, 4);
49pub const E7: Position = Position::new(6, 4);
50pub const E8: Position = Position::new(7, 4);
51
52pub const F1: Position = Position::new(0, 5);
53pub const F2: Position = Position::new(1, 5);
54pub const F3: Position = Position::new(2, 5);
55pub const F4: Position = Position::new(3, 5);
56pub const F5: Position = Position::new(4, 5);
57pub const F6: Position = Position::new(5, 5);
58pub const F7: Position = Position::new(6, 5);
59pub const F8: Position = Position::new(7, 5);
60
61pub const G1: Position = Position::new(0, 6);
62pub const G2: Position = Position::new(1, 6);
63pub const G3: Position = Position::new(2, 6);
64pub const G4: Position = Position::new(3, 6);
65pub const G5: Position = Position::new(4, 6);
66pub const G6: Position = Position::new(5, 6);
67pub const G7: Position = Position::new(6, 6);
68pub const G8: Position = Position::new(7, 6);
69
70pub const H1: Position = Position::new(0, 7);
71pub const H2: Position = Position::new(1, 7);
72pub const H3: Position = Position::new(2, 7);
73pub const H4: Position = Position::new(3, 7);
74pub const H5: Position = Position::new(4, 7);
75pub const H6: Position = Position::new(5, 7);
76pub const H7: Position = Position::new(6, 7);
77pub const H8: Position = Position::new(7, 7);
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
80pub struct Position {
81 row: i32,
82 col: i32,
83}
84
85impl core::fmt::Display for Position {
86 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
87 write!(
88 f,
89 "{}{}",
90 match self.col {
91 0 => 'a',
92 1 => 'b',
93 2 => 'c',
94 3 => 'd',
95 4 => 'e',
96 5 => 'f',
97 6 => 'g',
98 7 => 'h',
99 _ => '?',
100 },
101 self.row + 1
102 )
103 }
104}
105
106impl Position {
107 #[inline]
109 pub const fn king_pos(color: Color) -> Self {
110 match color {
111 Color::White => Self::new(0, 4),
112 Color::Black => Self::new(7, 4),
113 }
114 }
115
116 #[inline]
118 pub const fn queen_pos(color: Color) -> Self {
119 match color {
120 Color::White => Self::new(0, 3),
121 Color::Black => Self::new(7, 3),
122 }
123 }
124
125 #[inline]
134 pub const fn new(row: i32, col: i32) -> Self {
135 Self { row, col }
136 }
137
138 pub fn pgn(s: &str) -> Result<Self, String> {
141 let s = s.trim().to_lowercase();
142 let col = s.chars().next().ok_or(format!("invalid pgn `{}`", s))?;
143 let row = s
144 .chars()
145 .nth(1)
146 .ok_or(format!("invalid pgn `{}`", s))?
147 .to_string()
148 .parse::<u32>()
149 .map_err(|_| format!("invalid pgn `{}`", s))? as i32;
150 let c = match col {
151 'a' => 0,
152 'b' => 1,
153 'c' => 2,
154 'd' => 3,
155 'e' => 4,
156 'f' => 5,
157 'g' => 6,
158 'h' => 7,
159 _ => return Err(format!("invalid column character `{}`", col)),
160 };
161
162 if 1 <= row || row <= 8 {
163 Ok(Self::new(row - 1, c))
164 } else {
165 Err(format!("invalid row number `{}`", row))
166 }
167 }
168
169 #[inline]
171 pub fn is_on_board(&self) -> bool {
172 !self.is_off_board()
173 }
174
175 #[inline]
177 pub fn is_off_board(&self) -> bool {
178 self.row < 0 || self.row > 7 || self.col < 0 || self.col > 7
179 }
180
181 #[inline]
184 pub fn get_row(&self) -> i32 {
185 self.row
186 }
187
188 #[inline]
189 pub fn get_col(&self) -> i32 {
190 self.col
191 }
192
193 #[inline]
194 fn add_row(&self, drow: i32) -> Self {
195 let mut result = *self;
196 result.row += drow;
197 result
198 }
199
200 #[inline]
201 fn add_col(&self, dcol: i32) -> Self {
202 let mut result = *self;
203 result.col += dcol;
204 result
205 }
206
207 #[inline]
209 pub fn is_diagonal_to(&self, other: Self) -> bool {
210 (self.col - other.col).abs() == (self.row - other.row).abs()
213 }
214
215 #[inline]
217 fn diagonal_distance(&self, other: Self) -> i32 {
218 (self.col - other.col).abs()
219 }
220
221 #[inline]
223 pub fn is_orthogonal_to(&self, other: Self) -> bool {
224 (self.col == other.col) || (self.row == other.row)
225 }
226
227 #[inline]
229 fn orthogonal_distance(&self, other: Self) -> i32 {
230 (self.col - other.col).abs() + (self.row - other.row).abs()
231 }
232
233 #[inline]
239 pub fn is_adjacent_to(&self, other: Self) -> bool {
240 if self.is_orthogonal_to(other) {
241 self.orthogonal_distance(other) == 1
242 } else if self.is_diagonal_to(other) {
243 self.diagonal_distance(other) == 1
244 } else {
245 false
246 }
247 }
248
249 #[inline]
254 pub fn is_below(&self, other: Self) -> bool {
255 self.row < other.row
256 }
257
258 #[inline]
263 pub fn is_above(&self, other: Self) -> bool {
264 self.row > other.row
265 }
266
267 #[inline]
273 pub fn is_left_of(&self, other: Self) -> bool {
274 self.col < other.col
275 }
276
277 #[inline]
283 pub fn is_right_of(&self, other: Self) -> bool {
284 self.col > other.col
285 }
286
287 #[inline]
294 pub fn next_below(&self) -> Self {
295 Self::new(self.row - 1, self.col)
296 }
297
298 #[inline]
305 pub fn next_above(&self) -> Self {
306 Self::new(self.row + 1, self.col)
307 }
308
309 #[inline]
317 pub fn pawn_up(&self, ally_color: Color) -> Self {
318 match ally_color {
319 Color::White => self.next_above(),
320 Color::Black => self.next_below(),
321 }
322 }
323
324 #[inline]
332 pub fn pawn_back(&self, ally_color: Color) -> Self {
333 self.pawn_up(!ally_color)
334 }
335
336 #[inline]
343 pub fn next_left(&self) -> Self {
344 Self::new(self.row, self.col - 1)
345 }
346
347 #[inline]
354 pub fn next_right(&self) -> Self {
355 Self::new(self.row, self.col + 1)
356 }
357
358 #[inline]
360 pub fn is_starting_pawn(&self, color: Color) -> bool {
361 match color {
362 Color::White => self.row == 1,
363 Color::Black => self.row == 6,
364 }
365 }
366
367 #[inline]
369 pub fn is_kingside_rook(&self) -> bool {
370 (self.row == 0 || self.row == 7) && self.col == 7
371 }
372
373 #[inline]
375 pub fn is_queenside_rook(&self) -> bool {
376 (self.row == 0 || self.row == 7) && self.col == 0
377 }
378
379 pub fn diagonals_to(&self, to: Self) -> Vec<Self> {
384 if !self.is_diagonal_to(to) {
385 return Vec::new();
386 }
387
388 let row_step;
389 let col_step;
390 if self.is_left_of(to) {
391 col_step = 1;
392 } else {
393 col_step = -1;
394 }
395
396 if self.is_below(to) {
397 row_step = 1;
398 } else {
399 row_step = -1;
400 }
401
402 let mut acc = *self;
403 let mut result = Vec::new();
404 for _ in 0..self.diagonal_distance(to) {
405 acc = acc.add_row(row_step).add_col(col_step);
406 result.push(acc);
407 }
408
409 result
410 }
411
412 pub fn orthogonals_to(&self, to: Self) -> Vec<Self> {
417 if !self.is_orthogonal_to(to) {
418 return Vec::new();
419 }
420 let mut row_step = 0;
421 let mut col_step = 0;
422 if self.is_left_of(to) {
423 col_step = 1;
424 } else if self.is_right_of(to) {
425 col_step = -1;
426 } else if self.is_above(to) {
427 row_step = -1;
428 } else if self.is_below(to) {
429 row_step = 1;
430 }
431
432 let mut acc = *self;
433 let mut result = Vec::new();
434
435 for _ in 0..self.orthogonal_distance(to) {
436 acc = acc.add_row(row_step).add_col(col_step);
437 result.push(acc);
438 }
439
440 result
441 }
442
443 #[inline]
444 pub fn is_knight_move(&self, other: Self) -> bool {
445 (self.row - other.row).abs() == 2 && (self.col - other.col).abs() == 1
446 || (self.row - other.row).abs() == 1 && (self.col - other.col).abs() == 2
447 }
448}