Clickable buttons
This commit is contained in:
parent
9fd3208aaf
commit
977d2c342b
3
0hh1.p8
3
0hh1.p8
|
@ -2,6 +2,9 @@ pico-8 cartridge // http://www.pico-8.com
|
||||||
version 36
|
version 36
|
||||||
__lua__
|
__lua__
|
||||||
#include main.lua
|
#include main.lua
|
||||||
|
#include board.lua
|
||||||
|
#include states/menu.lua
|
||||||
|
#include states/game.lua
|
||||||
__gfx__
|
__gfx__
|
||||||
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
|
388
board.lua
Normal file
388
board.lua
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
local Board = {}
|
||||||
|
function Board.new()
|
||||||
|
local debug = false
|
||||||
|
local width = 10
|
||||||
|
local t = [[
|
||||||
|
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0
|
||||||
|
]]
|
||||||
|
a = {foo = 3}
|
||||||
|
local tiles = split(t)
|
||||||
|
|
||||||
|
return {
|
||||||
|
reset = function(self)
|
||||||
|
local t = [[
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0
|
||||||
|
]]
|
||||||
|
tiles = split(t)
|
||||||
|
end,
|
||||||
|
|
||||||
|
getTile = function(self, idx)
|
||||||
|
return tiles[idx]
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- returns a COPY of the tiles array
|
||||||
|
getTilesCopy = function(self)
|
||||||
|
return copy(tiles)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- overwrites the whole tiles array
|
||||||
|
setTiles = function(self, newtiles)
|
||||||
|
assert(#newtiles == #tiles, "New tiles array must have a length of " .. #tiles)
|
||||||
|
tiles = copy(newtiles)
|
||||||
|
end,
|
||||||
|
|
||||||
|
idx_xy = function(self, idx)
|
||||||
|
return idx_xy(idx, width)
|
||||||
|
end,
|
||||||
|
|
||||||
|
xy_idx = function(self, x, y)
|
||||||
|
return xy_idx(x, y, width)
|
||||||
|
end,
|
||||||
|
|
||||||
|
fill = function(self, idx, color, invert)
|
||||||
|
if invert then
|
||||||
|
color = color == YELLOW and BLUE or YELLOW
|
||||||
|
end
|
||||||
|
tiles[idx] = color
|
||||||
|
end,
|
||||||
|
|
||||||
|
getRows = function(self)
|
||||||
|
local ret = {}
|
||||||
|
for i = 1, width do
|
||||||
|
add(ret, slice(tiles, ((i - 1) * width) + 1, i * width))
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end,
|
||||||
|
|
||||||
|
getCols = function(self)
|
||||||
|
local ret = {}
|
||||||
|
local rows = self.getRows(self)
|
||||||
|
for i = 1, width do
|
||||||
|
add(ret, map(rows, function(v) return v[i] end))
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end,
|
||||||
|
|
||||||
|
isComplete = function(self)
|
||||||
|
return count(tiles, 0) == 0
|
||||||
|
end,
|
||||||
|
|
||||||
|
isValid = function(self)
|
||||||
|
local rows = self:getRows()
|
||||||
|
for y,row in ipairs(rows) do
|
||||||
|
-- check count
|
||||||
|
if count(row, BLUE) ~= count(row, YELLOW) then
|
||||||
|
if (debug) printh("uneven count on row "..y)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
-- check identical lines
|
||||||
|
for k,other in ipairs(rows) do
|
||||||
|
if equal(other, row) and other ~= row then
|
||||||
|
if (debug) printh("equal rows "..k)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- check triples
|
||||||
|
if self:countConsecutives(row) > 2 then
|
||||||
|
if (debug) printh("triples")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cols = self:getCols()
|
||||||
|
for col in all(cols) do
|
||||||
|
-- check count
|
||||||
|
if count(col, BLUE) ~= count(col, YELLOW) then
|
||||||
|
if (debug) printh("uneven count")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
-- check identical lines
|
||||||
|
for other in all(cols) do
|
||||||
|
if equal(other, col) and other ~= col then
|
||||||
|
if (debug) printh("equal cols")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- check triples
|
||||||
|
if self:countConsecutives(col) > 2 then
|
||||||
|
if (debug) printh("triples")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
|
||||||
|
countConsecutives = function(self, line)
|
||||||
|
local count = 0
|
||||||
|
local last = 0
|
||||||
|
for v in all(line) do
|
||||||
|
if v ~= last then
|
||||||
|
last = 0
|
||||||
|
else
|
||||||
|
last = v
|
||||||
|
count += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end,
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Returns the index of a random zero tile
|
||||||
|
--
|
||||||
|
getRandomZero = function(self)
|
||||||
|
assert(count(tiles, 0) > 0, "No zero left")
|
||||||
|
local zeroes = filter(tiles, function(v) return v == 0 end, true)
|
||||||
|
local z = {}
|
||||||
|
for k,v in pairs(zeroes) do
|
||||||
|
add(z, k)
|
||||||
|
end
|
||||||
|
return rnd(z)
|
||||||
|
end,
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Returns the index of a random non-zero tile
|
||||||
|
--
|
||||||
|
getRandomNonZero = function(self)
|
||||||
|
assert(count(tiles, 0) < #tiles, "All zeroes")
|
||||||
|
local numbers = filter(tiles, function(v) return v ~= 0 end, true)
|
||||||
|
local z = {}
|
||||||
|
for k,v in pairs(numbers) do
|
||||||
|
add(z, k)
|
||||||
|
end
|
||||||
|
return rnd(z)
|
||||||
|
end,
|
||||||
|
|
||||||
|
tostring = function(self)
|
||||||
|
local str = ''
|
||||||
|
for v in all(tiles) do
|
||||||
|
str ..= ", " .. v
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Solves 1 step of the board
|
||||||
|
-- Returns "valid" if it solved it without guessing
|
||||||
|
-- Returns "invalid" if the board cannot be solved
|
||||||
|
solveStep = function(self, random)
|
||||||
|
local zeroes = count(tiles, 0)
|
||||||
|
self:surroundDoubles()
|
||||||
|
self:splitTriples()
|
||||||
|
self:fillLines()
|
||||||
|
self:noIdenticalLines()
|
||||||
|
local changed = zeroes ~= count(tiles, 0)
|
||||||
|
if not changed and random and not self:isComplete() then
|
||||||
|
-- Set a random color
|
||||||
|
local z = self:getRandomZero()
|
||||||
|
self:fill(z, rnd({BLUE, YELLOW}))
|
||||||
|
if (debug) printh("!!!!!!!!!!!!!!!!! RANDOM FILL AT " .. z)
|
||||||
|
return "invalid"
|
||||||
|
end
|
||||||
|
return (changed or self:isComplete()) and "valid" or "invalid"
|
||||||
|
end,
|
||||||
|
|
||||||
|
surroundDoubles = function(self)
|
||||||
|
for idx,v in ipairs(tiles) do
|
||||||
|
local x,y = self:idx_xy(idx)
|
||||||
|
if v == 0 then
|
||||||
|
local neighbors = {}
|
||||||
|
-- 2 tiles on the left
|
||||||
|
if x >= 3 then
|
||||||
|
add(neighbors, {idx, idx-1, idx-2})
|
||||||
|
end
|
||||||
|
-- 2 tiles on the right
|
||||||
|
if x <= width-2 then
|
||||||
|
add(neighbors, {idx, idx+1, idx+2})
|
||||||
|
end
|
||||||
|
-- 2 tiles on top
|
||||||
|
if y >= 3 then
|
||||||
|
add(neighbors, {idx, idx-width, idx - width*2})
|
||||||
|
end
|
||||||
|
-- 2 tiles under
|
||||||
|
if y <= width-2 then
|
||||||
|
add(neighbors, {idx, idx+width, idx + width*2})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- only keep pairs that are identical (and not 0)
|
||||||
|
neighbors = filter(neighbors, function (o) return tiles[o[2]] == tiles[o[3]] and tiles[o[2]] ~= 0 end)
|
||||||
|
|
||||||
|
-- do the surrounding
|
||||||
|
for item in all(neighbors) do
|
||||||
|
if item[1] then
|
||||||
|
if (debug) printh("Surrounding at " .. item[1])
|
||||||
|
self:fill(item[1], tiles[item[2]], true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
splitTriples = function(self)
|
||||||
|
for idx, col in ipairs(tiles) do
|
||||||
|
local x,y = self:idx_xy(idx)
|
||||||
|
if col == 0 then
|
||||||
|
|
||||||
|
if x > 1 and x < width then
|
||||||
|
-- check horizontal
|
||||||
|
local prev = tiles[idx-1]
|
||||||
|
local next = tiles[idx+1]
|
||||||
|
if prev ~= 0 and prev == next then
|
||||||
|
if (debug) printh("Splitting at " .. idx)
|
||||||
|
self:fill(idx, prev, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if y>1 and y < width then
|
||||||
|
-- check vertical
|
||||||
|
local prev = tiles[idx-width]
|
||||||
|
local next = tiles[idx+width]
|
||||||
|
if prev ~= 0 and prev == next then
|
||||||
|
if (debug) printh("Splitting at " .. idx)
|
||||||
|
self:fill(idx, prev, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
fillLines = function(self)
|
||||||
|
local rows = self:getRows()
|
||||||
|
local cols = self:getCols()
|
||||||
|
|
||||||
|
-- rows
|
||||||
|
for y,row in ipairs(rows) do
|
||||||
|
local a = count(row, BLUE)
|
||||||
|
local b = count(row, YELLOW)
|
||||||
|
if a ~= b then
|
||||||
|
if a == width/2 then self:fillRow(y, YELLOW) end
|
||||||
|
if b == width/2 then self:fillRow(y, BLUE) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- columns
|
||||||
|
for x,col in ipairs(cols) do
|
||||||
|
local a = count(col, BLUE)
|
||||||
|
local b = count(col, YELLOW)
|
||||||
|
if a ~= b then
|
||||||
|
if a == width/2 then self:fillCol(x, YELLOW) end
|
||||||
|
if b == width/2 then self:fillCol(x, BLUE) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
fillRow = function(self, y, color)
|
||||||
|
if (debug) printh("Filling line " .. y .. " in " .. (color == BLUE and "blue" or "yellow"))
|
||||||
|
local idx = self:xy_idx(1, y)
|
||||||
|
for i = idx, (idx+width-1) do
|
||||||
|
if self:getTile(i) == 0 then
|
||||||
|
self:fill(i, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
fillCol = function(self, x, color)
|
||||||
|
if (debug) printh("Filling column " .. x .. " in " .. (color == BLUE and "blue" or "yellow"))
|
||||||
|
local idx = self:xy_idx(x, 1)
|
||||||
|
for i = idx, #tiles, width do
|
||||||
|
if self:getTile(i) == 0 then
|
||||||
|
self:fill(i, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
noIdenticalLines = function(self)
|
||||||
|
-- columns
|
||||||
|
local cols = self:getCols()
|
||||||
|
for x,col in ipairs(cols) do
|
||||||
|
if count(col, 0) == 2 and count(col, BLUE) == count(col, YELLOW) then
|
||||||
|
local y1, y2 = unpack(find(col, 0))
|
||||||
|
local ab = copy(col) ab[y1] = BLUE ab[y2] = YELLOW
|
||||||
|
local ba = copy(col) ba[y1] = YELLOW ba[y2] = BLUE
|
||||||
|
-- Check if a dupe exists
|
||||||
|
for x2,col in ipairs(cols) do
|
||||||
|
if equal(col, ab) then
|
||||||
|
if debug then
|
||||||
|
printh("No-dupe at col " .. x .. " from col " .. x2)
|
||||||
|
printh(" " .. self:xy_idx(x,y1) .. " in yellow")
|
||||||
|
printh(" " .. self:xy_idx(x,y2) .. " in blue")
|
||||||
|
end
|
||||||
|
self:fill(self:xy_idx(x,y1), YELLOW)
|
||||||
|
self:fill(self:xy_idx(x,y2), BLUE)
|
||||||
|
goto continue
|
||||||
|
elseif equal(col, ba) then
|
||||||
|
if debug then
|
||||||
|
printh("No-dupe at col " .. x .. " from col " .. x2)
|
||||||
|
printh(" " .. self:xy_idx(x,y1) .. " in blue")
|
||||||
|
printh(" " .. self:xy_idx(x,y2) .. " in yellow")
|
||||||
|
end
|
||||||
|
self:fill(self:xy_idx(x,y1), BLUE)
|
||||||
|
self:fill(self:xy_idx(x,y2), YELLOW)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
-- rows
|
||||||
|
local rows = self:getRows()
|
||||||
|
for y,row in ipairs(rows) do
|
||||||
|
if count(row, 0) == 2 and count(row, BLUE) == count(row, YELLOW) then
|
||||||
|
local x1, x2 = unpack(find(row, 0))
|
||||||
|
local ab = copy(row) ab[x1] = BLUE ab[x2] = YELLOW
|
||||||
|
local ba = copy(row) ba[x1] = YELLOW ba[x2] = BLUE
|
||||||
|
-- Check if a dupe exists
|
||||||
|
for y2,row in ipairs(rows) do
|
||||||
|
if equal(row, ab) then
|
||||||
|
if debug then
|
||||||
|
printh("No-dupe at row " .. y .. " from row " .. y2)
|
||||||
|
printh(" " .. self:xy_idx(x1,y) .. " in yellow")
|
||||||
|
printh(" " .. self:xy_idx(x2,y) .. " in blue")
|
||||||
|
end
|
||||||
|
self:fill(self:xy_idx(x1,y), YELLOW)
|
||||||
|
self:fill(self:xy_idx(x2,y), BLUE)
|
||||||
|
goto continue
|
||||||
|
elseif equal(row, ba) then
|
||||||
|
if debug then
|
||||||
|
printh("No-dupe at row " .. y .. " from row " .. y2)
|
||||||
|
printh(" " .. self:xy_idx(x1,y) .. " in blue")
|
||||||
|
printh(" " .. self:xy_idx(x2,y) .. " in yellow")
|
||||||
|
end
|
||||||
|
self:fill(self:xy_idx(x1,y), BLUE)
|
||||||
|
self:fill(self:xy_idx(x2,y), YELLOW)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
draw = function(self)
|
||||||
|
local w = 10
|
||||||
|
local padding = 1
|
||||||
|
local margin = 4
|
||||||
|
for k,v in ipairs(tiles) do
|
||||||
|
local x,y = self:idx_xy(k)
|
||||||
|
local color = v == BLUE and 9 or v == YELLOW and 3 or 1
|
||||||
|
rectfill(
|
||||||
|
margin + (x-1)*w + (x-1)*(padding+1),
|
||||||
|
margin + (y-1)*w + (y-1)*(padding+1),
|
||||||
|
w, w, color)
|
||||||
|
if debug then
|
||||||
|
print(k,
|
||||||
|
margin + (x-1)*w + (x-1)*(padding+1),
|
||||||
|
margin + (y-1)*w + (y-1)*(padding+1), 8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
465
main.lua
465
main.lua
|
@ -1,4 +1,4 @@
|
||||||
poke(0x5F2D, 3)
|
poke(0x5F2D, 1)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- constants
|
-- constants
|
||||||
|
@ -104,462 +104,81 @@ end
|
||||||
-- return str.."}"
|
-- return str.."}"
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
--
|
function makeButton(x, y, w, h, text, f)
|
||||||
-- Board
|
local state = 0 -- 0 = normal, 1 = hovered, 2 = pressed
|
||||||
--
|
|
||||||
|
|
||||||
local Board = {}
|
|
||||||
function Board.new()
|
|
||||||
local debug = false
|
|
||||||
local width = 10
|
|
||||||
local t = [[
|
|
||||||
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0
|
|
||||||
]]
|
|
||||||
a = {foo = 3}
|
|
||||||
local tiles = split(t)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
reset = function(self)
|
x = x,
|
||||||
local t = [[
|
y = y,
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
w = w,
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
h = h,
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
text = text,
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
draw=function(self)
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
rectfill(self.x, self.y, self.w, self.h, 8)
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
print(self.text.." "..state, self.x+1, self.y+1, 6)
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0
|
|
||||||
]]
|
|
||||||
tiles = split(t)
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
getTile = function(self, idx)
|
update=function(self)
|
||||||
return tiles[idx]
|
if mouse_x >= self.x and mouse_x <= self.x + self.w and
|
||||||
end,
|
mouse_y >= self.y and mouse_y <= self.y + self.h then
|
||||||
|
if stat(34)&1 == 0 and state == 2 and f then
|
||||||
-- returns a COPY of the tiles array
|
f()
|
||||||
getTilesCopy = function(self)
|
|
||||||
return copy(tiles)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- overwrites the whole tiles array
|
|
||||||
setTiles = function(self, newtiles)
|
|
||||||
assert(#newtiles == #tiles, "New tiles array must have a length of " .. #tiles)
|
|
||||||
tiles = copy(newtiles)
|
|
||||||
end,
|
|
||||||
|
|
||||||
idx_xy = function(self, idx)
|
|
||||||
return idx_xy(idx, width)
|
|
||||||
end,
|
|
||||||
|
|
||||||
xy_idx = function(self, x, y)
|
|
||||||
return xy_idx(x, y, width)
|
|
||||||
end,
|
|
||||||
|
|
||||||
fill = function(self, idx, color, invert)
|
|
||||||
if invert then
|
|
||||||
color = color == YELLOW and BLUE or YELLOW
|
|
||||||
end
|
end
|
||||||
tiles[idx] = color
|
if stat(34)&1 == 1 then
|
||||||
end,
|
state = 2
|
||||||
|
|
||||||
getRows = function(self)
|
|
||||||
local ret = {}
|
|
||||||
for i = 1, width do
|
|
||||||
add(ret, slice(tiles, ((i - 1) * width) + 1, i * width))
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end,
|
|
||||||
|
|
||||||
getCols = function(self)
|
|
||||||
local ret = {}
|
|
||||||
local rows = self.getRows(self)
|
|
||||||
for i = 1, width do
|
|
||||||
add(ret, map(rows, function(v) return v[i] end))
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end,
|
|
||||||
|
|
||||||
isComplete = function(self)
|
|
||||||
return count(tiles, 0) == 0
|
|
||||||
end,
|
|
||||||
|
|
||||||
isValid = function(self)
|
|
||||||
local rows = self:getRows()
|
|
||||||
for y,row in ipairs(rows) do
|
|
||||||
-- check count
|
|
||||||
if count(row, BLUE) ~= count(row, YELLOW) then
|
|
||||||
if (debug) printh("uneven count on row "..y)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
-- check identical lines
|
|
||||||
for k,other in ipairs(rows) do
|
|
||||||
if equal(other, row) and other ~= row then
|
|
||||||
if (debug) printh("equal rows "..k)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- check triples
|
|
||||||
if self:countConsecutives(row) > 2 then
|
|
||||||
if (debug) printh("triples")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local cols = self:getCols()
|
|
||||||
for col in all(cols) do
|
|
||||||
-- check count
|
|
||||||
if count(col, BLUE) ~= count(col, YELLOW) then
|
|
||||||
if (debug) printh("uneven count")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
-- check identical lines
|
|
||||||
for other in all(cols) do
|
|
||||||
if equal(other, col) and other ~= col then
|
|
||||||
if (debug) printh("equal cols")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- check triples
|
|
||||||
if self:countConsecutives(col) > 2 then
|
|
||||||
if (debug) printh("triples")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
|
|
||||||
countConsecutives = function(self, line)
|
|
||||||
local count = 0
|
|
||||||
local last = 0
|
|
||||||
for v in all(line) do
|
|
||||||
if v ~= last then
|
|
||||||
last = 0
|
|
||||||
else
|
else
|
||||||
last = v
|
state = 1
|
||||||
count += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return count
|
|
||||||
end,
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Returns the index of a random zero tile
|
|
||||||
--
|
|
||||||
getRandomZero = function(self)
|
|
||||||
assert(count(tiles, 0) > 0, "No zero left")
|
|
||||||
local zeroes = filter(tiles, function(v) return v == 0 end, true)
|
|
||||||
local z = {}
|
|
||||||
for k,v in pairs(zeroes) do
|
|
||||||
add(z, k)
|
|
||||||
end
|
|
||||||
return rnd(z)
|
|
||||||
end,
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Returns the index of a random non-zero tile
|
|
||||||
--
|
|
||||||
getRandomNonZero = function(self)
|
|
||||||
assert(count(tiles, 0) < #tiles, "All zeroes")
|
|
||||||
local numbers = filter(tiles, function(v) return v ~= 0 end, true)
|
|
||||||
local z = {}
|
|
||||||
for k,v in pairs(numbers) do
|
|
||||||
add(z, k)
|
|
||||||
end
|
|
||||||
return rnd(z)
|
|
||||||
end,
|
|
||||||
|
|
||||||
tostring = function(self)
|
|
||||||
local str = ''
|
|
||||||
for v in all(tiles) do
|
|
||||||
str ..= ", " .. v
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Solves 1 step of the board
|
|
||||||
-- Returns "valid" if it solved it without guessing
|
|
||||||
-- Returns "invalid" if the board cannot be solved
|
|
||||||
solveStep = function(self, random)
|
|
||||||
local zeroes = count(tiles, 0)
|
|
||||||
self:surroundDoubles()
|
|
||||||
self:splitTriples()
|
|
||||||
self:fillLines()
|
|
||||||
self:noIdenticalLines()
|
|
||||||
local changed = zeroes ~= count(tiles, 0)
|
|
||||||
if not changed and random and not self:isComplete() then
|
|
||||||
-- Set a random color
|
|
||||||
local z = self:getRandomZero()
|
|
||||||
self:fill(z, rnd({BLUE, YELLOW}))
|
|
||||||
if (debug) printh("!!!!!!!!!!!!!!!!! RANDOM FILL AT " .. z)
|
|
||||||
return "invalid"
|
|
||||||
end
|
|
||||||
return (changed or self:isComplete()) and "valid" or "invalid"
|
|
||||||
end,
|
|
||||||
|
|
||||||
surroundDoubles = function(self)
|
|
||||||
for idx,v in ipairs(tiles) do
|
|
||||||
local x,y = self:idx_xy(idx)
|
|
||||||
if v == 0 then
|
|
||||||
local neighbors = {}
|
|
||||||
-- 2 tiles on the left
|
|
||||||
if x >= 3 then
|
|
||||||
add(neighbors, {idx, idx-1, idx-2})
|
|
||||||
end
|
|
||||||
-- 2 tiles on the right
|
|
||||||
if x <= width-2 then
|
|
||||||
add(neighbors, {idx, idx+1, idx+2})
|
|
||||||
end
|
|
||||||
-- 2 tiles on top
|
|
||||||
if y >= 3 then
|
|
||||||
add(neighbors, {idx, idx-width, idx - width*2})
|
|
||||||
end
|
|
||||||
-- 2 tiles under
|
|
||||||
if y <= width-2 then
|
|
||||||
add(neighbors, {idx, idx+width, idx + width*2})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- only keep pairs that are identical (and not 0)
|
|
||||||
neighbors = filter(neighbors, function (o) return tiles[o[2]] == tiles[o[3]] and tiles[o[2]] ~= 0 end)
|
|
||||||
|
|
||||||
-- do the surrounding
|
|
||||||
for item in all(neighbors) do
|
|
||||||
if item[1] then
|
|
||||||
if (debug) printh("Surrounding at " .. item[1])
|
|
||||||
self:fill(item[1], tiles[item[2]], true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
splitTriples = function(self)
|
|
||||||
for idx, col in ipairs(tiles) do
|
|
||||||
local x,y = self:idx_xy(idx)
|
|
||||||
if col == 0 then
|
|
||||||
|
|
||||||
if x > 1 and x < width then
|
|
||||||
-- check horizontal
|
|
||||||
local prev = tiles[idx-1]
|
|
||||||
local next = tiles[idx+1]
|
|
||||||
if prev ~= 0 and prev == next then
|
|
||||||
if (debug) printh("Splitting at " .. idx)
|
|
||||||
self:fill(idx, prev, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if y>1 and y < width then
|
|
||||||
-- check vertical
|
|
||||||
local prev = tiles[idx-width]
|
|
||||||
local next = tiles[idx+width]
|
|
||||||
if prev ~= 0 and prev == next then
|
|
||||||
if (debug) printh("Splitting at " .. idx)
|
|
||||||
self:fill(idx, prev, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
fillLines = function(self)
|
|
||||||
local rows = self:getRows()
|
|
||||||
local cols = self:getCols()
|
|
||||||
|
|
||||||
-- rows
|
|
||||||
for y,row in ipairs(rows) do
|
|
||||||
local a = count(row, BLUE)
|
|
||||||
local b = count(row, YELLOW)
|
|
||||||
if a ~= b then
|
|
||||||
if a == width/2 then self:fillRow(y, YELLOW) end
|
|
||||||
if b == width/2 then self:fillRow(y, BLUE) end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- columns
|
|
||||||
for x,col in ipairs(cols) do
|
|
||||||
local a = count(col, BLUE)
|
|
||||||
local b = count(col, YELLOW)
|
|
||||||
if a ~= b then
|
|
||||||
if a == width/2 then self:fillCol(x, YELLOW) end
|
|
||||||
if b == width/2 then self:fillCol(x, BLUE) end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
fillRow = function(self, y, color)
|
|
||||||
if (debug) printh("Filling line " .. y .. " in " .. (color == BLUE and "blue" or "yellow"))
|
|
||||||
local idx = self:xy_idx(1, y)
|
|
||||||
for i = idx, (idx+width-1) do
|
|
||||||
if self:getTile(i) == 0 then
|
|
||||||
self:fill(i, color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
fillCol = function(self, x, color)
|
|
||||||
if (debug) printh("Filling column " .. x .. " in " .. (color == BLUE and "blue" or "yellow"))
|
|
||||||
local idx = self:xy_idx(x, 1)
|
|
||||||
for i = idx, #tiles, width do
|
|
||||||
if self:getTile(i) == 0 then
|
|
||||||
self:fill(i, color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
noIdenticalLines = function(self)
|
|
||||||
-- columns
|
|
||||||
local cols = self:getCols()
|
|
||||||
for x,col in ipairs(cols) do
|
|
||||||
if count(col, 0) == 2 and count(col, BLUE) == count(col, YELLOW) then
|
|
||||||
local y1, y2 = unpack(find(col, 0))
|
|
||||||
local ab = copy(col) ab[y1] = BLUE ab[y2] = YELLOW
|
|
||||||
local ba = copy(col) ba[y1] = YELLOW ba[y2] = BLUE
|
|
||||||
-- Check if a dupe exists
|
|
||||||
for x2,col in ipairs(cols) do
|
|
||||||
if equal(col, ab) then
|
|
||||||
if debug then
|
|
||||||
printh("No-dupe at col " .. x .. " from col " .. x2)
|
|
||||||
printh(" " .. self:xy_idx(x,y1) .. " in yellow")
|
|
||||||
printh(" " .. self:xy_idx(x,y2) .. " in blue")
|
|
||||||
end
|
|
||||||
self:fill(self:xy_idx(x,y1), YELLOW)
|
|
||||||
self:fill(self:xy_idx(x,y2), BLUE)
|
|
||||||
goto continue
|
|
||||||
elseif equal(col, ba) then
|
|
||||||
if debug then
|
|
||||||
printh("No-dupe at col " .. x .. " from col " .. x2)
|
|
||||||
printh(" " .. self:xy_idx(x,y1) .. " in blue")
|
|
||||||
printh(" " .. self:xy_idx(x,y2) .. " in yellow")
|
|
||||||
end
|
|
||||||
self:fill(self:xy_idx(x,y1), BLUE)
|
|
||||||
self:fill(self:xy_idx(x,y2), YELLOW)
|
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
|
|
||||||
-- rows
|
|
||||||
local rows = self:getRows()
|
|
||||||
for y,row in ipairs(rows) do
|
|
||||||
if count(row, 0) == 2 and count(row, BLUE) == count(row, YELLOW) then
|
|
||||||
local x1, x2 = unpack(find(row, 0))
|
|
||||||
local ab = copy(row) ab[x1] = BLUE ab[x2] = YELLOW
|
|
||||||
local ba = copy(row) ba[x1] = YELLOW ba[x2] = BLUE
|
|
||||||
-- Check if a dupe exists
|
|
||||||
for y2,row in ipairs(rows) do
|
|
||||||
if equal(row, ab) then
|
|
||||||
if debug then
|
|
||||||
printh("No-dupe at row " .. y .. " from row " .. y2)
|
|
||||||
printh(" " .. self:xy_idx(x1,y) .. " in yellow")
|
|
||||||
printh(" " .. self:xy_idx(x2,y) .. " in blue")
|
|
||||||
end
|
|
||||||
self:fill(self:xy_idx(x1,y), YELLOW)
|
|
||||||
self:fill(self:xy_idx(x2,y), BLUE)
|
|
||||||
goto continue
|
|
||||||
elseif equal(row, ba) then
|
|
||||||
if debug then
|
|
||||||
printh("No-dupe at row " .. y .. " from row " .. y2)
|
|
||||||
printh(" " .. self:xy_idx(x1,y) .. " in blue")
|
|
||||||
printh(" " .. self:xy_idx(x2,y) .. " in yellow")
|
|
||||||
end
|
|
||||||
self:fill(self:xy_idx(x1,y), BLUE)
|
|
||||||
self:fill(self:xy_idx(x2,y), YELLOW)
|
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
draw = function(self)
|
|
||||||
local w = 10
|
|
||||||
local padding = 1
|
|
||||||
local margin = 4
|
|
||||||
for k,v in ipairs(tiles) do
|
|
||||||
local x,y = self:idx_xy(k)
|
|
||||||
local color = v == BLUE and 9 or v == YELLOW and 3 or 1
|
|
||||||
rectfill(
|
|
||||||
margin + (x-1)*w + (x-1)*(padding+1),
|
|
||||||
margin + (y-1)*w + (y-1)*(padding+1),
|
|
||||||
w, w, color)
|
|
||||||
if debug then
|
|
||||||
print(k,
|
|
||||||
margin + (x-1)*w + (x-1)*(padding+1),
|
|
||||||
margin + (y-1)*w + (y-1)*(padding+1), 8)
|
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
state = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function setState(state, ...)
|
||||||
|
local args = {...}
|
||||||
|
if gs and gs._leave then gs._leave() end
|
||||||
|
gs = state
|
||||||
|
if gs and gs._enter then gs._enter(unpack(args)) end
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- main loop
|
-- main loop
|
||||||
--
|
--
|
||||||
|
|
||||||
local board = Board.new()
|
|
||||||
local create = true
|
local create = true
|
||||||
|
|
||||||
function _init()
|
function _init()
|
||||||
cls()
|
cls()
|
||||||
printh(" ")
|
printh(" ")
|
||||||
printh(" ")
|
printh(" ")
|
||||||
-- srand(42)
|
mouse_x = 0
|
||||||
|
mouse_y = 0
|
||||||
|
|
||||||
if create then
|
states = {
|
||||||
-- Create a board
|
menu = stateMenu(),
|
||||||
repeat
|
game = stateGame()
|
||||||
board:reset()
|
}
|
||||||
repeat
|
|
||||||
board:solveStep(true)
|
|
||||||
until board:isComplete()
|
|
||||||
until board:isValid()
|
|
||||||
|
|
||||||
-- Remove tiles until randomness is unavoidable
|
setState(states.menu)
|
||||||
local previous = {board:getTilesCopy()} -- initial state
|
|
||||||
local invalidcount = 0
|
|
||||||
while true do
|
|
||||||
-- remove a random tile
|
|
||||||
board:setTiles(previous[#previous])
|
|
||||||
local i = board:getRandomNonZero()
|
|
||||||
board:fill(i, 0)
|
|
||||||
add(previous, board:getTilesCopy())
|
|
||||||
|
|
||||||
-- try to solve the board
|
|
||||||
local solved = ""
|
|
||||||
repeat
|
|
||||||
solved = board:solveStep()
|
|
||||||
until board:isComplete() or solved == "invalid"
|
|
||||||
if board:isComplete() then
|
|
||||||
invalidcount = 0
|
|
||||||
end
|
|
||||||
if solved == "invalid" then
|
|
||||||
deli(previous)
|
|
||||||
invalidcount += 1
|
|
||||||
end
|
|
||||||
if invalidcount == 100 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end -- end while
|
|
||||||
|
|
||||||
board:setTiles(previous[#previous])
|
|
||||||
printh(board:tostring())
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function _update60()
|
function _update60()
|
||||||
|
gs._update()
|
||||||
-- if not create then
|
-- if not create then
|
||||||
-- board:solveStep()
|
-- board:solveStep()
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
|
-- mouse
|
||||||
|
mouse_x = stat(32)
|
||||||
|
mouse_y = stat(33)
|
||||||
end
|
end
|
||||||
|
|
||||||
function _draw()
|
function _draw()
|
||||||
cls()
|
cls()
|
||||||
board:draw()
|
gs._draw()
|
||||||
circfill(stat(32), stat(33), 0, 7)
|
circfill(stat(32), stat(33), 0, 7)
|
||||||
circ(stat(32), stat(33), 1, 0)
|
circ(stat(32), stat(33), 1, 0)
|
||||||
end
|
end
|
||||||
|
|
54
states/game.lua
Normal file
54
states/game.lua
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
function stateGame()
|
||||||
|
local board = Board.new()
|
||||||
|
|
||||||
|
function _enter()
|
||||||
|
-- Create a board
|
||||||
|
repeat
|
||||||
|
board:reset()
|
||||||
|
repeat
|
||||||
|
board:solveStep(true)
|
||||||
|
until board:isComplete()
|
||||||
|
until board:isValid()
|
||||||
|
|
||||||
|
-- Remove tiles until randomness is unavoidable
|
||||||
|
local previous = {board:getTilesCopy()} -- initial state
|
||||||
|
local invalidcount = 0
|
||||||
|
while true do
|
||||||
|
-- remove a random tile
|
||||||
|
board:setTiles(previous[#previous])
|
||||||
|
local i = board:getRandomNonZero()
|
||||||
|
board:fill(i, 0)
|
||||||
|
add(previous, board:getTilesCopy())
|
||||||
|
|
||||||
|
-- try to solve the board
|
||||||
|
local solved = ""
|
||||||
|
repeat
|
||||||
|
solved = board:solveStep()
|
||||||
|
until board:isComplete() or solved == "invalid"
|
||||||
|
if board:isComplete() then
|
||||||
|
invalidcount = 0
|
||||||
|
end
|
||||||
|
if solved == "invalid" then
|
||||||
|
deli(previous)
|
||||||
|
invalidcount += 1
|
||||||
|
end
|
||||||
|
if invalidcount == 100 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end -- end while
|
||||||
|
|
||||||
|
board:setTiles(previous[#previous])
|
||||||
|
printh(board:tostring())
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
_enter = _enter,
|
||||||
|
_update = function()
|
||||||
|
|
||||||
|
end,
|
||||||
|
|
||||||
|
_draw = function()
|
||||||
|
board:draw()
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
27
states/menu.lua
Normal file
27
states/menu.lua
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
function stateMenu()
|
||||||
|
|
||||||
|
local items = {"play", "rules"}
|
||||||
|
local buttons = {}
|
||||||
|
|
||||||
|
function _enter()
|
||||||
|
for k,v in ipairs(items) do
|
||||||
|
local button = makeButton(10, k*10, 30, 6, v, function() printh("click") end)
|
||||||
|
add(buttons, button)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
_enter = _enter,
|
||||||
|
_update = function()
|
||||||
|
for button in all(buttons) do
|
||||||
|
button:update()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
_draw = function()
|
||||||
|
for button in all(buttons) do
|
||||||
|
button:draw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user