-- 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