*#Define cnBase 3
*(3x3)x(3x3) = 9x9 Grid
*#Define ccCharacters "123456789"
*#Define ccSodukoSDF "D:\my\vfp9\soduko\simplesoduko.sdf"
#Define cnBase 5
*(5x5)x(5x5) = 25x25 Grid
#Define ccCharacters "abcdefghijklmnopqrstuvwxy"
#Define ccSodukoSDF "D:\my\vfp9\soduko\abcsoduko.sdf"
Local lnCount, lcFields
Public gcGridFields
gcGridFields=""
lcFields=""
For lnCount = 1 To cnBase*cnBase
lcFields = lcFields + "," + Chr(lnCount+64)+" C(1)"
gcGridFields = gcGridFields + "," + Chr(lnCount+64)
Endfor
gcGridFields = Substr(gcGridFields,2)
lcFields = Substr(lcFields,2)
Create Cursor curGrid (&lcFields, iLevel I)
Append From (ccSodukoSDF) Type Sdf
Replace All iLevel With 1
Local Array laGrid[1]
Select &gcGridFields From curGrid Into Array laGrid
Create Cursor curSteps (iRow I, iCol I, cLetter C(1), iLevel I, iSolvingCase I)
Clear
solvesudoku(@laGrid,2)
Select curGrid
Delete All In curGrid
Append From Array laGrid
Browse
Procedure solvesudoku()
Lparameters taGrid, tnLevel
Local Array laUsable[cnBase*cnBase,cnBase*cnBase]
Local lnCount, lnCount2, lcUsed, lcUsable
Local liRow, liCol
llContinue = .T.
Do While llContinue
llContinue = .F.
* determine used digits/letters of each row, column, block
Local Array laRows[cnBase*cnBase]
Local Array laCols[cnBase*cnBase]
Local Array laBlks[cnBase*cnBase]
For lnCount=1 To cnBase*cnBase
lcRowUsed = ""
lcColUsed = ""
lcBlkUsed = ""
For lnCount2=1 To cnBase*cnBase
lcRowUsed = lcRowUsed + taGrid[lnCount , lnCount2]
lcColUsed = lcColUsed + taGrid[lnCount2, lnCount ]
lcBlkUsed = lcBlkUsed +;
taGrid[Int((lnCount-1)/cnBase)*cnBase +Int((lnCount2-1)/cnBase)+1,;
(lnCount-1)%cnBase*cnBase+(lnCount2-1)%cnBase+1]
Endfor
laRows[lnCount] = lcRowUsed
laCols[lnCount] = lcColUsed
laBlks[lnCount] = lcBlkUsed
Endfor
* determine usable digits/letters of each field
For lnCount=1 To cnBase*cnBase
For lnCount2=1 To cnBase*cnBase
If !Empty(taGrid[lnCount , lnCount2])
Loop
Endif
lcUsed = laRows[lnCount]+laCols[lnCount2]+laBlks[Int((lnCount-1)/cnBase)*cnBase+Int((lnCount2-1)/cnBase)+1]
laUsable[lnCount,lnCount2] = Chrtran(ccCharacters,lcUsed,"")
If Len(laUsable[lnCount,lnCount2])=0
* error, no digit/letter available for a field!
Return .F.
Endif
If Len(laUsable[lnCount,lnCount2])=1
* only one digit/letter available, then take it!
taGrid[lnCount,lnCount2] = laUsable[lnCount,lnCount2]
laUsable[lnCount,lnCount2] = .F.
Insert Into curSteps Values (lnCount,lnCount2, taGrid[lnCount,lnCount2], tnLevel,1)
llContinue = .T.
Exit
Endif
Endfor
Endfor
If llContinue
Loop
Endif
* examine usable digits/letters of rows
For lnCount=1 To cnBase*cnBase
lcUsable = ""
For lnCount2=1 To cnBase*cnBase
If !Empty(taGrid[lnCount , lnCount2])
Loop
Endif
lcUsable = lcUsable + laUsable[lnCount,lnCount2]
Endfor
* any letter only once available?
lcLetter=""
For lnCount2=1 To Len(lcUsable)
If Occurs(Substr(lcUsable,lnCount2,1),lcUsable)=1
* yes!
lcLetter = Substr(lcUsable,lnCount2,1)
Exit
Endif
Endfor
If !Empty(lcLetter)
For lnCount2=1 To cnBase*cnBase
If !Empty(taGrid[lnCount,lnCount2])
Loop
Endif
If lcLetter $ laUsable[lnCount,lnCount2]
taGrid[lnCount,lnCount2] = lcLetter
laUsable[lnCount,lnCount2] = .F.
Insert Into curSteps Values (lnCount,lnCount2, taGrid[lnCount,lnCount2], tnLevel,2)
llContinue = .T.
Exit
Endif
Endfor
Endif
If llContinue
Exit
Endif
Endfor
If llContinue
Loop
Endif
* examine usable digits/letters of cols
For lnCount=1 To cnBase*cnBase
lcUsable = ""
For lnCount2=1 To cnBase*cnBase
If !Empty(taGrid[lnCount2, lnCount])
Loop
Endif
lcUsable = lcUsable + laUsable[lnCount2,lnCount]
Endfor
lcLetter=""
For lnCount2=1 To Len(lcUsable)
If Occurs(Substr(lcUsable,lnCount2,1),lcUsable)=1
lcLetter = Substr(lcUsable,lnCount2,1)
Exit
Endif
Endfor
If !Empty(lcLetter)
For lnCount2=1 To cnBase*cnBase
If !Empty(taGrid[lnCount2,lnCount])
Loop
Endif
If lcLetter $ laUsable[lnCount2,lnCount]
taGrid[lnCount2,lnCount] = lcLetter
laUsable[lnCount2,lnCount] = .F.
Insert Into curSteps Values (lnCount2, lnCount, taGrid[lnCount2,lnCount], tnLevel,3)
llContinue = .T.
Exit
Endif
Endfor
Endif
If llContinue
Exit
Endif
Endfor
If llContinue
Loop
Endif
* examine usable digits/letters of blocks
For lnCount=1 To cnBase*cnBase
lcUsable = ""
For lnCount2=1 To cnBase*cnBase
liRow = Int((lnCount-1)/cnBase)*cnBase +Int((lnCount2-1)/cnBase)+1
liCol = (lnCount-1)%cnBase*cnBase+(lnCount2-1)%cnBase+1
If !Empty(taGrid[liRow,liCol])
Loop
Endif
lcUsable = lcUsable + laUsable[liRow,liCol]
Endfor
lcLetter=""
For lnCount2=1 To Len(lcUsable)
If Occurs(Substr(lcUsable,lnCount2,1),lcUsable)=1
lcLetter = Substr(lcUsable,lnCount2,1)
Exit
Endif
Endfor
If !Empty(lcLetter)
For lnCount2=1 To cnBase*cnBase
liRow = Int((lnCount-1)/cnBase)*cnBase +Int((lnCount2-1)/cnBase)+1
liCol = (lnCount-1)%cnBase*cnBase+(lnCount2-1)%cnBase+1
If !Empty(taGrid[liRow,liCol])
Loop
Endif
If lcLetter $ laUsable[liRow,liCol]
taGrid[liRow,liCol] = lcLetter
laUsable[liRow,liCol] = .F.
Insert Into curSteps Values (liRow, liCol, lcLetter, tnLevel,4)
llContinue = .T.
Exit
Endif
Endfor
Endif
If llContinue
Exit
Endif
Endfor
Enddo
* no sure letter, so simply try and error now:
Local lcLeft,lcRight
For lnCount=1 To cnBase*cnBase
For lnCount2=1 To cnBase*cnBase
* two letters available only?
If Empty(taGrid[lnCount,lnCount2]) And Len(laUsable[lnCount,lnCount2])=2
* try both
lcLeft = Left(laUsable[lnCount,lnCount2],1)
lcRight = Right(laUsable[lnCount,lnCount2],1)
Select curGrid
Append From Array taGrid
Replace All iLevel With tnLevel For iLevel=0
laUsable[lnCount,lnCount2] = .F.
taGrid[lnCount,lnCount2] = lcLeft
Insert Into curSteps Values (lnCount, lnCount2, lcLeft, tnLevel,5)
If !solvesudoku(@taGrid, tnLevel+1) &&recurse
* restore Grid from before recursion
Select &gcGridFields From curGrid Where iLevel = tnLevel Into Array taGrid
* restore Grid log
Select * From curGrid Where iLevel<=tnLevel Into cursor curGrid readwrite
* restore steps from before recursion
Select * From curSteps Where iLevel<=tnLevel Into Cursor curSteps Readwrite
* including deletion of the last step
Go Bottom In curSteps
Delete Next 1 In curSteps
taGrid[lnCount,lnCount2] = lcRight
Insert Into curSteps Values (lnCount, lnCount2, lcRight, tnLevel,6)
If !solvesudoku(@taGrid, tnLevel+1) &&recurse
* restore Grid from before recursion
Select &gcGridFields From curGrid Where iLevel = tnLevel Into Array taGrid
* also restore Grid log
Select * From curGrid Where iLevel<tnLevel Into cursor curGrid readwrite
* restore steps from before recursion
Select * From curSteps Where iLevel<=tnLevel Into Cursor curSteps Readwrite
* including deletion of the last step
Go Bottom In curSteps
Delete Next 1 In curSteps
Return .F.
Endif
Endif
Endif
Endfor
Endfor
Endproc