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


 

Comments (8)

  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

Skip to main content