cleanroom implementation of a chess engine. doesn't work :)

add pawn moves :3

+186 -85
+186 -85
src/lib.rs
··· 52 52 fn value(&self) -> u32 { 53 53 self.backing.count_ones() 54 54 } 55 + fn or(self, other: Self) -> Self { 56 + Self { 57 + backing: self.backing | other.backing, 58 + } 59 + } 55 60 } 56 61 57 62 struct Move { ··· 63 68 64 69 pub struct Board { 65 70 white_to_move: bool, 71 + 66 72 white_pawns: BitBoard, 67 73 white_knights: BitBoard, 68 74 white_bishops: BitBoard, 69 75 white_rooks: BitBoard, 70 76 white_queens: BitBoard, 71 77 white_king: BitBoard, 78 + 72 79 black_pawns: BitBoard, 73 80 black_knights: BitBoard, 74 81 black_bishops: BitBoard, ··· 145 152 + self.black_queens.value() * 9; 146 153 white_value as i32 - black_value as i32 147 154 } 155 + fn white_pieces(&self) -> BitBoard { 156 + self.white_pawns 157 + .or(self.white_knights) 158 + .or(self.white_bishops) 159 + .or(self.white_rooks) 160 + .or(self.white_queens) 161 + .or(self.white_king) 162 + } 163 + 164 + fn black_pieces(&self) -> BitBoard { 165 + self.black_pawns 166 + .or(self.black_knights) 167 + .or(self.black_bishops) 168 + .or(self.black_rooks) 169 + .or(self.black_queens) 170 + .or(self.black_king) 171 + } /* 148 172 pub fn make_move(&mut self, algebra: &str) { 149 - let (mover, non_mover) = self.movers_or_not(); 150 - if algebra == "O-O" || algebra == "0-0" { 151 - mover 152 - .iter_mut() 153 - .find(|lp| lp.piece == Piece::King) 154 - .unwrap() 155 - .file = 6; //castles kingside 156 - mover 157 - .iter_mut() 158 - .find(|lp| lp.piece == Piece::Rook && lp.file == 7) 159 - .unwrap() 160 - .file = 5; 161 - } else if algebra == "O-O-O" || algebra == "0-0-0" { 162 - mover 163 - .iter_mut() 164 - .find(|lp| lp.piece == Piece::King) 165 - .unwrap() 166 - .file = 2; //castles queenside 167 - mover 168 - .iter_mut() 169 - .find(|lp| lp.piece == Piece::Rook && lp.file == 0) 170 - .unwrap() 171 - .file = 3; 173 + let (mover, non_mover) = self.movers_or_not(); 174 + if algebra == "O-O" || algebra == "0-0" { 175 + mover 176 + .iter_mut() 177 + .find(|lp| lp.piece == Piece::King) 178 + .unwrap() 179 + .file = 6; //castles kingside 180 + mover 181 + .iter_mut() 182 + .find(|lp| lp.piece == Piece::Rook && lp.file == 7) 183 + .unwrap() 184 + .file = 5; 185 + } else if algebra == "O-O-O" || algebra == "0-0-0" { 186 + mover 187 + .iter_mut() 188 + .find(|lp| lp.piece == Piece::King) 189 + .unwrap() 190 + .file = 2; //castles queenside 191 + mover 192 + .iter_mut() 193 + .find(|lp| lp.piece == Piece::Rook && lp.file == 0) 194 + .unwrap() 195 + .file = 3; 196 + } else { 197 + let mut move_segments = vec![]; 198 + let mut iter = algebra.chars(); 199 + while let Some(ch) = iter.next() { 200 + if let Some(piece) = Piece::from_char(ch) { 201 + move_segments.push(MoveSegment::Piece(piece)); 202 + } else if let Some(rank) = ch.to_digit(10) { 203 + move_segments.push(MoveSegment::File(rank as usize)); 204 + } else if ch >= 'a' && ch <= 'h' { 205 + move_segments.push(MoveSegment::File(ch as usize - 'a' as usize)); 206 + } else if ch == 'x' { 207 + move_segments.push(MoveSegment::Captures) 208 + } else if ch == '=' 209 + && let Some(next) = iter.next() // if given bad notation this breaks, but the notation was unparseable anyway 210 + && let Some(piece) = Piece::from_char(next) 211 + { 212 + move_segments.push(MoveSegment::Promotion(piece)); 213 + } 214 + } 215 + if move_segments.len() == 2 216 + && let MoveSegment::File(file) = move_segments[0] 217 + && let MoveSegment::Rank(rank) = move_segments[1] 218 + { 219 + self.move_unambiguous(Piece::Pawn, None, None, rank, file); 220 + } 221 + } 222 + } 223 + fn move_unambiguous( 224 + &mut self, 225 + piece: Piece, 226 + source_rank: Option<usize>, 227 + source_file: Option<usize>, 228 + dest_rank: usize, 229 + dest_file: usize, 230 + ) { 231 + let piece_to_move = (if self.white_to_move { 232 + &mut self.white 233 + } else { 234 + &mut self.black 235 + }) 236 + .iter_mut() 237 + .filter(|lp| lp.piece == piece) 238 + .filter(|lp| { 239 + source_rank.is_none_or(|rank| rank == lp.rank) 240 + && source_file.is_none_or(|file| file == lp.file) 241 + }) 242 + .find(|lp| { 243 + self.valid_moves(lp.clone()) 244 + .contains(&(dest_file, dest_rank)) 245 + }) 246 + .unwrap(); 247 + 248 + self.capture_if_possible(dest_file, dest_rank); 249 + piece_to_move.rank = dest_rank; 250 + piece_to_move.file = dest_file; 251 + }*/ 252 + fn pawn_moves(&self, file: u8, rank: u8) -> Vec<(u8, u8)> { 253 + let white = self.white_pieces(); 254 + let black = self.black_pieces(); 255 + let white_moving = self.white_pawns.get_square(file, rank); 256 + let either = white.or(black); 257 + let mut moves = vec![]; 258 + if white_moving { 259 + let (open_squares, _) = self.open_squares(file, rank, Direction::North); 260 + if (open_squares) > 0 { 261 + moves.push((file, rank + 1)); 262 + } 263 + if rank == 1 && open_squares > 1 { 264 + moves.push((file, rank + 2)); 265 + } 266 + if let (0, true) = self.open_squares(file, rank, Direction::Northeast) { 267 + moves.push((file + 1, rank + 1)) 268 + } 269 + if let (0, true) = self.open_squares(file, rank, Direction::Northwest) { 270 + moves.push((file - 1, rank + 1)) 271 + } 172 272 } else { 173 - let mut move_segments = vec![]; 174 - let mut iter = algebra.chars(); 175 - while let Some(ch) = iter.next() { 176 - if let Some(piece) = Piece::from_char(ch) { 177 - move_segments.push(MoveSegment::Piece(piece)); 178 - } else if let Some(rank) = ch.to_digit(10) { 179 - move_segments.push(MoveSegment::File(rank as usize)); 180 - } else if ch >= 'a' && ch <= 'h' { 181 - move_segments.push(MoveSegment::File(ch as usize - 'a' as usize)); 182 - } else if ch == 'x' { 183 - move_segments.push(MoveSegment::Captures) 184 - } else if ch == '=' 185 - && let Some(next) = iter.next() // if given bad notation this breaks, but the notation was unparseable anyway 186 - && let Some(piece) = Piece::from_char(next) 187 - { 188 - move_segments.push(MoveSegment::Promotion(piece)); 273 + let (open_squares, _) = self.open_squares(file, rank, Direction::South); 274 + if open_squares > 0 { 275 + moves.push((file, rank - 1)) 276 + } 277 + if rank == 6 && open_squares > 1 { 278 + moves.push((file, rank - 2)) 279 + } 280 + 281 + if let (0, true) = self.open_squares(file, rank, Direction::Southeast) { 282 + moves.push((file + 1, rank - 1)) 283 + } 284 + if let (0, true) = self.open_squares(file, rank, Direction::Southwest) { 285 + moves.push((file - 1, rank - 1)) 286 + } 287 + } 288 + moves 289 + } 290 + /// returns number of squares and whether that's a capture 291 + fn open_squares(&self, mut file: u8, mut rank: u8, direction: Direction) -> (u8, bool) { 292 + let white = self.white_pieces(); 293 + let black = self.black_pieces(); 294 + let white_playing = white.get_square(file, rank); 295 + let mut count = 0; 296 + loop { 297 + match direction { 298 + Direction::North => { 299 + if rank == 7 { 300 + return (count, false); 301 + } else { 302 + rank += 1 303 + } 189 304 } 305 + Direction::Northeast => { 306 + if rank == 7 || file == 7 { 307 + return (count, false); 308 + } else { 309 + rank += 1; 310 + file += 1; 311 + } 312 + } 313 + Direction::East => todo!(), 314 + Direction::Southeast => todo!(), 315 + Direction::South => todo!(), 316 + Direction::Southwest => todo!(), 317 + Direction::West => todo!(), 318 + Direction::Northwest => todo!(), 190 319 } 191 - if move_segments.len() == 2 192 - && let MoveSegment::File(file) = move_segments[0] 193 - && let MoveSegment::Rank(rank) = move_segments[1] 194 - { 195 - self.move_unambiguous(Piece::Pawn, None, None, rank, file); 320 + if white.get_square(file, rank) { 321 + return (count, !white_playing); 322 + } else if black.get_square(file, rank) { 323 + return (count, white_playing); 324 + } else { 325 + count += 1; 196 326 } 197 327 } 198 328 } 199 - fn move_unambiguous( 200 - &mut self, 201 - piece: Piece, 202 - source_rank: Option<usize>, 203 - source_file: Option<usize>, 204 - dest_rank: usize, 205 - dest_file: usize, 206 - ) { 207 - let piece_to_move = (if self.white_to_move { 208 - &mut self.white 209 - } else { 210 - &mut self.black 211 - }) 212 - .iter_mut() 213 - .filter(|lp| lp.piece == piece) 214 - .filter(|lp| { 215 - source_rank.is_none_or(|rank| rank == lp.rank) 216 - && source_file.is_none_or(|file| file == lp.file) 217 - }) 218 - .find(|lp| { 219 - self.valid_moves(lp.clone()) 220 - .contains(&(dest_file, dest_rank)) 221 - }) 222 - .unwrap(); 223 - 224 - self.capture_if_possible(dest_file, dest_rank); 225 - piece_to_move.rank = dest_rank; 226 - piece_to_move.file = dest_file; 227 - } 228 329 fn valid_moves(&self, lp: &LocatedPiece) -> Vec<(usize, usize)> { 229 330 let mut moves = vec![]; 230 331 match lp.piece { ··· 239 340 moves.retain(|(file, rank)| !self.mover_is_in_check(file, rank)); 240 341 moves 241 342 } 242 - fn movers_or_not(&mut self) -> (&mut Vec<LocatedPiece>, &mut Vec<LocatedPiece>) { 243 - if self.white_to_move { 244 - (&mut self.white, &mut self.black) 245 - } else { 246 - (&mut self.black, &mut self.white) 247 - } 248 - } 249 - fn capture_if_possible(&mut self, file: usize, rank: usize) { 250 - self.movers_or_not() 251 - .1 252 - .retain(|lp| lp.rank != rank && lp.file != file) 253 - } 343 + } 344 + 345 + #[derive(Clone, Copy, PartialEq, Eq)] 346 + enum Direction { 347 + North, 348 + Northeast, 349 + East, 350 + Southeast, 351 + South, 352 + Southwest, 353 + West, 354 + Northwest, 254 355 } 255 356 256 357 #[derive(Clone, Copy, PartialEq, Eq)]