Skip to content

Instantly share code, notes, and snippets.

@randrews
Created July 3, 2011 03:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save randrews/1061926 to your computer and use it in GitHub Desktop.
Save randrews/1061926 to your computer and use it in GitHub Desktop.
Final Lua puzzle game
function make_board(size)
local board = { size = size }
setmetatable(board, { __tostring = board_tostring })
for n = 0, size * size - 1 do
board[n] = 0
end
return board
end
function populate_board(board, filled, seed)
local size = board.size
if seed then math.randomseed(seed) end
filled = filled or size * size * 3 / 4
local function rand()
local c
repeat c = math.random(size * size) - 1 until board[c] == 0
return c
end
if filled > 0 then
for _,v in ipairs{'a','b','c','d'} do board[rand()] = v end
for n = 1, filled-4 do
board[rand()] = math.random(4)
end
return fall(board)
end
end
function board_tostring(board)
local lines = {}
local size = board.size
for y = 0, size - 1 do
local line = "|"
for x = 0, size - 1 do
line = line .. " " .. board[x+y*size]
end
table.insert(lines, line .. " |")
end
return table.concat(lines,"\n")
end
function fall(board)
local size = board.size
local new_board = make_board(size, 0)
local function fall_column(col)
local dest = size - 1
for y = size-1, 0, -1 do
if board[y*size + col] ~= 0 then
new_board[dest*size + col] = board[y*size + col]
dest = dest - 1
end
end
end
for x=0, size-1 do
fall_column(x)
end
return new_board
end
function rotate(board)
local size = board.size
local new_board = make_board(size, 0)
for y = 0, size-1 do
local dest_col = size - 1 - y
for n = 0, size-1 do
new_board[n*size + dest_col] = board[y*size + n]
end
end
return new_board
end
function crush(board)
local size = board.size
local new_board = make_board(size, 0)
local crushers = {'a','b','c','d'}
for n=0, size-1 do
new_board[n] = board[n]
end
for n = size, size*size - 1 do
if board[n-size] == crushers[board[n]] then
new_board[n] = 0
else
new_board[n] = board[n]
end
end
return new_board
end
function should_fall(board)
local size = board.size
local unsupported = make_board(size, 0)
local any_falling = false
local function heights(col)
local seen_empty = false
for y=size-1, 0, -1 do
if board[col + y*size] == 0 then seen_empty = true
else
unsupported[col+y*size] = seen_empty
any_falling = any_falling or seen_empty
end
end
end
for x=0, size-1 do heights(x) end
return any_falling, unsupported
end
function fall_one(board)
local should = select(2, should_fall(board))
local size = board.size
local new_board = make_board(size, 0)
for n = size*size-1, 0, -1 do
if board[n] ~= 0 and should[n] then
new_board[n+size] = board[n]
else
new_board[n] = board[n]
end
end
return new_board
end
function remove_crushers(board)
local crushers = {a=-1, b=-1, c=-1, d=-1}
local seen = {false, false, false, false}
local size = board.size
local new_board = make_board(size)
for n = 0, size * size - 1 do
local v = board[n]
if crushers[v] then crushers[v] = n
elseif v > 0 then seen[v] = true end
new_board[n] = v
end
local i = 1
for crusher, location in pairs(crushers) do
if location >= 0 and not seen[i] then
new_board[location] = 0
end
i = i + 1
end
return new_board
end
function game()
local board = populate_board(make_board(8))
local line = nil
repeat
print(board)
line = io.read()
local valid = false
if line == 'r' then
valid, board = true, rotate(board)
elseif line == 'l' then
valid, board = true, rotate(rotate(rotate(board)))
elseif line == 'exit' then
valid = true
end
if valid then
board = fall(crush(fall(board)))
else
print "Didn't understand that. Type 'l', 'r', or 'exit'."
end
until line == 'exit'
end
require 'fall'
pi = math.pi
animations = {}
game, state = nil
function love.load()
love.graphics.setBackgroundColor(82,82,128)
math.randomseed(love.timer.getMicroTime())
board = populate_board(make_board(8))
img = love.graphics.newImage("arrow_blocks.png")
tiles = {}
local tile_dims = {48, 48, 48*4, 48*2} -- width, height, image dimensions
for k,v in pairs{'a','b','c','d'} do
tiles[k] = love.graphics.newQuad(48*(k-1), 0, unpack(tile_dims))
tiles[v] = love.graphics.newQuad(48*(k-1), 48, unpack(tile_dims))
end
game = game_state_machine()
state = game()
end
function love.draw()
if animations.angle then
rotate_board(animations.angle.value)
end
for n=0, board.size * board.size - 1 do
local x, y = n % board.size, math.floor(n / board.size)
if board[n] ~= 0 then
if animations.offset and animations.offset.falling[n] then
draw_tile(board[n], x, y, animations.offset.value)
else
draw_tile(board[n], x, y)
end
end
end
end
function draw_tile(tile, x, y, offset)
local g = love.graphics
local w, h = g.getWidth(), g.getHeight()
local left, top = w/2 - 48*board.size/2, h/2 - 48*board.size/2
offset = offset or 0
g.drawq(img, tiles[tile],
left + x*48,
top + y*48 + offset)
end
function rotate_board(angle)
local g = love.graphics
local w, h = g.getWidth(), g.getHeight()
local left, top = w/2 - 48*board.size/2, h/2 - 48*board.size/2
g.translate(w/2, h/2)
g.rotate(angle)
g.translate(-w/2,-h/2)
end
function love.mousepressed(x, y, btn)
if state == 'input' then state = game(btn) end
end
function game_state_machine()
local rules = coroutine.create(
function()
while true do
local btn = coroutine.yield('input')
if btn == 'l' then
animations.angle = tween(0, -pi/2, -6)
coroutine.yield('animation')
board = rotate(rotate(rotate(board)))
elseif btn == 'r' then
animations.angle = tween(0, pi/2, 6)
coroutine.yield('animation')
board = rotate(board)
end
local function fall_loop()
local fall_p, falling = should_fall(board)
while fall_p do
animations.offset = tween(0, 48, 16 * 48)
animations.offset.falling = falling
coroutine.yield 'animation'
board = fall_one(board)
fall_p, falling = should_fall(board)
end
end
fall_loop()
board = crush(board)
fall_loop()
-- board = remove_crushers(board)
-- fall_loop()
end
end)
return function(input)
return select(2, coroutine.resume(rules, input))
end
end
function love.update(dt)
local unfinished = {}
local any_left = false
for name, animation in pairs(animations) do
animation.update(dt)
if not animation.finished() then
unfinished[name] = animation
any_left = true
end
end
animations = unfinished
if not any_left and state == 'animation' then
state = game()
end
end
function tween(from, to, speed)
speed = speed or 4
if to < from and speed > 0 then speed = -speed end
local tbl = {value=from}
tbl.update = function(dt)
tbl.value = tbl.value + dt * speed
end
tbl.finished = function()
return speed > 0 and tbl.value >= to or
speed < 0 and tbl.value <= to
end
return tbl
end
function animate(from, to, speed)
local speed = speed or 4
if to < 0 then speed = -speed end
animation_value, current_animation = tween(from, to, speed, game_rules)
coroutine.yield()
current_animation = nil
_G[value] = from
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment