1use core::fmt;
2
3use crate::{
4 Id, 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
99impl fmt::Display for Scharnagl {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 self.0.fmt(f)
102 }
103}
104
105#[cfg(feature = "const-fn-scharnagl-id")]
106#[allow(long_running_const_eval)]
108pub static SCHARNAGL_BY_ID: [(Id, Scharnagl); 960] = generate_scharnagl_by_id();
110#[cfg(not(feature = "const-fn-scharnagl-id"))]
111include!("scharnagl-id.rs");
112
113#[cfg(feature = "const-fn-scharnagl-id")]
114const fn generate_scharnagl_by_id() -> [(Id, Scharnagl); 960] {
115 let mut table = [(Id(0), Scharnagl(0)); 960];
116
117 let mut i = 0;
118 while i < 960 {
120 let scharnagl = Scharnagl(i as u16);
121 let entry = (Board::freestyle(scharnagl).standard_id(), scharnagl);
122
123 let mut j = i;
124 while j > 0 && entry.0.0 < table[j - 1].0.0 {
125 table[j] = table[j - 1];
126 j -= 1;
127 }
128 table[j] = entry;
129 i += 1;
130 }
131
132 table
133}
134
135pub fn scharnagl_by_id(id: Id) -> Option<Scharnagl> {
136 SCHARNAGL_BY_ID.binary_search_by_key(&id, |(id, _)| *id).ok().map(|i| SCHARNAGL_BY_ID[i].1)
137}
138
139#[test]
140fn freestyle_positions() {
141 use crate::{Position, Side};
142
143 assert_eq!(Scharnagl::new(960), None);
144 assert_eq!(Board::freestyle(Scharnagl(0)).fen(), "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR");
145 assert_eq!(
146 Board::freestyle(Scharnagl(631)).fen(),
147 "rnbkqrnb/pppppppp/8/8/8/8/PPPPPPPP/RNBKQRNB"
148 );
149 assert_eq!(Board::freestyle(Scharnagl::CHESS), Board::standard());
150 assert_eq!(
151 Board::freestyle(Scharnagl(959)).fen(),
152 "rkrnnqbb/pppppppp/8/8/8/8/PPPPPPPP/RKRNNQBB"
153 );
154
155 let position = Position::freestyle(Scharnagl::CHESS);
156 assert_eq!(position.board(), Position::start().board());
157 assert_eq!(position.castles().get(White, Side::Queen), Some(A));
158 assert_eq!(position.castles().get(White, Side::King), Some(H));
159 assert_eq!(position.castles().get(Black, Side::Queen), Some(A));
160 assert_eq!(position.castles().get(Black, Side::King), Some(H));
161}