1use crate::bitboard::*;
2use crate::constants::*;
3use crate::piece::*;
4use crate::square::*;
5
6#[derive(Clone)]
8pub struct MoveBuffItem {
9 pub mv: Move,
10 uci: String,
11}
12
13#[derive(Copy, Clone)]
15pub struct CastlingRight {
16 pub can_castle: bool,
17}
18
19#[derive(Copy, Clone)]
21pub struct ColorCastlingRights {
22 pub rights: [CastlingRight; 2],
23}
24
25#[derive(Copy, Clone)]
27pub struct CastlingRigths {
28 rights: [ColorCastlingRights; 2],
29}
30
31#[derive(Clone)]
33pub struct State {
34 variant: Variant,
35 rep: [Piece; BOARD_AREA],
36 pub turn: Color,
37 ep_square: Square,
38 halfmove_clock: usize,
39 fullmove_number: usize,
40 has_disabled_move: bool,
41 disable_from_sq: Square,
42 disable_to_sq: Square,
43 by_figure: [[Bitboard; FIGURE_ARRAY_SIZE]; 2],
44 by_color: [Bitboard; 2],
45 castling_rights: CastlingRigths,
46 pub move_buff: Vec<MoveBuffItem>,
47}
48
49pub type Variant = usize;
51
52trait VariantTrait {
53 fn string(self) -> String;
55}
56
57impl VariantTrait for Variant {
58 fn string(self) -> String {
60 let name = match self {
61 VARIANT_STANDARD => "Standard",
62 VARIANT_EIGHTPIECE => "Eightpiece",
63 VARIANT_ATOMIC => "Atomic",
64 _ => "Unknownvariant",
65 };
66 name.to_string()
67 }
68}
69
70pub struct VariantInfo {
72 pub start_fen: &'static str,
73 pub display_name: &'static str,
74}
75
76impl State {
78 pub fn parse_piece_placement(&mut self, fen: &str) {
80 self.by_figure = [EMTPY_FIGURE_BITBOARDS, EMTPY_FIGURE_BITBOARDS];
81 self.by_color = [0, 0];
82 let mut rank: Rank = 0;
83 let mut file: File = 0;
84 let mut lancer_color = 0;
85 let mut lancer_has_north = false;
86 let mut lancer_index = 0;
87 for i in 0..fen.len() {
88 let c = &fen[i..i + 1];
89 if file > LAST_FILE && c != "/" && c != " " {
90 panic!("invalid piece placement file");
91 }
92 let mut examine_c = true;
93 let sq: Square = (LAST_RANK - rank) * NUM_FILES + file;
94 if c == "l" {
95 lancer_color = BLACK;
96 lancer_index = 1;
97 } else if c == "L" {
98 lancer_color = WHITE;
99 lancer_index = 1;
100 } else if lancer_index == 1 {
101 if c == "n" {
102 lancer_has_north = true;
103 lancer_index = 2;
104 } else if c == "s" {
105 lancer_has_north = false;
106 lancer_index = 2;
107 } else if c == "e" {
108 self.put(sq, color_figure(lancer_color, LANCERE));
109 file += 1;
110 lancer_index = 0;
111 examine_c = false;
112 } else if c == "w" {
113 self.put(sq, color_figure(lancer_color, LANCERW));
114 file += 1;
115 lancer_index = 0;
116 examine_c = false;
117 } else {
118 panic!("invalid lancer")
119 }
120 } else if lancer_index == 2 {
121 if c == "e" {
122 if lancer_has_north {
123 self.put(sq, color_figure(lancer_color, LANCERNE));
124 file += 1;
125 lancer_index = 0;
126 examine_c = false;
127 } else {
128 self.put(sq, color_figure(lancer_color, LANCERSE));
129 file += 1;
130 lancer_index = 0;
131 examine_c = false;
132 }
133 } else if c == "w" {
134 if lancer_has_north {
135 self.put(sq, color_figure(lancer_color, LANCERNW));
136 file += 1;
137 lancer_index = 0;
138 examine_c = false;
139 } else {
140 self.put(sq, color_figure(lancer_color, LANCERSW));
141 file += 1;
142 lancer_index = 0;
143 examine_c = false;
144 }
145 } else {
146 if lancer_has_north {
147 self.put(sq, color_figure(lancer_color, LANCERN));
148 file += 1;
149 lancer_index = 0;
150 } else {
151 self.put(sq, color_figure(lancer_color, LANCERS));
152 file += 1;
153 lancer_index = 0;
154 }
155 }
156 }
157 if lancer_index == 0 && examine_c {
158 if c == " " {
159 return;
160 } else if c == "/" {
161 file = 0;
162 rank += 1;
163 } else if c >= "1" && c <= "8" {
164 for _ in 0..c.parse().expect("should not happen") {
165 if file > LAST_FILE {
166 panic!("invalid piece placement file");
167 }
168 self.remove((LAST_RANK - rank) * NUM_FILES + file);
169 file += 1;
170 }
171 } else {
172 let p = fen_symbol_to_piece(c);
173 if p != NO_PIECE {
174 self.put(sq, p);
175 file += 1;
176 } else {
177 panic!("invalid fen symbol")
178 }
179 }
180 }
181 }
182 }
183
184 pub fn new() -> State {
186 State {
187 variant: DEFAULT_VARIANT,
188 rep: EMPTY_REP,
189 turn: WHITE,
190 ep_square: SQUARE_A1,
191 halfmove_clock: 0,
192 fullmove_number: 1,
193 has_disabled_move: false,
194 disable_from_sq: SQUARE_A1,
195 disable_to_sq: SQUARE_A1,
196 by_figure: [EMTPY_FIGURE_BITBOARDS, EMTPY_FIGURE_BITBOARDS],
197 by_color: [0, 0],
198 castling_rights: CastlingRigths {
199 rights: [EMPTY_COLOR_CASTLING_RIGHTS, EMPTY_COLOR_CASTLING_RIGHTS],
200 },
201 move_buff: Vec::new(),
202 }
203 }
204
205 pub fn set_from_fen(&mut self, fen: &str) {
207 let parts: Vec<&str> = fen.split(" ").collect();
208
209 let l = parts.len();
210
211 if l != 4 && l != 6 && l != 7 {
212 panic!("invalid number of fen fields {}", l);
213 }
214
215 self.parse_piece_placement(parts[0]);
216
217 match parts[1] {
218 "w" => self.turn = WHITE,
219 "b" => self.turn = BLACK,
220 _ => panic!("invalid turn {}", parts[1]),
221 }
222
223 self.castling_rights = CastlingRigths {
224 rights: [EMPTY_COLOR_CASTLING_RIGHTS, EMPTY_COLOR_CASTLING_RIGHTS],
225 };
226
227 if parts[2] == "-" {
228 } else {
230 for i in 0..parts[2].len() {
231 let r = &parts[2][i..i + 1];
232 match r {
233 "K" => self.castling_rights.rights[WHITE].rights[KING_SIDE].can_castle = true,
234 "Q" => self.castling_rights.rights[WHITE].rights[QUEEN_SIDE].can_castle = true,
235 "k" => self.castling_rights.rights[BLACK].rights[KING_SIDE].can_castle = true,
236 "q" => self.castling_rights.rights[BLACK].rights[QUEEN_SIDE].can_castle = true,
237 _ => panic!("invalid castling right {}", r),
238 }
239 }
240 }
241
242 self.ep_square = SQUARE_A1;
243
244 if parts[3] != "-" {
245 self.ep_square = Square::from_uci(parts[3].to_string()).0;
246 }
247
248 self.halfmove_clock = parts[4].parse().expect("invalid halfmove clock");
249 self.fullmove_number = parts[5].parse().expect("invalid fullmove number");
250
251 self.has_disabled_move = false;
252
253 if self.variant == VARIANT_EIGHTPIECE {
254 if parts.len() > 6 {
255 if parts[6] != "-" {
256 if parts[6].len() != 4 {
257 panic!("invalid disabled mvoe {:?}", parts[6]);
258 }
259 let df = Square::from_uci(parts[6][0..2].to_string());
260 let dt = Square::from_uci(parts[6][2..4].to_string());
261 if df.1 && dt.1 {
262 self.disable_from_sq = df.0;
263 self.disable_to_sq = dt.0;
264 } else {
265 panic!("invalid disabled move {}", parts[6]);
266 }
267 self.has_disabled_move = true;
268 }
269 }
270 }
271 }
272
273 pub fn put(&mut self, sq: Square, p: Piece) {
275 if p == NO_PIECE {
276 return;
277 }
278 self.rep[sq] = p;
279 let bb = sq.bitboard();
280 self.by_figure[p.color()][p.figure()] |= bb;
281 self.by_color[p.color()] |= bb;
282 }
283
284 pub fn remove(&mut self, sq: Square) {
286 let p = self.piece_at_square(sq);
287 if p == NO_PIECE {
288 return;
289 }
290 self.rep[sq] = NO_PIECE;
291 let bb = sq.bitboard();
292 self.by_figure[p.color()][p.figure()] &= !bb;
293 self.by_color[p.color()] &= !bb;
294 }
295
296 pub fn init(&mut self, variant: Variant) {
298 self.variant = variant;
299 self.set_from_fen(VARIANT_INFOS[self.variant].start_fen);
300 }
301
302 pub fn piece_at_square(&self, sq: Square) -> Piece {
304 self.rep[sq]
305 }
306
307 pub fn report_fen(&self) -> String {
309 let mut buff = "".to_string();
310 let mut acc = 0;
311 for rank in 0..NUM_RANKS {
312 for file in 0..NUM_FILES {
313 let sq: Square = (LAST_RANK - rank) * NUM_FILES + file;
314 let p = self.piece_at_square(sq);
315 let mut should_flush = false;
316 if p == NO_PIECE {
317 acc += 1;
318 } else {
319 should_flush = true;
320 buff = format!("{}{}", buff, p.fen_symbol());
321 }
322 if acc > 0 && (should_flush || file == LAST_FILE) {
323 buff = format!("{}{}", buff, acc);
324 acc = 0;
325 }
326 if file == LAST_FILE && rank < LAST_RANK {
327 buff = format!("{}/", buff);
328 }
329 }
330 }
331 buff = format!("{} {}", buff, self.turn.turn_fen());
332 let mut cfen = "".to_string();
333 if self.castling_rights.rights[WHITE].rights[KING_SIDE].can_castle {
334 cfen = format!("{}{}", cfen, "K")
335 }
336 if self.castling_rights.rights[WHITE].rights[QUEEN_SIDE].can_castle {
337 cfen = format!("{}{}", cfen, "Q")
338 }
339 if self.castling_rights.rights[BLACK].rights[KING_SIDE].can_castle {
340 cfen = format!("{}{}", cfen, "k")
341 }
342 if self.castling_rights.rights[BLACK].rights[QUEEN_SIDE].can_castle {
343 cfen = format!("{}{}", cfen, "q")
344 }
345 if cfen == "" {
346 cfen = "-".to_string();
347 }
348 let mut epfen = "-".to_string();
349 if self.ep_square != SQUARE_A1 {
350 epfen = self.ep_square.uci();
351 }
352 buff = format!(
353 "{} {} {} {} {}",
354 buff, cfen, epfen, self.halfmove_clock, self.fullmove_number
355 );
356 if self.variant == VARIANT_EIGHTPIECE {
357 let mut dfen = "-".to_string();
358 if self.has_disabled_move {
359 dfen = format!("{}{}", self.disable_from_sq.uci(), self.disable_to_sq.uci());
360 }
361 buff = format!("{} {}", buff, dfen);
362 }
363 buff
364 }
365
366 pub fn print_bitboards(&self) {
368 for col in BLACK..WHITE + 1 {
369 for fig in FIG_MIN..FIG_MAX {
370 println!(
371 "{} {} {}",
372 col.turn_fen(),
373 fig.symbol(),
374 self.by_figure[col][fig].pretty_print_string()
375 );
376 }
377 }
378 }
379
380 pub fn color_figure_mobility_at_square(
382 &self,
383 sq: Square,
384 gen_mode: MoveGenMode,
385 col: Color,
386 fig: Figure,
387 ) -> Bitboard {
388 match fig.base_figure() {
389 KNIGHT => knight_mobility(
390 sq,
391 gen_mode,
392 self.by_color[col],
393 self.by_color[col.inverse()],
394 ),
395 BISHOP => bishop_mobility(
396 sq,
397 gen_mode,
398 self.by_color[col],
399 self.by_color[col.inverse()],
400 ),
401 SENTRY => bishop_mobility(
402 sq,
403 gen_mode,
404 self.by_color[col],
405 self.by_color[col.inverse()],
406 ),
407 ROOK => rook_mobility(
408 sq,
409 gen_mode,
410 self.by_color[col],
411 self.by_color[col.inverse()],
412 ),
413 JAILER => jailer_mobility(
414 sq,
415 gen_mode,
416 self.by_color[col],
417 self.by_color[col.inverse()],
418 ),
419 QUEEN => queen_mobility(
420 sq,
421 gen_mode,
422 self.by_color[col],
423 self.by_color[col.inverse()],
424 ),
425 LANCER => lancer_mobility(
426 sq,
427 gen_mode,
428 self.by_color[col],
429 self.by_color[col.inverse()],
430 LANCER_ATTACKS[fig.lancer_direction()][sq],
431 ),
432 KING => king_mobility(
433 sq,
434 gen_mode,
435 self.by_color[col],
436 self.by_color[col.inverse()],
437 ),
438 _ => 0,
439 }
440 }
441
442 pub fn generate_pseudo_legal_moves(&mut self, gen_mode: MoveGenMode) -> Vec<Move> {
444 self.generate_pseudo_legal_moves_for_color(gen_mode, self.turn)
445 }
446
447 pub fn generate_pseudo_legal_moves_for_color(
449 &self,
450 gen_mode: MoveGenMode,
451 col: Color,
452 ) -> Vec<Move> {
453 let mut moves: Vec<Move> = vec![0; 0];
454 let mut bb = self.by_color[col];
455 loop {
456 let (sq, ok) = bb.pop_square();
457 if ok {
458 let p = self.piece_at_square(sq);
459 let fig = p.figure();
460 match fig {
461 PAWN => {
462 let pi: &PawnInfo = &PAWN_INFOS[col][sq];
463 let mut do_pawn_moves = |pushes, captures| {
464 if pushes {
465 for to_sq in pi.pushes.iter() {
466 if self.piece_at_square(*to_sq) == NO_PIECE {
467 moves.push(Move::ft(sq, *to_sq));
468 }
469 }
470 }
471 if captures {
472 for to_sq in pi.captures.iter() {
473 let cp: Piece = self.piece_at_square(*to_sq);
474 if cp != NO_PIECE && cp.color() == col.inverse() {
475 moves.push(Move::ft(sq, *to_sq));
476 }
477 }
478 }
479 };
480 match gen_mode {
481 MoveGenMode::Violent => do_pawn_moves(false, true),
482 MoveGenMode::Quiet => do_pawn_moves(true, false),
483 MoveGenMode::All => do_pawn_moves(true, true),
484 }
485 }
486 _ => {
487 let mut mob =
488 self.color_figure_mobility_at_square(sq, gen_mode, col, p.figure());
489 loop {
490 let (to_sq, ok) = mob.pop_square();
491 if ok {
492 moves.push(Move::ft(sq, to_sq));
493 } else {
494 break;
495 }
496 }
497 }
498 }
499 } else {
500 break;
501 }
502 }
503 moves
504 }
505
506 pub fn pretty_print_string(&mut self) -> String {
508 let mut buff = "".to_string();
509 for rank in 0..NUM_RANKS {
510 for file in 0..NUM_FILES {
511 let sq = rank_file(LAST_RANK - rank, file);
512 let p = self.piece_at_square(sq);
513 buff = format!("{}{:^3}", buff, p.fen_symbol());
514 if file == LAST_FILE {
515 buff += "\n";
516 }
517 }
518 }
519 buff = format!(
520 "{}\nvariant {} fen {}\n",
521 buff,
522 self.variant.string(),
523 self.report_fen()
524 );
525 format!("{}\n{}\n", buff, self.gen_move_buff())
526 }
527
528 pub fn gen_move_buff(&mut self) -> String {
530 let moves = self.generate_pseudo_legal_moves_for_color(MoveGenMode::All, self.turn);
531 let mut move_buff = "".to_string();
532 self.move_buff = Vec::new();
533 for i in 0..moves.len() {
534 let mv = moves[i];
535 self.move_buff.push(MoveBuffItem {
536 mv: mv,
537 uci: mv.uci(),
538 });
539 }
540 self.move_buff.sort_by(|a, b| a.uci.cmp(&b.uci));
541 for i in 0..self.move_buff.len() {
542 let fromp = self.piece_at_square(self.move_buff[i].mv.from_sq());
543 let mut san_letter = fromp.san_letter();
544 if fromp.figure() == PAWN {
545 san_letter = "";
546 }
547 let move_str = format!("{}. {}{}", i + 1, san_letter, self.move_buff[i].uci);
548 move_buff = format!("{}{:16}", move_buff, move_str);
549 if i % 6 == 5 {
550 move_buff = format!("{}\n", move_buff);
551 }
552 }
553 move_buff
554 }
555
556 pub fn init_variant(&self) {}
558
559 pub fn variant_start_fen(&self) -> &str {
561 VARIANT_INFOS[self.variant].start_fen
562 }
563
564 pub fn make_move(&mut self, mv: Move) {
566 let from_sq = mv.from_sq();
567 let to_sq = mv.to_sq();
568 let fromp: Piece = self.piece_at_square(from_sq);
569 self.remove(from_sq);
571 self.put(to_sq, fromp);
572
573 self.turn = self.turn.inverse();
574 }
575}