Added snake example
This commit is contained in:
parent
9381de5039
commit
19ce383be1
5 changed files with 370 additions and 0 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -1106,6 +1106,18 @@ version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "snake"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cardputer-bsc-nostd",
|
||||||
|
"embedded-graphics",
|
||||||
|
"esp-bootloader-esp-idf",
|
||||||
|
"esp-hal",
|
||||||
|
"esp-println",
|
||||||
|
"heapless",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|
|
||||||
26
examples/snake/Cargo.toml
Normal file
26
examples/snake/Cargo.toml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
[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
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "s"
|
||||||
|
debug = true
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
debug = true # Symbols are nice and they don't increase the size on Flash
|
||||||
|
opt-level = "z"
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cardputer-bsc-nostd = { version = "0.1.0", path = "../.." }
|
||||||
|
embedded-graphics = "0.8.1"
|
||||||
|
esp-hal = {version = "=1.0.0-beta.1", features = ["esp32s3", "unstable"]}
|
||||||
|
esp-println = { version = "0.14.0", features = ["esp32s3"] }
|
||||||
|
esp-bootloader-esp-idf = "0.1.0"
|
||||||
|
heapless = "0.8.0"
|
||||||
52
examples/snake/build.rs
Normal file
52
examples/snake/build.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
fn main() {
|
||||||
|
linker_be_nice();
|
||||||
|
// make sure linkall.x is the last linker script (otherwise might cause problems with flip-link)
|
||||||
|
println!("cargo:rustc-link-arg=-Tlinkall.x");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn linker_be_nice() {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
if args.len() > 1 {
|
||||||
|
let kind = &args[1];
|
||||||
|
let what = &args[2];
|
||||||
|
|
||||||
|
match kind.as_str() {
|
||||||
|
"undefined-symbol" => match what.as_str() {
|
||||||
|
"_defmt_timestamp" => {
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("💡 `defmt` not found - make sure `defmt.x` is added as a linker script and you have included `use defmt_rtt as _;`");
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
"_stack_start" => {
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("💡 Is the linker script `linkall.x` missing?");
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
"esp_wifi_preempt_enable"
|
||||||
|
| "esp_wifi_preempt_yield_task"
|
||||||
|
| "esp_wifi_preempt_task_create" => {
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("💡 `esp-wifi` has no scheduler enabled. Make sure you have the `builtin-scheduler` feature enabled, or that you provide an external scheduler.");
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
"embedded_test_linker_file_not_added_to_rustflags" => {
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("💡 `embedded-test` not found - make sure `embedded-test.x` is added as a linker script for tests");
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
// we don't have anything helpful for "missing-lib" yet
|
||||||
|
_ => {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-link-arg=-Wl,--error-handling-script={}",
|
||||||
|
std::env::current_exe().unwrap().display()
|
||||||
|
);
|
||||||
|
}
|
||||||
11
examples/snake/readme.md
Normal file
11
examples/snake/readme.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# snake example
|
||||||
|
|
||||||
|
This is a more complex example, using the cardputer's display and keyboard to implement the game 'snake'.
|
||||||
|
|
||||||
|
## Run this example
|
||||||
|
|
||||||
|
With this repo cloned, run the following from either the root or this directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo espflash flash --release --package snake
|
||||||
|
```
|
||||||
269
examples/snake/src/main.rs
Normal file
269
examples/snake/src/main.rs
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::{ops::Rem, panic::PanicInfo};
|
||||||
|
|
||||||
|
use embedded_graphics::prelude::RgbColor;
|
||||||
|
use esp_hal::main;
|
||||||
|
use esp_println::println;
|
||||||
|
|
||||||
|
const GRID_SQUARE_SIZE: u16 = 15;
|
||||||
|
const GRID_WIDTH: i32 = 16;
|
||||||
|
const GRID_HEIGHT: i32 = 9;
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
println!("{info}");
|
||||||
|
esp_hal::system::software_reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_bootloader_esp_idf::esp_app_desc!();
|
||||||
|
|
||||||
|
#[main]
|
||||||
|
fn entrypoint() -> ! {
|
||||||
|
_main();
|
||||||
|
println!("main exited, entering infinite loop");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _main() {
|
||||||
|
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||||
|
|
||||||
|
let mut keyboard = cardputer_bsc_nostd::keyboard::Keyboard::new(
|
||||||
|
peripherals.GPIO8,
|
||||||
|
peripherals.GPIO9,
|
||||||
|
peripherals.GPIO11,
|
||||||
|
peripherals.GPIO13,
|
||||||
|
peripherals.GPIO15,
|
||||||
|
peripherals.GPIO3,
|
||||||
|
peripherals.GPIO4,
|
||||||
|
peripherals.GPIO5,
|
||||||
|
peripherals.GPIO6,
|
||||||
|
peripherals.GPIO7,
|
||||||
|
);
|
||||||
|
|
||||||
|
cardputer_bsc_nostd::setup_backlight! {
|
||||||
|
let (mut ledc, mut timer) = setup_backlight(peripherals.LEDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut display = cardputer_bsc_nostd::display::Display::new(
|
||||||
|
peripherals.SPI2,
|
||||||
|
peripherals.GPIO36,
|
||||||
|
peripherals.GPIO35,
|
||||||
|
peripherals.GPIO37,
|
||||||
|
peripherals.GPIO34,
|
||||||
|
peripherals.GPIO33,
|
||||||
|
&ledc,
|
||||||
|
&timer,
|
||||||
|
peripherals.GPIO38,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut rng = esp_hal::rng::Rng::new(peripherals.RNG);
|
||||||
|
|
||||||
|
clear_screen(&mut display, embedded_graphics::pixelcolor::Rgb565::BLACK);
|
||||||
|
display.set_backlight_brightness(100).unwrap();
|
||||||
|
|
||||||
|
let mut direction = (1, 0);
|
||||||
|
let mut snake_tiles =
|
||||||
|
heapless::Deque::<_, { GRID_WIDTH as usize * GRID_HEIGHT as usize }>::new();
|
||||||
|
starting_snake(&mut snake_tiles);
|
||||||
|
|
||||||
|
let mut apple = random_tile(&mut rng);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
for _ in 0..20 {
|
||||||
|
handle_input(&mut keyboard, &mut direction, &snake_tiles);
|
||||||
|
esp_hal::delay::Delay::new().delay_millis(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
let front_tile = *snake_tiles.front().unwrap();
|
||||||
|
|
||||||
|
let new_tile = (
|
||||||
|
(front_tile.0 + direction.0).rem_euclid(GRID_WIDTH),
|
||||||
|
(front_tile.1 + direction.1).rem_euclid(GRID_HEIGHT),
|
||||||
|
);
|
||||||
|
|
||||||
|
if snake_tiles.iter().any(|&tile| tile == new_tile) {
|
||||||
|
game_over(
|
||||||
|
&mut display,
|
||||||
|
&mut rng,
|
||||||
|
&mut direction,
|
||||||
|
&mut snake_tiles,
|
||||||
|
&mut apple,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
snake_tiles.push_front(new_tile).unwrap();
|
||||||
|
|
||||||
|
let old_tile = if new_tile == apple {
|
||||||
|
apple = random_tile(&mut rng);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
snake_tiles.pop_back()
|
||||||
|
};
|
||||||
|
if let Some(old_tile) = old_tile {
|
||||||
|
draw_tile(
|
||||||
|
&mut display,
|
||||||
|
old_tile.0,
|
||||||
|
old_tile.1,
|
||||||
|
embedded_graphics::pixelcolor::Rgb565::BLACK,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
draw_snake(&mut display, &snake_tiles);
|
||||||
|
draw_apple(&mut display, apple);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_input<const N: usize>(
|
||||||
|
keyboard: &mut cardputer_bsc_nostd::keyboard::Keyboard<'_>,
|
||||||
|
direction: &mut (i32, i32),
|
||||||
|
snake_tiles: &heapless::Deque<(i32, i32), N>,
|
||||||
|
) {
|
||||||
|
keyboard.scan();
|
||||||
|
let pressed_keys = keyboard.pressed_keys();
|
||||||
|
|
||||||
|
let key_directions = [
|
||||||
|
(cardputer_bsc_nostd::keyboard::Key::SemiColon, (0, -1)),
|
||||||
|
(cardputer_bsc_nostd::keyboard::Key::Comma, (-1, 0)),
|
||||||
|
(cardputer_bsc_nostd::keyboard::Key::Period, (0, 1)),
|
||||||
|
(cardputer_bsc_nostd::keyboard::Key::Slash, (1, 0)),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (key, new_direction) in key_directions {
|
||||||
|
if pressed_keys.contains(key) {
|
||||||
|
match (snake_tiles.iter().nth(0), snake_tiles.iter().nth(1)) {
|
||||||
|
(Some(&first), Some(&second)) => {
|
||||||
|
let mut disallowed_direction = (
|
||||||
|
(second.0 - first.0).rem(GRID_WIDTH),
|
||||||
|
(second.1 - first.1).rem(GRID_HEIGHT),
|
||||||
|
);
|
||||||
|
if disallowed_direction.0 == GRID_WIDTH - 1 {
|
||||||
|
disallowed_direction.0 = -1;
|
||||||
|
} else if disallowed_direction.0 == 1 - GRID_WIDTH {
|
||||||
|
disallowed_direction.0 = 1;
|
||||||
|
} else if disallowed_direction.1 == GRID_HEIGHT - 1 {
|
||||||
|
disallowed_direction.1 = -1;
|
||||||
|
} else if disallowed_direction.1 == 1 - GRID_HEIGHT {
|
||||||
|
disallowed_direction.1 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if disallowed_direction != new_direction {
|
||||||
|
*direction = new_direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*direction = new_direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyboard.clear_pressed_keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_tile(rng: &mut esp_hal::rng::Rng) -> (i32, i32) {
|
||||||
|
(
|
||||||
|
(rng.random() as i32).rem_euclid(GRID_WIDTH),
|
||||||
|
(rng.random() as i32).rem_euclid(GRID_HEIGHT),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn starting_snake<const N: usize>(snake_tiles: &mut heapless::Deque<(i32, i32), N>) {
|
||||||
|
let start_position = (GRID_WIDTH / 2, GRID_HEIGHT / 2);
|
||||||
|
snake_tiles.push_back(start_position).unwrap();
|
||||||
|
snake_tiles
|
||||||
|
.push_back((start_position.0 - 1, start_position.1))
|
||||||
|
.unwrap();
|
||||||
|
snake_tiles
|
||||||
|
.push_back((start_position.0 - 2, start_position.1))
|
||||||
|
.unwrap();
|
||||||
|
snake_tiles
|
||||||
|
.push_back((start_position.0 - 3, start_position.1))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_apple(display: &mut cardputer_bsc_nostd::display::Display<'_>, apple: (i32, i32)) {
|
||||||
|
draw_tile(
|
||||||
|
display,
|
||||||
|
apple.0,
|
||||||
|
apple.1,
|
||||||
|
embedded_graphics::pixelcolor::Rgb565::GREEN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_snake<const N: usize>(
|
||||||
|
display: &mut cardputer_bsc_nostd::display::Display<'_>,
|
||||||
|
snake_tiles: &heapless::Deque<(i32, i32), N>,
|
||||||
|
) {
|
||||||
|
for &tile in snake_tiles {
|
||||||
|
draw_tile(
|
||||||
|
display,
|
||||||
|
tile.0,
|
||||||
|
tile.1,
|
||||||
|
embedded_graphics::pixelcolor::Rgb565::RED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_screen(
|
||||||
|
display: &mut cardputer_bsc_nostd::display::Display<'_>,
|
||||||
|
colour: embedded_graphics::pixelcolor::Rgb565,
|
||||||
|
) {
|
||||||
|
display
|
||||||
|
.set_pixels(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
cardputer_bsc_nostd::display::DISPLAY_SIZE_WIDTH - 1,
|
||||||
|
cardputer_bsc_nostd::display::DISPLAY_SIZE_HEIGHT - 1,
|
||||||
|
core::iter::repeat_n(
|
||||||
|
colour,
|
||||||
|
cardputer_bsc_nostd::display::DISPLAY_SIZE_WIDTH as usize
|
||||||
|
* cardputer_bsc_nostd::display::DISPLAY_SIZE_HEIGHT as usize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_tile(
|
||||||
|
display: &mut cardputer_bsc_nostd::display::Display<'_>,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
colour: embedded_graphics::pixelcolor::Rgb565,
|
||||||
|
) {
|
||||||
|
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,
|
||||||
|
core::iter::repeat_n(colour, (GRID_SQUARE_SIZE as usize).pow(2)),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn game_over<const N: usize>(
|
||||||
|
display: &mut cardputer_bsc_nostd::display::Display<'_>,
|
||||||
|
rng: &mut esp_hal::rng::Rng,
|
||||||
|
direction: &mut (i32, i32),
|
||||||
|
snake_tiles: &mut heapless::Deque<(i32, i32), N>,
|
||||||
|
apple: &mut (i32, i32),
|
||||||
|
) {
|
||||||
|
for x in 0..GRID_WIDTH {
|
||||||
|
for y in 0..GRID_HEIGHT {
|
||||||
|
draw_tile(
|
||||||
|
display,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
embedded_graphics::pixelcolor::Rgb565::new(31, 63, 31),
|
||||||
|
);
|
||||||
|
esp_hal::delay::Delay::new().delay_millis(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_hal::delay::Delay::new().delay_millis(1000);
|
||||||
|
clear_screen(display, embedded_graphics::pixelcolor::Rgb565::BLACK);
|
||||||
|
snake_tiles.clear();
|
||||||
|
*direction = (1, 0);
|
||||||
|
starting_snake(snake_tiles);
|
||||||
|
*apple = random_tile(rng);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue