01-08-2011, 04:17 AM
Remixed the slide puzzle into something more closely resembling a jigsaw puzzle, giving a little different gameplay, and making the code a little lighter and less complex...scrambling is less restricted, there's no swapping of images, and images don't have to be scaled to preset dimensions.
It still needs work. Some images, particularly GIFs, don't tile properly, and I had to be cautious about how scaling is done so images with prime numbers as height or width don't create a billion tiles.
The scaling especially needs work. Ideally I'd like to not scale the image at all and just scale the tiles to fit the image, but there seems to be a problem with using floating point numbers in scaling and/or tiling. So for now it scales the image only enough to fit within a grid determined by the size of the tiles. It still results in some crunchy pixels.
I'd tried cropping the image instead of scaling, by using fl_read_image, but it seems I've completely forgotten how that works.
It still needs work. Some images, particularly GIFs, don't tile properly, and I had to be cautious about how scaling is done so images with prime numbers as height or width don't create a billion tiles.
The scaling especially needs work. Ideally I'd like to not scale the image at all and just scale the tiles to fit the image, but there seems to be a problem with using floating point numbers in scaling and/or tiling. So for now it scales the image only enough to fit within a grid determined by the size of the tiles. It still results in some crunchy pixels.
I'd tried cropping the image instead of scaling, by using fl_read_image, but it seems I've completely forgotten how that works.
Code:
old stuff removed
new stuff...
Code:
#!/usr/bin/env murgaLua
-- jigsaw-like puzzle for MurgaLua 0.6.4+
-- 2011 mikshaw
ts = 64 --tile size
function err(e,r)
if fltk then fltk.fl_alert(e) else print(e) end
os.exit(r)
end
if not Fl_Image.getTiles then
err("This program requires murgaLua 0.6.4 or newer.\n\n"..
"http://www.murga-projects.com/murgaLua/index.html", 1)
end
fileName = arg[1] -- if filename given as cmdline argument
-- browse for file
fltk.fl_register_images() -- do this early so chooser can show preview
if not fileName then
fileName = fltk.fl_file_chooser("Choose an RGB image", "Image Files (*.{jpg,png,bmp,xbm,xpm,gif})", nil, nil)
end
if not fileName then
err("Please supply an image filename.\nUse: "..
fltk.fl_filename_name(arg[0]).." \"filename\"\n"..
"or select an image with the file chooser." , 2)
end
-- check image
img = Fl_Shared_Image.get(fileName)
if not img then err("Could not open "..fileName, 3) end
iw = img:w(); ih = img:h()
-- try to find a balance between image size and tile size
if iw % ts == 0 and ih % ts == 0 then
tw, th = ts, ts
cols = iw / ts
rows = ih / ts
else
cols = math.floor(iw / ts)
rows = math.floor(ih / ts)
tw = math.floor(iw / cols)
th = math.floor(ih / rows)
iw = cols * tw
ih = rows * th
end
function set_tile_images()
-- take a crop of the box image
win:make_current()
imageString = fltk.fl_read_image(0, 0, iw, ih, 0)
image = fltk:Fl_RGB_Image(imageString, iw, ih, 3)
-- tile it up
imgtiles = image:getTiles(tw,th)
for i,_ in pairs(tile) do
tile[i]:image(imgtiles[i+1])
end
box:image(image)
box:hide()
end
function scramble()
math.randomseed(os.time())
for scram = 1, 10000 do
local a = math.random(0, rows * cols - 1)
tile_A_x = tile[a]:x(); tile_A_y = tile[a]:y()
local b = math.random(0, rows * cols - 1)
tile[a]:position(tile[b]:x(), tile[b]:y())
tile[b]:position(tile_A_x, tile_A_y)
end
end
function secondsToClock(sSeconds)
-- TJ_Tigger from http://www.indigorose.com/forums/archive/index.php/t-14669.html
local nSeconds = tonumber(sSeconds)
if nSeconds == 0 then
return "00:00:00";
else
nHours = string.format("%02.f", math.floor(nSeconds/3600));
nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60)));
nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60));
return nHours..":"..nMins..":"..nSecs
end
end
function drag_loop()
if inplay == 1 then
drag_box:position(Fl:event_x() - tw / 2, Fl:event_y() - th / 2)
win:redraw()
end
drag_timer:doWait(.02)
end
function isitfinish()
for i = 0, rows * cols - 1 do
if tile[i]:x() ~= pos[i].col or tile[i]:y() ~= pos[i].row then
return end
end
for i,_ in pairs(tile) do
tile[i]:hide()
end
preview:hide()
box:show()
if not seconds then
seconds = os.time() - starttime
fltk.fl_message("time: "..secondsToClock(seconds).."\n"..moves.." moves")
end
end
function move_tile(t)
if inplay == 0 then
if not starttime then starttime = os.time() end
inplay = 1
drag_box:show()
from_t, from_x, from_y = t, t:x(), t:y()
drag_box:image(t:image())
t:labeltype(fltk.FL_NO_LABEL)
t:box(fltk.FL_DOWN_BOX)
else
inplay = 0
from_t:labeltype(fltk.FL_NORMAL_LABEL)
from_t:box(fltk.FL_NO_BOX)
if from_t:x() ~= t:x() or from_t:y() ~= t:y() then
from_t:position(t:x(),t:y())
t:position(from_x, from_y)
moves = moves + 1
end
drag_box:hide()
win:redraw()
isitfinish()
end
end
fs, moves, starttime, seconds = 0, 0, nil, nil
Fl:visible_focus(0)
timer = murgaLua.createFltkTimer()
timer:callback(set_tile_images)
win = fltk:Fl_Double_Window(iw,ih, fltk.fl_filename_name(fileName))
tile = {}; pos = {}
row = 0; col = 0
-- -1 is used because the number of tiles starts at 1
-- but table starts at 0 (easier to position them from zero)
for i = 0, rows * cols - 1 do
tile[i] = fltk:Fl_Button(col,row,tw,th)
tile[i]:box(fltk.FL_NO_BOX)
tile[i]:align(80)
tile[i]:callback(move_tile)
pos[i]={col=col, row=row}
col = col + tw
-- start the next row
if col == iw then col = 0; row = row + th end
end
drag_box = fltk:Fl_Box(0,0,tw,th)
drag_box:hide()
drag_timer = murgaLua.createFltkTimer()
drag_timer:callback(drag_loop)
drag_timer:do_callback()
inplay = 0
preview = fltk:Fl_Button(-10,-10,1,1)
preview:shortcut("p")
preview:callback(
function()
if box:visible() == 1 then box:hide() else box:show() end
end
)
if iw <= Fl:w() and ih <= Fl:h() then
fullscr = fltk:Fl_Button(-10,-10,1,1)
fullscr:shortcut("f")
fullscr:callback(
function()
if fs == 0 then
fs = 1
wx, wy = win:x(), win:y()
win:fullscreen()
else
fs = 0
win:resize(wx,wy,iw,ih)
end
end
)
end
box = fltk:Fl_Box(0,0,iw,ih)
box:image(img)
win:show()
timer:doWait(1)
scramble()
Fl:run()