Add as command line tool

This commit is contained in:
cool-mist 2025-01-05 00:23:38 +05:30
parent 354b59c004
commit be9585d5c2
7 changed files with 224 additions and 15 deletions

59
Cargo.lock generated
View File

@ -2,6 +2,38 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "argh"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240"
dependencies = [
"argh_derive",
"argh_shared",
"rust-fuzzy-search",
]
[[package]]
name = "argh_derive"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803"
dependencies = [
"argh_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "argh_shared"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6"
dependencies = [
"serde",
]
[[package]]
name = "byteorder"
version = "1.5.0"
@ -88,10 +120,37 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rust-fuzzy-search"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
[[package]]
name = "serde"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sol_chess"
version = "0.1.0"
dependencies = [
"argh",
"rand",
]

View File

@ -4,4 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
argh = "0.1.13"
rand = "0.8.5"

View File

@ -2,6 +2,60 @@
Goal: Generate 'hard' puzzles.
## Install
- Install Rust from [here](https://www.rust-lang.org/tools/install).
- Run `cargo install --git https://github.com/cool-mist/sol_chess` to install the tool.
- Run `sol_chess --help` to see the options.
## Usage
- Generate a puzzle
```bash
$ sol_chess -g -n 6
Generated a puzzle with 6 pieces after 330 ms
PP..
..PB
.K..
.N..
```
- Solve a puzzle
```bash
$ sol_chess -- --solve PP....PB.K...N..
PP..
..PB
.K..
.N..
Found 1 solutions
1. Nb1 -> c3
2. Nc3 -> a4
3. Na4 -> b2
4. Nb2 -> d3
5. Nd3 -> b4
```
- Generate and solve a puzzle
```bash
$ sol_chess -g -n 6 --print
Generated a puzzle with 6 pieces after 933 ms
.P.N
B.R.
.K..
..N.
Found 1 solutions
1. Ba3 -> b4
2. Bb4 -> c3
3. Bc3 -> d4
4. Bd4 -> b2
5. Bb2 -> c1
```
## Heuristics of current algorithm
1. About 6-7 pieces on the board.

View File

@ -353,6 +353,36 @@ impl Board {
fn create_move(start: &Square, target: Square) -> Move {
Move::new(start.clone(), target)
}
pub(crate) fn from_string(board_string: String) -> Option<Board> {
if board_string.chars().count() != 16 {
return None;
}
let mut board = Board::new();
let mut file = 0;
let mut rank = 0;
let mut chars = board_string.chars();
for r in 0..4 {
for f in 0..4 {
let c = chars.next().unwrap();
let piece = match c {
'K' => Piece::King,
'Q' => Piece::Queen,
'B' => Piece::Bishop,
'N' => Piece::Knight,
'R' => Piece::Rook,
'P' => Piece::Pawn,
'.' => continue,
_ => return None,
};
let square = Square::Occupied(piece, Coord::new(f, r));
board.set(square);
}
}
Some(board)
}
}
#[cfg(test)]

View File

@ -12,7 +12,11 @@ impl Move {
}
pub(crate) fn notation(&self) -> String {
format!("{} -> {}", self.from.notation(), self.to.notation())
format!(
"{} -> {}",
self.from.notation(),
self.to.coord_ref().notation
)
}
}

View File

@ -4,7 +4,7 @@ use crate::{
};
use rand::{seq::*, Rng};
pub(crate) fn generate() -> Option<Board> {
pub(crate) fn generate(num_pieces: u32) -> Option<Board> {
let mut rand = rand::thread_rng();
let candidate_pieces = vec![
Piece::Pawn,
@ -17,7 +17,6 @@ pub(crate) fn generate() -> Option<Board> {
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());

View File

@ -7,26 +7,44 @@ mod solver;
#[allow(unused)]
mod generator;
use argh::FromArgs;
use engine::board::Board;
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();
let args: Args = argh::from_env();
if args.generate {
let puzzle = generate_puzzle(args.num_pieces);
let Some(board) = puzzle else {
return;
};
println!("Generated a problem in {} ms", elapsed.as_millis());
println!("{}", board.print());
let Some(board) = board else {
println!(
"Failed to generate a board after {} ms, Try again",
elapsed.as_millis()
);
return;
};
if args.print {
solve_puzzle(board);
}
} else if let Some(board_string) = args.solve {
let board = Board::from_string(board_string);
let Some(board) = board else {
println!("Invalid board string");
return;
};
println!("{}", board.print());
solve_puzzle(board);
} else {
println!("Use --help to see available options");
}
}
println!("{}", board.print());
fn solve_puzzle(board: Board) {
let solutions = Solver::new(board).solve();
if solutions.len() == 0 {
println!("No solutions found");
return;
}
println!("Found {} solutions", solutions.len());
let solution = solutions.first().unwrap();
let mut idx = 0;
@ -35,3 +53,47 @@ fn main() {
println!("{}. {}", idx, m.notation());
});
}
fn generate_puzzle(num_pieces: Option<u32>) -> Option<Board> {
let start = std::time::Instant::now();
let num_pieces = num_pieces.unwrap_or(5);
let board = generate(num_pieces);
let elapsed = start.elapsed();
let Some(board) = board else {
println!(
"Failed to generate a puzzle with {} pieces after {} ms, Try again",
num_pieces,
elapsed.as_millis()
);
return None;
};
println!(
"Generated a puzzle with {} pieces after {} ms",
num_pieces,
elapsed.as_millis()
);
Some(board)
}
/// Solitaire Chess puzzle generator and solver
/// - v0.0.1 cool-mist
#[derive(FromArgs)]
struct Args {
#[argh(switch, short = 'g')]
/// generate a puzzle
generate: bool,
#[argh(option, short = 'n')]
/// number of pieces to place on the board while generating a puzzle
num_pieces: Option<u32>,
#[argh(switch)]
/// print the solution. When solving a puzzle, this is always set to true
print: bool,
#[argh(option, short = 's')]
/// the board to solve
solve: Option<String>,
}