Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
26
Cargo.toml
Normal file
26
Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["rdraught", "rdraught-cli", "rdraught-pi", "rdraught-ui"]
|
||||||
|
resolver = "3"
|
||||||
|
|
||||||
|
[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"
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
rdraught = { path = "rdraught", version = "0.1.0" }
|
||||||
|
cairo-rs = "0.20"
|
||||||
|
cairo-sys-rs = "0.20"
|
||||||
|
librsvg = "2.60"
|
||||||
|
gtk4 = "0.9"
|
||||||
|
gdk4 = "0.9"
|
||||||
|
gio = "0.20.12"
|
||||||
|
glib = "0.20.12"
|
||||||
|
|
||||||
|
#[patch.crates-io]
|
||||||
|
#cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core.git", package = "cairo-rs", tag="0.20.12" }
|
||||||
|
#cairo-sys-rs = { git = "https://github.com/gtk-rs/gtk-rs-core.git", package = "cairo-sys-rs", tag="0.20.12" }
|
9
LICENSE
Normal file
9
LICENSE
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 woggioni
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
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);
|
||||||
|
}
|
||||||
|
}
|
20
rdraught-ui/Cargo.toml
Normal file
20
rdraught-ui/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[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.workspace = true
|
||||||
|
gdk4.workspace = true
|
||||||
|
rdraught.workspace = true
|
||||||
|
rmath = { version="0.1", registry="gitea" }
|
||||||
|
librsvg.workspace = true
|
||||||
|
cairo-rs.workspace = true
|
||||||
|
gio.workspace = true
|
||||||
|
glib.workspace = true
|
||||||
|
|
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_red.svg
Normal file
27
rdraught-ui/src/crown_red.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 |
27
rdraught-ui/src/crown_white.svg
Normal file
27
rdraught-ui/src/crown_white.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:#776b00; 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:#776b00; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 20,8 L 25,8"
|
||||||
|
style="fill:none; stroke:#776b00; 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:#776b00; 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:#776b00;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,30 C 17,27 27,27 32.5,30"
|
||||||
|
style="fill:none; stroke:#776b00;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5"
|
||||||
|
style="fill:none; stroke:#776b00;" />
|
||||||
|
<path
|
||||||
|
d="M 11.5,37 C 17,34 27,34 32.5,37"
|
||||||
|
style="fill:none; stroke:#776b00;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
221
rdraught-ui/src/geo2d.rs
Normal file
221
rdraught-ui/src/geo2d.rs
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
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>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, point: &Point) -> bool {
|
||||||
|
self.tl().x() < point.x()
|
||||||
|
&& self.tl().y() < point.y()
|
||||||
|
&& self.br().x() > point.x()
|
||||||
|
&& self.br().y() > point.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
365
rdraught-ui/src/main.rs
Normal file
365
rdraught-ui/src/main.rs
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
use gdk4::cairo::{Context as CairoContext, Matrix};
|
||||||
|
use gtk4::cairo::Error;
|
||||||
|
use gtk4::{self as gtk, gdk::ffi::GDK_BUTTON_PRIMARY};
|
||||||
|
use gtk4::{DrawingArea, prelude::*};
|
||||||
|
use rdraught::draughts::{self, DraughtsBoard, DraughtsGame, Move, Piece, Player};
|
||||||
|
use rdraught::position::Position;
|
||||||
|
use rmath::NumericalMatrix;
|
||||||
|
mod geo2d;
|
||||||
|
use core::f64::consts::PI;
|
||||||
|
use geo2d::{Point, Rect2d, Xform, scale, xlate};
|
||||||
|
use rsvg::SvgHandle;
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
const SQUARE_SIZE: f64 = 1.0;
|
||||||
|
|
||||||
|
const CROWN_RED: &'static [u8] = include_bytes!("crown_red.svg");
|
||||||
|
const CROWN_WHITE: &'static [u8] = include_bytes!("crown_white.svg");
|
||||||
|
|
||||||
|
fn draw_piece(
|
||||||
|
cr: &CairoContext,
|
||||||
|
square: &Rect2d,
|
||||||
|
piece: Piece,
|
||||||
|
crown_red: &SvgHandle,
|
||||||
|
crown_white: &SvgHandle,
|
||||||
|
) -> 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, 1.0, 1.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 renderer = match piece.player() {
|
||||||
|
Some(Player::Red) => rsvg::CairoRenderer::new(&crown_red),
|
||||||
|
Some(Player::White) => rsvg::CairoRenderer::new(&crown_white),
|
||||||
|
None => panic!("This should never happen"),
|
||||||
|
};
|
||||||
|
let m4 = {
|
||||||
|
let mut m1 = Matrix::identity();
|
||||||
|
m1.translate(-center.x(), -(center.y() - thickness / 1.0));
|
||||||
|
let mut m2 = Matrix::identity();
|
||||||
|
m2.scale(0.5, 0.5);
|
||||||
|
let mut m3 = Matrix::identity();
|
||||||
|
m3.translate(center.x(), center.y() - thickness / 1.0);
|
||||||
|
Matrix::multiply(&Matrix::multiply(&m1, &m2), &m3)
|
||||||
|
};
|
||||||
|
cr.set_matrix(Matrix::multiply(&matrix, &m4));
|
||||||
|
renderer
|
||||||
|
.render_document(
|
||||||
|
&cr,
|
||||||
|
&cairo::Rectangle::new(
|
||||||
|
square.tl().x(),
|
||||||
|
square.tl().y(),
|
||||||
|
square.width(),
|
||||||
|
square.height(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
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 = Rc::new(RefCell::new(gtk::DrawingArea::new()));
|
||||||
|
// Add the drawing area to the window.
|
||||||
|
window.set_child(Some(drawing_area.borrow().as_ref() as &DrawingArea));
|
||||||
|
|
||||||
|
let draughts_game = Rc::new(RefCell::new(DraughtsGame::default()));
|
||||||
|
let selected_piece: Rc<Cell<Option<Position>>> = Rc::new(Cell::new(None));
|
||||||
|
let available_moves: Rc<RefCell<Vec<Move>>> = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
// 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));
|
||||||
|
let board_clone = board.clone();
|
||||||
|
let crown_red_handle = {
|
||||||
|
let stream = gio::MemoryInputStream::from_bytes(&glib::Bytes::from_static(CROWN_RED));
|
||||||
|
|
||||||
|
rsvg::Loader::new()
|
||||||
|
.read_stream(
|
||||||
|
&stream,
|
||||||
|
None::<&gio::File>, // no base file as this document has no references
|
||||||
|
None::<&gio::Cancellable>, // no cancellable
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let crown_white_handle = {
|
||||||
|
let stream = gio::MemoryInputStream::from_bytes(&glib::Bytes::from_static(CROWN_WHITE));
|
||||||
|
|
||||||
|
rsvg::Loader::new()
|
||||||
|
.read_stream(
|
||||||
|
&stream,
|
||||||
|
None::<&gio::File>, // no base file as this document has no references
|
||||||
|
None::<&gio::Cancellable>, // no cancellable
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let xform = Rc::<RefCell<Xform>>::new(RefCell::new(Xform::identity(3)));
|
||||||
|
// 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).
|
||||||
|
{
|
||||||
|
let draughts_game = draughts_game.clone();
|
||||||
|
let xform = xform.clone();
|
||||||
|
let selected_piece = selected_piece.clone();
|
||||||
|
let board_clone = board.clone();
|
||||||
|
let available_moves = available_moves.clone();
|
||||||
|
let get_square_for_position = move |position: &Position, xform: &Xform| -> Rect2d {
|
||||||
|
let square_size = SQUARE_SIZE as f64;
|
||||||
|
|
||||||
|
let square = Rect2d::new(
|
||||||
|
&board_clone.tl()
|
||||||
|
+ &Point::new(
|
||||||
|
(position.col() as f64) * square_size,
|
||||||
|
((8 - 1 - position.row()) as f64) * square_size,
|
||||||
|
),
|
||||||
|
&board_clone.tl()
|
||||||
|
+ &Point::new(
|
||||||
|
((position.col() + 1) as f64) * square_size,
|
||||||
|
((8 - 1 - position.row() + 1) as f64) * square_size,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
&square * &xform
|
||||||
|
};
|
||||||
|
drawing_area
|
||||||
|
.borrow_mut()
|
||||||
|
.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 mut xform = xform.borrow_mut();
|
||||||
|
*xform = xlate(-board_center.x(), -board_center.y())
|
||||||
|
* scale(f, f)
|
||||||
|
* xlate(screen_center.x(), screen_center.y());
|
||||||
|
|
||||||
|
// Loop over rows and columns to draw each chessboard cell.
|
||||||
|
for row in 0..DraughtsBoard::rows() {
|
||||||
|
for col in 0..DraughtsBoard::columns() {
|
||||||
|
let position = Position::new((8 - row - 1) as u8, col as u8);
|
||||||
|
let square = get_square_for_position(&position, &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);
|
||||||
|
} else {
|
||||||
|
cr.set_source_rgb(0.4, 0.4, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_game.borrow().piece_at(position),
|
||||||
|
&crown_red_handle,
|
||||||
|
&crown_white_handle,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(selected_postion) = selected_piece.get() {
|
||||||
|
let piece = draughts_game.borrow().piece_at(selected_postion);
|
||||||
|
let square = get_square_for_position(&selected_postion, &xform);
|
||||||
|
cr.save().unwrap();
|
||||||
|
cr.new_path();
|
||||||
|
cr.move_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.clip();
|
||||||
|
cr.new_path();
|
||||||
|
cr.set_source_rgb(0.0, 0.0, 1.0);
|
||||||
|
cr.set_line_width((square.width() + square.height()) * 0.05);
|
||||||
|
cr.move_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.stroke().unwrap();
|
||||||
|
cr.restore().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let am = available_moves.borrow();
|
||||||
|
if !am.is_empty() {
|
||||||
|
for mv in am.iter() {
|
||||||
|
let square = get_square_for_position(&mv.get_end_position(), &xform);
|
||||||
|
cr.save().unwrap();
|
||||||
|
cr.new_path();
|
||||||
|
cr.move_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.clip();
|
||||||
|
cr.new_path();
|
||||||
|
cr.set_source_rgb(0.0, 1.0, 0.0);
|
||||||
|
cr.set_line_width((square.width() + square.height()) * 0.05);
|
||||||
|
cr.move_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.br().y());
|
||||||
|
cr.line_to(square.br().x(), square.tl().y());
|
||||||
|
cr.line_to(square.tl().x(), square.tl().y());
|
||||||
|
cr.stroke().unwrap();
|
||||||
|
cr.restore().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let gesture = gtk::GestureClick::new();
|
||||||
|
|
||||||
|
// Set the gestures button to the right mouse button (=3)
|
||||||
|
gesture.set_button(GDK_BUTTON_PRIMARY as u32);
|
||||||
|
|
||||||
|
// Assign your handler to an event of the gesture (e.g. the `pressed` event)
|
||||||
|
{
|
||||||
|
let selected_piece = selected_piece;
|
||||||
|
let draughts_game = draughts_game;
|
||||||
|
let xform = xform;
|
||||||
|
let drawing_area = drawing_area.clone();
|
||||||
|
let available_moves = available_moves.clone();
|
||||||
|
gesture.connect_pressed(move |gesture, _, x, y| {
|
||||||
|
gesture.set_state(gtk::EventSequenceState::Claimed);
|
||||||
|
let xform = xform.borrow();
|
||||||
|
let inverse = xform.clone().invert();
|
||||||
|
let p = &Point::new(x, y) * &inverse;
|
||||||
|
if board_clone.contains(&p) {
|
||||||
|
let p = &p - &board_clone.tl();
|
||||||
|
// println!("Point: {:?}", p);
|
||||||
|
let position = Position::new(
|
||||||
|
8 - 1 - (p.y() / SQUARE_SIZE) as u8,
|
||||||
|
(p.x() / SQUARE_SIZE) as u8,
|
||||||
|
);
|
||||||
|
// println!("Selected position: {:?}", position);
|
||||||
|
let mut draughts_game = draughts_game.borrow_mut();
|
||||||
|
let piece = draughts_game.piece_at(position);
|
||||||
|
let mut am = available_moves.borrow_mut();
|
||||||
|
let mut move_applied = false;
|
||||||
|
if !am.is_empty() {
|
||||||
|
for mv in am.iter() {
|
||||||
|
if mv.get_end_position() == position {
|
||||||
|
draughts_game.apply_move(mv).unwrap();
|
||||||
|
move_applied = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if move_applied {
|
||||||
|
selected_piece.set(None);
|
||||||
|
am.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !move_applied {
|
||||||
|
match piece.player() {
|
||||||
|
Some(Player::Red) => selected_piece.set(Some(position)),
|
||||||
|
Some(Player::White) => selected_piece.set(Some(position)),
|
||||||
|
None => selected_piece.set(None),
|
||||||
|
}
|
||||||
|
am.clear();
|
||||||
|
if let None = piece.player() {
|
||||||
|
selected_piece.set(None)
|
||||||
|
} else {
|
||||||
|
selected_piece.set(Some(position));
|
||||||
|
for mv in draughts_game.moves_for_piece(position) {
|
||||||
|
am.push(mv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selected_piece.set(None);
|
||||||
|
}
|
||||||
|
drawing_area.borrow_mut().queue_draw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Assign the gesture to the treeview
|
||||||
|
drawing_area.borrow_mut().add_controller(gesture);
|
||||||
|
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();
|
||||||
|
}
|
19
rdraught/Cargo.toml
Normal file
19
rdraught/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "rdraught"
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "rdraught"
|
||||||
|
crate-type = ["lib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
heapless = "0.8"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = "0.9"
|
27
rdraught/examples/checkers.rs
Normal file
27
rdraught/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(|pos| println!("({}, {}): {:?}", pos.row(), pos.col(), board[pos]));
|
||||||
|
// println!("{:?}", board[Position::new(0, 0)]);
|
||||||
|
|
||||||
|
// board
|
||||||
|
// .moves_for_piece(Position::new(2, 0), false)
|
||||||
|
// .for_each(|mv| {
|
||||||
|
// println!("{:?}", mv);
|
||||||
|
// });
|
||||||
|
}
|
131
rdraught/src/board.rs
Normal file
131
rdraught/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]
|
||||||
|
}
|
||||||
|
}
|
514
rdraught/src/draughts.rs
Normal file
514
rdraught/src/draughts.rs
Normal file
@@ -0,0 +1,514 @@
|
|||||||
|
use super::board::Board;
|
||||||
|
use super::board::BoardIteratorRef;
|
||||||
|
use super::board::RectangularBoard;
|
||||||
|
use core::iter::{Filter, Map};
|
||||||
|
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 {
|
||||||
|
pub 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>,
|
||||||
|
) -> Map<
|
||||||
|
Filter<BoardIteratorRef<'a, Piece, 8, 8>, impl FnMut(&(usize, usize, Piece)) -> bool>,
|
||||||
|
impl FnMut((usize, usize, Piece)) -> Position,
|
||||||
|
> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.filter(move |(_, _, piece)| {
|
||||||
|
for p in pieces {
|
||||||
|
if piece == p {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.map(|(row, col, _)| Position::new(row as u8, col as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Default for DraughtsGame {
|
||||||
|
fn default() -> Self {
|
||||||
|
DraughtsGame {
|
||||||
|
board: DraughtsBoard::default(),
|
||||||
|
next_move: Player::Red,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DraughtsGame {
|
||||||
|
pub fn new() -> DraughtsGame {
|
||||||
|
DraughtsGame::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn piece_at(&self, p: Position) -> Piece {
|
||||||
|
self.board[p]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn moves_for_piece(&self, position: Position) -> MoveIterator {
|
||||||
|
let player_opt = self.piece_at(position).player();
|
||||||
|
match player_opt {
|
||||||
|
Some(player) => {
|
||||||
|
if player == self.next_move {
|
||||||
|
let mut needle = Vec::<Piece, 2>::new();
|
||||||
|
match player {
|
||||||
|
Player::Red => {
|
||||||
|
needle.push(Piece::SimpleRedPawn).unwrap();
|
||||||
|
needle.push(Piece::CrownedRedPawn).unwrap();
|
||||||
|
}
|
||||||
|
Player::White => {
|
||||||
|
needle.push(Piece::SimpleWhitePawn).unwrap();
|
||||||
|
needle.push(Piece::CrownedWhitePawn).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut capture_available = false;
|
||||||
|
for pos in self.board.pieces(&needle) {
|
||||||
|
for _ in self.board.moves_for_piece(pos, true) {
|
||||||
|
capture_available = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.board.moves_for_piece(position, capture_available)
|
||||||
|
} else {
|
||||||
|
MoveIterator::empty_iterator(&self.board)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => MoveIterator::empty_iterator(&self.board),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.row() as usize;
|
||||||
|
match player {
|
||||||
|
Player::White => row == (DraughtsBoard::rows() - 1),
|
||||||
|
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 {
|
||||||
|
pub 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),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if piece.player() != piece_at_next_position.player() {
|
||||||
|
let capture = Move::Capture {
|
||||||
|
start: self.position,
|
||||||
|
direction,
|
||||||
|
};
|
||||||
|
let next_position = capture.get_end_position();
|
||||||
|
if DraughtsBoard::is_position_valid(next_position) {
|
||||||
|
let piece_at_end_position = self.board[next_position];
|
||||||
|
if piece_at_end_position == Piece::NoPiece {
|
||||||
|
return Some(capture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
7
rdraught/src/lib.rs
Normal file
7
rdraught/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;
|
110
rdraught/src/position.rs
Normal file
110
rdraught/src/position.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use core::fmt::Display;
|
||||||
|
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 Display for Position {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "({}, {})", self.row(), self.col())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn to_index(&self) -> (usize, usize) {
|
||||||
|
(self.0.1 as usize, self.0.0 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row(&self) -> u8 {
|
||||||
|
self.0.0
|
||||||
|
}
|
||||||
|
pub fn col(&self) -> u8 {
|
||||||
|
self.0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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