This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Exerro's profile picture

[MWIP] Sudoku solver

Started by Exerro, 29 March 2013 - 07:27 AM
Exerro #1
Posted 29 March 2013 - 08:27 AM
Hi,

I was playing sudoku the other day and wanted a way to check my results to see if they worked, so I made a sudoku checker. Then I got a bit stuck on one and though why don't I make it solve one for me

Code:
Spoiler


local args = { ... }
split = function( str )
local t = { }
for i = 1,str:len( ) do
  t[i] = str:sub( i, i )
end
return t
end
puzzle = {
{5,0,4,3,8,0,0,0,0};
{0,9,0,5,0,0,2,0,0};
{1,0,0,0,6,4,8,0,0};
{0,0,5,1,9,0,0,0,7};
{0,0,6,0,0,0,9,0,0};
{3,0,0,0,7,6,4,0,0};
{0,0,2,7,4,0,0,0,1};
{0,0,1,0,0,9,0,2,0};
{0,0,0,0,2,1,3,0,9};
}
if args[2] then
k = fs.open( args[2], "r" )
local con = { }
for i = 1,9 do
  con[i] = k.readLine( )
end
k.close( )
for i = 1,9 do
  print( con[i] )
  con[i] = split( con[i], "" )
  print( "here" )
  for k = 1,9 do
   con[i][k] = tonumber( con[i][k] ) or 0
  end
end
puzzle = con
end
term.clear( )
local correctpuzzle = {
{8,3,2,6,7,5,1,4,9};
{0,4,9,1,3,2,8,5,6};
{5,6,1,4,9,8,3,2,7};
{4,1,5,9,8,7,2,6,3};
{9,2,6,3,5,1,4,7,8};
{3,8,7,2,6,4,5,9,1};
{2,5,3,7,1,6,9,8,4};
{6,9,4,8,2,3,7,1,5};
{1,7,8,5,4,9,6,3,2};
}

local function checkBox( n )
if n < 1 or n > 9 then
  return nil
end
local x = 0
local y = 1
while n >= 4 do
  y = y+1
  n = n-3
end
x = x+n
local n = { }
for i = x*3-2, x*3 do
  for k = y*3-2, y*3 do
   n[puzzle[k][i]] = true
  end
end
for i = 1,9 do
  if not n[i] or n[i] ~= true then
   return false
  end
end
return true
end
local function checkColumn( n )
local num = { }
for i = 1,9 do
  num[puzzle[i][n]] = true
end
for i = 1,9 do
  if not num[i] or num[i] ~= true then
   return false
  end
end
return true
end
local function checkRow( n )
local num = { }
for i = 1,9 do
  num[puzzle[n][i]] = true
end
for i = 1,9 do
  if not num[i] or num[i] ~= true then
   return false
  end
end
return true
end
local function getPuzzle( )
local x = 1
local y = 1
while true do
  for i = 1,9 do
   for k = 1,9 do
	if i == y and x == k then
	 term.setBackgroundColour( colours.blue )
	else
	 term.setBackgroundColour( colours.lightBlue )
	end
	term.setCursorPos( k, i )
	term.write( tostring( puzzle[i][k] ):sub( 1, 1 ) )
   end
  end
  term.setCursorPos( 1, 10 )
  ev = { os.pullEvent( ) }
  if ev[1] == "key" then
   if ev[2] == keys.up and y > 1 then
	y = y - 1
   elseif ev[2] == keys.down and y < 9 then
	y = y + 1
   elseif ev[2] == keys.left and x > 1 then
	x = x - 1
   elseif ev[2] == keys.right and x < 9 then
	x = x + 1
   elseif ev[2] == keys.backspace then
	return false
   elseif ev[2] == keys.enter then
	return true
   end
  elseif ev[1] == "char" then
   puzzle[y][x] = tonumber( ev[2] ) or 0
  end
end
end
local function checkPuzzle( )
local message = "Failed"
for i = 1,9 do
  local f = checkRow( i )
  local s = checkColumn( i )
  local t = checkBox( i )
  if not f or not s or not t then
   if not f then
	message = message.."; R"..i
   end
   if not s then
	message = message.."; C"..i
   end
   if not t then
	message = message.."; B"..i
   end
   failed = true
  end
end
return message == "Failed" and "Fine" or message
end
local function getNumbers( ty, n, t )
local num = { }
if ty == "row" then
  for i = 1,9 do
   table.insert( num, t[n][i].n )
  end
elseif ty == "column" then
  for i = 1,9 do
   table.insert( num, t[i][n].n )
  end
