1use crate::error::ShogiUtilError::CsaParseError;
2use crate::piece::Piece;
3use crate::{Result, ShogiUtilError};
4use std::str::FromStr;
5
6#[derive(Debug, Eq, PartialEq, Copy, Clone)]
7pub enum Color {
8 Black,
9 White,
10}
11
12impl Color {
13 pub fn to_byte(&self) -> u8 {
14 match self {
15 Color::Black => 0,
16 Color::White => 1,
17 }
18 }
19 pub fn to_usize(&self) -> usize {
20 self.to_byte() as usize
21 }
22
23 pub fn opponent(&self) -> Color {
24 match self {
25 Color::White => Color::Black,
26 Color::Black => Color::White,
27 }
28 }
29}
30
31impl FromStr for Color {
32 type Err = ShogiUtilError;
33
34 fn from_str(s: &str) -> Result<Self> {
35 match s {
36 "+" => Ok(Color::Black),
37 "-" => Ok(Color::White),
38 _ => Err(CsaParseError(format!("Invalid color symbol: {}", s))),
39 }
40 }
41}
42
43#[derive(Debug, Eq, PartialEq, Clone)]
44pub struct Square {
45 pub file: u8,
46 pub rank: u8,
47}
48
49impl Square {
50 pub fn from_pos(i: usize, j: usize) -> Square {
51 assert!(i < 9 && j < 9);
52 Square {
53 rank: (i + 1) as u8,
54 file: (9 - j) as u8,
55 }
56 }
57 pub fn to_pos(&self) -> (usize, usize) {
58 assert!(self.is_valid());
59 let i = self.rank as usize - 1;
60 let j = 9 - self.file as usize;
61 (i, j)
62 }
63
64 pub fn is_valid(&self) -> bool {
65 1 <= self.rank && self.rank <= 9 && 1 <= self.file && self.file <= 9
66 }
67 pub fn rotate(&self) -> Self {
68 Self {
69 rank: 9 - self.rank + 1,
70 file: 9 - self.file + 1,
71 }
72 }
73}
74
75impl FromStr for Square {
76 type Err = ShogiUtilError;
77
78 fn from_str(s: &str) -> Result<Self> {
79 if s.len() != 2 {
80 Err(CsaParseError(format!("Invalid square: {}", s)))
81 } else {
82 let file = s[0..1].parse::<u8>();
83 let rank = s[1..2].parse::<u8>();
84 match (file, rank) {
85 (Ok(file), Ok(rank)) if file > 0 && rank > 0 => Ok(Square { file, rank }),
86 _ => Err(CsaParseError(format!("Invalid square {}", s))),
87 }
88 }
89 }
90}
91
92#[derive(Debug, Eq, PartialEq, Clone)]
93pub struct Move {
94 pub color: Color,
95 pub from: Option<Square>,
96 pub to: Square,
97 pub piece: Piece,
98}
99
100pub struct LegalMove {
101 pub mv: Move,
102 pub promoted: bool,
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 #[test]
109 fn test_square() {
110 let sq = Square { file: 2, rank: 3 };
111 assert_eq!(sq.to_pos(), (2, 7));
112
113 let sq = Square::from_pos(2, 7);
114 assert_eq!(Square { file: 2, rank: 3 }, sq);
115 }
116
117 #[test]
118 fn test_rotate_square() {
119 let sq = Square { file: 2, rank: 7 };
120 let rotated = sq.rotate();
121 assert_eq!(rotated.file, 8);
122 assert_eq!(rotated.rank, 3);
123 }
124}