Files
basic1/games/lua_examples/2048.lua
Adolfo Reyna fa7d2fd32f Scale games on both axes and 2x text size
All 4 games (simon_says, tic_tac_toe, memory_match, 2048):
- Changed sizing to use min(width, height) instead of just width
- Grids now scale proportionally on both axes
- All UI text now uses text_scale=2 for better visibility
- Games fill more of the screen properly on portrait mode

Fixes:
- Simon Says buttons now square grid on any orientation
- Tic-Tac-Toe grid centered vertically using full screen height
- Memory Match cards use smallest dimension for square sizing
- 2048 tiles scale and center on both axes
- All text (scores, menus, game over) now 2x larger
2026-02-12 21:23:34 -05:00

399 lines
11 KiB
Lua

-- NAME: 2048
-- DESC: Merge tiles to reach 2048
-- Game states
local STATE_MENU = 0
local STATE_PLAYING = 1
local STATE_GAME_OVER = 2
local STATE_WIN = 3
-- Game constants
local GRID_SIZE = 4
-- Calculate tile size based on screen size
local function get_tile_size()
-- Use smallest dimension for square grid
local min_dim = math.min(game.width(), game.height())
local padding = math.floor(min_dim / 8)
local available = min_dim - (padding * 2)
local tile_size = math.floor(available / GRID_SIZE)
return tile_size
end
local function get_grid_start_x()
local tile_size = get_tile_size()
local grid_width = tile_size * GRID_SIZE
return math.floor((game.width() - grid_width) / 2)
end
local function get_grid_start_y()
local tile_size = get_tile_size()
local grid_height = tile_size * GRID_SIZE
return math.floor((game.height() - grid_height) / 2)
end
function init()
game.vars.state = STATE_MENU
game.vars.score = 0
-- Grid (4x4, 0 = empty)
game.vars.grid = {}
for y = 1, GRID_SIZE do
game.vars.grid[y] = {}
for x = 1, GRID_SIZE do
game.vars.grid[y][x] = 0
end
end
game.vars.moved = false
game.vars.won = false
-- Enable continuous updates
game.set_frame_updates(true)
print("2048 initialized")
end
function update(event)
local state = game.vars.state
if state == STATE_MENU then
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
reset_game()
game.vars.state = STATE_PLAYING
return true
end
elseif state == STATE_PLAYING then
if event.type == INPUT.TOUCH_DOWN then
-- Determine swipe direction
local direction = get_swipe_direction(event.x, event.y)
if direction then
game.vars.moved = false
move_tiles(direction)
if game.vars.moved then
spawn_tile()
-- Check win/lose
if has_tile(2048) then
if not game.vars.won then
game.vars.state = STATE_WIN
game.vars.won = true
end
end
if not can_move() then
game.vars.state = STATE_GAME_OVER
end
return true
end
end
end
elseif state == STATE_WIN then
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
-- Can continue playing or go to menu
if event.y < game.height() / 2 then
game.vars.state = STATE_PLAYING
else
game.vars.state = STATE_MENU
end
return true
end
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
function draw()
renderer.clear(false)
local state = game.vars.state
local tile_size = get_tile_size()
local start_x = get_grid_start_x()
local start_y = get_grid_start_y()
if state == STATE_MENU then
renderer.text(game.width() / 2 - 15, game.height() / 2 - 30, "2048", true, 2)
renderer.text(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true, 2)
elseif state == STATE_PLAYING or state == STATE_WIN or state == STATE_GAME_OVER then
-- Draw grid
for y = 1, GRID_SIZE do
for x = 1, GRID_SIZE do
local tile_x = start_x + (x - 1) * tile_size
local tile_y = start_y + (y - 1) * tile_size
local value = game.vars.grid[y][x]
if value == 0 then
-- Empty tile
renderer.rect(tile_x, tile_y, tile_size, tile_size, true, false)
else
-- Filled tile
renderer.rect(tile_x, tile_y, tile_size, tile_size, true, true)
-- Draw value (simplified)
local text = tostring(value)
if string.len(text) <= 2 then
renderer.text(tile_x + 2, tile_y + 2, text, false, 2)
else
renderer.text(tile_x + 1, tile_y + 2, text, false, 2)
end
end
end
end
-- Draw score
renderer.text(10, 10, "Score: " .. tostring(game.vars.score), true, 2)
if state == STATE_WIN then
renderer.text(game.width() / 2 - 30, game.height() / 2 - 20, "YOU WIN!", true, 2)
renderer.text(game.width() / 2 - 60, game.height() / 2, "Tap up: Continue", true, 2)
renderer.text(game.width() / 2 - 50, game.height() / 2 + 15, "Tap down: Menu", true, 2)
end
if state == STATE_GAME_OVER then
renderer.text(game.width() / 2 - 40, game.height() / 2, "GAME OVER", true, 2)
renderer.text(game.width() / 2 - 30, game.height() / 2 + 15, "Score: " .. tostring(game.vars.score), true, 2)
renderer.text(game.width() / 2 - 60, game.height() / 2 + 30, "Tap to Menu", true, 2)
end
end
end
function get_swipe_direction(x, y)
-- Simple tap-based direction (could be enhanced with swipe tracking)
local mid_x = game.width() / 2
local mid_y = game.height() / 2
local dx = x - mid_x
local dy = y - mid_y
if math.abs(dx) > math.abs(dy) then
if dx > 0 then return "right" end
return "left"
else
if dy > 0 then return "down" end
return "up"
end
end
function move_tiles(direction)
local old_grid = {}
for y = 1, GRID_SIZE do
old_grid[y] = {}
for x = 1, GRID_SIZE do
old_grid[y][x] = game.vars.grid[y][x]
end
end
if direction == "left" then
move_left()
elseif direction == "right" then
move_right()
elseif direction == "up" then
move_up()
elseif direction == "down" then
move_down()
end
-- Check if grid changed
for y = 1, GRID_SIZE do
for x = 1, GRID_SIZE do
if old_grid[y][x] ~= game.vars.grid[y][x] then
game.vars.moved = true
return
end
end
end
end
function move_left()
for y = 1, GRID_SIZE do
-- Compact
local row = {}
for x = 1, GRID_SIZE do
if game.vars.grid[y][x] ~= 0 then
table.insert(row, game.vars.grid[y][x])
end
end
-- Merge
local i = 1
while i < #row do
if row[i] == row[i + 1] then
row[i] = row[i] * 2
game.vars.score = game.vars.score + row[i]
table.remove(row, i + 1)
end
i = i + 1
end
-- Fill back
for x = 1, GRID_SIZE do
if x <= #row then
game.vars.grid[y][x] = row[x]
else
game.vars.grid[y][x] = 0
end
end
end
end
function move_right()
for y = 1, GRID_SIZE do
local row = {}
for x = GRID_SIZE, 1, -1 do
if game.vars.grid[y][x] ~= 0 then
table.insert(row, game.vars.grid[y][x])
end
end
local i = 1
while i < #row do
if row[i] == row[i + 1] then
row[i] = row[i] * 2
game.vars.score = game.vars.score + row[i]
table.remove(row, i + 1)
end
i = i + 1
end
for x = GRID_SIZE, 1, -1 do
if GRID_SIZE - x + 1 <= #row then
game.vars.grid[y][x] = row[GRID_SIZE - x + 1]
else
game.vars.grid[y][x] = 0
end
end
end
end
function move_up()
for x = 1, GRID_SIZE do
local col = {}
for y = 1, GRID_SIZE do
if game.vars.grid[y][x] ~= 0 then
table.insert(col, game.vars.grid[y][x])
end
end
local i = 1
while i < #col do
if col[i] == col[i + 1] then
col[i] = col[i] * 2
game.vars.score = game.vars.score + col[i]
table.remove(col, i + 1)
end
i = i + 1
end
for y = 1, GRID_SIZE do
if y <= #col then
game.vars.grid[y][x] = col[y]
else
game.vars.grid[y][x] = 0
end
end
end
end
function move_down()
for x = 1, GRID_SIZE do
local col = {}
for y = GRID_SIZE, 1, -1 do
if game.vars.grid[y][x] ~= 0 then
table.insert(col, game.vars.grid[y][x])
end
end
local i = 1
while i < #col do
if col[i] == col[i + 1] then
col[i] = col[i] * 2
game.vars.score = game.vars.score + col[i]
table.remove(col, i + 1)
end
i = i + 1
end
for y = GRID_SIZE, 1, -1 do
if GRID_SIZE - y + 1 <= #col then
game.vars.grid[y][x] = col[GRID_SIZE - y + 1]
else
game.vars.grid[y][x] = 0
end
end
end
end
function spawn_tile()
local empty = {}
for y = 1, GRID_SIZE do
for x = 1, GRID_SIZE do
if game.vars.grid[y][x] == 0 then
table.insert(empty, {x = x, y = y})
end
end
end
if #empty > 0 then
local pos = empty[math.random(1, #empty)]
game.vars.grid[pos.y][pos.x] = math.random() > 0.9 and 4 or 2
end
end
function has_tile(value)
for y = 1, GRID_SIZE do
for x = 1, GRID_SIZE do
if game.vars.grid[y][x] == value then
return true
end
end
end
return false
end
function can_move()
-- Check if any empty tiles
for y = 1, GRID_SIZE do
for x = 1, GRID_SIZE do
if game.vars.grid[y][x] == 0 then
return true
end
end
end
-- Check if any merges possible
for y = 1, GRID_SIZE do
for x = 1, GRID_SIZE do
local val = game.vars.grid[y][x]
if x < GRID_SIZE and game.vars.grid[y][x + 1] == val then return true end
if y < GRID_SIZE and game.vars.grid[y + 1][x] == val then return true end
end
end
return false
end
function reset_game()
game.vars.score = 0
game.vars.won = false
for y = 1, GRID_SIZE do
for x = 1, GRID_SIZE do
game.vars.grid[y][x] = 0
end
end
spawn_tile()
spawn_tile()
end