elseif ty == "box" then
  local x = 0
  local y = 1
  while n >= 4 do
   y = y+1
   n = n-3
  end
  x = x+n
  local n = { }
  for i = x*3-2, x*3 do
   for k = y*3-2, y*3 do
	table.insert( num, t[k][i].n )
   end
  end
end
return num
end
local function solve( t, try )
local backupofpuzzle = { unpack( t ) }
if try > 1600 then
  return "unable to solve"
end
if math.floor( try/400 ) == try/400 then
  print( "This is taking a while" )
  sleep( 0.1 )
end
--converts it into a new format
for i = 1,9 do
  for k = 1,9 do
   t[i][k] = { n = t[i][k], pos = { } }
   for z = 1,9 do
	t[i][k].pos[z] = true
   end
  end
end
--removes incorrect possibilities
for i = 1,9 do
  --box checking
  local ns = getNumbers( "box", i, t )
  local n = i
  local x = 0
  local y = 1
  while n >= 4 do
   y = y+1
   n = n-3
  end
  x = x+n
  for m = 1,#ns do
   for y = y*3-2, y*3 do
	for x = x*3-2, x*3 do
	 if t[y][x].n == 0 then
	  t[y][x].pos[ns[m]] = false
	 end
	end
   end
  end
  --row checking
  local ns = getNumbers( "row", i, t )
  for m = 1,#ns do
   for x = 1,9 do
	if t[i][x].n == 0 then
	 t[i][x].pos[ns[m]] = false
	end
   end
  end
  --column checking
  local ns = getNumbers( "column", i, t )
  for m = 1,#ns do
   for x = 1,9 do
	if t[x][i].n == 0 then
	 t[x][i].pos[ns[m]] = false
	end
   end
  end
end
--updates the numbers ( if there is only one possibility then it has to be that... )
for i = 1,9 do
  for k = 1,9 do
   local correct = 0
   for z = 1,9 do
	if t[i][k].pos[z] == true then
	 correct = correct+1
	end
   end
   if correct == 1 then
	for z = 1,9 do
	 if t[i][k].pos[z] and t[i][k].n == 0 then
	  t[i][k].n = z
	 end
	end
   end
  end
end
--checks to see if it has completed the puzzle
local solved = true
for i = 1,9 do
  for k = 1,9 do
   if t[k][i].n == 0 then
	solved = false
   end
  end
end
--if it isn't solved then try again and
if not solved then
  --converts it back into just numbers
  for i = 1,9 do
   for k = 1,9 do
	t[i][k] = t[i][k].n
   end
  end
  if t == backupofpuzzle then
   for i = 1,9 do
	for k = 1,9 do
	 t[i][k] = { n = t[i][k] }
	end
   end
  else
   return solve( t, try+1 )
  end
end
--makes the rows of numbers into a string
local line = { }
for i = 1,9 do
  line[i] = ""
  for k = 1,9 do
   line[i] = line[i]..tostring( t[i][k].n )
  end
  line[i] = line[i].."\n"
end
return unpack( line )
end
if not getPuzzle( ) then
term.setBackgroundColour( colours.black )
term.setTextColour( colours.yellow )
print( "You Quitted" )
return
end
local failed = false
local message = "Failed"
if args[1] == "check" then
if checkPuzzle( puzzle ) ~= "Fine" then
  term.setBackgroundColour( colours.black )
  term.setTextColour( colours.red )
  print( checkPuzzle( puzzle ) )
  return
end
term.setBackgroundColour( colours.black )
term.setTextColour( colours.lime )
print( "It Works!" )
elseif args[1] == "solve" then
term.setBackgroundColour( colours.green )
print( solve( puzzle, 1 ) )
end

It is terrible , i'm not going to lie. It will only solve the basic sudoku's, but it is a start and I haven't seen anyone else even attempt it so I though I may as well post it and maybe give some people some inspiration.

The way it works is it checks for every possibility for each place and if there is one possibility then it must be that number so it puts it there and runs itself with the new numbers. The reason it won't solve advanced puzzles is because it won't recognise that with a 2&amp;3 for possibilities next to eachother the 2 and the 3 have to go on that row so it cannot be a 2 or 3 in the same row etc. If someone has a way to solve this please implement it because I have tried and failed 7 times with about 300 lines of code each time.

If anyone is interested in improving this feel free to use the code or if you would like some help or want to help just post a comment below.

Thanks for reading
lieudusty #2
Posted 29 March 2013 - 04:48 PM
Perhaps you could put a pastebin link?