pico8-0hh1/main.lua
2022-05-28 15:02:55 +02:00

355 lines
7.7 KiB
Lua

--
-- constants
--
local BLUE = 1
local YELLOW = 2
--
-- Utils
--
function idx_xy(idx, width)
return (idx - 1) % width + 1, (idx - 1) \ width + 1
end
function xy_idx(x, y, width)
return ((y - 1) * width) + x
end
function map(tbl, f)
local t = {}
for k, v in pairs(tbl) do
t[k] = f(v)
end
return t
end
function filter(tbl, f, keepindex)
local ret = {}
for k, v in pairs(tbl) do
if f(v) then
if keepindex
then ret[k] = v
else add(ret, v)
end
end
end
return ret
end
function slice(tbl, first, last, step)
local sliced = {}
for i = (first or 1), (last or #tbl), (step or 1) do
sliced[(#sliced + 1)] = tbl[i]
end
return sliced
end
local _rectfill = rectfill
function rectfill(x, y, w, h, col)
_rectfill(x, y, x+w, y+h, col)
end
local _count = count
function count(tbl, p)
if type(p) != "function" then return _count(tbl, p) end
local c = 0
for v in all(tbl) do
if p(v) then c+=1 end
end
return c
end
function find(tbl, o)
local indices = {}
for k,v in ipairs(tbl) do
if v == o then add(indices, k) end
end
return indices
end
-- shallow copy
function copy(tbl)
local copy = {}
for v in all(tbl) do add(copy, v) end
return copy
end
-- shallow equality
function equal(tbl1, tbl2)
for k, _ in ipairs(tbl1) do
if tbl1[k] ~= tbl2[k] then return false end
end
return true
end
--
-- Board
--
local Board = {}
function Board.new()
local width = 10
local t = [[
2,0,2,0,0,2,1,0,1,2,
0,0,0,0,0,0,0,0,0,1,
0,1,0,0,0,0,0,0,0,0,
0,2,0,0,0,0,0,1,0,2,
0,0,1,0,0,2,0,1,0,1,
0,0,0,0,1,1,0,0,0,0,
0,2,2,0,0,0,0,0,0,2,
0,0,0,0,2,0,0,0,0,0,
0,0,2,0,0,1,0,0,0,0,
0,2,0,0,2,0,2,0,0,0
]]
local tiles = split(t)
return {
getTile = function(self, idx)
return tiles[idx]
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,
solve = function(self)
self:surroundDoubles()
self:splitTriples()
self:fillLines()
self:noIdenticalLines()
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
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
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
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)
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)
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
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")
self:fill(self:xy_idx(x,y1), YELLOW)
self:fill(self:xy_idx(x,y2), BLUE)
goto continue
elseif equal(col, ba) 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")
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
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")
self:fill(self:xy_idx(x1,y), YELLOW)
self:fill(self:xy_idx(x2,y), BLUE)
goto continue
elseif equal(row, ba) 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")
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)
for k,v in ipairs(tiles) do
local x,y = self:idx_xy(k)
local color = v == BLUE and 12 or v == YELLOW and 10 or 1
local w = 10
local p = 1
rectfill((x-1)*w + (x-1)*(p+1), (y-1)*w + (y-1)*(p+1), w, w, color)
print(k, (x-1)*w + (x-1)*(p+1), (y-1)*w + (y-1)*(p+1), 8)
end
end
}
end
--
-- main loop
--
local board = Board.new()
function _init()
end
function _update()
end
function _draw()
cls()
if time() % .5 == 0 then
printh("==")
printh("==")
board:solve()
end
board:draw()
end