Add generator
This commit is contained in:
@@ -88,6 +88,18 @@ impl Board {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn empty_squares(&self) -> Vec<Coord> {
|
||||
let mut empty_squares = Vec::new();
|
||||
for file in 0..4 {
|
||||
for rank in 0..4 {
|
||||
if self.cells[file][rank].is_empty() {
|
||||
empty_squares.push(Coord::new(file, rank));
|
||||
}
|
||||
}
|
||||
}
|
||||
empty_squares
|
||||
}
|
||||
|
||||
pub(crate) fn print(&self) -> String {
|
||||
let mut builder: Vec<char> = Vec::new();
|
||||
for rank in 0..4 {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
use crate::{
|
||||
engine::{board::Board, coord::Coord, piece::Piece, square::Square},
|
||||
solver::{self, solver::Solver},
|
||||
};
|
||||
use rand::{seq::*, Rng};
|
||||
|
||||
pub(crate) fn generate() -> Option<Board> {
|
||||
let mut rand = rand::thread_rng();
|
||||
let candidate_pieces = vec![
|
||||
Piece::Pawn,
|
||||
Piece::Pawn,
|
||||
Piece::Pawn,
|
||||
Piece::Rook,
|
||||
Piece::Bishop,
|
||||
Piece::Knight,
|
||||
Piece::Knight,
|
||||
Piece::King,
|
||||
Piece::Queen,
|
||||
];
|
||||
let num_pieces = 7;
|
||||
let attempts = 1000;
|
||||
for i in 0..attempts {
|
||||
let board = try_generate(num_pieces, candidate_pieces.clone(), rand.clone());
|
||||
if let Some(board) = board {
|
||||
return Some(board);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn try_generate(
|
||||
num_pieces: u32,
|
||||
mut candidate_pieces: Vec<Piece>,
|
||||
mut rand: rand::prelude::ThreadRng,
|
||||
) -> Option<Board> {
|
||||
let mut board = Board::new();
|
||||
for _ in 0..num_pieces {
|
||||
let mut placed = false;
|
||||
let empty_squares = board.empty_squares();
|
||||
let mut attempts = 15;
|
||||
while !placed {
|
||||
if attempts == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
attempts -= 1;
|
||||
|
||||
let index = rand.gen_range(0..candidate_pieces.len());
|
||||
let piece = candidate_pieces[index];
|
||||
let coord = empty_squares.choose(&mut rand).unwrap().clone();
|
||||
|
||||
board.set(Square::Occupied(piece.clone(), coord.clone()));
|
||||
let solutions = Solver::new(board.clone()).solve();
|
||||
if solutions.len() > 0 {
|
||||
placed = true;
|
||||
candidate_pieces.remove(index);
|
||||
continue;
|
||||
}
|
||||
board.set(Square::Empty(coord));
|
||||
}
|
||||
}
|
||||
|
||||
let solutions = Solver::new(board.clone()).solve();
|
||||
if solutions.len() > 1 {
|
||||
None
|
||||
} else {
|
||||
Some(board)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{engine::board::GameState, solver::solver::Solver};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn generator_smoke() {
|
||||
let board = generate().unwrap();
|
||||
assert_eq!(board.game_state, GameState::InProgress);
|
||||
|
||||
let solutions = Solver::new(board).solve();
|
||||
assert_ne!(solutions.len(), 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod generator;
|
||||
+31
-1
@@ -4,4 +4,34 @@ mod engine;
|
||||
#[allow(unused)]
|
||||
mod solver;
|
||||
|
||||
fn main() {}
|
||||
#[allow(unused)]
|
||||
mod generator;
|
||||
|
||||
use crate::generator::generator::generate;
|
||||
use crate::solver::solver::Solver;
|
||||
|
||||
fn main() {
|
||||
let start = std::time::Instant::now();
|
||||
let board = generate();
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
println!("Generated a problem in {} ms", elapsed.as_millis());
|
||||
|
||||
let Some(board) = board else {
|
||||
println!(
|
||||
"Failed to generate a board after {} ms, Try again",
|
||||
elapsed.as_millis()
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
println!("{}", board.print());
|
||||
let solutions = Solver::new(board).solve();
|
||||
println!("Found {} solutions", solutions.len());
|
||||
let solution = solutions.first().unwrap();
|
||||
let mut idx = 0;
|
||||
solution.iter().for_each(|m| {
|
||||
idx += 1;
|
||||
println!("{}. {}", idx, m.notation());
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user