Page 1 of 2

HMG Sudoku

Posted: Sat Apr 24, 2010 3:32 pm
by Rathinagiri
Hi,

Had you ever imagined you can write a Sudoku game in just 162 lines of coding? HMG made it possible. :)

Believe me, it took only an hour of my life.

Though it is not a solver by itself, you can play the game. This is the initial version. Give your suggestions to improve.

Code: Select all

#include <hmg.ch>

Function Main
local bColor                   
private aSudoku := {{0,0,0,2,0,3,8,0,1},;
                    {0,0,0,7,0,6,0,5,2},;
                    {2,0,0,0,0,0,0,7,9},;
                    {0,2,0,1,5,7,9,3,4},;
                    {0,0,3,0,0,0,1,0,0},;
                    {9,1,7,3,8,4,0,2,0},;
                    {1,8,0,0,0,0,0,0,6},;
                    {7,3,0,6,0,1,0,0,0},;
                    {6,0,5,8,0,9,0,0,0}}
private aOriginal := {}

bColor := {||SudokuBackcolor()}

aOriginal := aclone(aSudoku)


define window Sudoku at 0,0 width 500 height 550 main title "HMG Sudoku"
   define grid square
      row 10
      col 10
      width 460
      height 445
      showheaders .f.
      headers {"","","","","","","","",""}
      widths {50,50,50,50,50,50,50,50,50}
      justify {2,2,2,2,2,2,2,2,2}
      allowedit .t.
      columncontrols {{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"}}
      fontsize 30
      dynamicbackcolor {bColor,bColor,bColor,bColor,bColor,bColor,bColor,bColor,bColor}
      columnwhen {{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()}}
      columnvalid {{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()}}
      onchange checkpossiblevalues()      
      fontname "Arial"
      cellnavigation .t.
   end grid
   define label valid
      row 460
      col 10
      fontname "Arial"
      width 480
      fontsize 18
   end label   
end window
on key ESCAPE of Sudoku action Sudoku.release()
refreshsudokugrid()
sudoku.center
sudoku.activate
Return nil

function SudokuBackColor
do case
   case this.cellrowindex <= 3 // first row
      do case
         case this.cellcolindex <= 3 // first col
            return {200,100,100}
         case this.cellcolindex > 3 .and. this.cellcolindex <= 6// second col
            return {100,200,100}
         case this.cellcolindex > 6 // third col
            return {100,100,200}
      endcase
   case this.cellrowindex > 3 .and. this.cellrowindex <= 6  // second row       
      do case
         case this.cellcolindex <= 3 // first col
            return {100,200,100}
         case this.cellcolindex > 3 .and. this.cellcolindex <= 6// second col
            return {200,200,100}
         case this.cellcolindex > 6 // third col
            return {100,200,100}
      endcase
   case this.cellrowindex > 6 // third row       
      do case
         case this.cellcolindex <= 3 // first col
            return {100,100,200}
         case this.cellcolindex > 3 .and. this.cellcolindex <= 6// second col
            return {100,200,100}
         case this.cellcolindex > 6 // third col
            return {200,100,100}
      endcase
endcase
return nil           

function refreshsudokugrid
local i := 0
local aLine := {}
local aValue := sudoku.square.value
sudoku.square.deleteallitems()
if len(aSudoku) == 9
   for i := 1 to len(aSudoku)
      asize(aLine,0)
      for j := 1 to len(aSudoku[i])
         if aSudoku[i,j] > 0
            aadd(aLine,str(aSudoku[i,j],1,0))
         else
            aadd(aLine,'')
         endif
      next j
      sudoku.square.additem(aLine)   
   next i
endif
sudoku.square.value := aValue
return nil

function entergrid
local aValue := sudoku.square.value
if len(aValue) > 0
  if aOriginal[aValue[1],aValue[2]] > 0
      return .f.
  else
      return .t.
  endif
endif
return .f.             

function checkgrid
local nRow := this.cellrowindex
local nCol := this.cellcolindex
local nValue := val(alltrim(this.cellvalue))
local aLine := {}
local i := 0
local j := 0
local nRowStart := (int((nRow-1)/3) * 3) + 1
local nRowEnd := nRowstart + 2
local nColStart := (int((nCol-1)/3) * 3) + 1
local nColEnd := nColstart + 2
if nValue == 0
   this.cellvalue := ''
   sudoku.valid.value := ''
   aSudoku[nRow,nCol] := 0
   return .t.
endif
for i := 1 to 9
   if aSudoku[nRow,i] == nValue //row checking
      return .f.
   endif
   if aSudoku[i,nCol] == nValue //col checking
      return .f.
   endif
next i
for i := nRowStart to nRowEnd
   for j := nColStart to nColEnd
      if aSudoku[i,j] == nValue
         return .f.
      endif
   next j
next i
sudoku.valid.value := ''
aSudoku[nRow,nCol] := nValue
checkcompletion()
return .t.
   

function checkcompletion
for i := 1 to len(aSudoku)
   for j := 1 to len(aSudoku[i])
      if aSudoku[i,j] == 0
         return nil
      endif
   next j
next i
msginfo("Congrats! You won!")
return nil

function checkpossiblevalues
local aValue := sudoku.square.value
local aAllowed := {}
local cAllowed := ""
local nRowStart := (int((aValue[1]-1)/3) * 3) + 1
local nRowEnd := nRowstart + 2
local nColStart := (int((aValue[2]-1)/3) * 3) + 1
local nColEnd := nColstart + 2
local lAllowed := .t.
local i := 0
local j := 0
local k := 0
if aValue[1] > 0 .and. aValue[2] > 0 
   if aOriginal[aValue[1],aValue[2]] > 0
      sudoku.valid.value := ""
      return nil
   else
      for i := 1 to 9
         lAllowed := .t.
         for j := 1 to 9
            if aSudoku[aValue[1],j] == i
               lAllowed := .f.
            endif
            if aSudoku[j,aValue[2]] == i
               lAllowed := .f.
            endif
         next j
         for j := nRowStart to nRowEnd
            for k := nColStart to nColEnd
               if aSudoku[j,k] == i
                  lAllowed := .f.
               endif
            next k
         next j
         if lAllowed
            aadd(aAllowed,i)
         endif
      next i
      if len(aAllowed) > 0
         for i := 1 to len(aAllowed)
            if i == 1
               cAllowed := cAllowed + alltrim(str(aAllowed[i]))
            else
               cAllowed := cAllowed + ", "+ alltrim(str(aAllowed[i]))
            endif
         next i
         sudoku.valid.value := "Possible Numbers: "+cAllowed
      else
         sudoku.valid.value := "Possible Numbers: Nil" 
      endif               
      return nil
   endif
endif
return nil                      
Screenshot:
sudoku.jpg
sudoku.jpg (40.03 KiB) Viewed 6675 times

Re: HMG Sudoku

Posted: Sat Apr 24, 2010 3:43 pm
by esgici
rathinagiri wrote: Had you ever imagined you can write a Sudoku game in just 257 lines of coding? HMG made it possible. :)
Believe me, it took only an hour of my life.
Fantastic :!: thanks Rathi :D

Regards

--

Esgici

Re: HMG Sudoku

Posted: Sat Apr 24, 2010 3:47 pm
by Czarny_Pijar
You can believe it or not, yesterday a friend called me and asked me if I know something about programming Sudoku. Therefore, I shall take a more detailed look at this.

Re: HMG Sudoku

Posted: Sat Apr 24, 2010 4:09 pm
by Rathinagiri
Thanks Esgici. Now it is only 162 Lines. ;)

