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