# The mechanics of Sudoku

The rules of Sudoku are so simple (see Sudoku puzzles screen capture)  that it seems easy to write the mechanics of the puzzle in Fox. It took 20 minutes to write this. Move the mouse over a desired square and type a digit key. ‘0’ means erase what’s there. It doesn’t know any of the rules, but is a tool that can help you solve a puzzle.

It includes a rudimentary input/storage/output mechanism of saving Sudoku puzzles as a text string of 81 digits. A blank space is represented by 0 (how convenient!)

The 2 Sudoku puzzles included (myvar and myvar2) are the same: one is more solved than the other.

You can write a subclass of this code and add some methods to actually give hints or even generate Sudoku puzzles

If you improve upon this please let me know.

CLEAR ALL

CLEAR

PUBLIC ox

#define CELLHEIGHT 60

#define CELLWIDTH 60

#define NCELLSX 9

#define NCELLSY 9

#define XOFFSET 50

#define YOFFSET 50

#define CELLOFFX CELLWIDTH/3

#define CELLOFFY CELLHEIGHT/3

#define BOLDLINE 0xff0000

#define NORMLINE 0xffff

ox=CREATEOBJECT("Sudoku")

ox.show

DEFINE CLASS Sudoku AS FORM

left=200

backcolor=0xffffff

fontname="Courier New"

allowoutput=.f.

DIMENSION aSq[NCELLSX, NCELLSY]

PROCEDURE Init

this.DrawGrid

TEXT TO myvar

704002603

602730590

305006070

456900030

238641957

179325468

063200005

027060309

041803726

ENDTEXT

TEXT TO myvar2

004002600

002030090

300000070

400900030

000641000

070005008

060000005

020060030

001800700

ENDTEXT

this.aSq=0

IF ALINES(aal,myvar)=NCELLSX

FOR j = 1 TO ALEN(aal,1)

FOR i = 1 TO LEN(aal[j])

this.aSq[i,j]=VAL(SUBSTR(aal[j],i,1))

ENDFOR

ENDFOR

ENDIF

this.ShowNums

PROCEDURE ShowNums

thisform.ForeColor=0

thisform.FontSize=20

FOR i = 1 TO NCELLSX

FOR j = 1 TO NCELLSY

this.shownum(i-1,j-1,this.aSQ[i,j])

ENDFOR

ENDFOR

PROCEDURE ShowNum(i,j,num)

thisform.Print(IIF(num=0," ",TRANSFORM(num)),CELLOFFX+XOFFSET +i * CELLWIDTH , CELLOFFY+YOFFSET + j * CELLHEIGHT)

PROCEDURE keypress(p1,p2)

DO CASE

CASE  p1=27

thisform.Release

CASE p1=ASC("C") OR p1=ASC("c")

OTHERWISE

IF p1 >= 48 AND p1 < 58

AMOUSEOBJ(aa,1)

i=1+INT((aa[3]-XOFFSET)/CELLWIDTH)

j=1+INT((aa[4]-YOFFSET)/CELLHEIGHT)

IF i>0 AND i <= NCELLSX AND J>0 AND j<=NCELLSY

thisform.asq[i,j]=p1-48

thisform.ForeColor=0xff0000

thisform.ShowNum(i-1,j-1,p1-48)

ELSE

?"Illegal"

ENDIF

ELSE

?p1,p2

ENDIF

ENDCASE

PROCEDURE DrawGrid

WITH this as Form

.Width = CELLWIDTH * NCELLSX+XOFFSET*2

.Height = CELLHEIGHT * NCELLSY+YOFFSET*2

FOR i = 0 TO NCELLSY    && draw horiz

IF MOD(i,3)=0

.ForeColor=BOLDLINE

FOR j = -1 TO 1

.Line(XOFFSET,YOFFSET+ CELLWIDTH*i+j, XOFFSET+CELLWIDTH * NCELLSX, YOFFSET+ CELLWIDTH*i+j)

ENDFOR

ELSE

.ForeColor=NORMLINE

.Line(XOFFSET,YOFFSET+ CELLWIDTH*i, XOFFSET+CELLWIDTH * NCELLSX, YOFFSET+ CELLWIDTH*i)

ENDIF

ENDFOR

FOR i = 0 TO NCELLSX    && draw VERT

