Files
basic1/emulator/games/lua_examples/snake.lua
T
Adolfo Reyna 38ffdac749 Add pagination to game launcher - 4 games per page
- GameLauncher now displays only 4 games per page to keep menu in bounds
- Added page navigation with page indicator (Page X/Y)
- KEY0 navigates between pages and within page
- KEY1 selects the highlighted game
- Touch selection works on current page only
- Helper methods: get_total_pages(), get_page_start_index(), get_page_end_index()
- Updated both lib/ and emulator/ versions for consistency
2026-02-12 20:39:38 -05:00

220 lines
6.4 KiB
Lua

-- NAME: Snake Game
-- DESC: Classic snake with state machine
-- Game states
local STATE_MENU = 0
local STATE_PLAYING = 1
local STATE_GAME_OVER = 2
-- Game constants
local CELL_SIZE = 10
local GRID_W = 40
local GRID_H = 28
-- Initialize game
function init()
game.vars.state = STATE_MENU
game.vars.score = 0
game.vars.high_score = 0
-- Snake as array of {x, y} positions
game.vars.snake = {}
game.vars.snake[1] = {x = 20, y = 14}
game.vars.snake[2] = {x = 19, y = 14}
game.vars.snake[3] = {x = 18, y = 14}
game.vars.dir_x = 1
game.vars.dir_y = 0
game.vars.food_x = 30
game.vars.food_y = 14
game.vars.frame_count = 0
game.vars.move_speed = 10 -- Frames between moves
-- Enable continuous frame updates
game.set_frame_updates(true)
print("Snake Game initialized")
end
-- Update game logic
function update(event)
local state = game.vars.state
-- State: MENU
if state == STATE_MENU then
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
game.vars.state = STATE_PLAYING
game.vars.score = 0
init_snake()
spawn_food()
return true
end
-- State: PLAYING
elseif state == STATE_PLAYING then
-- Handle input for direction change
if event.type == INPUT.TOUCH_DOWN then
local head = game.vars.snake[1]
local head_screen_x = head.x * CELL_SIZE
local head_screen_y = head.y * CELL_SIZE
local dx = event.x - head_screen_x
local dy = event.y - head_screen_y
-- Change direction based on touch relative to head
if math.abs(dx) > math.abs(dy) then
if dx > 0 and game.vars.dir_x ~= -1 then
game.vars.dir_x = 1
game.vars.dir_y = 0
elseif dx < 0 and game.vars.dir_x ~= 1 then
game.vars.dir_x = -1
game.vars.dir_y = 0
end
else
if dy > 0 and game.vars.dir_y ~= -1 then
game.vars.dir_x = 0
game.vars.dir_y = 1
elseif dy < 0 and game.vars.dir_y ~= 1 then
game.vars.dir_x = 0
game.vars.dir_y = -1
end
end
end
-- Move snake every N frames
game.vars.frame_count = game.vars.frame_count + 1
if game.vars.frame_count >= game.vars.move_speed then
game.vars.frame_count = 0
move_snake()
return true
end
-- State: GAME_OVER
elseif state == STATE_GAME_OVER then
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
game.vars.state = STATE_MENU
return true
end
end
return false
end
-- Draw game
function draw()
renderer.clear(false)
local state = game.vars.state
-- Draw: MENU
if state == STATE_MENU then
renderer.text(game.width() / 2 - 30, game.height() / 2 - 20, "SNAKE", true)
renderer.text(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true)
if game.vars.high_score > 0 then
local hs_text = "High: " .. tostring(game.vars.high_score)
renderer.text(game.width() / 2 - 30, game.height() / 2 + 20, hs_text, true)
end
-- Draw: PLAYING
elseif state == STATE_PLAYING then
-- Draw snake
for i = 1, #game.vars.snake do
local seg = game.vars.snake[i]
local filled = (i == 1) -- Head filled, body outline
renderer.rect(seg.x * CELL_SIZE, seg.y * CELL_SIZE, CELL_SIZE, CELL_SIZE, true, filled)
end
-- Draw food
renderer.circle(game.vars.food_x * CELL_SIZE + CELL_SIZE / 2,
game.vars.food_y * CELL_SIZE + CELL_SIZE / 2,
CELL_SIZE / 2, true, true)
-- Draw score
local score_text = "Score: " .. tostring(game.vars.score)
renderer.text(5, 5, score_text, true)
-- Draw: GAME_OVER
elseif state == STATE_GAME_OVER then
renderer.text(game.width() / 2 - 40, game.height() / 2 - 20, "GAME OVER", true)
local score_text = "Score: " .. tostring(game.vars.score)
renderer.text(game.width() / 2 - 40, game.height() / 2, score_text, true)
renderer.text(game.width() / 2 - 60, game.height() / 2 + 20, "Tap to Continue", true)
end
end
-- Helper: Initialize snake
function init_snake()
game.vars.snake = {}
game.vars.snake[1] = {x = 20, y = 14}
game.vars.snake[2] = {x = 19, y = 14}
game.vars.snake[3] = {x = 18, y = 14}
game.vars.dir_x = 1
game.vars.dir_y = 0
end
-- Helper: Spawn food at random position
function spawn_food()
game.vars.food_x = math.random(0, GRID_W - 1)
game.vars.food_y = math.random(0, GRID_H - 1)
end
-- Helper: Move snake
function move_snake()
local head = game.vars.snake[1]
local new_head = {
x = head.x + game.vars.dir_x,
y = head.y + game.vars.dir_y
}
-- Check wall collision
if new_head.x < 0 or new_head.x >= GRID_W or new_head.y < 0 or new_head.y >= GRID_H then
game_over()
return
end
-- Check self collision
for i = 1, #game.vars.snake do
local seg = game.vars.snake[i]
if new_head.x == seg.x and new_head.y == seg.y then
game_over()
return
end
end
-- Check food collision
local ate_food = false
if new_head.x == game.vars.food_x and new_head.y == game.vars.food_y then
ate_food = true
game.vars.score = game.vars.score + 10
spawn_food()
-- Increase speed slightly
if game.vars.move_speed > 3 then
game.vars.move_speed = game.vars.move_speed - 1
end
end
-- Move snake
table.insert(game.vars.snake, 1, new_head)
if not ate_food then
table.remove(game.vars.snake) -- Remove tail
end
end
-- Helper: Game over
function game_over()
game.vars.state = STATE_GAME_OVER
if game.vars.score > game.vars.high_score then
game.vars.high_score = game.vars.score
end
print("Game Over! Score: " .. game.vars.score)
end