pub struct Board { /* private fields */ }Expand description
A chessboard with atomic chess rules.
Maintains piece placement, side-to-move, castling rights, en-passant square, and the half-move clock. Supports FEN serialization/deserialization, making and unmaking moves, and legality checking.
Implementations§
Source§impl Board
impl Board
Sourcepub fn from_fen(fen: &str) -> Result<Self, FenError>
pub fn from_fen(fen: &str) -> Result<Self, FenError>
Parse a board from a FEN string.
Accepts standard FEN with 4–6 space-separated fields. The piece
character set includes standard chess pieces plus C/c and K/k
for commoners (king-like pseudo-royal pieces).
Examples found in repository?
7fn main() {
8 atomic_movegen::attacks::init();
9 let args: Vec<String> = env::args().collect();
10 if args.len() < 2 {
11 eprintln!("Usage: list_moves <fen>");
12 return;
13 }
14 let fen = &args[1];
15 let board = Board::from_fen(fen).expect("Invalid FEN");
16
17 let mut moves = MoveList::new();
18 movegen::generate_legal(&board, &mut moves);
19 moves
20 .as_mut_slice()
21 .sort_by_key(|m| (m.from_sq() as u16, m.to_sq() as u16));
22
23 println!("Legal moves ({} total):", moves.len());
24 for &m in moves.as_slice() {
25 println!(" {}{}", sq_str(m.from_sq()), sq_str(m.to_sq()));
26 }
27}More examples
3fn main() {
4 atomic_movegen::attacks::init();
5 let args: Vec<String> = env::args().collect();
6 if args.len() < 3 {
7 eprintln!("Usage: perft <fen> <depth>");
8 std::process::exit(1);
9 }
10
11 let fen = &args[1];
12 let depth: u32 = args[2]
13 .parse()
14 .expect("Depth must be a non-negative integer");
15
16 match atomic_movegen::board::Board::from_fen(fen) {
17 Ok(mut board) => {
18 let nodes = atomic_movegen::perft(&mut board, depth);
19 println!("{}", nodes);
20 }
21 Err(e) => {
22 eprintln!("Error parsing FEN: {}", e);
23 std::process::exit(1);
24 }
25 }
26}8fn main() {
9 atomic_movegen::attacks::init();
10 let args: Vec<String> = env::args().collect();
11 if args.len() < 3 {
12 eprintln!("Usage: perft_divide <fen> <depth>");
13 return;
14 }
15 let fen = &args[1];
16 let depth: u32 = args[2].parse().unwrap_or(1);
17
18 let mut board = Board::from_fen(fen).expect("Invalid FEN");
19
20 let mut moves = MoveList::new();
21 movegen::generate_legal(&board, &mut moves);
22 moves
23 .as_mut_slice()
24 .sort_by_key(|m| (m.from_sq() as u16, m.to_sq() as u16));
25
26 let mut total = 0u64;
27 for &m in moves.as_slice() {
28 let mut state = atomic_movegen::board::StateInfo::new();
29 board.do_move(m, &mut state);
30 let cnt = if depth <= 1 {
31 1
32 } else {
33 perft(&mut board, depth - 1)
34 };
35 board.undo_move(m, &state);
36 total += cnt;
37 println!("{}{}: {}", sq_str(m.from_sq()), sq_str(m.to_sq()), cnt);
38 }
39 println!("\nNodes searched: {}", total);
40}8fn main() {
9 atomic_movegen::attacks::init();
10 let args: Vec<String> = env::args().collect();
11 if args.len() < 3 {
12 eprintln!("Usage: fen_after <fen> <move>");
13 return;
14 }
15 let fen = &args[1];
16 let movestr = &args[2];
17 let mut board = Board::from_fen(fen).expect("Invalid FEN");
18
19 let from_sq = parse_sq(&movestr[0..2]);
20 let to_sq = parse_sq(&movestr[2..4]);
21
22 let mut moves = MoveList::new();
23 movegen::generate_legal(&board, &mut moves);
24
25 for &m in moves.as_slice() {
26 if m.from_sq() == from_sq && m.to_sq() == to_sq {
27 let mut state = atomic_movegen::board::StateInfo::new();
28 board.do_move(m, &mut state);
29 println!("{}", board.fen());
30
31 let mut moves2 = MoveList::new();
32 movegen::generate_legal(&board, &mut moves2);
33 moves2
34 .as_mut_slice()
35 .sort_by_key(|m| (m.from_sq() as u16, m.to_sq() as u16));
36 println!("Legal moves ({} total):", moves2.len());
37 for &m2 in moves2.as_slice() {
38 println!(" {}{}", sq_str(m2.from_sq()), sq_str(m2.to_sq()));
39 }
40 return;
41 }
42 }
43 println!("Move not found");
44}8fn main() {
9 atomic_movegen::attacks::init();
10 let args: Vec<String> = env::args().collect();
11 if args.len() < 2 {
12 eprintln!("Usage: debug_moves <fen>");
13 return;
14 }
15 let fen = &args[1];
16 let board = Board::from_fen(fen).expect("Invalid FEN");
17
18 let mut pseudo = MoveList::new();
19 movegen::generate_pseudo_legal(&board, &mut pseudo);
20
21 let mut pseudo_set = HashSet::new();
22 for &m in pseudo.as_slice() {
23 if !pseudo_set.insert((m.from_sq(), m.to_sq(), m.move_type())) {
24 println!(
25 "DUPLICATE PSEUDO-LEGAL: {}{}",
26 sq_str(m.from_sq()),
27 sq_str(m.to_sq())
28 );
29 }
30 }
31
32 let mut legal = MoveList::new();
33 movegen::generate_legal(&board, &mut legal);
34
35 let mut legal_set = HashSet::new();
36 for &m in legal.as_slice() {
37 if !legal_set.insert((m.from_sq(), m.to_sq(), m.move_type())) {
38 println!(
39 "DUPLICATE LEGAL: {}{}",
40 sq_str(m.from_sq()),
41 sq_str(m.to_sq())
42 );
43 }
44 }
45
46 println!(
47 "Pseudo-legal moves: {} (unique: {})",
48 pseudo.len(),
49 pseudo_set.len()
50 );
51 println!("Legal moves: {} (unique: {})", legal.len(), legal_set.len());
52}7fn main() {
8 atomic_movegen::attacks::init();
9 let args: Vec<String> = env::args().collect();
10 if args.len() < 2 {
11 eprintln!("Usage: pawn_debug <fen>");
12 return;
13 }
14 let fen = &args[1];
15 let board = Board::from_fen(fen).expect("Invalid FEN");
16
17 println!("Board side to move: {:?}", board.side_to_move());
18
19 // Manual check: iterate all pawns
20 let us = board.side_to_move();
21 let _them = us.flip();
22 let _occupied = board.occupied();
23
24 let pawns = board.pieces_color_pt(us, PieceType::Pawn);
25 println!("Pawns ({:?}): {}", us, pawns.count());
26 let mut p = pawns;
27 while !p.is_empty() {
28 let sq = p.pop_lsb();
29 println!(
30 " Pawn at {} (idx={}): file={} rank={}",
31 sq_str(sq),
32 sq as u8,
33 file_of(sq) as u8,
34 rank_of(sq) as u8
35 );
36 }
37
38 // Check what generate_legal produces
39 let mut moves = MoveList::new();
40 movegen::generate_legal(&board, &mut moves);
41
42 // Find all h3g2 moves
43 let h3 = Square::from_index(23);
44 let g2 = Square::from_index(14);
45 println!("\nAll h3g2 moves:");
46 for (i, &m) in moves.as_slice().iter().enumerate() {
47 if m.from_sq() == h3 && m.to_sq() == g2 {
48 println!(
49 " Move #{}: from={} to={} type={:?}",
50 i,
51 sq_str(m.from_sq()),
52 sq_str(m.to_sq()),
53 m.move_type()
54 );
55 }
56 }
57
58 // Also check how many times each move appears
59 use std::collections::HashMap;
60 let mut counts: HashMap<(u16, u16, u16), usize> = HashMap::new();
61 for &m in moves.as_slice() {
62 *counts
63 .entry((m.from_sq() as u16, m.to_sq() as u16, m.move_type() as u16))
64 .or_insert(0) += 1;
65 }
66 for ((from, to, mt), count) in &counts {
67 if *count > 1 {
68 println!(
69 "DUPLICATE: {}{} type={:?} appears {} times",
70 sq_str(Square::from_u8(*from as u8)),
71 sq_str(Square::from_u8(*to as u8)),
72 match mt {
73 0 => "Normal",
74 1 => "Promo",
75 2 => "EP",
76 3 => "Castle",
77 _ => "?",
78 },
79 count
80 );
81 }
82 }
83}Sourcepub fn fen(&self) -> String
pub fn fen(&self) -> String
Serialize the board to a FEN string.
Examples found in repository?
8fn main() {
9 atomic_movegen::attacks::init();
10 let args: Vec<String> = env::args().collect();
11 if args.len() < 3 {
12 eprintln!("Usage: fen_after <fen> <move>");
13 return;
14 }
15 let fen = &args[1];
16 let movestr = &args[2];
17 let mut board = Board::from_fen(fen).expect("Invalid FEN");
18
19 let from_sq = parse_sq(&movestr[0..2]);
20 let to_sq = parse_sq(&movestr[2..4]);
21
22 let mut moves = MoveList::new();
23 movegen::generate_legal(&board, &mut moves);
24
25 for &m in moves.as_slice() {
26 if m.from_sq() == from_sq && m.to_sq() == to_sq {
27 let mut state = atomic_movegen::board::StateInfo::new();
28 board.do_move(m, &mut state);
29 println!("{}", board.fen());
30
31 let mut moves2 = MoveList::new();
32 movegen::generate_legal(&board, &mut moves2);
33 moves2
34 .as_mut_slice()
35 .sort_by_key(|m| (m.from_sq() as u16, m.to_sq() as u16));
36 println!("Legal moves ({} total):", moves2.len());
37 for &m2 in moves2.as_slice() {
38 println!(" {}{}", sq_str(m2.from_sq()), sq_str(m2.to_sq()));
39 }
40 return;
41 }
42 }
43 println!("Move not found");
44}Sourcepub fn piece_on(&self, sq: Square) -> Piece
pub fn piece_on(&self, sq: Square) -> Piece
Return the piece on a square (or NO_PIECE if empty).
Sourcepub fn occupied(&self) -> Bitboard
pub fn occupied(&self) -> Bitboard
Return the bitboard of all occupied squares.
Examples found in repository?
7fn main() {
8 atomic_movegen::attacks::init();
9 let args: Vec<String> = env::args().collect();
10 if args.len() < 2 {
11 eprintln!("Usage: pawn_debug <fen>");
12 return;
13 }
14 let fen = &args[1];
15 let board = Board::from_fen(fen).expect("Invalid FEN");
16
17 println!("Board side to move: {:?}", board.side_to_move());
18
19 // Manual check: iterate all pawns
20 let us = board.side_to_move();
21 let _them = us.flip();
22 let _occupied = board.occupied();
23
24 let pawns = board.pieces_color_pt(us, PieceType::Pawn);
25 println!("Pawns ({:?}): {}", us, pawns.count());
26 let mut p = pawns;
27 while !p.is_empty() {
28 let sq = p.pop_lsb();
29 println!(
30 " Pawn at {} (idx={}): file={} rank={}",
31 sq_str(sq),
32 sq as u8,
33 file_of(sq) as u8,
34 rank_of(sq) as u8
35 );
36 }
37
38 // Check what generate_legal produces
39 let mut moves = MoveList::new();
40 movegen::generate_legal(&board, &mut moves);
41
42 // Find all h3g2 moves
43 let h3 = Square::from_index(23);
44 let g2 = Square::from_index(14);
45 println!("\nAll h3g2 moves:");
46 for (i, &m) in moves.as_slice().iter().enumerate() {
47 if m.from_sq() == h3 && m.to_sq() == g2 {
48 println!(
49 " Move #{}: from={} to={} type={:?}",
50 i,
51 sq_str(m.from_sq()),
52 sq_str(m.to_sq()),
53 m.move_type()
54 );
55 }
56 }
57
58 // Also check how many times each move appears
59 use std::collections::HashMap;
60 let mut counts: HashMap<(u16, u16, u16), usize> = HashMap::new();
61 for &m in moves.as_slice() {
62 *counts
63 .entry((m.from_sq() as u16, m.to_sq() as u16, m.move_type() as u16))
64 .or_insert(0) += 1;
65 }
66 for ((from, to, mt), count) in &counts {
67 if *count > 1 {
68 println!(
69 "DUPLICATE: {}{} type={:?} appears {} times",
70 sq_str(Square::from_u8(*from as u8)),
71 sq_str(Square::from_u8(*to as u8)),
72 match mt {
73 0 => "Normal",
74 1 => "Promo",
75 2 => "EP",
76 3 => "Castle",
77 _ => "?",
78 },
79 count
80 );
81 }
82 }
83}Sourcepub fn pieces_color(&self, c: Color) -> Bitboard
pub fn pieces_color(&self, c: Color) -> Bitboard
Return all pieces of a given color.
Sourcepub fn pieces_color_pt(&self, c: Color, pt: PieceType) -> Bitboard
pub fn pieces_color_pt(&self, c: Color, pt: PieceType) -> Bitboard
Return all pieces of a given color and type.
Examples found in repository?
7fn main() {
8 atomic_movegen::attacks::init();
9 let args: Vec<String> = env::args().collect();
10 if args.len() < 2 {
11 eprintln!("Usage: pawn_debug <fen>");
12 return;
13 }
14 let fen = &args[1];
15 let board = Board::from_fen(fen).expect("Invalid FEN");
16
17 println!("Board side to move: {:?}", board.side_to_move());
18
19 // Manual check: iterate all pawns
20 let us = board.side_to_move();
21 let _them = us.flip();
22 let _occupied = board.occupied();
23
24 let pawns = board.pieces_color_pt(us, PieceType::Pawn);
25 println!("Pawns ({:?}): {}", us, pawns.count());
26 let mut p = pawns;
27 while !p.is_empty() {
28 let sq = p.pop_lsb();
29 println!(
30 " Pawn at {} (idx={}): file={} rank={}",
31 sq_str(sq),
32 sq as u8,
33 file_of(sq) as u8,
34 rank_of(sq) as u8
35 );
36 }
37
38 // Check what generate_legal produces
39 let mut moves = MoveList::new();
40 movegen::generate_legal(&board, &mut moves);
41
42 // Find all h3g2 moves
43 let h3 = Square::from_index(23);
44 let g2 = Square::from_index(14);
45 println!("\nAll h3g2 moves:");
46 for (i, &m) in moves.as_slice().iter().enumerate() {
47 if m.from_sq() == h3 && m.to_sq() == g2 {
48 println!(
49 " Move #{}: from={} to={} type={:?}",
50 i,
51 sq_str(m.from_sq()),
52 sq_str(m.to_sq()),
53 m.move_type()
54 );
55 }
56 }
57
58 // Also check how many times each move appears
59 use std::collections::HashMap;
60 let mut counts: HashMap<(u16, u16, u16), usize> = HashMap::new();
61 for &m in moves.as_slice() {
62 *counts
63 .entry((m.from_sq() as u16, m.to_sq() as u16, m.move_type() as u16))
64 .or_insert(0) += 1;
65 }
66 for ((from, to, mt), count) in &counts {
67 if *count > 1 {
68 println!(
69 "DUPLICATE: {}{} type={:?} appears {} times",
70 sq_str(Square::from_u8(*from as u8)),
71 sq_str(Square::from_u8(*to as u8)),
72 match mt {
73 0 => "Normal",
74 1 => "Promo",
75 2 => "EP",
76 3 => "Castle",
77 _ => "?",
78 },
79 count
80 );
81 }
82 }
83}Sourcepub fn side_to_move(&self) -> Color
pub fn side_to_move(&self) -> Color
Return the side to move.
Examples found in repository?
7fn main() {
8 atomic_movegen::attacks::init();
9 let args: Vec<String> = env::args().collect();
10 if args.len() < 2 {
11 eprintln!("Usage: pawn_debug <fen>");
12 return;
13 }
14 let fen = &args[1];
15 let board = Board::from_fen(fen).expect("Invalid FEN");
16
17 println!("Board side to move: {:?}", board.side_to_move());
18
19 // Manual check: iterate all pawns
20 let us = board.side_to_move();
21 let _them = us.flip();
22 let _occupied = board.occupied();
23
24 let pawns = board.pieces_color_pt(us, PieceType::Pawn);
25 println!("Pawns ({:?}): {}", us, pawns.count());
26 let mut p = pawns;
27 while !p.is_empty() {
28 let sq = p.pop_lsb();
29 println!(
30 " Pawn at {} (idx={}): file={} rank={}",
31 sq_str(sq),
32 sq as u8,
33 file_of(sq) as u8,
34 rank_of(sq) as u8
35 );
36 }
37
38 // Check what generate_legal produces
39 let mut moves = MoveList::new();
40 movegen::generate_legal(&board, &mut moves);
41
42 // Find all h3g2 moves
43 let h3 = Square::from_index(23);
44 let g2 = Square::from_index(14);
45 println!("\nAll h3g2 moves:");
46 for (i, &m) in moves.as_slice().iter().enumerate() {
47 if m.from_sq() == h3 && m.to_sq() == g2 {
48 println!(
49 " Move #{}: from={} to={} type={:?}",
50 i,
51 sq_str(m.from_sq()),
52 sq_str(m.to_sq()),
53 m.move_type()
54 );
55 }
56 }
57
58 // Also check how many times each move appears
59 use std::collections::HashMap;
60 let mut counts: HashMap<(u16, u16, u16), usize> = HashMap::new();
61 for &m in moves.as_slice() {
62 *counts
63 .entry((m.from_sq() as u16, m.to_sq() as u16, m.move_type() as u16))
64 .or_insert(0) += 1;
65 }
66 for ((from, to, mt), count) in &counts {
67 if *count > 1 {
68 println!(
69 "DUPLICATE: {}{} type={:?} appears {} times",
70 sq_str(Square::from_u8(*from as u8)),
71 sq_str(Square::from_u8(*to as u8)),
72 match mt {
73 0 => "Normal",
74 1 => "Promo",
75 2 => "EP",
76 3 => "Castle",
77 _ => "?",
78 },
79 count
80 );
81 }
82 }
83}Sourcepub fn castling_rights(&self) -> u8
pub fn castling_rights(&self) -> u8
Return the castling rights bitmask.
Sourcepub fn commoners(&self, c: Color) -> Bitboard
pub fn commoners(&self, c: Color) -> Bitboard
Return the bitboard of commoners (king-like pseudo-royal pieces) for a color.
Sourcepub fn attackers_to(&self, sq: Square, occupied: Bitboard) -> Bitboard
pub fn attackers_to(&self, sq: Square, occupied: Bitboard) -> Bitboard
Return all pieces that attack sq on the given occupancy.
Includes pawns, knights, bishops, rooks, queens, and commoners of either
color that attack sq.
Sourcepub fn checkers(&self) -> Bitboard
pub fn checkers(&self) -> Bitboard
Return the bitboard of enemy pieces checking the side to move.
Sourcepub fn pinned(&self, c: Color) -> Bitboard
pub fn pinned(&self, c: Color) -> Bitboard
Return the bitboard of pieces of the given color that are pinned (cannot move without exposing a commoner to capture).
Sourcepub fn populate_state(&self, state: &mut StateInfo)
pub fn populate_state(&self, state: &mut StateInfo)
Fill cached state fields (checkers, pinned, commoner counts) for the
current position so that legal() can read them instead of recomputing.
Sourcepub fn do_move(&mut self, m: Move, state: &mut StateInfo)
pub fn do_move(&mut self, m: Move, state: &mut StateInfo)
Make m on the board, storing undo information in state.
Handles all move types (normal, promotion, en-passant, castling) as well as atomic-blast removal on captures.
Examples found in repository?
8fn main() {
9 atomic_movegen::attacks::init();
10 let args: Vec<String> = env::args().collect();
11 if args.len() < 3 {
12 eprintln!("Usage: perft_divide <fen> <depth>");
13 return;
14 }
15 let fen = &args[1];
16 let depth: u32 = args[2].parse().unwrap_or(1);
17
18 let mut board = Board::from_fen(fen).expect("Invalid FEN");
19
20 let mut moves = MoveList::new();
21 movegen::generate_legal(&board, &mut moves);
22 moves
23 .as_mut_slice()
24 .sort_by_key(|m| (m.from_sq() as u16, m.to_sq() as u16));
25
26 let mut total = 0u64;
27 for &m in moves.as_slice() {
28 let mut state = atomic_movegen::board::StateInfo::new();
29 board.do_move(m, &mut state);
30 let cnt = if depth <= 1 {
31 1
32 } else {
33 perft(&mut board, depth - 1)
34 };
35 board.undo_move(m, &state);
36 total += cnt;
37 println!("{}{}: {}", sq_str(m.from_sq()), sq_str(m.to_sq()), cnt);
38 }
39 println!("\nNodes searched: {}", total);
40}More examples
8fn main() {
9 atomic_movegen::attacks::init();
10 let args: Vec<String> = env::args().collect();
11 if args.len() < 3 {
12 eprintln!("Usage: fen_after <fen> <move>");
13 return;
14 }
15 let fen = &args[1];
16 let movestr = &args[2];
17 let mut board = Board::from_fen(fen).expect("Invalid FEN");
18
19 let from_sq = parse_sq(&movestr[0..2]);
20 let to_sq = parse_sq(&movestr[2..4]);
21
22 let mut moves = MoveList::new();
23 movegen::generate_legal(&board, &mut moves);
24
25 for &m in moves.as_slice() {
26 if m.from_sq() == from_sq && m.to_sq() == to_sq {
27 let mut state = atomic_movegen::board::StateInfo::new();
28 board.do_move(m, &mut state);
29 println!("{}", board.fen());
30
31 let mut moves2 = MoveList::new();
32 movegen::generate_legal(&board, &mut moves2);
33 moves2
34 .as_mut_slice()
35 .sort_by_key(|m| (m.from_sq() as u16, m.to_sq() as u16));
36 println!("Legal moves ({} total):", moves2.len());
37 for &m2 in moves2.as_slice() {
38 println!(" {}{}", sq_str(m2.from_sq()), sq_str(m2.to_sq()));
39 }
40 return;
41 }
42 }
43 println!("Move not found");
44}Sourcepub fn undo_move(&mut self, m: Move, state: &StateInfo)
pub fn undo_move(&mut self, m: Move, state: &StateInfo)
Unmake m, restoring the board to its state before do_move.
state must be the same StateInfo that was passed to do_move.
Examples found in repository?
8fn main() {
9 atomic_movegen::attacks::init();
10 let args: Vec<String> = env::args().collect();
11 if args.len() < 3 {
12 eprintln!("Usage: perft_divide <fen> <depth>");
13 return;
14 }
15 let fen = &args[1];
16 let depth: u32 = args[2].parse().unwrap_or(1);
17
18 let mut board = Board::from_fen(fen).expect("Invalid FEN");
19
20 let mut moves = MoveList::new();
21 movegen::generate_legal(&board, &mut moves);
22 moves
23 .as_mut_slice()
24 .sort_by_key(|m| (m.from_sq() as u16, m.to_sq() as u16));
25
26 let mut total = 0u64;
27 for &m in moves.as_slice() {
28 let mut state = atomic_movegen::board::StateInfo::new();
29 board.do_move(m, &mut state);
30 let cnt = if depth <= 1 {
31 1
32 } else {
33 perft(&mut board, depth - 1)
34 };
35 board.undo_move(m, &state);
36 total += cnt;
37 println!("{}{}: {}", sq_str(m.from_sq()), sq_str(m.to_sq()), cnt);
38 }
39 println!("\nNodes searched: {}", total);
40}Sourcepub fn legal(&self, m: Move, state: &StateInfo) -> bool
pub fn legal(&self, m: Move, state: &StateInfo) -> bool
Check whether m is legal under atomic chess rules.
Considers blast-zone effects (self-explosion), castling pass-through safety, pseudo-royal adjacency, and commoner extinction.
state must contain up-to-date cached fields for the current position.