-- NAME: Memory Match -- DESC: Find matching pairs -- Game states local STATE_MENU = 0 local STATE_PLAYING = 1 local STATE_GAME_OVER = 2 -- Game constants local GRID_COLS = 4 local GRID_ROWS = 4 local FLIP_DURATION = 15 -- Frames to show flip animation -- Calculate card size based on screen size local function get_card_size() -- Use available space with padding, based on smallest dimension local min_dim = math.min(game.width(), game.height()) local padding = math.floor(min_dim / 8) local available_w = game.width() - (padding * 2) local available_h = game.height() - 80 -- Leave room for text -- Calculate based on grid local card_width = math.floor(available_w / GRID_COLS) local card_height = math.floor(available_h / GRID_ROWS) -- Use the smaller dimension for square cards return math.min(card_width, card_height) end local function get_grid_start_x() local card_size = get_card_size() local grid_width = card_size * GRID_COLS return math.floor((game.width() - grid_width) / 2) end local function get_grid_start_y() return 60 end function init() game.vars.state = STATE_MENU game.vars.score = 0 game.vars.moves = 0 -- Card grid game.vars.cards = {} -- {id, face_up, matched} game.vars.selected = {} -- Indices of selected cards game.vars.flip_frame = 0 game.vars.waiting = false -- Waiting to flip back incorrect pair -- Enable continuous updates game.set_frame_updates(true) print("Memory Match 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 -- Handle card selection if event.type == INPUT.TOUCH_DOWN and not game.vars.waiting then local card_idx = get_card_at(event.x, event.y) if card_idx and not game.vars.cards[card_idx].matched and not game.vars.cards[card_idx].face_up then -- Select card game.vars.cards[card_idx].face_up = true table.insert(game.vars.selected, card_idx) if #game.vars.selected == 2 then game.vars.moves = game.vars.moves + 1 -- Check for match if game.vars.cards[game.vars.selected[1]].id == game.vars.cards[game.vars.selected[2]].id then -- Match! game.vars.cards[game.vars.selected[1]].matched = true game.vars.cards[game.vars.selected[2]].matched = true game.vars.score = game.vars.score + 1 game.vars.selected = {} -- Check win if game.vars.score == (GRID_COLS * GRID_ROWS) / 2 then game.vars.state = STATE_GAME_OVER end else -- No match, wait then flip back game.vars.waiting = true game.vars.flip_frame = 0 end end return true end end -- Handle flip-back animation if game.vars.waiting and event.type == INPUT.FRAME_TICK then game.vars.flip_frame = game.vars.flip_frame + 1 if game.vars.flip_frame >= FLIP_DURATION then -- Flip cards back game.vars.cards[game.vars.selected[1]].face_up = false game.vars.cards[game.vars.selected[2]].face_up = false game.vars.selected = {} game.vars.waiting = false game.vars.flip_frame = 0 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) -- Black background local state = game.vars.state local card_size = get_card_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 - 40, game.height() / 2 - 40, "MEMORY MATCH", true, 2) renderer.text(game.width() / 2 - 50, game.height() / 2 - 10, "Find all pairs", true, 2) renderer.text(game.width() / 2 - 50, game.height() / 2 + 20, "Tap to Start", true, 2) elseif state == STATE_PLAYING or state == STATE_GAME_OVER then -- Draw grid for row = 0, GRID_ROWS - 1 do for col = 0, GRID_COLS - 1 do local idx = row * GRID_COLS + col + 1 local card = game.vars.cards[idx] local x = start_x + col * card_size local y = start_y + row * card_size if card.face_up or card.matched then -- Show card value renderer.rect(x, y, card_size, card_size, true, true) local text = tostring(card.id) renderer.text(x + math.floor(card_size / 2 - 2), y + math.floor(card_size / 2 - 2), text, false, 2) else -- Face down (outline) renderer.rect(x, y, card_size, card_size, true, false) end end end -- Draw stats renderer.text(10, 10, "Pairs: " .. tostring(game.vars.score) .. "/" .. tostring((GRID_COLS * GRID_ROWS) / 2), true, 2) renderer.text(10, 25, "Moves: " .. tostring(game.vars.moves), true, 2) if state == STATE_GAME_OVER then renderer.text(game.width() / 2 - 40, 5, "YOU WIN!", true, 2) renderer.text(game.width() / 2 - 50, game.height() - 20, "Tap to Menu", true, 2) end end end function get_card_at(x, y) local card_size = get_card_size() local start_x = get_grid_start_x() local start_y = get_grid_start_y() for row = 0, GRID_ROWS - 1 do for col = 0, GRID_COLS - 1 do local card_x = start_x + col * card_size local card_y = start_y + row * card_size if x >= card_x and x < card_x + card_size and y >= card_y and y < card_y + card_size then return row * GRID_COLS + col + 1 end end end return nil end function reset_game() game.vars.score = 0 game.vars.moves = 0 game.vars.selected = {} game.vars.flip_frame = 0 game.vars.waiting = false -- Create shuffled card pairs local card_ids = {} local num_pairs = (GRID_COLS * GRID_ROWS) / 2 for i = 1, num_pairs do table.insert(card_ids, i) table.insert(card_ids, i) end -- Shuffle for i = #card_ids, 2, -1 do local j = math.random(1, i) card_ids[i], card_ids[j] = card_ids[j], card_ids[i] end -- Create card objects game.vars.cards = {} for i = 1, #card_ids do game.vars.cards[i] = { id = card_ids[i], face_up = false, matched = false } end end