1use core::{fmt, str};
2
3use crate::{
4 Square, finite_for,
5 square::{File, Rank},
6};
7
8use super::{Board, Player, Role};
9
10use Player::*;
11use Rank::*;
12use Role::*;
13
14#[cfg(test)]
15use File::*;
16
17#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
21pub struct Scharnagl(u16);
22
23impl Scharnagl {
24 pub const CHESS: Self = Self(518);
25
26 pub const fn new(i: u16) -> Option<Self> {
27 if i < 960 { Some(Self(i)) } else { None }
28 }
29
30 pub const fn board(self) -> Board {
31 const KNIGHTS: [(u8, u8); 10] =
32 [(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)];
33
34 const fn nth_free(roles: &[Role; 8], n: u8) -> File {
35 let mut seen = 0;
36 finite_for!(file in File {
37 if Pawn.eq(roles[file.index()]) {
38 if seen == n {
39 return file;
40 }
41 seen += 1;
42 }
43 });
44 unreachable!()
45 }
46
47 let mut i = self.0;
48 let mut roles = [Pawn; 8];
49
50 let light_bishop = i % 4;
53 i /= 4;
54 roles[(light_bishop * 2 + 1) as usize] = Bishop;
55
56 let dark_bishop = i % 4;
59 i /= 4;
60 roles[(dark_bishop * 2) as usize] = Bishop;
61
62 let queen = i % 6;
65 i /= 6;
66 let queen = nth_free(&roles, queen as u8);
67 roles[queen.index()] = Queen;
68
69 let (left_knight, right_knight) = KNIGHTS[i as usize];
73 let left_knight = nth_free(&roles, left_knight);
74 roles[left_knight.index()] = Knight;
75 let right_knight = nth_free(&roles, right_knight);
76 roles[right_knight.index()] = Knight;
77
78 let rook = nth_free(&roles, 0);
81 roles[rook.index()] = Rook;
82 let king = nth_free(&roles, 0);
83 roles[king.index()] = King;
84 let rook = nth_free(&roles, 0);
85 roles[rook.index()] = Rook;
86
87 let mut board = Board::EMPTY;
88 finite_for!(file in File {
89 let role = roles[file.index()];
90 board.insert(Square::new(file, One), role.of(White));
91 board.insert(Square::new(file, Two), White.pawn());
92 board.insert(Square::new(file, Seven), Black.pawn());
93 board.insert(Square::new(file, Eight), role.of(Black));
94 });
95 board
96 }
97
98 pub fn from_board(board: Board) -> Option<Self> {
99 (0..960).map(Self).find(|scharnagl| scharnagl.board() == board)
100 }
101}
102
103impl fmt::Display for Scharnagl {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 self.0.fmt(f)
106 }
107}
108
109impl str::FromStr for Scharnagl {
110 type Err = &'static str;
111
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 let i = s.parse().map_err(|_| "Scharnagl index must be an integer from 0 to 959")?;
114 Self::new(i).ok_or("Scharnagl index must be an integer from 0 to 959")
115 }
116}
117
118#[test]
119fn freestyle_positions() {
120 use crate::{Position, Side};
121
122 assert_eq!(Scharnagl::new(960), None);
123 assert_eq!(Board::freestyle(Scharnagl(0)).fen(), "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR");
124 assert_eq!(
125 Board::freestyle(Scharnagl(631)).fen(),
126 "rnbkqrnb/pppppppp/8/8/8/8/PPPPPPPP/RNBKQRNB"
127 );
128 assert_eq!(Board::freestyle(Scharnagl::CHESS), Board::standard());
129 assert_eq!(
130 Board::freestyle(Scharnagl(959)).fen(),
131 "rkrnnqbb/pppppppp/8/8/8/8/PPPPPPPP/RKRNNQBB"
132 );
133 assert_eq!(Scharnagl::from_board(Board::EMPTY), None);
134 for i in 0..960 {
135 let scharnagl = Scharnagl(i);
136 assert_eq!(Scharnagl::from_board(scharnagl.board()), Some(scharnagl));
137 }
138
139 let position = Position::freestyle(Scharnagl::CHESS);
140 assert_eq!(position.board(), Position::start().board());
141 assert_eq!(position.castles().get(White, Side::Queen), Some(A));
142 assert_eq!(position.castles().get(White, Side::King), Some(H));
143 assert_eq!(position.castles().get(Black, Side::Queen), Some(A));
144 assert_eq!(position.castles().get(Black, Side::King), Some(H));
145}