Czarny_Pijar, it is not a solver. It is a platform to play Sudoku. I am studying the solving logics to scientifically solve any sudoku puzzle.

Re: HMG Sudoku

Posted: Sat Apr 24, 2010 6:04 pm
by srvet_claudio
Fantastic !!!
Very good.
Congratulations Rathinagiri !!!

Best regards,
Claudio Soto.

Re: HMG Sudoku

Posted: Sun Apr 25, 2010 9:42 am
by Czarny_Pijar
A wish: Another non-editable grid below (1 row ,9 columns). Let we call it 'hint_grid'. It contains the numbers 1,2,3,4,5,6,7,8,9 red backgrounded.

After entering into the edit state in the sudoku-grid, the valid numbers in the hint_grid should get the green background,
when the edit is finished they should return to the red color.

Looks like the function 'checkgrid' can be re-utilized here.

Re: HMG Sudoku

Posted: Sun Apr 25, 2010 11:45 am
by Rathinagiri
Hi,

I had fixed some bugs and optimized the code. Implemented Czarny_Pijar's request too!

Code: Select all

#include <hmg.ch>

Function Main
local bColor                   
private aSudoku := {{0,0,0,2,0,3,8,0,1},;
                    {0,0,0,7,0,6,0,5,2},;
                    {2,0,0,0,0,0,0,7,9},;
                    {0,2,0,1,5,7,9,3,4},;
                    {0,0,3,0,0,0,1,0,0},;
                    {9,1,7,3,8,4,0,2,0},;
                    {1,8,0,0,0,0,0,0,6},;
                    {7,3,0,6,0,1,0,0,0},;
                    {6,0,5,8,0,9,0,0,0}}
