summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-10-26 11:04:14 -0400
committerMica White <botahamec@outlook.com>2026-10-26 11:04:14 -0400
commit2987e310e8e6eaeaa60546bb92a61d9c3713263a (patch)
tree530ff5abe1b584bc1a6da5babd328d71d1e1173c /src
Snake exampleHEADmain
Diffstat (limited to 'src')
-rw-r--r--src/exit_on_escape.rs10
-rw-r--r--src/main.rs51
-rw-r--r--src/move_snake.rs50
-rw-r--r--src/render.rs131
-rw-r--r--src/snake_controller.rs38
5 files changed, 280 insertions, 0 deletions
diff --git a/src/exit_on_escape.rs b/src/exit_on_escape.rs
new file mode 100644
index 0000000..a7885ca
--- /dev/null
+++ b/src/exit_on_escape.rs
@@ -0,0 +1,10 @@
+use tortuise_script::{input::KeyCode, ScriptContext, TortuiseScript};
+
+pub const EXIT_ON_ESCAPE: TortuiseScript = TortuiseScript {
+ update: |mut context: ScriptContext| {
+ if context.is_key_pressed_this_frame(KeyCode::Esc) {
+ context.exit();
+ }
+ },
+ ..TortuiseScript::empty()
+};
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..3b6605a
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,51 @@
+#![cfg_attr(target_arch = "wasm32", no_std)]
+#![cfg_attr(target_arch = "wasm32", no_main)]
+
+extern crate alloc;
+
+mod exit_on_escape;
+mod move_snake;
+mod render;
+mod snake_controller;
+
+use alloc::vec::Vec;
+use core::time::Duration;
+use tortuise_script::{entity::Entity, time::Instant, TortuiseScene};
+
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
+struct Position {
+ pub x: i16,
+ pub y: i16,
+}
+
+const GRID_SIZE: u16 = 16;
+
+static APPLE_LOCATION: Entity<Position> =
+ Entity::new("apple_location", |_| Position { x: 2, y: 2 });
+static SNAKE_CELLS: Entity<Vec<Position>> = Entity::new("snake_cells", |_| {
+ alloc::vec![Position { x: 10, y: 5 }, Position { x: 9, y: 5 }]
+});
+static DIRECTION: Entity<Position> = Entity::new("direction", |_| Position { x: 1, y: 0 });
+static NEXT_MOVE_TIME: Entity<Instant> = Entity::new("next_move_time", |cx| {
+ Instant::now(cx) + Duration::from_millis(200)
+});
+
+tortuise_script::game! {
+ name: "snake",
+ author: "botahamec",
+ tld: "dev",
+ first_scene: "main",
+ scenes: {
+ "main" => TortuiseScene {
+ scripts: &["snake_controller", "exit_on_escape", "move_snake", "render"],
+ }
+ },
+ scripts: {
+ "snake_controller" => snake_controller::SNAKE_CONTROLLER,
+ "exit_on_escape" => exit_on_escape::EXIT_ON_ESCAPE,
+ "move_snake" => move_snake::MOVE_SNAKE,
+ "render" => render::RENDER
+ }
+}
diff --git a/src/move_snake.rs b/src/move_snake.rs
new file mode 100644
index 0000000..453e795
--- /dev/null
+++ b/src/move_snake.rs
@@ -0,0 +1,50 @@
+use core::time::Duration;
+
+use alloc::vec::Vec;
+use rand::RngExt;
+use tortuise_script::{random::TortuiseRng, time::Instant, ScriptContext, TortuiseScript};
+
+use crate::{Position, APPLE_LOCATION, DIRECTION, GRID_SIZE, NEXT_MOVE_TIME, SNAKE_CELLS};
+
+pub const MOVE_SNAKE: TortuiseScript = TortuiseScript {
+ update: |context: ScriptContext| {
+ let mut apple_location = APPLE_LOCATION.get_mut(&context);
+ let mut snake_cells = SNAKE_CELLS.get_mut(&context);
+ let mut next_move = NEXT_MOVE_TIME.get_mut(&context);
+ let direction = DIRECTION.get(&context);
+
+ let mut available_squares =
+ Vec::with_capacity((GRID_SIZE * GRID_SIZE) as usize - snake_cells.len());
+ for i in 0..GRID_SIZE {
+ for j in 0..GRID_SIZE {
+ let position = Position {
+ x: i as i16,
+ y: j as i16,
+ };
+ if !snake_cells.contains(&position) {
+ available_squares.push(position);
+ }
+ }
+ }
+
+ let mut rng = TortuiseRng::new();
+ let time = Instant::now(&context);
+ if time > *next_move {
+ if snake_cells[0] == *apple_location {
+ *apple_location = available_squares[rng.random_range(0..available_squares.len())];
+ let new_head = Position {
+ x: snake_cells[0].x + direction.x,
+ y: snake_cells[0].y + direction.y,
+ };
+ snake_cells.insert(0, new_head);
+ }
+ *next_move += Duration::from_millis(200);
+ for i in (1..snake_cells.len()).rev() {
+ snake_cells[i] = snake_cells[i - 1];
+ }
+ snake_cells[0].x += direction.x;
+ snake_cells[0].y += direction.y;
+ }
+ },
+ ..TortuiseScript::empty()
+};
diff --git a/src/render.rs b/src/render.rs
new file mode 100644
index 0000000..80b06f7
--- /dev/null
+++ b/src/render.rs
@@ -0,0 +1,131 @@
+use alloc::string::ToString;
+use tortuise_script::{
+ buffer::{Cell, Color},
+ ScriptContext, TortuiseScript,
+};
+
+use crate::{APPLE_LOCATION, GRID_SIZE, SNAKE_CELLS};
+
+fn draw_box(cx: &mut ScriptContext, char: char, x: u16, y: u16) {
+ for i in 1..5 {
+ cx.add_cell(Cell {
+ character: '-',
+ x: x + i,
+ y,
+ ..Default::default()
+ });
+ cx.add_cell(Cell {
+ character: '-',
+ x: x + i,
+ y: y + 2,
+ ..Default::default()
+ });
+ }
+ cx.add_cell(Cell {
+ character: '|',
+ x,
+ y: y + 1,
+ ..Default::default()
+ });
+ cx.add_cell(Cell {
+ character: '|',
+ x: x + 5,
+ y: y + 1,
+ ..Default::default()
+ });
+ cx.add_cell(Cell {
+ character: char,
+ x: x + 3,
+ y: y + 1,
+ ..Default::default()
+ });
+}
+
+pub const RENDER: TortuiseScript = TortuiseScript {
+ update: |mut context: ScriptContext| {
+ let apple_location = APPLE_LOCATION.get(&context);
+ let snake_cells = SNAKE_CELLS.get(&context);
+ let window_size = context.window_size();
+ let offset_x = window_size.columns.saturating_sub(GRID_SIZE) / 2;
+ let offset_y = window_size.rows.saturating_sub(GRID_SIZE) / 2;
+
+ for i in 0..GRID_SIZE {
+ context.add_cell(Cell {
+ character: '|',
+ x: offset_x.saturating_sub(1),
+ y: offset_y + i,
+ z: 0,
+ ..Default::default()
+ });
+ context.add_cell(Cell {
+ character: '|',
+ x: offset_x + GRID_SIZE,
+ y: offset_y + i,
+ z: 0,
+ ..Default::default()
+ });
+ context.add_cell(Cell {
+ character: '-',
+ x: offset_x + i,
+ y: offset_y.saturating_sub(1),
+ z: 0,
+ ..Default::default()
+ });
+ context.add_cell(Cell {
+ character: '-',
+ x: offset_x + i,
+ y: offset_y + GRID_SIZE,
+ z: 0,
+ ..Default::default()
+ });
+ }
+
+ for (i, char) in "Score:".chars().enumerate() {
+ context.add_cell(Cell {
+ character: char,
+ x: offset_x + GRID_SIZE - 10 + i as u16,
+ y: offset_y.saturating_sub(2),
+ ..Default::default()
+ });
+ }
+
+ draw_box(&mut context, '<', 4, 4);
+ draw_box(&mut context, '>', 16, 4);
+ draw_box(&mut context, '^', 10, 1);
+ draw_box(&mut context, 'v', 10, 7);
+
+ for (i, char) in (snake_cells.len() - 2)
+ .to_string()
+ .chars()
+ .rev()
+ .enumerate()
+ {
+ context.add_cell(Cell {
+ character: char,
+ x: offset_x + GRID_SIZE - i as u16,
+ y: offset_y.saturating_sub(2),
+ ..Default::default()
+ });
+ }
+
+ context.add_cell(Cell {
+ character: '●',
+ foreground_color: Color::Red,
+ x: apple_location.x.unsigned_abs() + offset_x,
+ y: apple_location.y.unsigned_abs() + offset_y,
+ z: 0,
+ ..Default::default()
+ });
+ for cell in &*snake_cells {
+ context.add_cell(Cell {
+ character: '█',
+ foreground_color: Color::Green,
+ x: cell.x.unsigned_abs() + offset_x,
+ y: cell.y.unsigned_abs() + offset_y,
+ z: 1,
+ ..Default::default()
+ });
+ }
+ },
+ ..TortuiseScript::empty()
+};
diff --git a/src/snake_controller.rs b/src/snake_controller.rs
new file mode 100644
index 0000000..0c48175
--- /dev/null
+++ b/src/snake_controller.rs
@@ -0,0 +1,38 @@
+use tortuise_script::{
+ input::{KeyCode, Position as MousePosition},
+ ScriptContext, TortuiseScript,
+};
+
+use crate::{Position, DIRECTION};
+
+fn is_inside_box(mouse: Option<MousePosition>, button: MousePosition, size: u16) -> bool {
+ mouse.is_some_and(|mouse| {
+ (mouse.x >= button.x && mouse.x < button.x + size * 2)
+ && (mouse.y >= button.y && mouse.y < button.y + size)
+ })
+}
+
+pub const SNAKE_CONTROLLER: TortuiseScript = TortuiseScript {
+ update: |context: ScriptContext| {
+ let mut direction = DIRECTION.get_mut(&context);
+ let mouse_location = context.mouse_location();
+ if is_inside_box(mouse_location, MousePosition { x: 4, y: 4 }, 3) {
+ *direction = Position { x: -1, y: 0 };
+ } else if is_inside_box(mouse_location, MousePosition { x: 16, y: 4 }, 3) {
+ *direction = Position { x: 1, y: 0 };
+ } else if is_inside_box(mouse_location, MousePosition { x: 10, y: 1 }, 3) {
+ *direction = Position { x: 0, y: -1 };
+ } else if is_inside_box(mouse_location, MousePosition { x: 10, y: 7 }, 3) {
+ *direction = Position { x: 0, y: 1 };
+ } else if context.is_key_pressed_this_frame(KeyCode::Up) {
+ *direction = Position { x: 0, y: -1 };
+ } else if context.is_key_pressed_this_frame(KeyCode::Right) {
+ *direction = Position { x: 1, y: 0 };
+ } else if context.is_key_pressed_this_frame(KeyCode::Left) {
+ *direction = Position { x: -1, y: 0 };
+ } else if context.is_key_pressed_this_frame(KeyCode::Down) {
+ *direction = Position { x: 0, y: 1 };
+ }
+ },
+ ..TortuiseScript::empty()
+};