initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
24
Cargo.toml
Normal file
24
Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[workspace.package]
|
||||||
|
homepage = "https://gitea.woggioni.net/woggioni/rdraught"
|
||||||
|
authors = ["Walter Oggioni <oggioni.walter@gmail.com>"]
|
||||||
|
version = "0.1.0"
|
||||||
|
repository = "https://github.com/gtk-rs/gtk-rs-core"
|
||||||
|
license = "MIT"
|
||||||
|
edition = "2024"
|
||||||
|
rust-version = "1.87"
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "rdraught"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "rdraught"
|
||||||
|
crate-type = ["lib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
heapless = "0.8"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = "0.9"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["rdraught-cli", "rdraught-pi", "rdraught-ui"]
|
27
examples/checkers.rs
Normal file
27
examples/checkers.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use heapless::Vec;
|
||||||
|
use rdraught::draughts::{DraughtsBoard, Piece};
|
||||||
|
use rdraught::position::Position;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let board = DraughtsBoard::default();
|
||||||
|
println!("{:?}", board);
|
||||||
|
// let red_pieces: Vec<(usize, usize, Piece)> = board
|
||||||
|
// .into_iter()
|
||||||
|
// .filter(|(i, j, p)| *p == Piece::SimpleRedPawn || *p == Piece::CrownedRedPawn)
|
||||||
|
// .collect();
|
||||||
|
|
||||||
|
let mut pieces = Vec::<Piece, 2>::new();
|
||||||
|
pieces.push(Piece::SimpleRedPawn).unwrap();
|
||||||
|
pieces.push(Piece::CrownedRedPawn).unwrap();
|
||||||
|
|
||||||
|
// board
|
||||||
|
// .pieces(&pieces)
|
||||||
|
// .for_each(|(i, j, piece)| println!("({}, {}): {:?}", i, j, piece));
|
||||||
|
// println!("{:?}", board[Position::new(0, 0)]);
|
||||||
|
|
||||||
|
board
|
||||||
|
.moves_for_piece(Position::new(2, 0), false)
|
||||||
|
.for_each(|mv| {
|
||||||
|
println!("{:?}", mv);
|
||||||
|
});
|
||||||
|
}
|
17
rdraught-cli/Cargo.toml
Normal file
17
rdraught-cli/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "rdraught-cli"
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rustyline-test"
|
||||||
|
path = "src/rustyline-test.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
#reedline = "0.40"
|
||||||
|
rustyline = {version = "16.0", default-features = false }
|
33
rdraught-cli/src/rustyline-test.rs
Normal file
33
rdraught-cli/src/rustyline-test.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use rustyline::error::ReadlineError;
|
||||||
|
use rustyline::{DefaultEditor, Result};
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// `()` can be used when no completer is required
|
||||||
|
let mut rl = DefaultEditor::new()?;
|
||||||
|
/* if rl.load_history("history.txt").is_err() {
|
||||||
|
println!("No previous history.");
|
||||||
|
} */
|
||||||
|
loop {
|
||||||
|
let readline = rl.readline(">> ");
|
||||||
|
match readline {
|
||||||
|
Ok(line) => {
|
||||||
|
rl.add_history_entry(line.as_str())?;
|
||||||
|
println!("Line: {}", line);
|
||||||
|
}
|
||||||
|
Err(ReadlineError::Interrupted) => {
|
||||||
|
println!("CTRL-C");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(ReadlineError::Eof) => {
|
||||||
|
println!("CTRL-D");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("Error: {:?}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// rl.save_history("history.txt").unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
16
rdraught-pi/Cargo.toml
Normal file
16
rdraught-pi/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "rdraught-pi"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
authors = ["Walter Oggioni <oggioni.walter@gmail.com>"]
|
||||||
|
license = "MIT"
|
||||||
|
rust-version = "1.87"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "hello"
|
||||||
|
path = "src/hello.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
panic-halt = "1.0"
|
||||||
|
libc = { version = "0.2", default-features = false }
|
||||||
|
|
17
rdraught-pi/src/hello.rs
Normal file
17
rdraught-pi/src/hello.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use libc::printf;
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
extern crate panic_halt;
|
||||||
|
|
||||||
|
#[link(name = "c", kind = "static")]
|
||||||
|
unsafe extern "C" {}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn main() {
|
||||||
|
unsafe {
|
||||||
|
printf("Hello world!!\n".as_ptr() as *const i8);
|
||||||
|
}
|
||||||
|
}
|
21
rdraught-ui/Cargo.toml
Normal file
21
rdraught-ui/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "rdraught-ui"
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gtk4 = "0.9"
|
||||||
|
gdk4 = "0.9"
|
||||||
|
rdraught = {version = "0.1", path= ".." }
|
||||||
|
rmath = { version="0.1", registry="gitea" }
|
||||||
|
rsvg = "0.4"
|
||||||
|
cairo-rs = "0.20"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
cairo-rs = "0.20.10"
|
||||||
|
cairo-sys-rs = "0.20.10"
|
254
rdraught-ui/Chess_Pieces_Sprite.svg
Normal file
254
rdraught-ui/Chess_Pieces_Sprite.svg
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="270" height="90">
|
||||||
|
<!-- white king //-->
|
||||||
|
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0)">
|
||||||
|
<path
|
||||||
|
d="M 22.5,11.63 L 22.5,6"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 20,8 L 25,8"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
|
||||||
|
style="fill:#ffffff; stroke:#000000; stroke-linecap:butt; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
|
||||||
|
style="fill:#ffffff; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,30 C 17,27 27,27 32.5,30"
|
||||||
|
style="fill:none; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5"
|
||||||
|
style="fill:none; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,37 C 17,34 27,34 32.5,37"
|
||||||
|
style="fill:none; stroke:#000000;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- white queen //-->
|
||||||
|
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(45,0)">
|
||||||
|
<path
|
||||||
|
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
|
||||||
|
transform="translate(-1,-1)" />
|
||||||
|
<path
|
||||||
|
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
|
||||||
|
transform="translate(15.5,-5.5)" />
|
||||||
|
<path
|
||||||
|
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
|
||||||
|
transform="translate(32,-1)" />
|
||||||
|
<path
|
||||||
|
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
|
||||||
|
transform="translate(7,-4.5)" />
|
||||||
|
<path
|
||||||
|
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
|
||||||
|
transform="translate(24,-4)" />
|
||||||
|
<path
|
||||||
|
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38,14 L 31,25 L 31,11 L 25.5,24.5 L 22.5,9.5 L 19.5,24.5 L 14,10.5 L 14,25 L 7,14 L 9,26 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,30 C 15,29 30,29 33.5,30"
|
||||||
|
style="fill:none;" />
|
||||||
|
<path
|
||||||
|
d="M 12,33.5 C 18,32.5 27,32.5 33,33.5"
|
||||||
|
style="fill:none;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- white bishop //-->
|
||||||
|
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(90,0)">
|
||||||
|
<g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;">
|
||||||
|
<path
|
||||||
|
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
|
||||||
|
<path
|
||||||
|
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
|
||||||
|
<path
|
||||||
|
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- white knight //-->
|
||||||
|
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(135,0)">
|
||||||
|
<path
|
||||||
|
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
|
||||||
|
style="fill:#ffffff; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
|
||||||
|
style="fill:#ffffff; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
|
||||||
|
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- white rook //-->
|
||||||
|
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(180,0)">
|
||||||
|
<path
|
||||||
|
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14"
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 34,14 L 31,17 L 14,17 L 11,14" />
|
||||||
|
<path
|
||||||
|
d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
|
||||||
|
style="stroke-linecap:butt; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 34,14"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- white pawn //-->
|
||||||
|
<g transform="translate(225,0)">
|
||||||
|
<path
|
||||||
|
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
|
||||||
|
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- black king //-->
|
||||||
|
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,45)">
|
||||||
|
<path
|
||||||
|
d="M 22.5,11.63 L 22.5,6"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
|
||||||
|
style="fill:#000000;fill-opacity:1; stroke-linecap:butt; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 20,8 L 25,8"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.51,26.6 L 22.5,24.5 C 20,18 9.906,14 6.997,19.85 C 4.5,25.5 11.85,28.85 11.85,28.85"
|
||||||
|
style="fill:none; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,30 C 17,27 27,27 32.5,30 M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5 M 11.5,37 C 17,34 27,34 32.5,37"
|
||||||
|
style="fill:none; stroke:#ffffff;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- black queen //-->
|
||||||
|
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(45,45)">
|
||||||
|
<g style="fill:#000000; stroke:none;">
|
||||||
|
<circle cx="6" cy="12" r="2.75" />
|
||||||
|
<circle cx="14" cy="9" r="2.75" />
|
||||||
|
<circle cx="22.5" cy="8" r="2.75" />
|
||||||
|
<circle cx="31" cy="9" r="2.75" />
|
||||||
|
<circle cx="39" cy="12" r="2.75" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z"
|
||||||
|
style="stroke-linecap:butt; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z"
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 11,38.5 A 35,35 1 0 0 34,38.5"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 11,29 A 35,35 1 0 1 34,29"
|
||||||
|
style="fill:none; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 12.5,31.5 L 32.5,31.5"
|
||||||
|
style="fill:none; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,34.5 A 35,35 1 0 0 33.5,34.5"
|
||||||
|
style="fill:none; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 10.5,37.5 A 35,35 1 0 0 34.5,37.5"
|
||||||
|
style="fill:none; stroke:#ffffff;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- black bishop //-->
|
||||||
|
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(90,45)">
|
||||||
|
<g style="fill:#000000; stroke:#000000; stroke-linecap:butt;">
|
||||||
|
<path
|
||||||
|
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
|
||||||
|
<path
|
||||||
|
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
|
||||||
|
<path
|
||||||
|
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-linejoin:miter;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- black knight //-->
|
||||||
|
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(135,45)">
|
||||||
|
<path
|
||||||
|
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
|
||||||
|
style="fill:#ffffff; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
|
||||||
|
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
|
||||||
|
style="fill:#ffffff; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z "
|
||||||
|
style="fill:#ffffff; stroke:none;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- black rook //-->
|
||||||
|
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(180,45)">
|
||||||
|
<path
|
||||||
|
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z "
|
||||||
|
style="stroke-linecap:butt;stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12,35.5 L 33,35.5 L 33,35.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 13,31.5 L 32,31.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 14,29.5 L 31,29.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 14,16.5 L 31,16.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 34,14"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- black pawn //-->
|
||||||
|
<g transform="translate(225,45)">
|
||||||
|
<path
|
||||||
|
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
|
||||||
|
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 15 KiB |
27
rdraught-ui/src/crown.svg
Normal file
27
rdraught-ui/src/crown.svg
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="fill:none; fill-opacity:0; fill-rule:evenodd; stroke:#ffc837; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0)">
|
||||||
|
<path
|
||||||
|
d="M 22.5,11.63 L 22.5,6"
|
||||||
|
style="fill:none; stroke:#ffc837; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 20,8 L 25,8"
|
||||||
|
style="fill:none; stroke:#ffc837; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
|
||||||
|
style="fill:#ffffff; stroke:#ffc837; stroke-linecap:butt; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
|
||||||
|
style="fill:#ffffff; stroke:#ffc837;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,30 C 17,27 27,27 32.5,30"
|
||||||
|
style="fill:none; stroke:#ffc837;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5"
|
||||||
|
style="fill:none; stroke:#ffc837;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,37 C 17,34 27,34 32.5,37"
|
||||||
|
style="fill:none; stroke:#ffc837;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
211
rdraught-ui/src/geo2d.rs
Normal file
211
rdraught-ui/src/geo2d.rs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
use rmath::SMatrix;
|
||||||
|
use std::clone::Clone;
|
||||||
|
use std::cmp::Eq;
|
||||||
|
use std::cmp::PartialEq;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::marker::Copy;
|
||||||
|
use std::ops::Add;
|
||||||
|
use std::ops::Div;
|
||||||
|
use std::ops::Mul;
|
||||||
|
use std::ops::Neg;
|
||||||
|
use std::ops::Sub;
|
||||||
|
|
||||||
|
pub type Xform = SMatrix<f64, 3, 3>;
|
||||||
|
pub struct Point(SMatrix<f64, 1, 3>);
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
pub fn x(&self) -> f64 {
|
||||||
|
self.0[(0, 0)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y(&self) -> f64 {
|
||||||
|
self.0[(0, 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(x: f64, y: f64) -> Point {
|
||||||
|
Point(SMatrix::new(|pos| match pos {
|
||||||
|
(0, 0) => x,
|
||||||
|
(0, 1) => y,
|
||||||
|
(0, 2) => 1f64,
|
||||||
|
_ => 0f64,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Point> for Point {
|
||||||
|
fn add(self, rhs: Point) -> Self::Output {
|
||||||
|
self * &xlate(rhs.x(), rhs.y())
|
||||||
|
}
|
||||||
|
type Output = Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Point> for Point {
|
||||||
|
fn sub(self, rhs: Point) -> Self::Output {
|
||||||
|
self * &xlate(-rhs.x(), -rhs.y())
|
||||||
|
}
|
||||||
|
type Output = Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for Point {
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
Point::new(self.0[(0, 0)] * rhs, self.0[(0, 1)] * rhs)
|
||||||
|
}
|
||||||
|
type Output = Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f64> for Point {
|
||||||
|
fn div(self, rhs: f64) -> Self::Output {
|
||||||
|
Point::new(self.0[(0, 0)] / rhs, self.0[(0, 1)] / rhs)
|
||||||
|
}
|
||||||
|
type Output = Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Point {
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Point::new(-self.x(), -self.y())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output = Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Point {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0.eq(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Point {}
|
||||||
|
|
||||||
|
impl Mul<&Xform> for Point {
|
||||||
|
fn mul(self, rhs: &Xform) -> Self::Output {
|
||||||
|
Point(self.0 * rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output = Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Point {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Copy for Point {}
|
||||||
|
|
||||||
|
impl Display for Point {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Rect2d {
|
||||||
|
tl: Point,
|
||||||
|
br: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rect2d {
|
||||||
|
pub fn new(tl: Point, br: Point) -> Rect2d {
|
||||||
|
Rect2d { tl, br }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn center(&self) -> Point {
|
||||||
|
(self.tl + self.br) / 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tl(&self) -> Point {
|
||||||
|
self.tl
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn br(&self) -> Point {
|
||||||
|
self.br
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> f64 {
|
||||||
|
(self.br.x() - self.tl.x()).abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> f64 {
|
||||||
|
(self.br.y() - self.tl.y()).abs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<&Xform> for Rect2d {
|
||||||
|
fn mul(self, rhs: &Xform) -> Self::Output {
|
||||||
|
Rect2d {
|
||||||
|
tl: Point(self.tl.0 * rhs),
|
||||||
|
br: Point(self.br.0 * rhs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output = Rect2d;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rot(alpha: f64) -> Xform {
|
||||||
|
let sa = alpha.sin();
|
||||||
|
let ca = alpha.cos();
|
||||||
|
Xform::new(|position| match position {
|
||||||
|
(0, 0) => ca,
|
||||||
|
(1, 1) => ca,
|
||||||
|
(1, 0) => -sa,
|
||||||
|
(0, 1) => sa,
|
||||||
|
(2, 2) => 1f64,
|
||||||
|
_ => 0f64,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point {}
|
||||||
|
|
||||||
|
pub fn scale(x: f64, y: f64) -> Xform {
|
||||||
|
Xform::new(|position| match position {
|
||||||
|
(0, 0) => x,
|
||||||
|
(1, 1) => y,
|
||||||
|
(2, 2) => 1f64,
|
||||||
|
_ => 0f64,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xlate(x: f64, y: f64) -> Xform {
|
||||||
|
Xform::new(|position| match position {
|
||||||
|
(0, 0) => 1f64,
|
||||||
|
(1, 1) => 1f64,
|
||||||
|
(2, 2) => 1f64,
|
||||||
|
(2, 0) => x,
|
||||||
|
(2, 1) => y,
|
||||||
|
_ => 0f64,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
|
use super::Point;
|
||||||
|
use super::rot;
|
||||||
|
use super::scale;
|
||||||
|
use super::xlate;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_xlate() {
|
||||||
|
let p = Point::new(1.0, 3.0);
|
||||||
|
let xform = xlate(-1.0, 0.0);
|
||||||
|
let p2 = p * &xform * &rot(-PI / 2.0) * &xlate(-2.0, 3.0);
|
||||||
|
assert!(p == p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rotate() {
|
||||||
|
let p = Point::new(0.0, 3.0);
|
||||||
|
let p2 = p * &rot(-PI / 2.0);
|
||||||
|
assert!((p2.x() - 3.0).abs() < 1e-3);
|
||||||
|
assert!((p2.y() - 0.0).abs() < 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scale() {
|
||||||
|
let p = Point::new(1.0, 3.0);
|
||||||
|
let p2 = p * &scale(2.0, 3.0);
|
||||||
|
assert!((p2.x() - 2.0).abs() < 1e-3);
|
||||||
|
assert!((p2.y() - 9.0).abs() < 1e-3);
|
||||||
|
}
|
||||||
|
}
|
180
rdraught-ui/src/main.rs
Normal file
180
rdraught-ui/src/main.rs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
use gdk4::cairo::{Context as CairoContext, Matrix};
|
||||||
|
use gtk4::cairo::Error;
|
||||||
|
use gtk4::prelude::*;
|
||||||
|
use gtk4::{self as gtk};
|
||||||
|
use rdraught::draughts::{self, DraughtsBoard, Piece};
|
||||||
|
use rdraught::position::Position;
|
||||||
|
mod geo2d;
|
||||||
|
use core::f64::consts::PI;
|
||||||
|
use geo2d::{Point, Rect2d, Xform, scale, xlate};
|
||||||
|
use rsvg::{Handle, HandleExt};
|
||||||
|
|
||||||
|
const SQUARE_SIZE: f64 = 1.0;
|
||||||
|
|
||||||
|
const CROWN: &'static [u8] = include_bytes!("crown.svg");
|
||||||
|
|
||||||
|
fn draw_piece(cr: &CairoContext, square: &Rect2d, piece: Piece) -> Result<(), Error> {
|
||||||
|
if let Piece::NoPiece = piece {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
let center = square.center();
|
||||||
|
let outer_radius = square.width() * 0.3;
|
||||||
|
let vertical_scale_factor = 0.8;
|
||||||
|
let matrix = {
|
||||||
|
let mut m1 = Matrix::identity();
|
||||||
|
m1.translate(0.0, -(center.y() - outer_radius));
|
||||||
|
let mut m2 = Matrix::identity();
|
||||||
|
m2.scale(1.0, vertical_scale_factor);
|
||||||
|
let mut m3 = Matrix::identity();
|
||||||
|
m3.translate(0.0, center.y() - outer_radius * vertical_scale_factor);
|
||||||
|
Matrix::multiply(&Matrix::multiply(&m1, &m2), &m3)
|
||||||
|
};
|
||||||
|
cr.save()?;
|
||||||
|
cr.set_matrix(matrix);
|
||||||
|
cr.set_source_rgb(0.0, 0.0, 0.0);
|
||||||
|
let thickness = outer_radius * 0.3;
|
||||||
|
cr.arc(
|
||||||
|
center.x(),
|
||||||
|
center.y() + thickness / 2.0,
|
||||||
|
outer_radius,
|
||||||
|
0.0,
|
||||||
|
2.0 * PI,
|
||||||
|
);
|
||||||
|
cr.rectangle(
|
||||||
|
center.x() - outer_radius,
|
||||||
|
center.y() - thickness / 2.0,
|
||||||
|
outer_radius * 2.0,
|
||||||
|
thickness,
|
||||||
|
);
|
||||||
|
cr.arc(
|
||||||
|
center.x(),
|
||||||
|
center.y() - thickness / 2.0,
|
||||||
|
outer_radius,
|
||||||
|
0.0,
|
||||||
|
2.0 * PI,
|
||||||
|
);
|
||||||
|
cr.fill().unwrap();
|
||||||
|
let (color, crowned) = match piece {
|
||||||
|
Piece::NoPiece => return Ok(()),
|
||||||
|
Piece::SimpleRedPawn => ((1.0, 0.0, 0.0), false),
|
||||||
|
Piece::SimpleWhitePawn => ((1.0, 1.0, 1.0), false),
|
||||||
|
Piece::CrownedRedPawn => ((1.0, 0.0, 0.0), true),
|
||||||
|
Piece::CrownedWhitePawn => ((1.0, 0.0, 0.0), true),
|
||||||
|
};
|
||||||
|
let radius = square.width() * 0.275;
|
||||||
|
cr.set_source_rgb(color.0, color.1, color.2);
|
||||||
|
cr.arc(
|
||||||
|
center.x(),
|
||||||
|
center.y() - thickness / 2.0,
|
||||||
|
radius,
|
||||||
|
0.0,
|
||||||
|
2.0 * PI,
|
||||||
|
);
|
||||||
|
cr.fill()?;
|
||||||
|
if crowned {
|
||||||
|
let handle = Handle::new_from_data(CROWN).unwrap();
|
||||||
|
handle.render_cairo(cr as &cairo::Context);
|
||||||
|
cr.move_to(center.x(), center.y());
|
||||||
|
cr.set_source_rgb(1.0, 1.0, 0.0);
|
||||||
|
cr.set_font_size(20.0);
|
||||||
|
cr.show_text("A♔")?;
|
||||||
|
}
|
||||||
|
cr.restore()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_activate(application: >k::Application) {
|
||||||
|
// Initialize GTK before using any GTK functions.
|
||||||
|
if gtk::init().is_err() {
|
||||||
|
panic!("Failed to initialize GTK.");
|
||||||
|
}
|
||||||
|
// Create a new window.
|
||||||
|
let window = gtk::ApplicationWindow::builder()
|
||||||
|
.application(application)
|
||||||
|
.title("Rdraught")
|
||||||
|
.default_width(800)
|
||||||
|
.default_height(800)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Create a DrawingArea widget where we will draw the chessboard.
|
||||||
|
let drawing_area = gtk::DrawingArea::new();
|
||||||
|
// Add the drawing area to the window.
|
||||||
|
window.set_child(Some(&drawing_area));
|
||||||
|
|
||||||
|
let draughts_board = DraughtsBoard::default();
|
||||||
|
println!("{:?}", draughts_board[Position::new(0, 0)]);
|
||||||
|
// Get the allocation information for the widget.
|
||||||
|
let board_width = SQUARE_SIZE * DraughtsBoard::rows() as f64;
|
||||||
|
let board_height = SQUARE_SIZE * DraughtsBoard::columns() as f64;
|
||||||
|
let board = Rect2d::new(Point::new(0.0, 0.0), Point::new(board_width, board_height));
|
||||||
|
// Set the "draw" function of the drawing area. This callback is called
|
||||||
|
// whenever GTK needs to redraw this widget (for example, on first display or when resized).
|
||||||
|
drawing_area.set_draw_func(move |_widget, cr, width, height| {
|
||||||
|
let screen = Rect2d::new(
|
||||||
|
Point::new(0.0, 0.0),
|
||||||
|
Point::new(width as f64, height as f64),
|
||||||
|
);
|
||||||
|
let f = f64::min(
|
||||||
|
screen.width() / board.width(),
|
||||||
|
screen.height() / board.height(),
|
||||||
|
);
|
||||||
|
let screen_center = screen.center();
|
||||||
|
let board_center = board.center();
|
||||||
|
let xform = xlate(-board_center.x(), -board_center.y())
|
||||||
|
* scale(f, f)
|
||||||
|
* xlate(screen_center.x(), screen_center.y());
|
||||||
|
//let xlation = screen.center() - board.center();
|
||||||
|
//let xform = xform * xlate(xlation.x(), xlation.y());
|
||||||
|
|
||||||
|
let square_size = SQUARE_SIZE as f64;
|
||||||
|
|
||||||
|
// Loop over rows and columns to draw each chessboard cell.
|
||||||
|
for row in 0..DraughtsBoard::rows() {
|
||||||
|
for col in 0..DraughtsBoard::columns() {
|
||||||
|
let square = Rect2d::new(
|
||||||
|
board.tl() + Point::new((col as f64) * square_size, (row as f64) * square_size),
|
||||||
|
board.tl()
|
||||||
|
+ Point::new(
|
||||||
|
((col + 1) as f64) * square_size,
|
||||||
|
((row + 1) as f64) * square_size,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let square = square * &xform;
|
||||||
|
// Alternate colors based on the sum of row and column indices.
|
||||||
|
if (row + col) % 2 == 0 {
|
||||||
|
cr.set_source_rgb(0.8, 0.8, 0.6); // white
|
||||||
|
} else {
|
||||||
|
cr.set_source_rgb(0.4, 0.4, 0.2); // black
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw and fill the square.
|
||||||
|
cr.rectangle(
|
||||||
|
square.tl().x(),
|
||||||
|
square.tl().y(),
|
||||||
|
square.width(),
|
||||||
|
square.height(),
|
||||||
|
);
|
||||||
|
cr.fill().unwrap();
|
||||||
|
draw_piece(
|
||||||
|
cr,
|
||||||
|
&square,
|
||||||
|
draughts_board[Position::new(col as u8, (8 - row - 1) as u8)],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Create a new application with the builder pattern
|
||||||
|
let app = gtk::Application::builder()
|
||||||
|
.application_id("net.woggioni.rdraught")
|
||||||
|
.build();
|
||||||
|
app.connect_activate(on_activate);
|
||||||
|
// Run the application
|
||||||
|
app.run();
|
||||||
|
}
|
131
src/board.rs
Normal file
131
src/board.rs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
use core::iter::Enumerate;
|
||||||
|
use core::iter::Flatten;
|
||||||
|
use core::ops::Index;
|
||||||
|
use core::ops::IndexMut;
|
||||||
|
|
||||||
|
use super::position::Position;
|
||||||
|
|
||||||
|
pub trait RectangularBoard {
|
||||||
|
fn rows() -> usize;
|
||||||
|
fn columns() -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct Board<TYPE, const ROWS: usize, const COLUMNS: usize> {
|
||||||
|
data: [[TYPE; COLUMNS]; ROWS],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE, const ROWS: usize, const COLUMNS: usize> RectangularBoard
|
||||||
|
for Board<TYPE, ROWS, COLUMNS>
|
||||||
|
{
|
||||||
|
fn rows() -> usize {
|
||||||
|
ROWS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn columns() -> usize {
|
||||||
|
COLUMNS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Default for Board<TYPE, ROWS, COLUMNS>
|
||||||
|
where
|
||||||
|
TYPE: Default,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(|_| TYPE::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE, const ROWS: usize, const COLUMNS: usize> IntoIterator for Board<TYPE, ROWS, COLUMNS> {
|
||||||
|
type Item = (usize, usize, TYPE);
|
||||||
|
|
||||||
|
type IntoIter = BoardIterator<TYPE, ROWS, COLUMNS>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
BoardIterator {
|
||||||
|
it: self.data.into_iter().flatten().enumerate(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BoardIteratorRef<'a, TYPE, const ROWS: usize, const COLUMNS: usize> {
|
||||||
|
it: Enumerate<Flatten<core::slice::Iter<'a, [TYPE; COLUMNS]>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, TYPE, const ROWS: usize, const COLUMNS: usize> Iterator
|
||||||
|
for BoardIteratorRef<'a, TYPE, ROWS, COLUMNS>
|
||||||
|
where
|
||||||
|
TYPE: Clone + Copy,
|
||||||
|
{
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.it
|
||||||
|
.next()
|
||||||
|
.map(|(index, value)| (index / COLUMNS, index % COLUMNS, *value))
|
||||||
|
}
|
||||||
|
type Item = (usize, usize, TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, TYPE, const ROWS: usize, const COLUMNS: usize> IntoIterator
|
||||||
|
for &'a Board<TYPE, ROWS, COLUMNS>
|
||||||
|
where
|
||||||
|
TYPE: Clone + Copy,
|
||||||
|
{
|
||||||
|
fn into_iter(self) -> BoardIteratorRef<'a, TYPE, ROWS, COLUMNS> {
|
||||||
|
BoardIteratorRef {
|
||||||
|
it: (self.data).iter().flatten().enumerate(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntoIter = BoardIteratorRef<'a, TYPE, ROWS, COLUMNS>;
|
||||||
|
type Item = (usize, usize, TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Board<TYPE, ROWS, COLUMNS> {
|
||||||
|
pub(crate) fn new<INITIALIZER>(mut cb: INITIALIZER) -> Self
|
||||||
|
where
|
||||||
|
INITIALIZER: FnMut((usize, usize)) -> TYPE,
|
||||||
|
{
|
||||||
|
let values: [[TYPE; COLUMNS]; ROWS] =
|
||||||
|
core::array::from_fn(|i| core::array::from_fn(|j| cb((i, j))));
|
||||||
|
Board { data: values }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iter(&self) -> BoardIteratorRef<'_, TYPE, ROWS, COLUMNS> {
|
||||||
|
BoardIteratorRef {
|
||||||
|
it: (self.data).iter().flatten().enumerate(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BoardIterator<TYPE, const ROWS: usize, const COLUMNS: usize> {
|
||||||
|
it: Enumerate<Flatten<core::array::IntoIter<[TYPE; COLUMNS], ROWS>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Iterator
|
||||||
|
for BoardIterator<TYPE, ROWS, COLUMNS>
|
||||||
|
{
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.it
|
||||||
|
.next()
|
||||||
|
.map(|(index, value)| (index / COLUMNS, index % COLUMNS, value))
|
||||||
|
}
|
||||||
|
type Item = (usize, usize, TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Index<Position> for Board<TYPE, ROWS, COLUMNS> {
|
||||||
|
type Output = TYPE;
|
||||||
|
|
||||||
|
fn index(&self, position: Position) -> &Self::Output {
|
||||||
|
let index = position.to_index();
|
||||||
|
&self.data[index.1][index.0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE, const ROWS: usize, const COLUMNS: usize> IndexMut<Position>
|
||||||
|
for Board<TYPE, ROWS, COLUMNS>
|
||||||
|
{
|
||||||
|
fn index_mut(&mut self, position: Position) -> &mut Self::Output {
|
||||||
|
let index = position.to_index();
|
||||||
|
&mut self.data[index.1][index.0]
|
||||||
|
}
|
||||||
|
}
|
462
src/draughts.rs
Normal file
462
src/draughts.rs
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
use super::board::Board;
|
||||||
|
use super::board::BoardIteratorRef;
|
||||||
|
use super::board::RectangularBoard;
|
||||||
|
use core::iter::Filter;
|
||||||
|
use core::ops::Index;
|
||||||
|
use core::ops::IndexMut;
|
||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
|
use super::position::Position;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum Player {
|
||||||
|
White = 0,
|
||||||
|
Red = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum Piece {
|
||||||
|
NoPiece = 0,
|
||||||
|
SimpleRedPawn = 1,
|
||||||
|
CrownedRedPawn = 3,
|
||||||
|
SimpleWhitePawn = 2,
|
||||||
|
CrownedWhitePawn = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Piece {
|
||||||
|
fn player(&self) -> Option<Player> {
|
||||||
|
if self.is_present() && (*self as u32) % 2 == 1 {
|
||||||
|
Some(Player::Red)
|
||||||
|
} else if self.is_present() && (*self as u32) % 2 == 0 {
|
||||||
|
Some(Player::White)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_present(&self) -> bool {
|
||||||
|
*self != Piece::NoPiece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_crowned(&self) -> bool {
|
||||||
|
(*self as u32) > 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_move_backward(&self) -> bool {
|
||||||
|
self.is_crowned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DraughtsBoard(Board<Piece, 8, 8>);
|
||||||
|
|
||||||
|
impl Default for DraughtsBoard {
|
||||||
|
fn default() -> DraughtsBoard {
|
||||||
|
DraughtsBoard(Board::<Piece, 8usize, 8usize>::new(|(i, j)| {
|
||||||
|
if i < 3 {
|
||||||
|
if (i + j) % 2 == 0 {
|
||||||
|
Piece::SimpleWhitePawn
|
||||||
|
} else {
|
||||||
|
Piece::NoPiece
|
||||||
|
}
|
||||||
|
} else if i > 4 {
|
||||||
|
if (i + j) % 2 == 0 {
|
||||||
|
Piece::SimpleRedPawn
|
||||||
|
} else {
|
||||||
|
Piece::NoPiece
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Piece::NoPiece
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Position> for DraughtsBoard {
|
||||||
|
type Output = Piece;
|
||||||
|
|
||||||
|
fn index(&self, position: Position) -> &Self::Output {
|
||||||
|
&self.0[position]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<Position> for DraughtsBoard {
|
||||||
|
fn index_mut(&mut self, position: Position) -> &mut Self::Output {
|
||||||
|
&mut self.0[position]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RectangularBoard for DraughtsBoard {
|
||||||
|
fn rows() -> usize {
|
||||||
|
Board::<Piece, 8, 8>::rows()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn columns() -> usize {
|
||||||
|
Board::<Piece, 8, 8>::columns()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DraughtsBoard {
|
||||||
|
pub fn rows() -> usize {
|
||||||
|
Board::<Piece, 8, 8>::rows()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn columns() -> usize {
|
||||||
|
Board::<Piece, 8, 8>::columns()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pieces<'a, const PIECES: usize>(
|
||||||
|
self: &'a Self,
|
||||||
|
pieces: &'a Vec<Piece, PIECES>,
|
||||||
|
) -> Filter<BoardIteratorRef<'a, Piece, 8, 8>, impl FnMut(&(usize, usize, Piece)) -> bool> {
|
||||||
|
self.0.iter().filter(move |(_, _, piece)| {
|
||||||
|
for p in pieces {
|
||||||
|
if piece == p {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_position_valid(position: Position) -> bool {
|
||||||
|
let position = position.to_index();
|
||||||
|
return position.0 < DraughtsBoard::rows() && position.1 < DraughtsBoard::columns();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_piece(&self, p: &Position) -> Piece {
|
||||||
|
self[*p]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_move_valid(&self, mv: &Move) -> Result<(), Error> {
|
||||||
|
let start = mv.get_start_position();
|
||||||
|
match mv {
|
||||||
|
Move::Movement { .. } => {
|
||||||
|
let mut move_is_possible = false;
|
||||||
|
for possible_move in self.moves_for_piece(start, false) {
|
||||||
|
if let Move::Capture { .. } = possible_move {
|
||||||
|
// Capture is mandatory
|
||||||
|
return Err(Error::MovementInsteadOfCapture);
|
||||||
|
}
|
||||||
|
if possible_move == *mv {
|
||||||
|
move_is_possible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if move_is_possible {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidMove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Move::Capture { .. } => {
|
||||||
|
let mut move_is_possible = false;
|
||||||
|
for possible_move in self.moves_for_piece(start, true) {
|
||||||
|
if possible_move == *mv {
|
||||||
|
move_is_possible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if move_is_possible {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidMove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_move(&mut self, mv: &Move) -> Result<(), Error> {
|
||||||
|
let start = mv.get_start_position();
|
||||||
|
let piece = self.get_piece(&start);
|
||||||
|
if let Piece::NoPiece = piece {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
} else {
|
||||||
|
let player = piece.player().unwrap();
|
||||||
|
match mv {
|
||||||
|
Move::Movement { .. } => {
|
||||||
|
let end = mv.get_end_position();
|
||||||
|
// Make sure the move ends in a vlid position
|
||||||
|
if !DraughtsBoard::is_position_valid(end) {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
}
|
||||||
|
let piece_at_destination = self[mv.get_end_position()];
|
||||||
|
// Make sure there is no piece at destination
|
||||||
|
if let Piece::NoPiece = piece_at_destination {
|
||||||
|
self[start] = Piece::NoPiece;
|
||||||
|
self[end] = piece;
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Move::Capture { .. } => {
|
||||||
|
let end = mv.get_end_position();
|
||||||
|
// Make sure the move ends in a valid position
|
||||||
|
if !DraughtsBoard::is_position_valid(end) {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
}
|
||||||
|
let piece_at_destination = self[mv.get_end_position()];
|
||||||
|
// Make sure there is no piece at destination
|
||||||
|
if let Piece::NoPiece = piece_at_destination {
|
||||||
|
let captured = self[(end + start) / (2, 2)];
|
||||||
|
// Make sure there is a piece to be captured
|
||||||
|
if let Piece::NoPiece = captured {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
}
|
||||||
|
// Make sure the captured piece belongs to the opposite player
|
||||||
|
if let Some(captured_piece_player) = captured.player() {
|
||||||
|
if captured_piece_player != player {
|
||||||
|
self[start] = Piece::NoPiece;
|
||||||
|
self[end] = piece;
|
||||||
|
self[(end + start) / (2, 2)] = Piece::NoPiece;
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidMove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn moves_for_piece<'a>(
|
||||||
|
&'a self,
|
||||||
|
position: Position,
|
||||||
|
captures_only: bool,
|
||||||
|
) -> MoveIterator<'a> {
|
||||||
|
let piece = self[position];
|
||||||
|
match piece.player() {
|
||||||
|
Some(_) => MoveIterator::new(self, position, captures_only),
|
||||||
|
None => MoveIterator::empty_iterator(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DraughtsGame {
|
||||||
|
board: DraughtsBoard,
|
||||||
|
next_move: Player,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
WrongPlayer,
|
||||||
|
NoPiece,
|
||||||
|
InvalidMove,
|
||||||
|
InvalidPosition,
|
||||||
|
NoPieceCaptured,
|
||||||
|
FirendlyPieceCaptured,
|
||||||
|
PositionIsOccupied,
|
||||||
|
MovementInsteadOfCapture,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DraughtsGame {
|
||||||
|
pub fn new() -> DraughtsGame {
|
||||||
|
DraughtsGame {
|
||||||
|
board: DraughtsBoard::default(),
|
||||||
|
next_move: Player::Red,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_turn(&mut self) {
|
||||||
|
match self.next_move {
|
||||||
|
Player::White => {
|
||||||
|
self.next_move = Player::Red;
|
||||||
|
}
|
||||||
|
Player::Red => {
|
||||||
|
self.next_move = Player::White;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_last_row(pos: &Position, player: Player) -> bool {
|
||||||
|
let row = pos.to_index().0;
|
||||||
|
match player {
|
||||||
|
Player::White => row == DraughtsBoard::rows(),
|
||||||
|
Player::Red => row == 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_move(&mut self, mv: &Move) -> Result<(), Error> {
|
||||||
|
let start = mv.get_start_position();
|
||||||
|
let piece = self.board.get_piece(&start);
|
||||||
|
if let Some(player) = piece.player() {
|
||||||
|
match mv {
|
||||||
|
Move::Movement { .. } => {
|
||||||
|
if self.next_move == player {
|
||||||
|
self.board.apply_move(&mv)?;
|
||||||
|
self.next_turn();
|
||||||
|
} else {
|
||||||
|
return Err(Error::WrongPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Move::Capture { .. } => {
|
||||||
|
if self.next_move == player {
|
||||||
|
self.board.apply_move(&mv)?;
|
||||||
|
// Check if more captures are available for the current piece
|
||||||
|
for _ in self.board.moves_for_piece(mv.get_end_position(), true) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.next_turn();
|
||||||
|
} else {
|
||||||
|
return Err(Error::WrongPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let end = mv.get_end_position();
|
||||||
|
//Promote pawns that reach the last row
|
||||||
|
if !piece.is_crowned() && DraughtsGame::is_last_row(&end, player) {
|
||||||
|
self.board[end] = match player {
|
||||||
|
Player::White => Piece::CrownedWhitePawn,
|
||||||
|
Player::Red => Piece::CrownedRedPawn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::NoPiece)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum MoveDirection {
|
||||||
|
NE,
|
||||||
|
SE,
|
||||||
|
SW,
|
||||||
|
NW,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MoveDirection {
|
||||||
|
fn is_forward(&self, piece: Piece) -> Result<bool, Error> {
|
||||||
|
match piece.player() {
|
||||||
|
Some(Player::Red) => Ok(matches!(self, Self::SE) || matches!(self, Self::SW)),
|
||||||
|
Some(Player::White) => Ok(matches!(self, Self::NE) || matches!(self, Self::NW)),
|
||||||
|
None => Err(Error::NoPiece),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Move {
|
||||||
|
Movement {
|
||||||
|
start: Position,
|
||||||
|
direction: MoveDirection,
|
||||||
|
},
|
||||||
|
Capture {
|
||||||
|
start: Position,
|
||||||
|
direction: MoveDirection,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Move {
|
||||||
|
fn get_end_position(&self) -> Position {
|
||||||
|
match self {
|
||||||
|
Move::Movement { start, direction } => match direction {
|
||||||
|
MoveDirection::NE => *start + (1, 1),
|
||||||
|
MoveDirection::SE => *start + (-1, 1),
|
||||||
|
MoveDirection::SW => *start + (-1, -1),
|
||||||
|
MoveDirection::NW => *start + (1, -1),
|
||||||
|
},
|
||||||
|
Move::Capture { start, direction } => match direction {
|
||||||
|
MoveDirection::NE => *start + (2, 2),
|
||||||
|
MoveDirection::SE => *start + (-2, 2),
|
||||||
|
MoveDirection::SW => *start + (-2, -2),
|
||||||
|
MoveDirection::NW => *start + (2, -2),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_start_position(&self) -> Position {
|
||||||
|
match self {
|
||||||
|
Move::Movement { start, .. } => *start,
|
||||||
|
Move::Capture { start, .. } => *start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MoveIterator<'a> {
|
||||||
|
board: &'a DraughtsBoard,
|
||||||
|
position: Position,
|
||||||
|
state: u8,
|
||||||
|
capture_only: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MoveIterator<'_> {
|
||||||
|
const MOVES: [MoveDirection; 4] = [
|
||||||
|
MoveDirection::NE,
|
||||||
|
MoveDirection::SE,
|
||||||
|
MoveDirection::SW,
|
||||||
|
MoveDirection::NW,
|
||||||
|
];
|
||||||
|
|
||||||
|
fn piece(&self) -> Piece {
|
||||||
|
self.board[self.position]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new<'a>(
|
||||||
|
board: &'a DraughtsBoard,
|
||||||
|
position: Position,
|
||||||
|
capture_only: bool,
|
||||||
|
) -> MoveIterator<'a> {
|
||||||
|
MoveIterator {
|
||||||
|
board,
|
||||||
|
position,
|
||||||
|
state: 0u8,
|
||||||
|
capture_only,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn empty_iterator<'a>(board: &'a DraughtsBoard) -> MoveIterator<'a> {
|
||||||
|
MoveIterator {
|
||||||
|
board,
|
||||||
|
position: Position::new(0, 0),
|
||||||
|
state: 4,
|
||||||
|
capture_only: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for MoveIterator<'a> {
|
||||||
|
type Item = Move;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let piece = self.piece();
|
||||||
|
while self.state < 4 {
|
||||||
|
let direction = MoveIterator::MOVES[self.state as usize];
|
||||||
|
self.state += 1;
|
||||||
|
if !piece.can_move_backward() && !direction.is_forward(piece).unwrap() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let movement = Move::Movement {
|
||||||
|
start: self.position,
|
||||||
|
direction,
|
||||||
|
};
|
||||||
|
let next_pos = movement.get_end_position();
|
||||||
|
if DraughtsBoard::is_position_valid(next_pos) {
|
||||||
|
let piece_at_next_position = self.board[next_pos];
|
||||||
|
match piece_at_next_position {
|
||||||
|
Piece::NoPiece => {
|
||||||
|
if self.capture_only {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return Some(movement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let capture = Move::Capture {
|
||||||
|
start: self.position,
|
||||||
|
direction,
|
||||||
|
};
|
||||||
|
let next_position = capture.get_end_position();
|
||||||
|
if let Piece::NoPiece = self.board[next_position] {
|
||||||
|
return Some(capture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
7
src/lib.rs
Normal file
7
src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#![no_std]
|
||||||
|
mod board;
|
||||||
|
pub mod draughts;
|
||||||
|
pub mod position;
|
||||||
|
|
||||||
|
//use draughts::DraughtsBoard;
|
||||||
|
//use draughts::Piece;
|
97
src/position.rs
Normal file
97
src/position.rs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
use core::ops::Add;
|
||||||
|
use core::ops::Div;
|
||||||
|
use core::ops::Mul;
|
||||||
|
use core::ops::Sub;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct Position((u8, u8));
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn to_index(&self) -> (usize, usize) {
|
||||||
|
(self.0.0 as usize, self.0.1 as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_index(index: (usize, usize)) -> Position {
|
||||||
|
Position((index.0 as u8, index.1 as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(row: u8, column: u8) -> Position {
|
||||||
|
Position((row, column))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Position> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn add(self, rhs: Position) -> Self::Output {
|
||||||
|
Position((self.0.0 + rhs.0.0, self.0.1 + rhs.0.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Position> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Position) -> Self::Output {
|
||||||
|
Position((self.0.0 - rhs.0.0, self.0.1 - rhs.0.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Position> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn div(self, rhs: Position) -> Self::Output {
|
||||||
|
Position((self.0.0 / rhs.0.0, self.0.1 / rhs.0.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Position> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Position) -> Self::Output {
|
||||||
|
Position((self.0.0 * rhs.0.0, self.0.1 * rhs.0.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<(i32, i32)> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn add(self, rhs: (i32, i32)) -> Self::Output {
|
||||||
|
Position((
|
||||||
|
(self.0.0 as i32 + rhs.0) as u8,
|
||||||
|
(self.0.1 as i32 + rhs.1) as u8,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<(i32, i32)> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn sub(self, rhs: (i32, i32)) -> Self::Output {
|
||||||
|
Position((
|
||||||
|
(self.0.0 as i32 - rhs.0) as u8,
|
||||||
|
(self.0.1 as i32 - rhs.1) as u8,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<(i32, i32)> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn div(self, rhs: (i32, i32)) -> Self::Output {
|
||||||
|
Position((
|
||||||
|
(self.0.0 as i32 / rhs.0) as u8,
|
||||||
|
(self.0.1 as i32 / rhs.1) as u8,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<(i32, i32)> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
|
||||||
|
fn mul(self, rhs: (i32, i32)) -> Self::Output {
|
||||||
|
Position((
|
||||||
|
(self.0.0 as i32 * rhs.0) as u8,
|
||||||
|
(self.0.1 as i32 * rhs.1) as u8,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user