private aOriginal := {}

bColor := {||SudokuBackcolor()}

aOriginal := aclone(aSudoku)


define window Sudoku at 0,0 width 500 height 550 main title "HMG Sudoku"
   define grid square
      row 10
      col 10
      width 460
      height 445
      showheaders .f.
      headers {"","","","","","","","",""}
      widths {50,50,50,50,50,50,50,50,50}
      justify {2,2,2,2,2,2,2,2,2}
      allowedit .t.
      columncontrols {{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"},{"TEXTBOX","CHARACTER","9"}}
      fontsize 30
      dynamicbackcolor {bColor,bColor,bColor,bColor,bColor,bColor,bColor,bColor,bColor}
      columnwhen {{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()},{||entergrid()}}
      columnvalid {{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()},{||checkgrid()}}
      onchange checkpossiblevalues()      
      fontname "Arial"
      cellnavigation .t.
   end grid
   define label valid
      row 460
      col 10
      fontname "Arial"
      width 480
      fontsize 18
   end label   
end window
on key ESCAPE of Sudoku action Sudoku.release()
refreshsudokugrid()
sudoku.center
sudoku.activate
Return nil

function SudokuBackColor
do case
   case this.cellrowindex <= 3 // first row
      do case
         case this.cellcolindex <= 3 // first col
            return {200,100,100}
         case this.cellcolindex > 3 .and. this.cellcolindex <= 6// second col
            return {100,200,100}
         case this.cellcolindex > 6 // third col
            return {100,100,200}
      endcase
   case this.cellrowindex > 3 .and. this.cellrowindex <= 6  // second row       
      do case
         case this.cellcolindex <= 3 // first col
            return {100,200,100}
         case this.cellcolindex > 3 .and. this.cellcolindex <= 6// second col
            return {200,200,100}
         case this.cellcolindex > 6 // third col
            return {100,200,100}
      endcase
   case this.cellrowindex > 6 // third row       
      do case
         case this.cellcolindex <= 3 // first col
            return {100,100,200}
         case this.cellcolindex > 3 .and. this.cellcolindex <= 6// second col
            return {100,200,100}
         case this.cellcolindex > 6 // third col
            return {200,100,100}
      endcase
endcase
return nil           

function refreshsudokugrid
local i := 0
local aLine := {}
local aValue := sudoku.square.value
sudoku.square.deleteallitems()
if len(aSudoku) == 9
   for i := 1 to len(aSudoku)
      asize(aLine,0)
      for j := 1 to len(aSudoku[i])
         if aSudoku[i,j] > 0
            aadd(aLine,str(aSudoku[i,j],1,0))
         else
            aadd(aLine,'')
         endif
      next j
      sudoku.square.additem(aLine)   
   next i
endif
sudoku.square.value := aValue
return nil

function entergrid
local aValue := sudoku.square.value
if len(aValue) > 0
  if aOriginal[aValue[1],aValue[2]] > 0
      return .f.
  else
      return .t.
  endif
endif
return .f.             

function checkgrid
local nRow := this.cellrowindex
local nCol := this.cellcolindex
local nValue := val(alltrim(this.cellvalue))
local aLine := {}
local i := 0
local j := 0
local nRowStart := (int((nRow-1)/3) * 3) + 1
local nRowEnd := nRowstart + 2
local nColStart := (int((nCol-1)/3) * 3) + 1
local nColEnd := nColstart + 2
if nValue == 0
   this.cellvalue := ''
   sudoku.valid.value := ''
   aSudoku[nRow,nCol] := 0
   return .t.
endif
for i := 1 to 9
   if aSudoku[nRow,i] == nValue //row checking
      return .f.
   endif
   if aSudoku[i,nCol] == nValue //col checking
      return .f.
   endif
next i
for i := nRowStart to nRowEnd
   for j := nColStart to nColEnd
      if aSudoku[i,j] == nValue
         return .f.
      endif
   next j
next i
sudoku.valid.value := ''
aSudoku[nRow,nCol] := nValue
checkcompletion()
return .t.
   

function checkcompletion
for i := 1 to len(aSudoku)
   for j := 1 to len(aSudoku[i])
      if aSudoku[i,j] == 0
         return nil
      endif
   next j
next i
msginfo("Congrats! You won!")
return nil

function checkpossiblevalues
local aValue := sudoku.square.value
local aAllowed := {}
local cAllowed := ""
local nRowStart := (int((aValue[1]-1)/3) * 3) + 1
local nRowEnd := nRowstart + 2
local nColStart := (int((aValue[2]-1)/3) * 3) + 1
local nColEnd := nColstart + 2
local lAllowed := .t.
local i := 0
local j := 0
local k := 0
if aValue[1] > 0 .and. aValue[2] > 0 
   if aOriginal[aValue[1],aValue[2]] > 0
      sudoku.valid.value := ""
      return nil
   else
      for i := 1 to 9
         lAllowed := .t.
         for j := 1 to 9
            if aSudoku[aValue[1],j] == i
               lAllowed := .f.
            endif
            if aSudoku[j,aValue[2]] == i
               lAllowed := .f.
            endif
         next j
         for j := nRowStart to nRowEnd
            for k := nColStart to nColEnd
               if aSudoku[j,k] == i
                  lAllowed := .f.
               endif
            next k
         next j
         if lAllowed
            aadd(aAllowed,i)
         endif
      next i
      if len(aAllowed) > 0
         for i := 1 to len(aAllowed)
            if i == 1
               cAllowed := cAllowed + alltrim(str(aAllowed[i]))
            else
               cAllowed := cAllowed + ", "+ alltrim(str(aAllowed[i]))
            endif
         next i
         sudoku.valid.value := "Possible Numbers: "+cAllowed
      else
         sudoku.valid.value := "Possible Numbers: Nil" 
      endif               
      return nil
   endif
endif
return nil

Re: HMG Sudoku

Posted: Sun Apr 25, 2010 1:48 pm
by Czarny_Pijar
Thank you for taking into account my wish.
Now, solving sudoku will be much easier for me. I will depend more on thinking than perceptiveness.

Re: HMG Sudoku

Posted: Mon Apr 26, 2010 10:41 am
by Rathinagiri
In this version, I have fixed some bugs.

Puzzles can be imported from a csv text file 'sudoku.csv' in the program directory. I included 10 puzzles in a sample file. Enjoy. :)

Re: HMG Sudoku

Posted: Tue Apr 27, 2010 7:35 am
by Alex Gustow
Hi, Rathi

I added my 5 cents to our common enjoying with your Sudoku :)

1. I used (I wrote about few weeks/months ago) "auto-zoom" technique (look at relative window size - and all controls too - with different screen resolutions: ie 800x600, 1024x768 etc... you see "the same view" of app [so-so - but "something like"]; my users likes this "design style" :) ).

2. I added "Help" button and little help (compiled to .CHM; borrowed from http://www.sudoku.name ; separated files - .HTM, images etc. - in subfolder \HELP [it's no need to app! only .CHM]) - it helps for beginners (like me :) ) I think.

3. and little "editing" into PRG (for better viewing/understanding).

Your "old" PRG is MAIN.PRG, but mine - MAIN_GU.PRG