Updated games: - pong.lua: All text now uses text_scaled with scale=2 - air_hockey.lua: All text now uses text_scaled with scale=2 - asteroids.lua: All text now uses text_scaled with scale=2 - ball.lua: All text now uses text_scaled with scale=2 - breakout.lua: All text now uses text_scaled with scale=2 - counter.lua: All text now uses text_scaled with scale=2 - flappy_bird.lua: All text now uses text_scaled with scale=2 - lunar_lander.lua: All text now uses text_scaled with scale=2 - snake.lua: All text now uses text_scaled with scale=2 - tetris.lua: All text now uses text_scaled with scale=2 All 14 games now have consistent 2x text scaling for better readability.
218 lines
6.6 KiB
Lua
218 lines
6.6 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 = 20 -- Frames between moves (doubled from 10 for half speed)
|
|
|
|
-- 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_scaled(game.width(, 2) / 2 - 30, game.height() / 2 - 20, "SNAKE", true)
|
|
renderer.text_scaled(game.width(, 2) / 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_scaled(game.width(, 2) / 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 (convert center to integers)
|
|
renderer.circle(math.floor(game.vars.food_x * CELL_SIZE + CELL_SIZE / 2 + 0.5),
|
|
math.floor(game.vars.food_y * CELL_SIZE + CELL_SIZE / 2 + 0.5),
|
|
CELL_SIZE / 2, true, true)
|
|
|
|
-- Draw score
|
|
local score_text = "Score: " .. tostring(game.vars.score)
|
|
renderer.text_scaled(5, 5, score_text, true, 2)
|
|
|
|
-- Draw: GAME_OVER
|
|
elseif state == STATE_GAME_OVER then
|
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2 - 20, "GAME OVER", true)
|
|
|
|
local score_text = "Score: " .. tostring(game.vars.score)
|
|
renderer.text_scaled(game.width(, 2) / 2 - 40, game.height() / 2, score_text, true)
|
|
|
|
renderer.text_scaled(game.width(, 2) / 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) % GRID_W,
|
|
y = (head.y + game.vars.dir_y) % GRID_H
|
|
}
|
|
|
|
-- Wrap around negative values (Lua modulo behavior)
|
|
if new_head.x < 0 then new_head.x = new_head.x + GRID_W end
|
|
if new_head.y < 0 then new_head.y = new_head.y + GRID_H 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 (but keep it slower overall)
|
|
if game.vars.move_speed > 5 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
|