commit 7b597748bb5dd8e004490f940fa3b191cda80f54 Author: core Date: Tue Jun 27 22:42:48 2023 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1de5659 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e89304d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,11 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "sycamore" +version = "0.1.0" + +[[package]] +name = "sycamore-harness" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0dc2520 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "sycamore", + "sycamore-harness" +] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..085a721 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Sycamore: Speedrunning Minesweeper + +Sycamore is a Rust minesweeper bot, intenting to solve any board permutation as quickly as possible. \ No newline at end of file diff --git a/sycamore-harness/Cargo.lock b/sycamore-harness/Cargo.lock new file mode 100644 index 0000000..535c607 --- /dev/null +++ b/sycamore-harness/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "sycamore-harness" +version = "0.1.0" diff --git a/sycamore-harness/Cargo.toml b/sycamore-harness/Cargo.toml new file mode 100644 index 0000000..1b50451 --- /dev/null +++ b/sycamore-harness/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sycamore-harness" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/sycamore-harness/src/main.rs b/sycamore-harness/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/sycamore-harness/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/sycamore/Cargo.lock b/sycamore/Cargo.lock new file mode 100644 index 0000000..24ac018 --- /dev/null +++ b/sycamore/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "sycamore" +version = "0.1.0" diff --git a/sycamore/Cargo.toml b/sycamore/Cargo.toml new file mode 100644 index 0000000..2012ebd --- /dev/null +++ b/sycamore/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sycamore" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/sycamore/src/board.rs b/sycamore/src/board.rs new file mode 100644 index 0000000..aa7c730 --- /dev/null +++ b/sycamore/src/board.rs @@ -0,0 +1,76 @@ +/* +Board representation + +Sycamore uses a bytefield representation of the board, enabling arbitrary sized boards to be indexed extremely quickly. + +There are 10 states a square can be in: +- 0000 empty +- 0001 revealed, 1 +- 0010 revealed, 2 +- 0011 revealed, 3 +- 0100 revealed, 4 +- 0101 revealed, 5 +- 0110 revealed, 6 +- 0111 revealed, 7 +- 1000 revealed, 8 +- 1001 flagged +- 1010 hidden +These numbers have been picked to allow easily performing calculations when solving - 0-8 adjacent mines per square can be represented by immediately doing arithmetic with this value. + +To represent this, at minimum 4 bits are needed, however for fast and easy access, an entire byte is used. + +This allows us to represent the entire board with a simple WIDTH*HEIGHT-length array, and easily access +the value of a specific coordinate with bytefield[WIDTH * row + col]. + + */ + +pub const SQUARESTATE_EMPTY: u8 = 0; +pub const SQUARESTATE_FLAGGED: u8 = 9; +pub const SQUARESTATE_HIDDEN: u8 = 10; +pub const SQUARESTATE_MAX_VAL: u8 = SQUARESTATE_HIDDEN; + +pub struct Board { + field: Vec, + width: usize, + height: usize +} + +impl Board { + pub fn new(width: usize, height: usize, pre_allocate: bool) -> Self { + let mut vec = if pre_allocate { + Vec::with_capacity(width * height) + } else { + Vec::new() + }; + for i in 0..width * height { + vec.insert(i, SQUARESTATE_HIDDEN); + } + Self { + field: vec, + width, + height + } + } + + pub fn get(&self, row: usize, col: usize) -> &u8 { + if col > self.width { panic!("board access out of bounds (width is {} but col is {})", self.width, col); } + if row > self.height { panic!("board access out of bounds (height is {} but row is {})", self.height, row); } + + &self.field[row * self.width + col] + } + + pub fn get_mut(&mut self, row: usize, col: usize) -> &mut u8 { + if col > self.width { panic!("board access out of bounds (width is {} but col is {})", self.width, col); } + if row > self.height { panic!("board access out of bounds (height is {} but row is {})", self.height, row); } + + &mut self.field[row * self.width + col] + } + + pub fn set(&mut self, row: usize, col: usize, to: u8) { + if col > self.width { panic!("board access out of bounds (width is {} but col is {})", self.width, col); } + if row > self.height { panic!("board access out of bounds (height is {} but row is {})", self.height, row); } + if to > SQUARESTATE_MAX_VAL { panic!("board set to invalid state (max allowed state is {} but 'to' is {})", SQUARESTATE_MAX_VAL, to); } + + self.field[row * self.width + col] = to; + } +} \ No newline at end of file diff --git a/sycamore/src/lib.rs b/sycamore/src/lib.rs new file mode 100644 index 0000000..90d1407 --- /dev/null +++ b/sycamore/src/lib.rs @@ -0,0 +1 @@ +pub mod board; \ No newline at end of file