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