IF MOD(i,3)=0

.ForeColor=BOLDLINE

FOR j = -1 TO 1

.Line(j+XOFFSET+ CELLHEIGHT*i,YOFFSET, j+XOFFSET+ CELLHEIGHT*i,YOFFSET+ CELLHEiGHT * NCELLSY)

ENDFOR

ELSE

.ForeColor=NORMLINE

.Line(XOFFSET+ CELLHEIGHT*i,YOFFSET, XOFFSET+ CELLHEIGHT*i,YOFFSET+ CELLHEiGHT * NCELLSY)

ENDIF

ENDFOR

ENDWITH

ENDDEFINE

1. soe says:

hey, how ’bout making it ink ready for tabletters?

2. Fabio Lunardon says:

Can you answer to the two matters?

– Text on right border

– Paint

CLEAR ALL

CLEAR

#define CELLHEIGHT 60

#define CELLWIDTH 60

#define NCELLSX 9

#define NCELLSY 9

#define XOFFSET 50

#define YOFFSET 50

#define CELLOFFX CELLWIDTH/3

#define CELLOFFY CELLHEIGHT/3

#define BOLDLINE 0xff0000

#define NORMLINE 0xffff

PUBLIC ox

ox=CREATEOBJECT("Sudoku")

ox.show

DEFINE CLASS Sudoku AS FORM

PaintMutex = .F.

left=200

backcolor=0xffffff

fontname="Courier New"

allowoutput=.f.

Width = CELLWIDTH * NCELLSX+XOFFSET*2

Height = CELLHEIGHT * NCELLSY+YOFFSET*2

DIMENSION aSq[NCELLSX, NCELLSY]

PROCEDURE Init

this.DrawGrid

TEXT TO myvar

704002603

602730590

305006070

456900030

238641957

179325468

063200005

027060309

041803726

ENDTEXT

TEXT TO myvar2

004002600

002030090

300000070

400900030

000641000

070005008

060000005

020060030

001800700

ENDTEXT

this.aSq=0

IF ALINES(aal,myvar)=NCELLSX

FOR j = 1 TO ALEN(aal,1)

FOR i = 1 TO LEN(aal[j])

this.aSq[i,j]=VAL(SUBSTR(aal[j],i,1))

ENDFOR

ENDFOR

ENDIF

this.ShowNums

PROCEDURE ShowNums

thisform.ForeColor=0

thisform.FontSize=20

FOR i = 1 TO NCELLSX

FOR j = 1 TO NCELLSY

this.shownum(i-1,j-1,this.aSQ[i,j])

ENDFOR

ENDFOR

PROCEDURE ShowNum(i,j,num)

* NEXT OPEN A ISSUE #1 :

* IS IMPOSSIBLE REDRAW A PARTIAL TEXT ( NEAR TO THE BORDER ) WHEN A FORM IS RESIZED

* MOVE THE RIGHT BORDER OVER A NUMBER

IF CELLOFFX+XOFFSET +i * CELLWIDTH + FONTMETRIC(6,WFONT(1,thisform.Name),WFONT(2,thisform.Name),WFONT(3,thisform.Name)) < this.Width

thisform.Print(IIF(num=0," ",TRANSFORM(num)),CELLOFFX+XOFFSET +i * CELLWIDTH , CELLOFFY+YOFFSET + j * CELLHEIGHT)

ENDIF

PROCEDURE keypress(p1,p2)

DO CASE

CASE p1=27

thisform.Release

CASE p1=ASC("C") OR p1=ASC("c")

OTHERWISE

IF p1 >= 48 AND p1 < 58

AMOUSEOBJ(aa,1)

i=1+INT((aa[3]-XOFFSET)/CELLWIDTH)

j=1+INT((aa[4]-YOFFSET)/CELLHEIGHT)

IF i>0 AND i <= NCELLSX AND J>0 AND j<=NCELLSY

thisform.asq[i,j]=p1-48

thisform.ForeColor=0xff0000

thisform.ShowNum(i-1,j-1,p1-48)

ELSE

?"Illegal"

ENDIF

ELSE

?p1,p2

ENDIF

ENDCASE

PROCEDURE DrawGrid

WITH THIS AS FORM

* this order is better

.FORECOLOR=NORMLINE

