Build make
This commit is contained in:
commit
353e59d66a
18
.cargo/config.toml
Normal file
18
.cargo/config.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[target.riscv32imac-unknown-none-elf]
|
||||||
|
runner = "espflash flash --monitor"
|
||||||
|
|
||||||
|
|
||||||
|
[env]
|
||||||
|
ESP_LOGLEVEL="INFO"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
rustflags = [
|
||||||
|
# Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.)
|
||||||
|
# NOTE: May negatively impact performance of produced code
|
||||||
|
"-C", "force-frame-pointers",
|
||||||
|
]
|
||||||
|
|
||||||
|
target = "riscv32imac-unknown-none-elf"
|
||||||
|
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core"]
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
1190
Cargo.lock
generated
Normal file
1190
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
Cargo.toml
Normal file
35
Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
[package]
|
||||||
|
name = "readdy"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
display-interface-spi = "0.5.0"
|
||||||
|
embedded-graphics = "0.8.1"
|
||||||
|
embedded-hal-bus = "0.2.0"
|
||||||
|
esp-backtrace = { version = "0.13.0", features = ["esp32c6", "exception-handler", "panic-handler", "println"] }
|
||||||
|
esp-hal = { version = "0.19.0", features = ["esp32c6", "embedded-hal"] }
|
||||||
|
esp-println = { version = "0.10.0", default-features = false, features = ["esp32c6", "log", "jtag-serial"] }
|
||||||
|
heapless = "0.8.0"
|
||||||
|
log = { version = "0.4.21" }
|
||||||
|
nutype = { version = "0.5.0", default-features = false }
|
||||||
|
profont = "0.7.0"
|
||||||
|
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
|
||||||
|
weact-studio-epd = { version = "0.1.2", features = ["blocking"] }
|
||||||
|
|
||||||
|
shared = { path = "./shared", features = ['trng'] }
|
||||||
|
maze = { path = "./maze" }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
# Rust debug is too slow.
|
||||||
|
# For debug builds always builds with some optimization
|
||||||
|
opt-level = "s"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1 # LLVM can perform better optimizations using a single thread
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 's'
|
||||||
|
overflow-checks = false
|
3
build.rs
Normal file
3
build.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlinkall.x");
|
||||||
|
}
|
1
maze/.gitignore
vendored
Normal file
1
maze/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
target
|
271
maze/Cargo.lock
generated
Normal file
271
maze/Cargo.lock
generated
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diff"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kinded"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce4bdbb2f423660b19f0e9f7115182214732d8dd5f840cd0a3aee3e22562f34c"
|
||||||
|
dependencies = [
|
||||||
|
"kinded_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kinded_macros"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a13b4ddc5dcb32f45dac3d6f606da2a52fdb9964a18427e63cd5ef6c0d13288d"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.158"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maze"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"heapless",
|
||||||
|
"nutype",
|
||||||
|
"pretty_assertions",
|
||||||
|
"rand",
|
||||||
|
"shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nutype"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8789358e2d6cdffb0cb170c7802ee7548beb8067ed643f3122fa36c335f3c64"
|
||||||
|
dependencies = [
|
||||||
|
"nutype_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nutype_macros"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93a3e222ba1f06a03552910fe89a232a1661dcf8ad4c837531fb199828d0916b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"kinded",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"urlencoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty_assertions"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||||
|
dependencies = [
|
||||||
|
"diff",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nutype",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
17
maze/Cargo.toml
Normal file
17
maze/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "maze"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nutype = { version = "0.5.0", default-features = false }
|
||||||
|
heapless = "0.8.0"
|
||||||
|
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
bitflags = "2.6.0"
|
||||||
|
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = { version = "0.8.5", features = ["getrandom", "small_rng", "std", "std_rng"] }
|
||||||
|
shared = { path = "../shared", features = ['full-rand'] }
|
||||||
|
pretty_assertions = "1"
|
4
maze/rust-toolchain.toml
Normal file
4
maze/rust-toolchain.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rust-src"]
|
||||||
|
# targets = ["riscv32imac-unknown-none-elf"]
|
582
maze/src/lib.rs
Normal file
582
maze/src/lib.rs
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use nutype::nutype;
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use shared::GenU32;
|
||||||
|
|
||||||
|
pub type Coords = (usize, usize);
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct Cell: u8 {
|
||||||
|
const NORTH = 0b0001;
|
||||||
|
const SOUTH = 0b0010;
|
||||||
|
const EAST = 0b0100;
|
||||||
|
const WEST = 0b1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cell {
|
||||||
|
pub fn south_blocked(&self) -> bool {
|
||||||
|
self.contains(Cell::SOUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn east_blocked(&self) -> bool {
|
||||||
|
self.contains(Cell::EAST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Copy, Clone, Debug)]
|
||||||
|
pub struct CellStatus {
|
||||||
|
visited: bool,
|
||||||
|
marked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellStatus {
|
||||||
|
pub fn visited(&self) -> bool {
|
||||||
|
self.visited
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn marked(&self) -> bool {
|
||||||
|
self.marked
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn visit(&mut self) {
|
||||||
|
self.visited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark(&mut self) {
|
||||||
|
self.marked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[nutype(derive(Deref, Debug, Clone, Copy, From, PartialEq, Eq, Hash, PartialOrd, Ord))]
|
||||||
|
pub struct XPos(usize);
|
||||||
|
|
||||||
|
#[nutype(derive(Deref, Debug, Clone, Copy, From, PartialEq, Eq, Hash, PartialOrd, Ord))]
|
||||||
|
pub struct YPos(usize);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Grid<const X: usize, const Y: usize, const SIZE: usize> {
|
||||||
|
pub cells: [Cell; SIZE],
|
||||||
|
pub cell_statuses: [CellStatus; SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const X: usize, const Y: usize, const SIZE: usize> Grid<X, Y, SIZE> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
cells: core::array::from_fn(|_x| Cell::default()),
|
||||||
|
cell_statuses: core::array::from_fn(|_x| CellStatus::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x(&self) -> usize {
|
||||||
|
X
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y(&self) -> usize {
|
||||||
|
Y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_cell(&mut self, coords: Coords) {
|
||||||
|
self.get_cell_status_mut(coords).mark()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_cell_visited(&self, coords: Coords) -> bool {
|
||||||
|
self.get_cell_status(coords).visited()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_cell_marked(&self, coords: Coords) -> bool {
|
||||||
|
self.get_cell_status(coords).marked()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cell_status(&self, coords: Coords) -> CellStatus {
|
||||||
|
let (x, y) = coords;
|
||||||
|
self.cell_statuses[y * X + x].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_carved(&self, coords: Coords, direction: Cell) -> bool {
|
||||||
|
let (x, y) = coords;
|
||||||
|
self.cells[y * X + x].contains(direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn carve_passage(&mut self, coords: Coords, direction: Cell) -> TransitResult<Coords> {
|
||||||
|
let (x, y) = coords;
|
||||||
|
let (nx, ny) = self.get_next_cell_coords(coords, direction)?;
|
||||||
|
|
||||||
|
match direction {
|
||||||
|
Cell::NORTH => {
|
||||||
|
self.cells[y * X + x] |= Cell::NORTH;
|
||||||
|
self.cells[ny * X + nx] |= Cell::SOUTH;
|
||||||
|
}
|
||||||
|
Cell::SOUTH => {
|
||||||
|
self.cells[y * X + x] |= Cell::SOUTH;
|
||||||
|
self.cells[ny * X + nx] |= Cell::NORTH;
|
||||||
|
}
|
||||||
|
Cell::EAST => {
|
||||||
|
self.cells[y * X + x] |= Cell::EAST;
|
||||||
|
self.cells[ny * X + nx] |= Cell::WEST;
|
||||||
|
}
|
||||||
|
Cell::WEST => {
|
||||||
|
self.cells[y * X + x] |= Cell::WEST;
|
||||||
|
self.cells[ny * X + nx] |= Cell::EAST;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.visit_cell(coords);
|
||||||
|
self.visit_cell((nx, ny));
|
||||||
|
|
||||||
|
Ok((nx, ny))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_next_cell_coords(&self, coords: Coords, direction: Cell) -> TransitResult<Coords> {
|
||||||
|
self.validate_transit(coords, direction)?;
|
||||||
|
|
||||||
|
let (x, y) = coords;
|
||||||
|
let (nx, ny) = match direction {
|
||||||
|
Cell::NORTH => (x, y - 1),
|
||||||
|
Cell::SOUTH => (x, y + 1),
|
||||||
|
Cell::WEST => (x - 1, y),
|
||||||
|
Cell::EAST => (x + 1, y),
|
||||||
|
_ => (x, y),
|
||||||
|
};
|
||||||
|
Ok((nx, ny))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_cell(&mut self, coords: Coords) {
|
||||||
|
self.get_cell_status_mut(coords).visit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cell_status_mut(&mut self, coords: Coords) -> &mut CellStatus {
|
||||||
|
let (x, y) = coords;
|
||||||
|
&mut self.cell_statuses[y * X + x]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_transit(&self, coords: Coords, direction: Cell) -> TransitResult<()> {
|
||||||
|
let (x, y) = coords;
|
||||||
|
let reason = match direction {
|
||||||
|
Cell::NORTH if y < 1 => "First row in the grid cannot go North",
|
||||||
|
Cell::SOUTH if y + 1 == Y => "Last row in the grid cannot go South",
|
||||||
|
Cell::WEST if x < 1 => "First cell in a row cannot go West",
|
||||||
|
Cell::EAST if x + 1 == X => "Last column in the grid cannot go East",
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(TransitError {
|
||||||
|
coords: (x, y),
|
||||||
|
reason,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransitResult<T> = Result<T, TransitError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TransitError {
|
||||||
|
pub coords: Coords,
|
||||||
|
pub reason: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Orientation {
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The "Recursive Division" algorithm for generating mazes
|
||||||
|
///
|
||||||
|
/// According to [Wikipedia](https://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_division_method)
|
||||||
|
/// and [James Buck's maze generation blogs](https://weblog.jamisbuck.org/2011/1/12/maze-generation-recursive-division-algorithm)
|
||||||
|
/// this algorithm should be implemented as a wall adder, as opposed to the rest of the algorithms
|
||||||
|
/// in this library which are designated as "passage carvers". In a nutshell, the idea is to
|
||||||
|
/// recursively divide the original grid with no walls — call this a "chamber" — into smaller
|
||||||
|
/// subchambers with randomly positioned walls and single passages within them. This fractal nature
|
||||||
|
/// makes this algorithm novel. You could theoretically continue the process indefinitely at
|
||||||
|
/// progressively finer and finer levels of detail on demand. This may be useful when developing a
|
||||||
|
/// game where a player wanders from one section of the maze to another without a need to store the
|
||||||
|
/// entire maze in the memory.
|
||||||
|
///
|
||||||
|
/// My implementation of this algorithm is a bit different from the above. The design of this
|
||||||
|
/// library supports only the passage-carving technique (mixing both techniques would lead to an
|
||||||
|
/// uglier API and less efficient generation mechanism, which I tested using benchmarks). Given that
|
||||||
|
/// I've slightly modified the algorithm to be rather a "passage carver". The method and its fractal
|
||||||
|
/// nature remain though. Of course, the generation process would be less fancy this way, but this
|
||||||
|
/// library has nothing to do with visualizing the generation progress step-by-step. If you're
|
||||||
|
/// interested in this side of the maze generation world, feel free to check my other program for
|
||||||
|
/// the terminal called [Daedalus](https://github.com/unrenamed/daedalus).
|
||||||
|
///
|
||||||
|
/// It's also worth mentioning that the algorithm's fractal nature leads to some visual artifacts
|
||||||
|
/// and bottlenecks like a single passage between two sections that effectively divide the entire
|
||||||
|
/// maze into two distinct regions, thus making it easy to spot the passage and work backward to a
|
||||||
|
/// solution.
|
||||||
|
pub struct RecursiveDivision<const X: usize, const Y: usize, const SIZE: usize>;
|
||||||
|
|
||||||
|
impl<const X: usize, const Y: usize, const SIZE: usize> RecursiveDivision<X, Y, SIZE> {
|
||||||
|
fn divide(
|
||||||
|
grid: &mut Grid<X, Y, SIZE>,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
ax: usize,
|
||||||
|
ay: usize,
|
||||||
|
trng: &mut impl GenU32,
|
||||||
|
) {
|
||||||
|
// Calculate subfield width
|
||||||
|
let w = ax - x + 1;
|
||||||
|
// Calculate subfield height
|
||||||
|
let h = ay - y + 1;
|
||||||
|
|
||||||
|
if w < 2 || h < 2 {
|
||||||
|
if w > 1 {
|
||||||
|
// Carve passages till the horizontal end of the subfield
|
||||||
|
for cx in x..ax {
|
||||||
|
grid.carve_passage((cx, y), Cell::EAST).unwrap();
|
||||||
|
}
|
||||||
|
} else if h > 1 {
|
||||||
|
// Carve passages till the vertical end of the subfield
|
||||||
|
for cy in y..ay {
|
||||||
|
grid.carve_passage((x, cy), Cell::SOUTH).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rng = rand::rngs::SmallRng::seed_from_u64(trng.random() as u64);
|
||||||
|
|
||||||
|
// Which way a subfield with the given dimensions ought to be bisected
|
||||||
|
let orientation = choose_orientation(w, h, trng);
|
||||||
|
|
||||||
|
// Get X and Y coordinates of a cell where a passage will be carved
|
||||||
|
let px = rng.gen_range(x..ax);
|
||||||
|
let py = rng.gen_range(y..ay);
|
||||||
|
|
||||||
|
// Define what direction is corresponding to the wall orientation
|
||||||
|
let dir = match orientation {
|
||||||
|
Orientation::Horizontal => Cell::SOUTH,
|
||||||
|
Orientation::Vertical => Cell::EAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Carve passage
|
||||||
|
let (nx, ny) = grid.carve_passage((px, py), dir).unwrap();
|
||||||
|
|
||||||
|
// Determine the bounds of the subfields and get them split
|
||||||
|
match orientation {
|
||||||
|
Orientation::Horizontal => {
|
||||||
|
// Top subfield
|
||||||
|
Self::divide(grid, x, y, ax, py, trng);
|
||||||
|
// Bottom subfield
|
||||||
|
Self::divide(grid, x, ny, ax, ay, trng);
|
||||||
|
}
|
||||||
|
Orientation::Vertical => {
|
||||||
|
// Left subfield
|
||||||
|
Self::divide(grid, x, y, px, ay, trng);
|
||||||
|
// Right subfield
|
||||||
|
Self::divide(grid, nx, y, ax, ay, trng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation of the "Recursive Division" algorithm for generating mazes
|
||||||
|
///
|
||||||
|
/// It works like this:
|
||||||
|
///
|
||||||
|
/// 1. Begins with an original grid as a working field
|
||||||
|
///
|
||||||
|
/// 2. Bisects the field either horizontally or vertically by carving a passage
|
||||||
|
/// through the wall from a random cell
|
||||||
|
///
|
||||||
|
/// 3. Repeats step #2 with the areas on either side of the wall where the passage
|
||||||
|
/// was carved.
|
||||||
|
///
|
||||||
|
/// 4. Continues, recursively, until the maze reaches the desired resolution
|
||||||
|
pub fn generate(&mut self, grid: &mut Grid<X, Y, SIZE>, trng: &mut impl GenU32) {
|
||||||
|
RecursiveDivision::divide(grid, 0, 0, X - 1, Y - 1, trng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_orientation(width: usize, height: usize, trng: &mut impl GenU32) -> Orientation {
|
||||||
|
if width < height {
|
||||||
|
return Orientation::Horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if height < width {
|
||||||
|
return Orientation::Vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
if trng.random() % 1 == 0 {
|
||||||
|
Orientation::Horizontal
|
||||||
|
} else {
|
||||||
|
Orientation::Vertical
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use shared::GenU32;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_maze() {
|
||||||
|
let maze = Grid::<4, 4, 16>::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate_4x4() {
|
||||||
|
let mut trng = rand::thread_rng();
|
||||||
|
let mut grid = Grid::<4, 4, 16>::new();
|
||||||
|
RecursiveDivision.generate(&mut grid, &mut trng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A formatter to emit the maze as ASCII with narrow passages
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```no_test
|
||||||
|
/// _______
|
||||||
|
/// | ___| |
|
||||||
|
/// |_ | _|
|
||||||
|
/// | | |_ |
|
||||||
|
/// |_______|
|
||||||
|
/// ```
|
||||||
|
pub struct AsciiNarrow<const X: usize, const Y: usize, const SIZE: usize>;
|
||||||
|
|
||||||
|
/// An implementation of an broad ASCII formatter
|
||||||
|
impl<const X: usize, const Y: usize, const SIZE: usize> AsciiNarrow<X, Y, SIZE> {
|
||||||
|
/// Generate ASCII image which:
|
||||||
|
///
|
||||||
|
/// * takes 2 characters per 1 cell
|
||||||
|
/// * each row ends with newline character
|
||||||
|
/// * has 1 additional row for top border
|
||||||
|
/// * each row starts with border
|
||||||
|
fn format(&self, grid: &Grid<X, Y, SIZE>, result: &mut impl Write) {
|
||||||
|
write!(result, " ").unwrap();
|
||||||
|
(0..(X * 2 - 1)).into_iter().for_each(|_| {
|
||||||
|
write!(result, "_").unwrap();
|
||||||
|
});
|
||||||
|
writeln!(result, " ").unwrap();
|
||||||
|
|
||||||
|
(0..Y).into_iter().for_each(|y| {
|
||||||
|
write!(result, "|").unwrap();
|
||||||
|
|
||||||
|
(0..X).into_iter().for_each(|x| {
|
||||||
|
if grid.is_carved((x, y), Cell::SOUTH) {
|
||||||
|
write!(result, " ").unwrap();
|
||||||
|
} else {
|
||||||
|
write!(result, "_").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if grid.is_carved((x, y), Cell::EAST) {
|
||||||
|
if grid.is_carved((x, y), Cell::SOUTH)
|
||||||
|
|| grid.is_carved((x + 1, y), Cell::SOUTH)
|
||||||
|
{
|
||||||
|
write!(result, " ").unwrap();
|
||||||
|
} else {
|
||||||
|
write!(result, "_").unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write!(result, "|").unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
writeln!(result).unwrap();
|
||||||
|
});
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A formatter to emit the maze as ASCII with broad passages and "+" to contact walls
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```no_test
|
||||||
|
/// +---+---+---+---+
|
||||||
|
/// | |
|
||||||
|
/// +---+---+ + +
|
||||||
|
/// | | |
|
||||||
|
/// + +---+ + +
|
||||||
|
/// | | | |
|
||||||
|
/// + +---+---+ +
|
||||||
|
/// | | |
|
||||||
|
/// +---+---+---+---+
|
||||||
|
/// ```
|
||||||
|
pub struct AsciiBroad<const X: usize, const Y: usize, const SIZE: usize>;
|
||||||
|
|
||||||
|
/// An implementation of a narrow ASCII formatter
|
||||||
|
impl<const X: usize, const Y: usize, const SIZE: usize> AsciiBroad<X, Y, SIZE> {
|
||||||
|
// const CELL_SIZE: usize = 3;
|
||||||
|
// const LINE_SIZE: usize = X * Self::CELL_SIZE + 1;
|
||||||
|
// const BUFFER_SIZE: usize = Y + Self::CELL_SIZE * Self::LINE_SIZE;
|
||||||
|
|
||||||
|
fn format(&self, grid: &Grid<X, Y, SIZE>, output: &mut impl Write) {
|
||||||
|
write!(output, "#").unwrap();
|
||||||
|
(0..X).into_iter().for_each(|_| {
|
||||||
|
write!(output, "##").unwrap();
|
||||||
|
});
|
||||||
|
writeln!(output, "").unwrap();
|
||||||
|
|
||||||
|
let mut top_line = heapless::String::<SIZE>::new();
|
||||||
|
let mut bottom_line = heapless::String::<SIZE>::new();
|
||||||
|
|
||||||
|
for y in 0..Y {
|
||||||
|
top_line.clear();
|
||||||
|
write!(top_line, "#").unwrap();
|
||||||
|
bottom_line.clear();
|
||||||
|
write!(bottom_line, "#").unwrap();
|
||||||
|
|
||||||
|
for x in 0..X {
|
||||||
|
write!(top_line, " ").unwrap();
|
||||||
|
let east_boundary = if grid.is_carved((x, y), Cell::EAST) {
|
||||||
|
" "
|
||||||
|
} else {
|
||||||
|
"#"
|
||||||
|
};
|
||||||
|
write!(top_line, "{east_boundary}").unwrap();
|
||||||
|
|
||||||
|
let south_boundary = if grid.is_carved((x, y), Cell::SOUTH) {
|
||||||
|
" "
|
||||||
|
} else {
|
||||||
|
"#"
|
||||||
|
};
|
||||||
|
write!(bottom_line, "{south_boundary}#").unwrap();
|
||||||
|
|
||||||
|
if x == X - 1 {
|
||||||
|
writeln!(output, "{top_line}").unwrap();
|
||||||
|
writeln!(output, "{bottom_line}").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BufWriter<'s>(&'s mut [MazePart]);
|
||||||
|
|
||||||
|
impl<'s> BufWriter<'s> {
|
||||||
|
pub fn push(&mut self, part: MazePart) {
|
||||||
|
self.0[0] = part;
|
||||||
|
self.0 = &mut self.0[1..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BinaryMap<const X: usize, const Y: usize, const SIZE: usize>;
|
||||||
|
|
||||||
|
impl<const X: usize, const Y: usize, const SIZE: usize> BinaryMap<X, Y, SIZE> {
|
||||||
|
fn format(&self, grid: &Grid<X, Y, SIZE>, buffer: &mut [MazePart]) {
|
||||||
|
let push = |p: MazePart, buffer: &mut [MazePart]| {
|
||||||
|
buffer[0] = p;
|
||||||
|
&mut buffer[1..]
|
||||||
|
};
|
||||||
|
let copy = |src: &[MazePart], dst: &mut [MazePart]| {
|
||||||
|
src.iter()
|
||||||
|
.take_while(|p| **p != MazePart::Noop)
|
||||||
|
.for_each(|p| {
|
||||||
|
dst = push(*p, dst);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
buffer = push(MazePart::Wall, buffer);
|
||||||
|
(0..X).into_iter().for_each(|_| {
|
||||||
|
push(MazePart::Wall, buffer);
|
||||||
|
push(MazePart::Wall, buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut top_line = [MazePart::Noop; SIZE];
|
||||||
|
let mut bottom_line = [MazePart::Noop; SIZE];
|
||||||
|
|
||||||
|
for y in 0..Y {
|
||||||
|
top_line.fill(MazePart::Noop);
|
||||||
|
bottom_line.fill(MazePart::Noop);
|
||||||
|
let tl = &mut top_line;
|
||||||
|
let bl = &mut bottom_line;
|
||||||
|
|
||||||
|
push(MazePart::Wall, tl);
|
||||||
|
push(MazePart::Wall, bl);
|
||||||
|
|
||||||
|
for x in 0..X {
|
||||||
|
push(MazePart::Passage, tl);
|
||||||
|
push(
|
||||||
|
if grid.is_carved((x, y), Cell::EAST) {
|
||||||
|
MazePart::Passage
|
||||||
|
} else {
|
||||||
|
MazePart::Wall
|
||||||
|
},
|
||||||
|
tl,
|
||||||
|
);
|
||||||
|
|
||||||
|
push(
|
||||||
|
if grid.is_carved((x, y), Cell::SOUTH) {
|
||||||
|
MazePart::Passage
|
||||||
|
} else {
|
||||||
|
MazePart::Wall
|
||||||
|
},
|
||||||
|
bl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
copy(&top_line, buffer);
|
||||||
|
copy(&bottom_line, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum MazePart {
|
||||||
|
Noop,
|
||||||
|
Passage,
|
||||||
|
Wall,
|
||||||
|
Player,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod print_tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print() {
|
||||||
|
let mut trng = rand::thread_rng();
|
||||||
|
let mut grid = Grid::<4, 4, 16>::new();
|
||||||
|
RecursiveDivision.generate(&mut grid, &mut trng);
|
||||||
|
let mut result = String::with_capacity(40);
|
||||||
|
AsciiNarrow.format(&mut grid, &mut result);
|
||||||
|
println!("{result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heapless_narrow() {
|
||||||
|
let mut trng = rand::thread_rng();
|
||||||
|
let mut grid = Grid::<4, 4, 16>::new();
|
||||||
|
RecursiveDivision.generate(&mut grid, &mut trng);
|
||||||
|
|
||||||
|
// 4 rows * 2 + 1 newline + 1 border = 10
|
||||||
|
// 4 rows + 1 header row = 5
|
||||||
|
let mut result = heapless::String::<50>::new();
|
||||||
|
AsciiNarrow.format(&mut grid, &mut result);
|
||||||
|
println!("{result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heapless_broad() {
|
||||||
|
let mut trng = rand::thread_rng();
|
||||||
|
let mut grid = Grid::<4, 4, 16>::new();
|
||||||
|
RecursiveDivision.generate(&mut grid, &mut trng);
|
||||||
|
|
||||||
|
// 4 rows * 3 = 12
|
||||||
|
// 4 rows + 3 = 12
|
||||||
|
let mut result = heapless::String::<144>::new();
|
||||||
|
AsciiBroad.format(&mut grid, &mut result);
|
||||||
|
println!("{result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn large() {
|
||||||
|
let mut trng = rand::thread_rng();
|
||||||
|
let mut grid = Grid::<20, 20, 400>::new();
|
||||||
|
RecursiveDivision.generate(&mut grid, &mut trng);
|
||||||
|
let mut result = String::with_capacity(40);
|
||||||
|
AsciiBroad.format(&mut grid, &mut result);
|
||||||
|
println!("{result}");
|
||||||
|
}
|
||||||
|
}
|
1
maze/test.sh
Executable file
1
maze/test.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
cargo test --target x86_64-unknown-linux-gnu $*
|
4
rust-toolchain.toml
Normal file
4
rust-toolchain.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rust-src"]
|
||||||
|
targets = ["riscv32imac-unknown-none-elf"]
|
14
shared/Cargo.toml
Normal file
14
shared/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default-features = []
|
||||||
|
trng = ["esp-hal"]
|
||||||
|
full-rand = [ 'rand' ]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nutype = { version = "0.5.0", default-features = false }
|
||||||
|
esp-hal = { version = "0.19.0", features = ["esp32c6", "embedded-hal"], optional = true }
|
||||||
|
rand = { version = "0.8.5", features = ["getrandom", "small_rng", "std", "std_rng"], optional = true }
|
19
shared/src/lib.rs
Normal file
19
shared/src/lib.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
|
||||||
|
pub trait GenU32 {
|
||||||
|
fn random(&mut self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "trng")]
|
||||||
|
impl<'l> GenU32 for esp_hal::rng::Trng<'l> {
|
||||||
|
fn random(&mut self) -> u32 {
|
||||||
|
self.random()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "full-rand")]
|
||||||
|
impl GenU32 for rand::rngs::ThreadRng {
|
||||||
|
fn random(&mut self) -> u32 {
|
||||||
|
use rand::RngCore;
|
||||||
|
self.next_u32()
|
||||||
|
}
|
||||||
|
}
|
134
src/main.rs
Normal file
134
src/main.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
use display_interface_spi::SPIInterface;
|
||||||
|
use embedded_graphics::{
|
||||||
|
geometry::Point,
|
||||||
|
mono_font::MonoTextStyle,
|
||||||
|
text::{Text, TextStyle},
|
||||||
|
Drawable,
|
||||||
|
};
|
||||||
|
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||||
|
use esp_backtrace as _;
|
||||||
|
use esp_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
delay::Delay,
|
||||||
|
gpio::{Input, Io, Level, Output, NO_PIN},
|
||||||
|
peripherals::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
rng::Trng,
|
||||||
|
spi::{master::Spi, SpiMode},
|
||||||
|
system::SystemControl,
|
||||||
|
};
|
||||||
|
use heapless::String;
|
||||||
|
use profont::PROFONT_24_POINT;
|
||||||
|
use shared::GenU32;
|
||||||
|
use weact_studio_epd::{graphics::Display290BlackWhite, Color};
|
||||||
|
use weact_studio_epd::{graphics::DisplayRotation, WeActStudio290BlackWhiteDriver};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let peripherals = Peripherals::take();
|
||||||
|
let system = SystemControl::new(peripherals.SYSTEM);
|
||||||
|
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
|
let clocks = ClockControl::max(system.clock_control).freeze();
|
||||||
|
let delay = Delay::new(&clocks);
|
||||||
|
let mut trng = Trng::new(peripherals.RNG, peripherals.ADC1);
|
||||||
|
|
||||||
|
let _ = trng.random();
|
||||||
|
|
||||||
|
esp_println::logger::init_logger_from_env();
|
||||||
|
|
||||||
|
log::info!("Intializing SPI Bus...");
|
||||||
|
|
||||||
|
// Pins for Seeedstudio XIAO ESP32-C6
|
||||||
|
let sclk = io.pins.gpio19; // D8 / GPIO19
|
||||||
|
let mosi = io.pins.gpio18; // D10 / GPIO18
|
||||||
|
let cs = io.pins.gpio20; // D9 / GPIO20
|
||||||
|
let dc = io.pins.gpio21; // D3 / GPIO21
|
||||||
|
let rst = io.pins.gpio22; // D4 / GPIO22
|
||||||
|
let busy = io.pins.gpio23; // D5 / GPIO23
|
||||||
|
|
||||||
|
let spi_bus = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks).with_pins(
|
||||||
|
Some(sclk),
|
||||||
|
Some(mosi),
|
||||||
|
NO_PIN,
|
||||||
|
NO_PIN, // cs is handled by the exclusive device
|
||||||
|
);
|
||||||
|
|
||||||
|
// Convert pins into InputPins and OutputPins
|
||||||
|
/*
|
||||||
|
CS: OutputPin,
|
||||||
|
BUSY: InputPin,
|
||||||
|
DC: OutputPin,
|
||||||
|
RST: OutputPin,
|
||||||
|
*/
|
||||||
|
let cs = Output::new(cs, Level::High);
|
||||||
|
let busy = Input::new(busy, esp_hal::gpio::Pull::Up);
|
||||||
|
let dc = Output::new(dc, Level::Low);
|
||||||
|
let rst = Output::new(rst, Level::High);
|
||||||
|
|
||||||
|
log::info!("Intializing SPI Device...");
|
||||||
|
let spi_device = ExclusiveDevice::new(spi_bus, cs, delay).expect("SPI device initialize error");
|
||||||
|
let spi_interface = SPIInterface::new(spi_device, dc);
|
||||||
|
|
||||||
|
// Setup EPD
|
||||||
|
log::info!("Intializing EPD...");
|
||||||
|
let mut driver = WeActStudio290BlackWhiteDriver::new(spi_interface, busy, rst, delay);
|
||||||
|
let mut display = Display290BlackWhite::new();
|
||||||
|
display.set_rotation(DisplayRotation::Rotate90);
|
||||||
|
driver.init().unwrap();
|
||||||
|
|
||||||
|
let style = MonoTextStyle::new(&PROFONT_24_POINT, Color::Black);
|
||||||
|
let _ = Text::with_text_style(
|
||||||
|
"Hello World!",
|
||||||
|
Point::new(8, 68),
|
||||||
|
style,
|
||||||
|
TextStyle::default(),
|
||||||
|
)
|
||||||
|
.draw(&mut display);
|
||||||
|
|
||||||
|
driver.full_update(&display).unwrap();
|
||||||
|
|
||||||
|
log::info!("Sleeping for 5s...");
|
||||||
|
driver.sleep().unwrap();
|
||||||
|
delay.delay(5_000.millis());
|
||||||
|
|
||||||
|
let mut n: u8 = 0;
|
||||||
|
loop {
|
||||||
|
log::info!("Wake up!");
|
||||||
|
driver.wake_up().unwrap();
|
||||||
|
|
||||||
|
display.clear(Color::White);
|
||||||
|
|
||||||
|
let mut string_buf = String::<30>::new();
|
||||||
|
write!(string_buf, "Hello World {}!", n).unwrap();
|
||||||
|
let _ = Text::with_text_style(&string_buf, Point::new(8, 68), style, TextStyle::default())
|
||||||
|
.draw(&mut display)
|
||||||
|
.unwrap();
|
||||||
|
string_buf.clear();
|
||||||
|
|
||||||
|
// TODO: try fast update?
|
||||||
|
driver.full_update(&display).unwrap();
|
||||||
|
|
||||||
|
n = n.wrapping_add(1); // Wrap from 0..255
|
||||||
|
|
||||||
|
log::info!("Sleeping for 5s...");
|
||||||
|
driver.sleep().unwrap();
|
||||||
|
delay.delay(5_000.millis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct MazeGame {
|
||||||
|
maze: Option<maze::Grid<100, 100, 10_000>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MazeGame {
|
||||||
|
pub fn start(&mut self, trng: &mut Trng) {
|
||||||
|
let mut maze = maze::Grid::new();
|
||||||
|
maze::RecursiveDivision.generate(&mut maze, trng);
|
||||||
|
self.maze = Some(maze);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user