midway refactoring sudoku logic
This commit is contained in:
parent
3555c49ad4
commit
ba7db7a04e
@ -1,8 +1,86 @@
|
||||
local utils = require "sudoku.utils"
|
||||
-- dbg = require ("debugger")
|
||||
-- dbg.auto_where = 2
|
||||
|
||||
local sudoku = {}
|
||||
function table2string(t)
|
||||
local myself = table2string
|
||||
local l = {}
|
||||
local n = 0
|
||||
for k, v in pairs(t) do
|
||||
n = n + 1
|
||||
if type(v) == "table" then
|
||||
l[n] = k .. ': ' .. myself(v)
|
||||
elseif type(v) == "string" then
|
||||
l[n] = k .. ': ' .. '"' .. v .. '"'
|
||||
else
|
||||
l[n] = k .. ': ' .. v
|
||||
end
|
||||
end
|
||||
return "{" .. table.concat(l, ", ") .. "}"
|
||||
end
|
||||
|
||||
function sudoku.iterZones(board)
|
||||
function find(array, element)
|
||||
local index = 0
|
||||
for i = 1, #array do
|
||||
if array[i] == element then
|
||||
index = i
|
||||
break
|
||||
end
|
||||
end
|
||||
return index
|
||||
end
|
||||
|
||||
function createEmptyBoard()
|
||||
local b = {}
|
||||
for i = 1, 9 do
|
||||
b[i] = {}
|
||||
for j = 1, 9 do
|
||||
b[i][j] = 0
|
||||
end
|
||||
end
|
||||
return b
|
||||
end
|
||||
|
||||
function cloneBoard(b)
|
||||
local c = {}
|
||||
for i = 1, 9 do
|
||||
c[i] = {}
|
||||
for j = 1, 9 do
|
||||
c[i][j] = b[i][j]
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function loadBoard(fn)
|
||||
local boards = {}
|
||||
local r = 0
|
||||
local board = {}
|
||||
for line in io.lines(fn) do
|
||||
if line:gsub("%s+", "") == "" then
|
||||
table.insert(boards, board)
|
||||
board = {}
|
||||
r = 0
|
||||
else
|
||||
r = r + 1
|
||||
local c = 0
|
||||
board[r] = {}
|
||||
for item in line:gmatch("%w+") do
|
||||
c = c + 1
|
||||
board[r][c] = tonumber(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(boards, board)
|
||||
return boards
|
||||
end
|
||||
|
||||
function showBoard(board)
|
||||
for row = 1, 9 do
|
||||
print(table.concat(board[row], " "))
|
||||
end
|
||||
end
|
||||
|
||||
function iterZones(board)
|
||||
local row = 0
|
||||
local column = 0
|
||||
local block = 0
|
||||
@ -44,7 +122,7 @@ function sudoku.iterZones(board)
|
||||
end
|
||||
end
|
||||
|
||||
local function checkAUX(zone, pos)
|
||||
function checkAUX(zone, pos)
|
||||
local isComplete = true
|
||||
local ok = true
|
||||
local conflictList = {}
|
||||
@ -63,11 +141,11 @@ local function checkAUX(zone, pos)
|
||||
return ok, isComplete, conflictList
|
||||
end
|
||||
|
||||
function sudoku.checkBoard(board)
|
||||
function checkBoard(board)
|
||||
local ok = true
|
||||
local isComplete = true
|
||||
local conflictList = {}
|
||||
for zone, pos in sudoku.iterZones(board) do
|
||||
for zone, pos in iterZones(board) do
|
||||
local tOk, tIsComplete, tConflictList = checkAUX(zone, pos)
|
||||
if not tOk then ok = false end
|
||||
if not tIsComplete then isComplete = false end
|
||||
@ -75,10 +153,11 @@ function sudoku.checkBoard(board)
|
||||
table.insert(conflictList, conflict)
|
||||
end
|
||||
end
|
||||
if not ok then isComplete = false end
|
||||
return ok, isComplete, conflictList
|
||||
end
|
||||
|
||||
function sudoku.calCellValidOptions(board, x, y)
|
||||
function calCellValidOptions(board, x, y)
|
||||
if board[x][y] ~= 0 then return {} end
|
||||
local u = {}
|
||||
local v = {}
|
||||
@ -104,19 +183,360 @@ function sudoku.calCellValidOptions(board, x, y)
|
||||
return v
|
||||
end
|
||||
|
||||
function sudoku.buildSearchSpace(board)
|
||||
function buildSearchSpace(board)
|
||||
local s = {}
|
||||
for i = 1, 9 do
|
||||
s[i] = {}
|
||||
for j = 1, 9 do
|
||||
s[i][j] = sudoku.calCellValidOptions(board, i, j)
|
||||
s[i][j] = calCellValidOptions(board, i, j)
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
sudoku.cloneBoard = utils.cloneBoard
|
||||
sudoku.createEmptyBoard = utils.createEmptyBoard
|
||||
sudoku.loadBoard = utils.loadBoard
|
||||
return sudoku
|
||||
function findPairsIndSearchSpace(board, searchSpace)
|
||||
local r = {}
|
||||
local zoneIndex = 0
|
||||
for zone, pos in iterZones(board) do
|
||||
zoneIndex = zoneIndex + 1
|
||||
for i = 1, 8 do
|
||||
local x1 = pos[i][1]
|
||||
local y1 = pos[i][2]
|
||||
local s1 = searchSpace[x1][y1]
|
||||
if #s1 == 2 then
|
||||
for j = i + 1, 9 do
|
||||
local x2 = pos[j][1]
|
||||
local y2 = pos[j][2]
|
||||
local s2 = searchSpace[x2][y2]
|
||||
if #s2 == 2 then
|
||||
if s1[1] == s2[1] and s1[2] == s2[2] then
|
||||
-- if zoneIndex <= 18 or x1 ~= x2 and y1 ~= y2 then
|
||||
table.insert(r, {zoneIndex=zoneIndex, cells={i, j}, pair=s1})
|
||||
-- print("----------")
|
||||
-- print("zone: ", zoneIndex)
|
||||
-- print(table2string(zone))
|
||||
-- print(i, j)
|
||||
-- print(table2string(s1))
|
||||
-- end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
function showSearchSpace(s)
|
||||
local hl = ("+-------+-------+-------+-------+-------+-------+-------+-------+-------+")
|
||||
print(hl)
|
||||
for i = 1, 27 do
|
||||
local x = math.floor((i - 1) / 3) + 1
|
||||
local a = (i - 1) % 3
|
||||
local row = {}
|
||||
for j = 1, 27 do
|
||||
local y = math.floor((j - 1) / 3) + 1
|
||||
local k = ((j - 1) % 3 + 1) + 3 * a
|
||||
local found = false
|
||||
for _, m in ipairs(s[x][y]) do
|
||||
if m == k then found = true; break end
|
||||
end
|
||||
if found then
|
||||
table.insert(row, k)
|
||||
else
|
||||
table.insert(row, " ")
|
||||
end
|
||||
if j % 3 == 0 then table.insert(row, "|") end
|
||||
end
|
||||
print("| "..table.concat(row, " "))
|
||||
if i % 3 == 0 then print(hl) end
|
||||
end
|
||||
end
|
||||
|
||||
function getZoneByIndex(board, zoneIndex)
|
||||
local index = (zoneIndex - 1) % 9 + 1
|
||||
local block = math.floor((zoneIndex - 1) / 9) + 1
|
||||
local a = {}
|
||||
local p = {}
|
||||
if block == 1 then
|
||||
for i = 1, 9 do
|
||||
a[i] = board[index][i]
|
||||
p[i] = {index, i}
|
||||
end
|
||||
elseif block == 2 then
|
||||
for i = 1, 9 do
|
||||
a[i] = board[i][index]
|
||||
p[i] = {index, i}
|
||||
end
|
||||
elseif block == 3 then
|
||||
local dx = math.floor((index - 1) / 3)
|
||||
local dy = (index - 1) % 3
|
||||
local n = 0
|
||||
for i = 1, 3 do
|
||||
for j = 1, 3 do
|
||||
local x = i + 3 * dx
|
||||
local y = j + 3 * dy
|
||||
n = n + 1
|
||||
a[n] = board[x][y]
|
||||
p[n] = {x, y}
|
||||
end
|
||||
end
|
||||
end
|
||||
return a, p
|
||||
end
|
||||
|
||||
function getZonePosByIndex(zoneIndex)
|
||||
local index = (zoneIndex - 1) % 9 + 1
|
||||
local block = math.floor((zoneIndex - 1) / 9) + 1
|
||||
local p = {}
|
||||
if block == 1 then
|
||||
for i = 1, 9 do
|
||||
p[i] = {index, i}
|
||||
end
|
||||
elseif block == 2 then
|
||||
for i = 1, 9 do
|
||||
p[i] = {index, i}
|
||||
end
|
||||
elseif block == 3 then
|
||||
local dx = math.floor((index - 1) / 3)
|
||||
local dy = (index - 1) % 3
|
||||
local n = 0
|
||||
for i = 1, 3 do
|
||||
for j = 1, 3 do
|
||||
local x = i + 3 * dx
|
||||
local y = j + 3 * dy
|
||||
n = n + 1
|
||||
p[n] = {x, y}
|
||||
end
|
||||
end
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
function iterZonePos(zoneIndex)
|
||||
local p = getZonePosByIndex(zoneIndex)
|
||||
local i = 0
|
||||
return function()
|
||||
if i < 9 then
|
||||
i = i + 1
|
||||
return p[i][1], p[i][2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function fillSingles(board, searchSpace)
|
||||
local count = 0
|
||||
for i = 1, 9 do
|
||||
for j = 1, 9 do
|
||||
if #searchSpace[i][j] == 1 then
|
||||
board[i][j] = searchSpace[i][j][1]
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function applyFillSinglesRepeatedly(board, s)
|
||||
local s = s or buildSearchSpace(board)
|
||||
local count = fillSingles(board, s)
|
||||
local total = count
|
||||
while count ~= 0 do
|
||||
s = buildSearchSpace(board)
|
||||
count = fillSingles(board, s)
|
||||
total = total + count
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
function eliminatePairsInSearchSpaceAUX(board, searchSpace, pair, zoneIndex, cells)
|
||||
local _, pos = getZoneByIndex(board, zoneIndex)
|
||||
local count = 0
|
||||
for i = 1, 9 do
|
||||
if i ~= cells[1] and i ~= cells[2] then
|
||||
local x = pos[i][1]
|
||||
local y = pos[i][2]
|
||||
local del = {}
|
||||
for index, v in ipairs(searchSpace[x][y]) do
|
||||
if v == pair[1] or v == pair[2] then
|
||||
count = count + 1
|
||||
table.insert(del, index)
|
||||
end
|
||||
end
|
||||
for _, index in ipairs(del) do
|
||||
table.remove(searchSpace[x][y], index)
|
||||
end
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function eliminatePairsInSearchSpace(board, searchSpace)
|
||||
local p = findPairsIndSearchSpace(board, searchSpace)
|
||||
local count = 0
|
||||
for _, m in ipairs(p) do
|
||||
count = count + eliminatePairsInSearchSpaceAUX(board, searchSpace, m.pair, m.zoneIndex, m.cells)
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function allPairs(board)
|
||||
local s = buildSearchSpace(board)
|
||||
applyFillSinglesRepeatedly(board, s)
|
||||
eliminatePairsInSearchSpace(board, s)
|
||||
local count = applyFillSinglesRepeatedly(board, s)
|
||||
while count ~= 0 do
|
||||
-- print("--------------------------------------------------")
|
||||
-- showBoard(board)
|
||||
-- showSearchSpace(s)
|
||||
-- print(count)
|
||||
-- io.read(1)
|
||||
s = buildSearchSpace(board)
|
||||
eliminatePairsInSearchSpace(board, s)
|
||||
count = applyFillSinglesRepeatedly(board, s)
|
||||
end
|
||||
end
|
||||
|
||||
function findFirstEmptyCell(board)
|
||||
for i = 1, 9 do
|
||||
for j = 1, 9 do
|
||||
if board[i][j] == 0 then
|
||||
return i, j
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function checkSearchSpace(board, searchSpace)
|
||||
local s = searchSpace or buildSearchSpace(board)
|
||||
for i = 1, 9 do
|
||||
for j = 1, 9 do
|
||||
if board[i][j] == 0 then
|
||||
if #s[i][j] == 0 then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function backTrace2(board, s)
|
||||
local s = s or buildSearchSpace(board)
|
||||
local x, y = findFirstEmptyCell(board)
|
||||
for _, v in ipairs(s[x][y]) do
|
||||
local b = cloneBoard(board)
|
||||
b[x][y] = v
|
||||
allPairs(b)
|
||||
local ok, finished = checkBoard(b)
|
||||
if finished then return b end
|
||||
if ok and checkSearchSpace(b) then
|
||||
local r = backTrace(b)
|
||||
if r then return r end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function backTrace3(board, tried)
|
||||
-- dbg()
|
||||
local tried = tried or createEmptyBoard()
|
||||
for x = 1, 9 do
|
||||
for y = 1, 9 do
|
||||
if tried[x][y] == 0 and board[x][y] == 0 then
|
||||
tried[x][y] = 1
|
||||
print(x, y)
|
||||
local s = buildSearchSpace(board)
|
||||
for _, v in ipairs(s[x][y]) do
|
||||
local b = cloneBoard(board)
|
||||
b[x][y] = v
|
||||
allPairs(b)
|
||||
local ok, finished = checkBoard(b)
|
||||
if finished then return b end
|
||||
if ok and checkSearchSpace(b) then
|
||||
local r = backTrace(b, tried)
|
||||
if r then return r end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function backTrace4(board, cx, cy)
|
||||
local s = buildSearchSpace(board)
|
||||
for x = 1, 9 do
|
||||
for y = 1, 9 do
|
||||
if x ~= cx and y ~=cy then
|
||||
for _, v in ipairs(s[x][y]) do
|
||||
local b= cloneBoard(board)
|
||||
b[x][y] = v
|
||||
print(x, y, v)
|
||||
-- allPairs(b)
|
||||
applyFillSinglesRepeatedly(b)
|
||||
local ok, finished = checkBoard(b)
|
||||
if finished then return b end
|
||||
if ok and checkSearchSpace(b) then
|
||||
local r = backTrace(b, x, y)
|
||||
if r then return r end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function backTrace(board)
|
||||
local s = buildSearchSpace(board)
|
||||
for x = 1, 9 do
|
||||
for y = 1, 9 do
|
||||
for _, v in ipairs(s[x][y]) do
|
||||
local b= cloneBoard(board)
|
||||
b[x][y] = v
|
||||
-- allPairs(b)
|
||||
applyFillSinglesRepeatedly(b)
|
||||
local ok, finished = checkBoard(b)
|
||||
print(x, y, v, ok, finished)
|
||||
showBoard(b)
|
||||
io.read(1)
|
||||
if finished then return b end
|
||||
if ok and checkSearchSpace(b) then
|
||||
local r = backTrace(b, x, y)
|
||||
if r then return r end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function solve(board)
|
||||
-- allPairs(board)
|
||||
applyFillSinglesRepeatedly(board)
|
||||
local ok, finished = checkBoard(board)
|
||||
if not ok then print("something's wrong!"); return end
|
||||
if finished then showBoard(board); return end
|
||||
local r = backTrace(board)
|
||||
if r then
|
||||
showBoard(r)
|
||||
else
|
||||
print "Could not solve!"
|
||||
end
|
||||
end
|
||||
|
||||
function sHelper(n)
|
||||
local fn = string.format("/home/reza/p/lua/22.sudoku/boards/%03d.txt", n)
|
||||
local board = loadBoard(fn)[1]
|
||||
solve(board)
|
||||
end
|
||||
|
||||
-- b = loadBoard("b.txt")[1]
|
||||
-- applyFillSinglesRepeatedly(b)
|
||||
-- s = buildSearchSpace(b)
|
||||
-- allPairs(b)
|
||||
|
||||
|
||||
return {
|
||||
loadBoard = loadBoard,
|
||||
calCellValidOptions = calCellValidOptions,
|
||||
cloneBoard = cloneBoard,
|
||||
checkBoard = checkBoard,
|
||||
createEmptyBoard = createEmptyBoard,
|
||||
}
|
||||
|
@ -1,84 +0,0 @@
|
||||
local function table2string(t)
|
||||
local myself = table2string
|
||||
local l = {}
|
||||
local n = 0
|
||||
for k, v in pairs(t) do
|
||||
n = n + 1
|
||||
if type(v) == "table" then
|
||||
l[n] = k .. ': ' .. myself(v)
|
||||
elseif type(v) == "string" then
|
||||
l[n] = k .. ': ' .. '"' .. v .. '"'
|
||||
else
|
||||
l[n] = k .. ': ' .. v
|
||||
end
|
||||
end
|
||||
return "{" .. table.concat(l, ", ") .. "}"
|
||||
end
|
||||
|
||||
local function find(array, element)
|
||||
local index = 0
|
||||
for i = 1, #array do
|
||||
if array[i] == element then
|
||||
index = i
|
||||
break
|
||||
end
|
||||
end
|
||||
return index
|
||||
end
|
||||
|
||||
local function createEmptyBoard()
|
||||
local b = {}
|
||||
for i = 1, 9 do
|
||||
b[i] = {}
|
||||
for j = 1, 9 do
|
||||
b[i][j] = 0
|
||||
end
|
||||
end
|
||||
return b
|
||||
end
|
||||
|
||||
local function cloneBoard(b)
|
||||
local c = {}
|
||||
for i = 1, 9 do
|
||||
c[i] = {}
|
||||
for j = 1, 9 do
|
||||
c[i][j] = b[i][j]
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
local function loadBoard(fn)
|
||||
local boards = {}
|
||||
local r = 0
|
||||
local board = {}
|
||||
for line in io.lines(fn) do
|
||||
if line:gsub("%s+", "") == "" then
|
||||
table.insert(boards, board)
|
||||
board = {}
|
||||
r = 0
|
||||
else
|
||||
r = r + 1
|
||||
local c = 0
|
||||
board[r] = {}
|
||||
for item in line:gmatch("%w+") do
|
||||
c = c + 1
|
||||
board[r][c] = tonumber(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(boards, board)
|
||||
return boards
|
||||
end
|
||||
|
||||
local function showBoard(board)
|
||||
for row = 1, 9 do
|
||||
print(table.concat(board[row], " "))
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
createEmptyBoard = createEmptyBoard,
|
||||
cloneBoard = cloneBoard,
|
||||
loadBoard = loadBoard,
|
||||
}
|
Loading…
Reference in New Issue
Block a user