FOR i = 0 TO NCELLSY && draw horiz

IF MOD(i,3)#0

.LINE(XOFFSET,YOFFSET+ CELLWIDTH*i, XOFFSET+CELLWIDTH * NCELLSX, YOFFSET+ CELLWIDTH*i)

ENDIF

ENDFOR

FOR i = 0 TO NCELLSX && draw VERT

IF MOD(i,3)#0

.LINE(XOFFSET+ CELLHEIGHT*i,YOFFSET, XOFFSET+ CELLHEIGHT*i,YOFFSET+ CELLHEiGHT * NCELLSY)

ENDIF

ENDFOR

.FORECOLOR=BOLDLINE

FOR j = -1 TO 1

FOR i = 0 TO NCELLSY STEP 3 && draw horiz

.LINE(XOFFSET,YOFFSET+ CELLWIDTH*i+j, XOFFSET+CELLWIDTH * NCELLSX, YOFFSET+ CELLWIDTH*i+j)

ENDFOR

FOR i = 0 TO NCELLSX STEP 3 && draw VERT

.LINE(j+XOFFSET+ CELLHEIGHT*i,YOFFSET, j+XOFFSET+ CELLHEIGHT*i,YOFFSET+ CELLHEiGHT * NCELLSY)

ENDFOR

ENDFOR

ENDWITH

PROCEDURE MouseMove(nButton, nShift, nXCoord, nYCoord)

THISFORM.MousePointer =;

IIF( BETWEEN(nXCoord,XOFFSET,XOFFSET+CELLWIDTH * NCELLSX);

AND BETWEEN(nyCoord,YOFFSET,YOFFSET+ CELLHEiGHT * NCELLSY);

, 2 ;

, 12)

PROCEDURE PAINT

* ISSUE #2 : Paint a Line,Box ….

* a VFP9 backward way

IF m.this.PaintMutex

this.PaintMutex = .F.

RETURN

ENDIF

this.PaintMutex = .T.

this.DrawGrid

this.ShowNums

* or capture the Paint message with BINDEVENT

* or a best Calvin’s solution ?

* Calvin:

* If it very quickly move a window of another process above to this,

* it happens that some piece of graphics is Not Repainted ( AutoYield F/T)

* This also happens with objects and Paint() empty

ENDDEFINE

3. Craig Snively says:

Wonderful Calvin. Thanks. I don’t have any code to add, but if you are looking at enhancements, take a look at what this person has done with an XLS spreadsheet. It is what we use for our Sudoku plays.

http://www.sudoku-xls.com/ It is a pretty complete package for playing the game.

Craig

4. Lou Harris says:

I modified the code so that conflicting numbers are shown in red. It required redrawing all the numbers each time a new number is added / removed, but here is the code:

<pre>

*!* CLEAR ALL

CLEAR

PUBLIC ox

#define CELLHEIGHT 60

#define CELLWIDTH 60

#define NCELLSX 9

#define NCELLSY 9

#define NDIVX 3

#define NDIVY 3

#define XOFFSET 50

#define YOFFSET 50

#define CELLOFFX CELLWIDTH/3

#define CELLOFFY CELLHEIGHT/3

#define BOLDLINE 0xff0000

#define NORMLINE 0xffff

ox=CREATEOBJECT("Sudoku")

ox.show

DEFINE CLASS Sudoku AS FORM

left=200

backcolor=0xffffff

fontname="Courier New"

allowoutput=.f.

windowtype = 1

name="sudoku"

DIMENSION aSq[NCELLSX, NCELLSY]

PROCEDURE Init

this.DrawGrid

TEXT TO myvar

704002603

602730590

305006070

456900030

238641957

179325468

063200005

027060309

041803726

ENDTEXT

TEXT TO myvar2

004002600

002030090

300000070

400900030

000641000

070005008

060000005

020060030

001800700

ENDTEXT

this.aSq=0

IF ALINES(aal,myvar)=NCELLSX

FOR j = 1 TO ALEN(aal,1)

FOR i = 1 TO LEN(aal[j])

this.aSq[i,j]=VAL(SUBSTR(aal[j],i,1))

ENDFOR

ENDFOR

ENDIF

this.ShowNums

PROCEDURE ShowNums

thisform.ForeColor=0

thisform.FontSize=20

