Created
July 3, 2011 03:23
-
-
Save randrews/1061926 to your computer and use it in GitHub Desktop.
Final Lua puzzle game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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