HMG Sudoku

You can share your experience with HMG. Share with some screenshots/project details so that others will also be benefited.

Moderator: Rathinagiri

User avatar
Rathinagiri
Posts: 5173
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 136 times
Been thanked: 131 times
Contact:

HMG Sudoku

Post by Rathinagiri » Sat Apr 24, 2010 3:32 pm

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 2107 times
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
esgici
Posts: 4346
Joined: Wed Jul 30, 2008 9:17 pm
DBs Used: DBF
Location: iskenderun / Turkiye
Has thanked: 234 times
Been thanked: 80 times
Contact:

Post by esgici » Sat Apr 24, 2010 3:43 pm

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
Viva INTERNATIONAL HMG :D

User avatar
Czarny_Pijar
Posts: 172
Joined: Thu Mar 18, 2010 11:31 pm
Location: 19.2341 E 50.2267 N

Post by Czarny_Pijar » Sat Apr 24, 2010 3:47 pm

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.

User avatar
Rathinagiri
Posts: 5173
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 136 times
Been thanked: 131 times
Contact:

Post by Rathinagiri » Sat Apr 24, 2010 4:09 pm

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.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
srvet_claudio
Posts: 1953
Joined: Thu Feb 25, 2010 8:43 pm
Location: Uruguay
Has thanked: 31 times
Been thanked: 120 times
Contact:

Post by srvet_claudio » Sat Apr 24, 2010 6:04 pm

Fantastic !!!
Very good.
Congratulations Rathinagiri !!!

Best regards,
Claudio Soto.
Best regards.
Dr. Claudio Soto
(from Uruguay)
http://srvet.blogspot.com

User avatar
Czarny_Pijar
Posts: 172
Joined: Thu Mar 18, 2010 11:31 pm
Location: 19.2341 E 50.2267 N

Post by Czarny_Pijar » Sun Apr 25, 2010 9:42 am

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.

User avatar
Rathinagiri
Posts: 5173
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 136 times
Been thanked: 131 times
Contact:

Post by Rathinagiri » Sun Apr 25, 2010 11:45 am

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
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
Czarny_Pijar
Posts: 172
Joined: Thu Mar 18, 2010 11:31 pm
Location: 19.2341 E 50.2267 N

Post by Czarny_Pijar » Sun Apr 25, 2010 1:48 pm

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.

User avatar
Rathinagiri
Posts: 5173
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 136 times
Been thanked: 131 times
Contact:

Post by Rathinagiri » Mon Apr 26, 2010 10:41 am

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. :)
Attachments
sudoku.zip
(2.93 KiB) Downloaded 181 times
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
Alex Gustow
Posts: 290
Joined: Thu Dec 04, 2008 1:05 pm
Location: Yekaterinburg, Russia
Contact:

Post by Alex Gustow » Tue Apr 27, 2010 7:35 am

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
Attachments
Sudoku_Ra_Gu.zip
(152.05 KiB) Downloaded 196 times

Post Reply