FOR i = 1 TO NCELLSX

FOR j = 1 TO NCELLSY

IF this.aSQ[i,j] > 0

lnColor = 0

FOR k = 1 to NCELLSX

IF i#k AND this.aSQ[i,j] = this.aSQ[k,j]

lnColor = 255

EXIT

ENDIF

ENDFOR

IF m.lnColor = 0

FOR k = 1 to NCELLSY

IF j#k AND this.aSQ[i,j] = this.aSQ[i,k]

lnColor = 255

EXIT

ENDIF

ENDFOR

IF m.lnColor = 0

subx = FLOOR((i-1)/NDIVX) * NDIVX + 1

suby = FLOOR((j-1)/NDIVY) * NDIVY + 1

FOR k = subx to subx + NDIVX – 1

FOR l = suby to suby + NDIVY – 1

IF i#k AND j#l AND this.aSQ[i,j] = this.aSQ[k,l]

lnColor = 255

EXIT

ENDIF

ENDFOR

IF m.lnColor = 255

EXIT

ENDIF

ENDFOR

ENDIF

ENDIF

thisform.ForeColor=m.lnColor

ENDIF

this.shownum(i-1,j-1,this.aSQ[i,j])

ENDFOR

ENDFOR

PROCEDURE ShowNum(i,j,num)

thisform.Print(IIF(num=0," ",TRANSFORM(num)),CELLOFFX+XOFFSET +i * CELLWIDTH , CELLOFFY+YOFFSET + j * CELLHEIGHT)

PROCEDURE keypress(p1,p2)

DO CASE

CASE p1=27

thisform.Release

CASE p1=ASC("C") OR p1=ASC("c")

OTHERWISE

IF p1 >= 48 AND p1 < 58

AMOUSEOBJ(aa,1)

i=1+INT((aa[3]-XOFFSET)/CELLWIDTH)

j=1+INT((aa[4]-YOFFSET)/CELLHEIGHT)

IF i>0 AND i <= NCELLSX AND J>0 AND j<=NCELLSY

thisform.asq[i,j]=p1-48

thisform.ShowNums()

*!* thisform.ForeColor=0xff0000

*!* thisform.ShowNum(i-1,j-1,p1-48)

ELSE

?"Illegal"

ENDIF

ELSE

?p1,p2

ENDIF

ENDCASE

PROCEDURE DrawGrid

WITH this && as Form

.Width = CELLWIDTH * NCELLSX+XOFFSET*2

.Height = CELLHEIGHT * NCELLSY+YOFFSET*2

FOR i = 0 TO NCELLSY && draw horiz

IF MOD(i,3)=0

.ForeColor=BOLDLINE

FOR j = -1 TO 1

.Line(XOFFSET,YOFFSET+ CELLWIDTH*i+j, XOFFSET+CELLWIDTH * NCELLSX, YOFFSET+ CELLWIDTH*i+j)

ENDFOR

ELSE

.ForeColor=NORMLINE

.Line(XOFFSET,YOFFSET+ CELLWIDTH*i, XOFFSET+CELLWIDTH * NCELLSX, YOFFSET+ CELLWIDTH*i)

ENDIF

ENDFOR

FOR i = 0 TO NCELLSX && draw VERT

IF MOD(i,3)=0

.ForeColor=BOLDLINE

FOR j = -1 TO 1

.Line(j+XOFFSET+ CELLHEIGHT*i,YOFFSET, j+XOFFSET+ CELLHEIGHT*i,YOFFSET+ CELLHEiGHT * NCELLSY)

ENDFOR

ELSE

.ForeColor=NORMLINE

.Line(XOFFSET+ CELLHEIGHT*i,YOFFSET, XOFFSET+ CELLHEIGHT*i,YOFFSET+ CELLHEiGHT * NCELLSY)

ENDIF

ENDFOR

ENDWITH

ENDDEFINE

</pre>

5. Russell Campbell says:

You were discovering Sudoku about the same time I was. I posted some code to solve a puzzle at http://www.universalthread.com in message ID 1073558.

6. Several months ago, I wrote The mechanics of Sudoku and Sudoku puzzles screen capture. As a result, Stephen…

7. My wife and I like to listen to PuzzleMaster Will Shortz.on NPR. This week’s challenge is from one of