shogiutil/
model.rs

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}