Version 0 of Playing sudoku

Updated 2005-05-31 21:19:25

if 0 {Richard Suchenwirth 2005-05-31 - The Sudoku puzzle seems to be quite popular in the UK. After reading the n-th time about it, I wanted to play with it, in my usual quest to implement things in Tcl, in order to understand them better.

A sudoku is a 9x9 matrix of digits 1..9, such that each occurs exactly once in every row, column, and 3x3 submatrix, if I understand the rules correctly. A matrix in Tcl is most easily implemented as a list of lists, whose elements can be conveniently accessed with two-index lindex and lset. The following matrix may be the "canonical" sudoku, as the first row and the first (top-left) "box" come in natural 1..9 order: }

 set s {
    {1 2 3  4 5 6  7 8 9}
    {4 5 6  7 8 9  1 2 3}
    {7 8 9  1 2 3  4 5 6}

    {2 3 4  5 6 7  8 9 1}
    {5 6 7  8 9 1  2 3 4}
    {8 9 1  2 3 4  5 6 7}

    {3 4 5  6 7 8  9 1 2}
    {6 7 8  9 1 2  3 4 5}
    {9 1 2  3 4 5  6 7 8}
 }

if 0 {It is also evident that the rows below the first are produced by rotation, by 3, 6, 1, 4, 7, 2, 5, 8 positions. Also, boxes are related to their left neighbor by row rotation, or to their upper neighbor by element rotation of one position.

Here's accessor functions to extract a given row, column, or box, returned as a list (boxes being indexed by

 0 3 6
 1 4 7
 2 5 8

):}

 proc sudoku'row {s i} {lindex $s $i}
 proc sudoku'col {s i} {
    foreach row $s {lappend res [lindex $row $i]}
    set res
 }
 proc sudoku'box {s i} {
    for {set r [expr ($i%3)*3]} {$r<($i%3)*3+3} {incr r} {
        for {set c [expr ($i/3)*3]} {$c<($i/3)*3+3} {incr c} {
            lappend res [lindex $s $r $c]
        }
    }
    set res
 }

if 0 {To validate a sudoku, each row, column, and box has to be tested whether it contains the digits 1..9 exactly once:}

 proc sudoku'ok1 list {expr {[lsort $list] eq {1 2 3 4 5 6 7 8 9}}}
 proc sudoku'ok s {
    foreach dim {row col box} {
        foreach $dim {0 1 2 3 4 5 6 7 8} {
            if ![sudoku'ok1 [sudoku'$dim $s [set $dim]]] {
                error "invalid sudoku $dim [set $dim]: \
                        {[sudoku'$dim $s [set $dim]]}"
            }
        }
    }
 }

#-- Testing

 sudoku'ok $s

if 0 {No error is thrown, so it looks like the code above is good...

As regular as this canonical sudoku is, it is perhaps not exactly challenging for a trained user, once he discovers the regularity. Without proof, I just postulate that every combination of

  • regular permutation (e.g. swap all 1's and 2's)
  • swap of box-rows or box-columns (boxes staying intact)
  • transposition or mirroring along x or y axes

will again produce a valid sudoku, and after several of these it might even be a challenge for puzzlers...

For example, here's a simple transposition:}

 proc transpose matrix {
    set cmd list
    set i -1
    foreach col [lindex $matrix 0] {append cmd " $[incr i]"}
    foreach row $matrix {
        set i -1
        foreach col $row {lappend [incr i] $col}
    }
    eval $cmd
 }
 sudoku'ok [transpose $s]

#-- Trying a permutation:

 sudoku'ok [string map {1 2 2 1 3 4 4 3 5 6 6 5 7 8 8 7} $s]

#-- and a mirroring (along the y axis):

 proc lreverse list {
    if [set i [llength $list]] {
        while {[incr i -1]>=0} {lappend res [lindex $list $i]}
        set res
    }
 }
 sudoku'ok [lreverse $s]

#-- Reformatting a transformed matrix to look like a sudoku again:

 proc sudoku'format s {
    set n 0
    foreach row $s {
        append res [regsub -all (......?) $row {\1 }]\n
        if {([incr n]%3)==0} {append res \n}
    }
    set res
 }

if 0 {Example:

 % sudoku'format [transpose $s]
 1 4 7  2 5 8  3 6 9 
 2 5 8  3 6 9  4 7 1 
 3 6 9  4 7 1  5 8 2 

 4 7 1  5 8 2  6 9 3 
 5 8 2  6 9 3  7 1 4 
 6 9 3  7 1 4  8 2 5 

 7 1 4  8 2 5  9 3 6 
 8 2 5  9 3 6  1 4 7 
 9 3 6  1 4 7  2 5 8 

Arts and crafts of Tcl-Tk programming }