Working basics
This commit is contained in:
parent
7a68f5ad19
commit
e174ecdeaf
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -789,6 +789,7 @@ dependencies = [
|
||||
"profont",
|
||||
"rand",
|
||||
"shared",
|
||||
"strum 0.26.3",
|
||||
"weact-studio-epd",
|
||||
]
|
||||
|
||||
|
@ -19,6 +19,7 @@ weact-studio-epd = { version = "0.1.2", features = ["blocking"] }
|
||||
|
||||
shared = { path = "./shared", features = ['trng'] }
|
||||
maze = { path = "./maze" }
|
||||
strum = { version = "0.26.3", default-features = false, features = ["derive"] }
|
||||
|
||||
[profile.dev]
|
||||
# Rust debug is too slow.
|
||||
|
@ -315,7 +315,6 @@ fn choose_orientation(width: usize, height: usize, trng: &mut impl GenU32) -> Or
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use shared::GenU32;
|
||||
|
||||
#[test]
|
||||
fn create_maze() {
|
||||
@ -485,19 +484,19 @@ impl<const X: usize, const Y: usize, const SIZE: usize> BinaryMap<X, Y, SIZE> {
|
||||
Self([MazePart::Noop; SIZE])
|
||||
}
|
||||
|
||||
pub fn at(&self, x: usize, y: usize) -> MazePart {
|
||||
self.0[y * X + x]
|
||||
pub fn at(&self, x: u16, y: u16) -> MazePart {
|
||||
self.0[y as usize * X + x as usize]
|
||||
}
|
||||
|
||||
pub fn can_move(&self, player: (usize, usize), dir: Direction) -> bool {
|
||||
pub fn can_move(&self, player: (u16, u16), dir: Direction) -> bool {
|
||||
match dir {
|
||||
Direction::West if player.0 == 0 => false,
|
||||
Direction::West => self.at(player.0 - 1, player.1) != MazePart::Wall,
|
||||
Direction::East if player.0 == X - 1 => false,
|
||||
Direction::East if player.0 as usize == X - 1 => false,
|
||||
Direction::East => self.at(player.0 + 1, player.1) != MazePart::Wall,
|
||||
Direction::North if player.1 == 0 => false,
|
||||
Direction::North => self.at(player.0, player.1 - 1) != MazePart::Wall,
|
||||
Direction::South if player.1 == Y - 1 => false,
|
||||
Direction::South if player.1 as usize == Y - 1 => false,
|
||||
Direction::South => self.at(player.0, player.1 + 1) != MazePart::Wall,
|
||||
}
|
||||
}
|
||||
@ -624,8 +623,6 @@ pub enum MazePart {
|
||||
#[cfg(test)]
|
||||
mod print_tests {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
|
0
src/apps/guess_game.rs
Normal file
0
src/apps/guess_game.rs
Normal file
89
src/apps/maze_game.rs
Normal file
89
src/apps/maze_game.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use maze::{BinaryMapVisitor, Direction};
|
||||
|
||||
use crate::Button;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct MazeGame {
|
||||
map: maze::BinaryMap<122, 122, 14884>,
|
||||
player: (u16, u16),
|
||||
}
|
||||
|
||||
impl MazeGame {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: maze::BinaryMap::new(),
|
||||
player: (0, 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App for MazeGame {
|
||||
fn start(&mut self, trng: &mut Trng) {
|
||||
let mut grid = maze::Grid::<60, 60, 3600>::new();
|
||||
maze::RecursiveDivision.generate(&mut grid, trng);
|
||||
BinaryMapVisitor.format(&mut grid, &mut self.map.0);
|
||||
}
|
||||
|
||||
fn draw(&self, display: &mut Display290TriColor) {
|
||||
let wall_style = PrimitiveStyleBuilder::new()
|
||||
.stroke_color(TriColor::Black)
|
||||
.stroke_width(3)
|
||||
.fill_color(TriColor::Black)
|
||||
.build();
|
||||
let player_style = PrimitiveStyleBuilder::new()
|
||||
.stroke_color(TriColor::Red)
|
||||
.stroke_width(3)
|
||||
.fill_color(TriColor::Red)
|
||||
.build();
|
||||
|
||||
const X_OFFSET: i32 = 2;
|
||||
const Y_OFFSET: i32 = 2;
|
||||
|
||||
for x in 0..122 {
|
||||
for y in 0..122 {
|
||||
match self.map.at(x, y) {
|
||||
maze::MazePart::Wall => {
|
||||
let p = Rectangle::new(
|
||||
Point::new(x as i32 + X_OFFSET, y as i32 + Y_OFFSET),
|
||||
Size::new(1, 1),
|
||||
);
|
||||
p.draw_styled(&wall_style, display).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let p = Rectangle::new(
|
||||
Point::new(
|
||||
self.player.0 as i32 + X_OFFSET,
|
||||
self.player.1 as i32 + Y_OFFSET,
|
||||
),
|
||||
Size::new(1, 1),
|
||||
);
|
||||
p.draw_styled(&player_style, display).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context) -> Action {
|
||||
let Some(button) = ctx.button_pressed else {
|
||||
return Action::Noop;
|
||||
};
|
||||
match button {
|
||||
Button::Up if self.map.can_move(self.player, Direction::North) => {
|
||||
self.player.1 -= 1;
|
||||
}
|
||||
Button::Down if self.map.can_move(self.player, Direction::South) => {
|
||||
self.player.1 += 1;
|
||||
}
|
||||
Button::Left if self.map.can_move(self.player, Direction::West) => {
|
||||
self.player.0 -= 1;
|
||||
}
|
||||
Button::Right if self.map.can_move(self.player, Direction::East) => {
|
||||
self.player.0 += 1;
|
||||
}
|
||||
Button::Back => return Action::GoToMenu,
|
||||
_ => {}
|
||||
};
|
||||
Action::Noop
|
||||
}
|
||||
}
|
90
src/apps/menu.rs
Normal file
90
src/apps/menu.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use crate::Button;
|
||||
|
||||
use super::*;
|
||||
use profont::PROFONT_24_POINT;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, strum::EnumIter)]
|
||||
enum MenuEntry {
|
||||
#[default]
|
||||
Labirynth,
|
||||
Pairs,
|
||||
}
|
||||
|
||||
impl MenuEntry {
|
||||
fn as_str(&self) -> &str {
|
||||
match self {
|
||||
MenuEntry::Labirynth => "Labirynt",
|
||||
MenuEntry::Pairs => "Pary",
|
||||
}
|
||||
}
|
||||
|
||||
fn position(&self) -> Point {
|
||||
match self {
|
||||
MenuEntry::Labirynth => Point::new(8, 68),
|
||||
MenuEntry::Pairs => Point::new(38, 68),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Menu {
|
||||
selected: MenuEntry,
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new() -> Menu {
|
||||
Menu {
|
||||
selected: MenuEntry::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App for Menu {
|
||||
fn start(&mut self, _trng: &mut Trng) {}
|
||||
|
||||
fn update(&mut self, ctx: &Context) -> Action {
|
||||
let Some(button) = ctx.button_pressed else {
|
||||
return Action::Noop;
|
||||
};
|
||||
match button {
|
||||
Button::Up => match self.selected {
|
||||
MenuEntry::Labirynth => return Action::Noop,
|
||||
MenuEntry::Pairs => {
|
||||
self.selected = MenuEntry::Labirynth;
|
||||
}
|
||||
},
|
||||
Button::Down => match self.selected {
|
||||
MenuEntry::Labirynth => {
|
||||
self.selected = MenuEntry::Pairs;
|
||||
}
|
||||
MenuEntry::Pairs => return Action::Noop,
|
||||
},
|
||||
Button::Circle => match self.selected {
|
||||
MenuEntry::Labirynth => return Action::StartMaze,
|
||||
MenuEntry::Pairs => return Action::StartPairs,
|
||||
},
|
||||
_ => return Action::Noop,
|
||||
};
|
||||
Action::Noop
|
||||
}
|
||||
|
||||
fn draw(&self, display: &mut Display290TriColor) {
|
||||
let style = MonoTextStyle::new(&PROFONT_24_POINT, TriColor::Black);
|
||||
let selected_style = MonoTextStyle::new(&PROFONT_24_POINT, TriColor::Red);
|
||||
|
||||
MenuEntry::iter().for_each(|entry| {
|
||||
let _ = Text::with_text_style(
|
||||
entry.as_str(),
|
||||
entry.position(),
|
||||
if entry == self.selected {
|
||||
selected_style
|
||||
} else {
|
||||
style
|
||||
},
|
||||
TextStyle::default(),
|
||||
)
|
||||
.draw(display)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
67
src/apps/mod.rs
Normal file
67
src/apps/mod.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use embedded_graphics::{
|
||||
geometry::{Point, Size},
|
||||
mono_font::MonoTextStyle,
|
||||
primitives::{PrimitiveStyleBuilder, Rectangle, StyledDrawable},
|
||||
text::{Text, TextStyle},
|
||||
Drawable,
|
||||
};
|
||||
use esp_hal::rng::Trng;
|
||||
use maze_game::MazeGame;
|
||||
use menu::Menu;
|
||||
use weact_studio_epd::{graphics::Display290TriColor, TriColor};
|
||||
|
||||
use crate::Context;
|
||||
|
||||
pub mod guess_game;
|
||||
pub mod maze_game;
|
||||
pub mod menu;
|
||||
|
||||
pub enum Action {
|
||||
Noop,
|
||||
GoToMenu,
|
||||
StartMaze,
|
||||
StartPairs,
|
||||
}
|
||||
|
||||
pub enum Application {
|
||||
Menu(Menu),
|
||||
Maze(maze_game::MazeGame),
|
||||
}
|
||||
|
||||
impl Application {
|
||||
pub fn update(&mut self, ctx: &Context, trng: &mut Trng) {
|
||||
let action = match self {
|
||||
Self::Menu(menu) => menu.update(ctx),
|
||||
Self::Maze(maze) => maze.update(ctx),
|
||||
};
|
||||
match action {
|
||||
Action::StartMaze => {
|
||||
let mut maze = MazeGame::new();
|
||||
maze.start(trng);
|
||||
*self = Application::Maze(maze);
|
||||
}
|
||||
Action::GoToMenu => {
|
||||
let mut menu = Menu::new();
|
||||
menu.start(trng);
|
||||
*self = Application::Menu(menu);
|
||||
}
|
||||
Action::Noop => {}
|
||||
Action::StartPairs => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, display: &mut Display290TriColor) {
|
||||
match self {
|
||||
Self::Menu(menu) => menu.draw(display),
|
||||
Self::Maze(maze) => maze.draw(display),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait App {
|
||||
fn start(&mut self, trng: &mut Trng);
|
||||
|
||||
fn draw(&self, display: &mut Display290TriColor);
|
||||
|
||||
fn update(&mut self, ctx: &Context) -> Action;
|
||||
}
|
117
src/main.rs
117
src/main.rs
@ -1,13 +1,11 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::fmt::Write;
|
||||
use apps::{menu::Menu, Application};
|
||||
use display_interface_spi::SPIInterface;
|
||||
use embedded_graphics::{
|
||||
geometry::Point,
|
||||
mono_font::MonoTextStyle,
|
||||
pixelcolor::Rgb565,
|
||||
primitives::{Line, PrimitiveStyle},
|
||||
text::{Text, TextStyle},
|
||||
Drawable,
|
||||
};
|
||||
@ -16,19 +14,22 @@ use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
delay::Delay,
|
||||
gpio::{Input, Io, Level, Output, NO_PIN},
|
||||
gpio::{
|
||||
Gpio0, Gpio1, Gpio2, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, GpioPin, Input, Io, Level, Output,
|
||||
NO_PIN,
|
||||
},
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
rng::Trng,
|
||||
spi::{master::Spi, SpiMode},
|
||||
system::SystemControl,
|
||||
};
|
||||
use heapless::String;
|
||||
use maze::{BinaryMap, BinaryMapVisitor};
|
||||
use profont::PROFONT_24_POINT;
|
||||
use weact_studio_epd::{graphics::Display290TriColor, TriColor};
|
||||
use weact_studio_epd::{graphics::DisplayRotation, WeActStudio290TriColorDriver};
|
||||
|
||||
mod apps;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
@ -44,9 +45,9 @@ fn main() -> ! {
|
||||
|
||||
log::info!("Intializing SPI Bus...");
|
||||
|
||||
// Pins for Seeedstudio XIAO ESP32-C6
|
||||
let sclk = io.pins.gpio19; // D8 / GPIO19
|
||||
// Pins for WeAct
|
||||
let mosi = io.pins.gpio18; // D10 / GPIO18
|
||||
let sclk = io.pins.gpio19; // D8 / GPIO19
|
||||
let cs = io.pins.gpio20; // D9 / GPIO20
|
||||
let dc = io.pins.gpio21; // D3 / GPIO21
|
||||
let rst = io.pins.gpio22; // D4 / GPIO22
|
||||
@ -84,7 +85,7 @@ fn main() -> ! {
|
||||
|
||||
let style = MonoTextStyle::new(&PROFONT_24_POINT, TriColor::Black);
|
||||
let _ = Text::with_text_style(
|
||||
"Hello World!",
|
||||
"Welcome in Reddy",
|
||||
Point::new(8, 68),
|
||||
style,
|
||||
TextStyle::default(),
|
||||
@ -93,51 +94,95 @@ fn main() -> ! {
|
||||
|
||||
driver.full_update(&display).unwrap();
|
||||
|
||||
log::info!("Sleeping for 5s...");
|
||||
log::info!("Sleeping for 0.5s...");
|
||||
driver.sleep().unwrap();
|
||||
delay.delay(5_000.millis());
|
||||
delay.delay(500.millis());
|
||||
|
||||
let kbd = Keyboard {
|
||||
gpio0: io.pins.gpio0,
|
||||
gpio1: io.pins.gpio1,
|
||||
gpio2: io.pins.gpio2,
|
||||
gpio3: io.pins.gpio3,
|
||||
gpio4: io.pins.gpio4,
|
||||
gpio5: io.pins.gpio5,
|
||||
gpio6: io.pins.gpio6,
|
||||
gpio7: io.pins.gpio7,
|
||||
};
|
||||
|
||||
let mut ctx = Context {
|
||||
button_pressed: None,
|
||||
};
|
||||
let mut app = Application::Menu(Menu::new());
|
||||
|
||||
let mut n: u8 = 0;
|
||||
loop {
|
||||
log::info!("Wake up!");
|
||||
driver.wake_up().unwrap();
|
||||
|
||||
display.clear(TriColor::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();
|
||||
ctx.button_pressed = kbd.pressed();
|
||||
app.update(&ctx, &mut trng);
|
||||
app.draw(&mut display);
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
struct MazeGame {
|
||||
map: maze::BinaryMap<122, 122, 14884>,
|
||||
/// Buttons
|
||||
///
|
||||
/// +-------------------------------+
|
||||
/// | A | Up | B | X |
|
||||
/// | Left | Circle | Right | Y |
|
||||
/// | C | Down | D | Z |
|
||||
/// | M1 | M2 | M3 | Back |
|
||||
/// +-------------------------------+
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Button {
|
||||
// row 1
|
||||
A,
|
||||
Up,
|
||||
B,
|
||||
X,
|
||||
// row 2
|
||||
Left,
|
||||
Circle,
|
||||
Right,
|
||||
Y,
|
||||
// row 3
|
||||
C,
|
||||
Down,
|
||||
D,
|
||||
Z,
|
||||
// row 4
|
||||
M1,
|
||||
M2,
|
||||
M3,
|
||||
Back,
|
||||
}
|
||||
|
||||
impl MazeGame {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: maze::BinaryMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, trng: &mut Trng) {
|
||||
let mut grid = maze::Grid::<60, 60, 3600>::new();
|
||||
maze::RecursiveDivision.generate(&mut grid, trng);
|
||||
BinaryMapVisitor.format(&mut grid, &mut self.map.0);
|
||||
}
|
||||
|
||||
pub fn draw(&self, dispay: Display290TriColor) {}
|
||||
pub struct Context {
|
||||
button_pressed: Option<Button>,
|
||||
}
|
||||
|
||||
struct Keyboard {
|
||||
gpio0: Gpio0, //
|
||||
gpio1: Gpio1, //
|
||||
gpio2: Gpio2, //
|
||||
gpio3: Gpio3, //
|
||||
//
|
||||
gpio4: Gpio4, //
|
||||
gpio5: Gpio5, //
|
||||
gpio6: Gpio6, //
|
||||
gpio7: Gpio7, //
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn pressed(&self) -> Option<Button> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user