Implemented snake
This commit is contained in:
parent
da5edb5cbf
commit
5ac79ce0ce
4 changed files with 223 additions and 2 deletions
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
members = [ "cardputer-bsc",
|
members = [
|
||||||
|
"cardputer-bsc",
|
||||||
|
"snake",
|
||||||
"test-app",
|
"test-app",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -11,4 +13,4 @@ opt-level = "s"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
debug = true # Symbols are nice and they don't increase the size on Flash
|
debug = true # Symbols are nice and they don't increase the size on Flash
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|
|
||||||
23
snake/Cargo.toml
Normal file
23
snake/Cargo.toml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "snake"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "snake"
|
||||||
|
harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
experimental = ["esp-idf-svc/experimental"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
esp-idf-svc = { version = "0.51", features = ["critical-section", "embassy-time-driver", "embassy-sync"] }
|
||||||
|
cardputer-bsc = { version = "0.1.0", path = "../cardputer-bsc" }
|
||||||
|
embedded-graphics-core = "0.4.0"
|
||||||
|
anyhow = "1.0.98"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
embuild = "0.33"
|
||||||
3
snake/build.rs
Normal file
3
snake/build.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
embuild::espidf::sysenv::output();
|
||||||
|
}
|
||||||
193
snake/src/main.rs
Normal file
193
snake/src/main.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
use std::{collections::VecDeque, ops::Rem, thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use cardputer_bsc::display::{DISPLAY_SIZE_HEIGHT, DISPLAY_SIZE_WIDTH, Drawable};
|
||||||
|
use embedded_graphics_core::{pixelcolor::Rgb565, prelude::RgbColor};
|
||||||
|
use esp_idf_svc::{
|
||||||
|
hal::{
|
||||||
|
ledc::{self, LedcDriver, LedcTimerDriver, SpeedMode, config::TimerConfig},
|
||||||
|
prelude::Peripherals,
|
||||||
|
rmt::config::DutyPercent,
|
||||||
|
units::Hertz,
|
||||||
|
},
|
||||||
|
sys::esp_random,
|
||||||
|
};
|
||||||
|
|
||||||
|
const GRID_SQUARE_SIZE: u16 = 15;
|
||||||
|
const GRID_WIDTH: i32 = 16;
|
||||||
|
const GRID_HEIGHT: i32 = 9;
|
||||||
|
|
||||||
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
|
// It is necessary to call this function once. Otherwise some patches to the runtime
|
||||||
|
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
|
||||||
|
esp_idf_svc::sys::link_patches();
|
||||||
|
|
||||||
|
// Bind the log crate to the ESP Logging facilities
|
||||||
|
esp_idf_svc::log::EspLogger::initialize_default();
|
||||||
|
|
||||||
|
log::info!("Hello, world!");
|
||||||
|
|
||||||
|
let peripherals = Peripherals::take()?;
|
||||||
|
|
||||||
|
let mut display = cardputer_bsc::display::build(
|
||||||
|
peripherals.spi2,
|
||||||
|
peripherals.pins.gpio36,
|
||||||
|
peripherals.pins.gpio35,
|
||||||
|
peripherals.pins.gpio37,
|
||||||
|
peripherals.pins.gpio34,
|
||||||
|
peripherals.pins.gpio33,
|
||||||
|
)
|
||||||
|
.context("Failed to initialise display")?;
|
||||||
|
|
||||||
|
let timer_config = TimerConfig::new()
|
||||||
|
.resolution(ledc::Resolution::Bits8)
|
||||||
|
.frequency(Hertz(256).into());
|
||||||
|
let bl_timer_driver = LedcTimerDriver::new(peripherals.ledc.timer3, &timer_config)
|
||||||
|
.context("Failed to create backlight timer")?;
|
||||||
|
let mut bl_driver = LedcDriver::new(
|
||||||
|
peripherals.ledc.channel7,
|
||||||
|
bl_timer_driver,
|
||||||
|
peripherals.pins.gpio38,
|
||||||
|
)
|
||||||
|
.context("Failed to create backlight driver")?;
|
||||||
|
|
||||||
|
dbg!(display.is_sleeping());
|
||||||
|
|
||||||
|
clear_screen(&mut display, Rgb565::BLACK)?;
|
||||||
|
bl_driver
|
||||||
|
.set_duty(bl_driver.get_max_duty())
|
||||||
|
.context("Failed to set duty")?;
|
||||||
|
bl_driver.enable().context("Failed to enable driver")?;
|
||||||
|
|
||||||
|
let start_position = (GRID_WIDTH / 2, GRID_HEIGHT / 2);
|
||||||
|
let mut direction = (1, 0);
|
||||||
|
let mut snake_tiles = VecDeque::new();
|
||||||
|
snake_tiles.push_back(start_position);
|
||||||
|
snake_tiles.push_back((start_position.0 - 1, start_position.1));
|
||||||
|
snake_tiles.push_back((start_position.0 - 2, start_position.1));
|
||||||
|
snake_tiles.push_back((start_position.0 - 3, start_position.1));
|
||||||
|
|
||||||
|
let mut keyboard = cardputer_bsc::keyboard::Keyboard::new(
|
||||||
|
peripherals.pins.gpio8,
|
||||||
|
peripherals.pins.gpio9,
|
||||||
|
peripherals.pins.gpio11,
|
||||||
|
peripherals.pins.gpio13,
|
||||||
|
peripherals.pins.gpio15,
|
||||||
|
peripherals.pins.gpio3,
|
||||||
|
peripherals.pins.gpio4,
|
||||||
|
peripherals.pins.gpio5,
|
||||||
|
peripherals.pins.gpio6,
|
||||||
|
peripherals.pins.gpio7,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut apple = random_tile();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let pressed_keys = keyboard.scan_pressed_keys()?;
|
||||||
|
dbg!(&pressed_keys);
|
||||||
|
for key in pressed_keys {
|
||||||
|
let new_direction = match key {
|
||||||
|
cardputer_bsc::keyboard::KeyImprint::SemiColon => (0, -1),
|
||||||
|
cardputer_bsc::keyboard::KeyImprint::Comma => (-1, 0),
|
||||||
|
cardputer_bsc::keyboard::KeyImprint::Period => (0, 1),
|
||||||
|
cardputer_bsc::keyboard::KeyImprint::Slash => (1, 0),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
match (snake_tiles.get(0), snake_tiles.get(1)) {
|
||||||
|
(Some(&first), Some(&second)) => {
|
||||||
|
let disallowed_direction = (
|
||||||
|
(second.0 - first.0).rem(GRID_WIDTH),
|
||||||
|
(second.1 - first.1).rem(GRID_HEIGHT),
|
||||||
|
);
|
||||||
|
dbg!(disallowed_direction);
|
||||||
|
if disallowed_direction != new_direction {
|
||||||
|
direction = new_direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
direction = new_direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
|
let front_file = *snake_tiles.get(0).unwrap();
|
||||||
|
|
||||||
|
let new_tile = (
|
||||||
|
(front_file.0 + direction.0).rem_euclid(GRID_WIDTH),
|
||||||
|
(front_file.1 + direction.1).rem_euclid(GRID_HEIGHT),
|
||||||
|
);
|
||||||
|
|
||||||
|
if snake_tiles.iter().any(|&tile| tile == new_tile) {
|
||||||
|
for x in 0..GRID_WIDTH {
|
||||||
|
for y in 0..GRID_HEIGHT {
|
||||||
|
draw_tile(&mut display, x, y, Rgb565::new(31, 63, 31))?;
|
||||||
|
sleep(Duration::from_millis(20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(Duration::from_secs(1));
|
||||||
|
clear_screen(&mut display, Rgb565::BLACK)?;
|
||||||
|
snake_tiles.clear();
|
||||||
|
direction = (1, 0);
|
||||||
|
snake_tiles.push_back(start_position);
|
||||||
|
snake_tiles.push_back((start_position.0 - 1, start_position.1));
|
||||||
|
snake_tiles.push_back((start_position.0 - 2, start_position.1));
|
||||||
|
snake_tiles.push_back((start_position.0 - 3, start_position.1));
|
||||||
|
apple = random_tile();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
snake_tiles.push_front(new_tile);
|
||||||
|
|
||||||
|
let old_tile = if new_tile == apple {
|
||||||
|
apple = random_tile();
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
snake_tiles.pop_back()
|
||||||
|
};
|
||||||
|
if let Some(old_tile) = old_tile {
|
||||||
|
draw_tile(&mut display, old_tile.0, old_tile.1, Rgb565::BLACK)?;
|
||||||
|
}
|
||||||
|
for &tile in &snake_tiles {
|
||||||
|
draw_tile(&mut display, tile.0, tile.1, Rgb565::RED)?;
|
||||||
|
}
|
||||||
|
draw_tile(&mut display, apple.0, apple.1, Rgb565::GREEN)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_tile() -> (i32, i32) {
|
||||||
|
(
|
||||||
|
unsafe { esp_random() as i32 }.rem_euclid(GRID_WIDTH),
|
||||||
|
unsafe { esp_random() as i32 }.rem_euclid(GRID_HEIGHT),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_screen(display: &mut Drawable, colour: Rgb565) -> Result<(), anyhow::Error> {
|
||||||
|
display
|
||||||
|
.set_pixels(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
DISPLAY_SIZE_WIDTH - 1,
|
||||||
|
DISPLAY_SIZE_HEIGHT - 1,
|
||||||
|
std::iter::repeat_n(
|
||||||
|
colour,
|
||||||
|
DISPLAY_SIZE_WIDTH as usize * DISPLAY_SIZE_HEIGHT as usize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow::anyhow!("{e:?}"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_tile(display: &mut Drawable, x: i32, y: i32, colour: Rgb565) -> Result<(), anyhow::Error> {
|
||||||
|
display
|
||||||
|
.set_pixels(
|
||||||
|
x as u16 * GRID_SQUARE_SIZE,
|
||||||
|
y as u16 * GRID_SQUARE_SIZE,
|
||||||
|
(x + 1) as u16 * GRID_SQUARE_SIZE - 1,
|
||||||
|
(y + 1) as u16 * GRID_SQUARE_SIZE - 1,
|
||||||
|
std::iter::repeat_n(colour, (GRID_SQUARE_SIZE as usize).pow(2)),
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow::anyhow!("{e:?}"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue