Files
wadamesh/deploy/apps/snake/1.0/snake.lua
T
Kaj SchittecatandClaude Opus 4.8 14d5b06560 lua: Phase 3 — wada.mesh + wada.net, RF Monitor + Airtime as catalog apps, IDF wiring
wada.mesh: read-only contacts/rx_log/stats/self via thin UITask bridges
(no mesh types cross into the host TU). wada.net.http_get: async on the
existing net worker, http-only, 32 KB cap, one in flight per app, callback
delivered on the UI thread under the guarded pcall. RF Monitor and Airtime
rewritten as Lua catalog apps (live on the VPS with snake); their native
versions deliberately stay compiled in for beta_59 until the ports are
field-proven. lib/lua joined both IDF component builds (P4 verified on
RISC-V). Catalog source tracked at deploy/apps/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 13:18:12 +02:00

122 lines
3.2 KiB
Lua

-- Snake — wada.* reference app. Swipe (or trackball) to steer; eat the red
-- food, don't bite yourself. Tap after a game over to play again.
local ui, sys, store, timer = wada.ui, wada.sys, wada.store, wada.timer
local C = ui.colors
local CELL = 14
local app = {}
local cv, score_lbl
local cols, rows, px_w, px_h
local body, len, dx, dy, ndx, ndy, fx, fy
local score, hiscore, running, over
local function place_food()
repeat
fx, fy = sys.random(0, cols - 1), sys.random(0, rows - 1)
local clash = false
for i = 1, len do
if body[i].x == fx and body[i].y == fy then clash = true break end
end
until not clash
end
local function reset()
len, score, over, running = 3, 0, false, false
dx, dy, ndx, ndy = 1, 0, 1, 0
body = {}
local cx, cy = math.floor(cols / 2), math.floor(rows / 2)
for i = 1, len do body[i] = { x = cx - i + 1, y = cy } end
place_food()
end
local function scoreline()
score_lbl:set(string.format("Score %d Best %d%s", score, hiscore,
running and "" or (over and " - tap to retry" or " - swipe to start")))
end
local function draw()
cv:fill(0x101418)
cv:rect(fx * CELL + 2, fy * CELL + 2, CELL - 4, CELL - 4, C.bad, true, 4)
for i = len, 1, -1 do
cv:rect(body[i].x * CELL + 1, body[i].y * CELL + 1, CELL - 2, CELL - 2,
i == 1 and C.accent or C.good, true, 3)
end
if over then
cv:text(math.floor(px_w / 2) - 40, math.floor(px_h / 2) - 10, "GAME OVER", C.text, 16)
end
end
local function step()
dx, dy = ndx, ndy
local head = body[1]
local nx, ny = head.x + dx, head.y + dy
if nx < 0 or ny < 0 or nx >= cols or ny >= rows then over = true end
if not over then
for i = 1, len - 1 do
if body[i].x == nx and body[i].y == ny then over = true break end
end
end
if over then
running = false
if score > hiscore then
hiscore = score
store.set("hiscore", hiscore)
sys.toast("New high score: " .. hiscore, 1600)
end
scoreline()
draw()
return
end
table.insert(body, 1, { x = nx, y = ny })
if nx == fx and ny == fy then
len = len + 1
score = score + 10
place_food()
scoreline()
else
body[#body] = nil
end
draw()
end
function app.on_open(w, h)
hiscore = store.get("hiscore", 0)
cols = math.floor(w / CELL)
rows = math.floor((h - 26) / CELL)
px_w, px_h = cols * CELL, rows * CELL
score_lbl = ui.label("", 4, 4, 14, C.text)
cv = ui.canvas(px_w, px_h)
cv:pos(math.floor((w - px_w) / 2), 26)
reset()
scoreline()
draw()
timer.every(120)
end
function app.on_input(ev)
if ev.type == "swipe" then
local d = ev.dir
if d == "up" and dy ~= 1 then ndx, ndy = 0, -1
elseif d == "down" and dy ~= -1 then ndx, ndy = 0, 1
elseif d == "left" and dx ~= 1 then ndx, ndy = -1, 0
elseif d == "right" and dx ~= -1 then ndx, ndy = 1, 0
end
if not running and not over then running = true; scoreline() end
elseif ev.type == "down" and over then
reset()
scoreline()
draw()
end
end
function app.on_tick(dt)
if running and not over then step() end
end
function app.on_close()
-- high score is already persisted on set; nothing to tear down (the host
-- frees every widget and the canvas with the app)
end
return app