Working basics

This commit is contained in:
eraden 2024-09-17 13:19:24 +02:00
parent 7a68f5ad19
commit e174ecdeaf
8 changed files with 334 additions and 44 deletions

1
Cargo.lock generated
View File

@ -789,6 +789,7 @@ dependencies = [
"profont", "profont",
"rand", "rand",
"shared", "shared",
"strum 0.26.3",
"weact-studio-epd", "weact-studio-epd",
] ]

View File

@ -19,6 +19,7 @@ weact-studio-epd = { version = "0.1.2", features = ["blocking"] }
shared = { path = "./shared", features = ['trng'] } shared = { path = "./shared", features = ['trng'] }
maze = { path = "./maze" } maze = { path = "./maze" }
strum = { version = "0.26.3", default-features = false, features = ["derive"] }
[profile.dev] [profile.dev]
# Rust debug is too slow. # Rust debug is too slow.

View File

@ -315,7 +315,6 @@ fn choose_orientation(width: usize, height: usize, trng: &mut impl GenU32) -> Or
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use shared::GenU32;
#[test] #[test]
fn create_maze() { 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]) Self([MazePart::Noop; SIZE])
} }
pub fn at(&self, x: usize, y: usize) -> MazePart { pub fn at(&self, x: u16, y: u16) -> MazePart {
self.0[y * X + x] 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 { match dir {
Direction::West if player.0 == 0 => false, Direction::West if player.0 == 0 => false,
Direction::West => self.at(player.0 - 1, player.1) != MazePart::Wall, 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::East => self.at(player.0 + 1, player.1) != MazePart::Wall,
Direction::North if player.1 == 0 => false, Direction::North if player.1 == 0 => false,
Direction::North => self.at(player.0, player.1 - 1) != MazePart::Wall, 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, Direction::South => self.at(player.0, player.1 + 1) != MazePart::Wall,
} }
} }
@ -624,8 +623,6 @@ pub enum MazePart {
#[cfg(test)] #[cfg(test)]
mod print_tests { mod print_tests {
use super::*; use super::*;
use std::io::Cursor;
use std::io::Write;
#[test] #[test]
fn print() { fn print() {

0
src/apps/guess_game.rs Normal file
View File

89
src/apps/maze_game.rs Normal file
View 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
View 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
View 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;
}

View File

@ -1,13 +1,11 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use core::fmt::Write; use apps::{menu::Menu, Application};
use display_interface_spi::SPIInterface; use display_interface_spi::SPIInterface;
use embedded_graphics::{ use embedded_graphics::{
geometry::Point, geometry::Point,
mono_font::MonoTextStyle, mono_font::MonoTextStyle,
pixelcolor::Rgb565,
primitives::{Line, PrimitiveStyle},
text::{Text, TextStyle}, text::{Text, TextStyle},
Drawable, Drawable,
}; };
@ -16,19 +14,22 @@ use esp_backtrace as _;
use esp_hal::{ use esp_hal::{
clock::ClockControl, clock::ClockControl,
delay::Delay, 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, peripherals::Peripherals,
prelude::*, prelude::*,
rng::Trng, rng::Trng,
spi::{master::Spi, SpiMode}, spi::{master::Spi, SpiMode},
system::SystemControl, system::SystemControl,
}; };
use heapless::String;
use maze::{BinaryMap, BinaryMapVisitor};
use profont::PROFONT_24_POINT; use profont::PROFONT_24_POINT;
use weact_studio_epd::{graphics::Display290TriColor, TriColor}; use weact_studio_epd::{graphics::Display290TriColor, TriColor};
use weact_studio_epd::{graphics::DisplayRotation, WeActStudio290TriColorDriver}; use weact_studio_epd::{graphics::DisplayRotation, WeActStudio290TriColorDriver};
mod apps;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
let peripherals = Peripherals::take(); let peripherals = Peripherals::take();
@ -44,9 +45,9 @@ fn main() -> ! {
log::info!("Intializing SPI Bus..."); log::info!("Intializing SPI Bus...");
// Pins for Seeedstudio XIAO ESP32-C6 // Pins for WeAct
let sclk = io.pins.gpio19; // D8 / GPIO19
let mosi = io.pins.gpio18; // D10 / GPIO18 let mosi = io.pins.gpio18; // D10 / GPIO18
let sclk = io.pins.gpio19; // D8 / GPIO19
let cs = io.pins.gpio20; // D9 / GPIO20 let cs = io.pins.gpio20; // D9 / GPIO20
let dc = io.pins.gpio21; // D3 / GPIO21 let dc = io.pins.gpio21; // D3 / GPIO21
let rst = io.pins.gpio22; // D4 / GPIO22 let rst = io.pins.gpio22; // D4 / GPIO22
@ -84,7 +85,7 @@ fn main() -> ! {
let style = MonoTextStyle::new(&PROFONT_24_POINT, TriColor::Black); let style = MonoTextStyle::new(&PROFONT_24_POINT, TriColor::Black);
let _ = Text::with_text_style( let _ = Text::with_text_style(
"Hello World!", "Welcome in Reddy",
Point::new(8, 68), Point::new(8, 68),
style, style,
TextStyle::default(), TextStyle::default(),
@ -93,51 +94,95 @@ fn main() -> ! {
driver.full_update(&display).unwrap(); driver.full_update(&display).unwrap();
log::info!("Sleeping for 5s..."); log::info!("Sleeping for 0.5s...");
driver.sleep().unwrap(); 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 { loop {
log::info!("Wake up!"); log::info!("Wake up!");
driver.wake_up().unwrap(); driver.wake_up().unwrap();
display.clear(TriColor::White); display.clear(TriColor::White);
let mut string_buf = String::<30>::new(); ctx.button_pressed = kbd.pressed();
write!(string_buf, "Hello World {}!", n).unwrap(); app.update(&ctx, &mut trng);
let _ = Text::with_text_style(&string_buf, Point::new(8, 68), style, TextStyle::default()) app.draw(&mut display);
.draw(&mut display)
.unwrap();
string_buf.clear();
// TODO: try fast update? // TODO: try fast update?
driver.full_update(&display).unwrap(); driver.full_update(&display).unwrap();
n = n.wrapping_add(1); // Wrap from 0..255
log::info!("Sleeping for 5s..."); log::info!("Sleeping for 5s...");
driver.sleep().unwrap(); driver.sleep().unwrap();
delay.delay(5_000.millis()); delay.delay(5_000.millis());
} }
} }
struct MazeGame { /// Buttons
map: maze::BinaryMap<122, 122, 14884>, ///
/// +-------------------------------+
/// | 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 struct Context {
pub fn new() -> Self { button_pressed: Option<Button>,
Self {
map: maze::BinaryMap::new(),
}
} }
pub fn start(&mut self, trng: &mut Trng) { struct Keyboard {
let mut grid = maze::Grid::<60, 60, 3600>::new(); gpio0: Gpio0, //
maze::RecursiveDivision.generate(&mut grid, trng); gpio1: Gpio1, //
BinaryMapVisitor.format(&mut grid, &mut self.map.0); gpio2: Gpio2, //
gpio3: Gpio3, //
//
gpio4: Gpio4, //
gpio5: Gpio5, //
gpio6: Gpio6, //
gpio7: Gpio7, //
} }
pub fn draw(&self, dispay: Display290TriColor) {} impl Keyboard {
pub fn pressed(&self) -> Option<Button> {
None
}
} }