[Keith Vetter] 2007-02-09 : Artificial intelligence in Tcl? Here's a program that while I wouldn't call intelligent it clearly does a task which for humans requires intelligence--solving logic problems. I guess that qualifies as weak AI [http://en.wikipedia.org/wiki/Weak_AI]. Anyway, recently I received the fun book ''Challenging Logic Puzzles'' by Barry R. Clarke [http://www.amazon.com/Challenging-Logic-Puzzles-Mensa-Clarke/dp/1402705417/sr=1-1/qid=1170993886/ref=sr_1_1/104-4625890-2573534]. Many of the puzzles in the book are of the following format: several rows of information organized into several columns categories such as name, occupation, favorite food, etc. But for various (amusing) reasons, the lists are scrambled such that one and only one item in each column is in its correct position. Finally we're given a list of constraints such as ''the person who eats ice cream is two place above the man in the red shirt''. The goal is to unscramble the list. Here's an example from the book: Beating at Eating Name Surname Dessert Agatha Greed cream puffs Bugsy Forager trifle Delilah Eatalot cheesecake Chuck Hunk ice cream (short spiel about an eating contest and how the lists got messed up so that one and only one item in each column is correctly positioned) 1) Chuck is one place above ice cream 2) Trifle is not above Delilah 3) Greed is two places below Delilah 4) Trifle is one place above Forager. Fun little puzzle, give it a try. After solving a bunch of these, I decided to see if I could program the computer to solve these types of problems. The approach I took is to enumerate all possible solutions and test each one to see if it matches all the conditions. There were two challenges. First is to enumerate and test all possible combinations. [Permutations] helped with the enumeration and some fun coding help with the testing. The bigger challenge was to make the process efficient. The number of possible solutions grows really fast: N rows of M columns yields N!**M possibilities. A simple 4x3 puzzle has 13,824 possible solutions, one 6x4 has 268,738,560,000. I found 3 ways to reduce this number. First, throw out all possibilities not having exactly one item correctly placed. Second, you can prune whole chunks of possibilities by noting that if a certain ordering of column A fails some constraint then you don't need to test that ordering of A with all the possible orderings of the other columns. Third, an optimal reordering of the constraints can increase the likelihood of big chunks being pruned. How good were these optimations? Well a 5x4 puzzle originally took over thirteen minutes to solve now completes in three seconds (207,360,000 possible solutions reduced to 5,765). The purpose of this program is to let you enter in puzzles of this type and have the computer solve them. I've included a dozen samples from the book of varying complexity. Give a try at solving them by hand then check out how well the program does. ---- ##+########################################################################## # # LogicPuzzleSolver.tcl -- Solves one type of logic problems # by Keith Vetter, February 2007 # package require Tk package require tile set S(title) "TCL Logic Puzzle Solver" array set PERMCNT {0 0 1 1 2 0 3 3 4 8 5 45 6 264 7 1855 8 14832 9 133497} set PLACES {xx first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth thirteenth fourteenth fifteenth} proc DoDisplay {} { global S wm title . $S(title) label .tmp eval font create boldFont [font actual "[.tmp cget -font] bold"] font create bigBold -family Helvetica -size 18 -weight bold destroy .tmp frame .master -bd 0 -relief ridge -padx 10 frame .const -bd 0 -relief ridge button .solve -text "Solve" -font boldFont -bd 5 -command Solve pack .master .const -side top -fill x -pady {0 30} pack .solve -side top -expand 1 -pady 15 DoMenus DrawMaster DrawConstraints bind all [bind all ] bind all {console show} } proc DoMenus {} { . configure -menu [menu .m -tearoff 0] .m add cascade -menu [menu .m.game -tearoff 0] -label "Game" -underline 0 .m add cascade -menu [menu .m.puzzle -tearoff 0] -label "Puzzles" -underline 0 .m add cascade -menu [menu .m.help -tearoff 0] -label "Help" -underline 0 .m.game add command -label "Load Puzzle" -under 0 -command LoadPuzzle .m.game add command -label "Save Puzzle" -under 0 -command SavePuzzle .m.game add separator .m.game add command -label "Blank Puzzle" -under 0 -command BlankDlg .m.game add separator .m.game add command -label "Exit" -under 1 -command exit set cnt -1 foreach title [GetTitles] { incr cnt #.m.puzzle add command -label $title -command [list NewPuzzle $cnt] .m.puzzle add radiobutton -label $title -variable S(who) -value $cnt \ -command [list NewPuzzle $cnt] } .m.help add command -label About -under 0 -command About } proc About {} { set txt "$::S(title)\nby Keith Vetter, February 2007\n\n" append txt "There's a class of logic puzzle in which\n" append txt "there is a grid of people and attributes.\n" append txt "We know that EXACTLY one attribute in\n" append txt "each column is positioned correctly.\n\n" append txt "Also in the puzzle are a set of constraints\n" append txt "on the items. For example, a typical one\n" append txt "might be \"Sally is 2 places below the\n" append txt "doctor\" or \"Bob is not the butcher.\"\n\n" append txt "This program lets you enter and solve\n" append txt "such logic puzzles." tk_messageBox -icon info -message $txt -title "About $::S(title)" } proc DrawMaster {} { global S MASTER set S(total) [expr {wide(pow($::PERMCNT($S(numElem)), $S(numCol)))}] set S(total,disp) "[Comma $S(total)] configurations" set W .master eval destroy [winfo child $W] entry $W.title -textvariable ::MASTER(title) -font bigBold -justify c frame $W.grid label $W.total -textvariable S(total,disp) label $W.help -fg red -font boldFont \ -text "EXACTLY one item in each column is correctly positioned" pack $W.title $W.grid $W.help $W.total -side top -fill both for {set col 0} {$col < $S(numCol)} {incr col} { entry $W.title,$col -textvariable ::MASTER(t,$col) \ -justify c -font boldFont -relief solid grid $W.title,$col -row 0 -column $col -sticky ew -in $W.grid } for {set row 0} {$row < $S(numElem)} {incr row} { for {set col 0} {$col < $S(numCol)} {incr col} { entry $W.$row,$col -textvariable ::MASTER($row,$col) \ -justify c -bd 1 -relief solid grid $W.$row,$col -row [expr {$row+1}] -column $col \ -sticky news -in $W.grid } } trace remove variable MASTER write Tracer trace variable MASTER w Tracer Tracer MASTER {} w } proc DrawConstraints {} { global S C set names [GetMasterNames] set ops [GetOperations] set W .const eval destroy [winfo child $W] label $W.title -text Constraints -font bigBold -justify c frame $W.grid pack $W.title $W.grid -side top -fill both label $W.t,0 -text "Who" -font boldFont -justify c label $W.t,1 -text "Operation" -font boldFont -justify c label $W.t,2 -text "Whom" -font boldFont -justify c grid $W.t,0 $W.t,1 $W.t,2 -row 0 -sticky ew -in $W.grid for {set row 0} {$row < 10} {incr row} { set w1 $W.grid.$row,who set w2 $W.grid.$row,op set w3 $W.grid.$row,whom ::ttk::combobox $w1 -textvariable C($row,who) \ -values $names -state readonly -justify c ::ttk::combobox $w2 -textvariable C($row,op) \ -values $ops -state readonly -justify c ::ttk::combobox $w3 -textvariable C($row,whom) \ -values $names -state readonly -justify c grid $w1 $w2 $w3 -row [expr {$row+1}] } trace remove variable C write Tracer trace variable C w Tracer Tracer C {} w } proc Tracer {var1 var2 op} { global C S MASTER set valid 1 # MASTER tests for {set row 0} {$row < $S(numElem)} {incr row} { for {set col 0} {$col < $S(numCol)} {incr col} { if {! [info exists MASTER($row,$col)]} { set MASTER($row,$col) ""} if {$MASTER($row,$col) eq ""} { set valid 0 ; break } } if {! $valid} break } # Constraint tests set W .const if {[winfo exists $W.grid]} { set names [GetMasterNames] set ops [GetOperations] for {set row 0} {$row < 10} {incr row} { foreach a {who op whom} { if {! [info exists C($row,$a)]} { set C($row,$a) "" } if {$C($row,$a) eq ""} {set C($row,$a) ""} } set newState readonly set r [expr {$row - 1}] if {$row > 0 && ($C($r,who) eq "" || $C($r,op) eq "" || $C($r,whom) eq "")} { set newState disabled if {$C($r,who) ne "" || $C($r,op) ne "" || $C($r,whom) ne ""} { set valid 0 } } set w1 $W.grid.$row,who set w2 $W.grid.$row,op set w3 $W.grid.$row,whom $w1 config -state $newState -value $names $w2 config -state $newState -values $ops $w3 config -state $newState -value $names } } .solve config -state [expr {$valid ? "normal" : "disabled"}] } proc GetOperations {} { global S set all {} lappend all "above" "not above" for {set i 1} {$i < $S(numElem)} {incr i} { lappend all "$i above" } lappend all "is" "is not" "below" "not below" for {set i 1} {$i < $S(numElem)} {incr i} { lappend all "$i below" } lappend all "next to" "not next to" return $all } proc GetMasterNames {} { global S MASTER set all {} for {set col 0} {$col < $S(numCol)} {incr col} { for {set row 0} {$row < $S(numElem)} {incr row} { lappend all $MASTER($row,$col) } } set all [concat $all [lrange $::PLACES 1 $S(numElem)]] return $all } proc GetColumnNames {col} { set all {""} for {set row 0} {$row < $::S(numElem)} {incr row} { lappend all $::MASTER($row,$col) } return $all } proc Solve {} { global S PERMCNT cnt Init set S(result) {} set S(stat) "" set S(cnt,disp) 0 set S(where) {} set S(C) [SortConstraints] SolveDialog set start [clock seconds] set cnt 0 set mod 100 while {1} { if {($cnt % $mod) == 0} { set S(cnt,disp) [Comma $cnt]; if {$cnt >= 1000} {set mod 1000} if {$cnt >= 5000} {set mod 5000} update } incr cnt if {! [winfo exists .sdlg]} break if {[TestTrialSolution]} { lappend S(result) [array get ::TRIAL] lappend S(where) $cnt set S(stat) [llength $S(result)] } if {[StepTrialSolution]} break } set S(cnt) $cnt set S(ttime) [expr {[clock seconds] - $start}] if {[winfo exists .sdlg] || $S(result) ne {}} { SolutionDialog } return [llength $S(result)] } proc SolutionDialog {} { global S MASTER TRIAL set W .sdlg set WB .sdlg.body if {! [winfo exists $W]} SolveDialog set S(cancel) "Dismiss" eval destroy [winfo child $WB] set len [llength $S(result)] if {$len == 0} { set S(title) "ERROR: no solution" return } if {$len > 1} { set S(title) "ERROR: $len solutions" return } wm title $W "Solution" set S(title) $MASTER(title) $WB config -padx 20 -pady 10 set S(stat) [clock format $S(ttime) -gmt 1 -format %M:%S] append S(stat) "\t[Comma $S(cnt)]/[Comma $S(total)]" array set TRIAL [lindex $S(result) 0] for {set col 0} {$col < $S(numCol)} {incr col} { label $WB.title,$col -textvariable MASTER(t,$col) \ -justify c -font boldFont -relief solid -bg white grid $WB.title,$col -row 0 -column $col -sticky ew grid columnconfigure $WB $col -weight 1 -uniform a } for {set row 0} {$row < $S(numElem)} {incr row} { for {set col 0} {$col < $S(numCol)} {incr col} { set bg white if {$TRIAL($row,$col) eq $MASTER($row,$col)} { set bg cyan } label $WB.$row,$col -textvariable TRIAL($row,$col) \ -justify c -bd 1 -relief solid -bg $bg grid $WB.$row,$col -row [expr {$row+1}] -column $col \ -sticky news } } CenterWindow $W bind $W [list destroy $W] bind $W [list destroy $W] } proc SolveDialog {} { global S PERMCNT set S(cancel) "Stop" set S(title) "Solving..." set W .sdlg destroy $W toplevel $W wm withdraw $W wm title $W "" wm transient $W . label $W.title -textvariable S(title) -font bigBold frame $W.body label $W.body.cnt -textvariable S(cnt,disp) -anchor e label $W.body.ttl -text " out of [Comma $S(total)]" -anchor w label $W.stat -textvariable S(stat) frame $W.buttons -bd 2 -relief ridge ::ttk::button $W.buttons.cancel -textvariable S(cancel) \ -command [list destroy $W] grid $W.title -padx 30 -sticky ew grid $W.body -sticky ew grid $W.body.cnt $W.body.ttl -sticky ew grid $W.stat grid $W.buttons -sticky ew pack $W.buttons.cancel -side bottom -pady 10 CenterWindow $W . wm deiconify $W grab $W } proc CenterWindow {w {W .}} { update idletasks ;# Need to get geometry correct set wh [winfo reqheight $w] ; set ww [winfo reqwidth $w] set sw [winfo width $W] ; set sh [winfo height $W] set sy [winfo y $W] ; set sx [winfo x $W] set x [expr {$sx + ($sw - $ww)/2}] ; set y [expr {$sy + ($sh - $wh)/2}] incr y -130 if {$x < 0} { set x 0 } ; if {$y < 0} {set y 0} wm geometry $w +$x+$y } proc TestTrialSolution {} { global C COL S if {! [TestColumns]} { return 0 } ;# Shouldn't happen foreach which $S(C) { foreach {ok badCol} [Test1Constraint $which] break if {! $ok} { for {set i 0} {$i < $badCol} {incr i} { set COL($i) {} } return 0 } } return 1 } proc TestColumns {} { global S MASTER TRIAL # One correct per column for {set col 0} {$col < $S(numCol)} {incr col} { set numOK 0 for {set row 0} {$row < $S(numElem)} {incr row} { incr numOK [string equal $MASTER($row,$col) $TRIAL($row,$col)] } if {$numOK != 1} { return 0 } } return 1 } proc Test1Column {col} { global S COL set numOK 0 for {set row 0} {$row < $S(numElem)} {incr row} { incr numOK [expr {$row == [lindex $COL($col) $row]}] } if {$numOK != 1} { return 0 } return 1 } proc Dump {} { global S TRIAL for {set row 0} {$row < $S(numElem)} {incr row} { set line "" for {set col 0} {$col < $S(numCol)} {incr col} { append line "$TRIAL($row,$col)\t" } puts $line } } proc StepTrialSolution {} { global S COL for {set i 0} {$i < $S(numCol)} {incr i} { set done 1 while {1} { set COL($i) [NextPerm $COL($i)] if {$COL($i) eq {}} { set done 0 set COL($i) [FirstPerm] } if {[Test1Column $i]} break } FillColumn $i if {$done} { return 0} } return 1 } proc FillColumn {col} { global MASTER TRIAL COL S if {$COL($col) eq {}} return for {set row 0} {$row < $S(numElem)} {incr row} { set from [lindex $COL($col) $row] set TRIAL($row,$col) $MASTER($from,$col) } } proc SortConstraints {} { global C set all {} foreach arr [array names C *,who] { if {$C($arr) eq ""} continue set which [lindex [split $arr ","] 0] foreach {. col0} [FindWho $C($which,who) $which] break foreach {. col1} [FindWho $C($which,whom) $which] break set minCol [expr {$col0 < $col1 ? $col0 : $col1}] lappend all [list $which $minCol] } set result {} foreach arr [lsort -index 1 -decreasing -integer $all] { lappend result [lindex $arr 0] } return $result } proc Init {} { global COL for {set i 0} {$i < $::S(numCol)} {incr i} { set COL($i) [FirstPerm] FillColumn $i while {1} { if {[Test1Column $i]} break set COL($i) [NextPerm $COL($i)] FillColumn $i } } } proc FirstPerm {} { set result {} for {set i 0} {$i < $::S(numElem)} {incr i} { lappend result $i } return $result } # http://wiki.tcl.tk/11262 proc NextPerm { perm } { # Find the smallest subscript j such that we have already visited # all permutations beginning with the first j elements. set j [expr { [llength $perm] - 1 }] set ajp1 [lindex $perm $j] while { $j > 0 } { incr j -1 set aj [lindex $perm $j] if { [string compare $ajp1 $aj] > 0 } { set foundj {} break } set ajp1 $aj } if { ![info exists foundj] } return # Find the smallest element greater than the j'th among the elements # following aj. Let its index be l, and interchange aj and al. set l [expr { [llength $perm] - 1 }] while { $aj >= [set al [lindex $perm $l]] } { incr l -1 } lset perm $j $al lset perm $l $aj # Reverse a_j+1 ... an set k [expr {$j + 1}] set l [expr { [llength $perm] - 1 }] while { $k < $l } { set al [lindex $perm $l] lset perm $l [lindex $perm $k] lset perm $k $al incr k incr l -1 } return $perm } # +# # -# # nextto # not nextto # is # is not # below # above # is 2nd # is not 2nd # return 1 if satisfies constraint proc Test1Constraint {which} { global C foreach {row0 col0} [FindWho $C($which,who) $which] break set op $C($which,op) foreach {row1 col1} [FindWho $C($which,whom) $which] break set minCol [expr {$col0 < $col1 ? $col0 : $col1}] if {[regexp {^(\d+) below$} $op => num]} { set n [expr {$row0 - $num}] return [list [expr {$n == $row1}] $minCol] } if {[regexp {^(\d+) above$} $op => num]} { set n [expr {$row0 + $num}] return [list [expr {$n == $row1}] $minCol] } if {$op eq "is"} { return [list [expr {$row0 == $row1}] $minCol] } if {$op eq "is not"} { return [list [expr {$row0 != $row1}] $minCol] } if {$op eq "next to"} { set n [expr {abs($row0 - $row1)}] return [list [expr {$n == 1}] $minCol] } if {$op eq "not next to"} { set n [expr {abs($row0 - $row1)}] return [list [expr {$n != 1}] $minCol] } if {$op eq "above"} { return [list [expr {$row0 < $row1}] $minCol] } if {$op eq "not above"} { return [list [expr {$row0 >= $row1}] $minCol] } if {$op eq "below"} { return [list [expr {$row0 > $row1}] $minCol] } if {$op eq "not below"} { return [list [expr {$row0 <= $row1}] $minCol] } error "bad op" return 0 } proc FindWho {who which} { global S TRIAL set who [string tolower $who] for {set row 0} {$row < $S(numElem)} {incr row} { for {set col 0} {$col < $S(numCol)} {incr col} { set t [string tolower $TRIAL($row,$col)] if {$t eq $who} { return [list $row $col] } } } set n [lsearch $::PLACES $who] if {$n != -1} { return [list [expr {$n - 1}] 9999] } error "Can't find '$who' => which: $which" return {-1 -1} } proc Comma { num } { while {[regsub {^([-+]?[0-9]+)([0-9][0-9][0-9])} $num {\1,\2} num]} {} return $num } proc Reset {} { global S MASTER C #unset -nocomplain S unset -nocomplain MASTER unset -nocomplain C set S(numCol) 3 set S(numElem) 4 } proc NewPuzzle {{who ""}} { global S MASTER C PUZZLE set S(who) $who if {$who ne ""} { Reset foreach {title s m c} $PUZZLE($who) break array set S $s array set MASTER $m array set C $c set MASTER(title) $title } else { set col $S(numCol) set elem $S(numElem) Reset set S(numCol) $col set S(numElem) $elem foreach arr [array names MASTER] { set MASTER($arr) "" } } DrawMaster DrawConstraints } proc BlankDlg {} { global S set W .ndlg destroy $W toplevel $W wm title $W "" wm transient $W . wm withdraw $W wm protocol $W WM_DELETE_WINDOW BlankDone label $W.title -text "New Puzzle Size" -font bigBold label $W.lcol -text "Columns" tk_optionMenu $W.ecol S(numCol) 2 3 4 5 6 label $W.lrow -text "Rows" tk_optionMenu $W.erow S(numElem) 2 3 4 5 6 frame $W.buttons -bd 2 -relief ridge ::ttk::button $W.buttons.ok -text Okay -command BlankDone grid $W.title - -sticky ew grid $W.lcol -row 1 -column 0 -sticky e grid $W.ecol -row 1 -column 1 -sticky w grid $W.lrow -row 2 -column 0 -sticky e grid $W.erow -row 2 -column 1 -sticky w grid $W.buttons - -sticky ew pack $W.buttons.ok -pady 15 -expand 1 CenterWindow $W wm deiconify $W } proc BlankDone {} { destroy .ndlg NewPuzzle } proc GetTitles {} { global PUZZLE set all {} for {set i 0} {[info exists PUZZLE($i)]} {incr i} { lappend all [lindex $PUZZLE($i) 0] } return $all } proc LoadPuzzle {} { global S PUZZLE set types {{{Puzzle Files} {.pzl}} {{All Files} *}} set fname [tk_getOpenFile -filetypes $types -initialfile puzzle.pzl] if {$fname eq ""} return if {[interp exists newInterp]} { interp delete newInterp } interp create -safe newInterp newInterp eval set P {{}} if {[catch {newInterp invokehidden source $fname}]} { interp delete newInterp error "Bad puzzle file: $fname" } set P [newInterp eval set P] interp delete newInterp if {[llength $P] != 4} { error "Bad puzzle file data: $fname" } if {[array names PUZZLE *,user] eq {}} {.m.puzzle add separator} set n [llength [array names PUZZLE]] set PUZZLE($n,user) $P set title [lindex $PUZZLE($n,user) 0] .m.puzzle add radiobutton -label $title -variable S(who) -value $n,user \ -command [list NewPuzzle $n,user] NewPuzzle $n,user } proc SavePuzzle {} { set txt [SerializePuzzle] set types {{{Puzzle Files} {.pzl}} {{All Files} *}} set fname [tk_getSaveFile -filetypes $types -initialfile puzzle.pzl] if {$fname eq ""} return set fout [open $fname w] puts $fout $txt close $fout } proc SerializePuzzle {} { global S MASTER C set t " " set p "set P {\n" append p "$t\"$MASTER(title)\"\n" append p "$t{numCol $S(numCol) numElem $S(numElem)}\n" append p "$t{\n$t$t" for {set col 0} {$col < $S(numCol)} {incr col} { append p "t,$col \"$MASTER(t,$col)\"\t" } for {set row 0} {$row < $S(numElem)} {incr row} { append p "\n$t$t" for {set col 0} {$col < $S(numCol)} {incr col} { append p "$row,$col \"$MASTER($row,$col)\"\t" } } append p "\n$t}\n" append p "$t{\n" foreach arr [lsort -dictionary [array names C *,who]] { if {$C($arr) eq ""} break set which [lindex [split $arr ","] 0] append p "$t$t$which,who \"$C($which,who)\"" append p "\t$which,op \"$C($which,op)\"" append p "\t$which,whom \"$C($which,whom)\"\n" } append p "$t}\n" append p "}" return $p } ################################################################ set n -1 set PUZZLE([incr n]) { "The Greatest Human Being" { numCol 3 numElem 4} { t,0 "First Name" t,1 "Surname" t,2 "Speciality" 0,0 Isaac 0,1 Newtune 0,2 welder 1,0 Albert 1,1 Eyeline 1,2 gardener 2,0 Marie 2,1 Curious 2,2 cleaner 3,0 Charles 3,1 Darling 3,2 bricklayer } { 0,who Albert 0,op "2 above" 0,whom bricklayer 1,who Darling 1,op "1 below" 1,whom Charles 2,who Curious 2,op "2 below" 2,whom welder } } set PUZZLE([incr n]) { "Tape Teaser" { numCol 3 numElem 4 } { t,0 Nickname t,1 Surname t,2 Hometown 0,0 Rocky 0,1 Tryson 0,2 Boston 1,0 Sugar 1,1 Holyhead 1,2 Seattle 2,0 Basher 2,1 McCool 2,2 Texas 3,0 Iron 3,1 Freeman 3,2 {New York} } { 0,op {1 below} 0,who Freeman 0,whom Boston 1,op {1 above} 1,who Seattle 1,whom Iron 2,op {2 below} 2,who McCool 2,whom Sugar 3,op {} 3,who {} 3,whom {} 4,op {} 4,who {} 4,whom {} 5,op {} 5,who {} 5,whom {} 6,op {} 6,who {} 6,whom {} 7,op {} 7,who {} 7,whom {} 8,op {} 8,who {} 8,whom {} 9,op {} 9,who {} 9,whom {} } } set PUZZLE([incr n]) { "Puzzle in the Park" {numCol 3 numElem 4} { t,0 "Squirrel" t,1 "Tree" t,2 "Nuts" 0,0 "Gerald" 0,1 "Birch" 0,2 "11" 1,0 "Scamper" 1,1 "Sycamore" 1,2 "12" 2,0 "Basil" 2,1 "Ash" 2,2 "10" 3,0 "Tufty" 3,1 "Oak" 3,2 "9" } { 0,who "Sycamore" 0,op "1 below" 0,whom "12" 1,who "10" 1,op "1 above" 1,whom "Tufty" 2,who "Ash" 2,op "2 below" 2,whom "Scamper" 3,who "10" 3,op "is not" 3,whom "second" } } set PUZZLE([incr n]) { "Beating at Eating" {numCol 3 numElem 4} { t,0 "Name" t,1 "Surname" t,2 "Dessert" 0,0 "Agatha" 0,1 "Greed" 0,2 "cream puffs" 1,0 "Bugsy" 1,1 "Forager" 1,2 "trifle" 2,0 "Delilah" 2,1 "Eatalot" 2,2 "cheesecake" 3,0 "Chuck" 3,1 "Hunk" 3,2 "ice cream" } { 0,who "Chuck" 0,op "1 above" 0,whom "ice cream" 1,who "trifle" 1,op "not above" 1,whom "Delilah" 2,who "Greed" 2,op "2 below" 2,whom "Delilah" 3,who "trifle" 3,op "1 above" 3,whom "Forager" } } set PUZZLE([incr n]) { "Best Book Prize" { numCol 4 numElem 4} { t,0 "Verb 1" t,1 "Noun" t,2 "Verb 2" t,3 Adverb 0,0 Killing 0,1 Puddings 0,2 Laughing 0,3 Stupidly 1,0 Making 1,1 Sharks 1,2 Jumping 1,3 Loudly 2,0 Hitting 2,1 Cakes 2,2 Running 2,3 Cruelly 3,0 Shooting 3,1 Flies 3,2 Hopping 3,3 Quickly } { 0,who Jumping 0,op "not next to" 0,whom Running 1,who Sharks 1,op "1 below" 1,whom Loudly 2,who Killing 2,op "1 above" 2,whom Quickly 3,who Sharks 3,op "is not" 3,whom Running 4,who Making 4,op "1 below" 4,whom Jumping 5,who Cakes 5,op "1 above" 5,whom Running } } set PUZZLE([incr n]) { "Alien Court" {numCol 3 numElem 5} { t,0 "Captain" t,1 "Planet" t,2 "Spaceship" 0,0 "Ponga" 0,1 "Blink" 0,2 "Outagas" 1,0 "Bleep" 1,1 "Loopy" 1,2 "Boldleego" 2,0 "Arial" 2,1 "Grunt" 2,2 "Rustcan" 3,0 "Tweak" 3,1 "Pobble" 3,2 "Hosspuld" 4,0 "Riddle" 4,1 "Ether" 4,2 "Supersnail" } { 0,who "Boldleego" 0,op "above" 0,whom "Pobble" 1,who "Pobble" 1,op "not above" 1,whom "Rustcan" 2,who "Ponga" 2,op "1 below" 2,whom "Grunt" 3,who "Ponga" 3,op "1 above" 3,whom "Outagas" 4,who "Arial" 4,op "is not" 4,whom "fourth" 5,who "Blink" 5,op "2 above" 5,whom "Riddle" 6,who "Blink" 6,op "1 above" 6,whom "Hosspuld" } } set PUZZLE([incr n]) { "Court Napping" { numCol 3 numElem 5} { t,0 Title t,1 Name t,2 "Favorite Game" 0,0 Princess 0,1 Yawny 0,2 hearts 1,0 Duke 1,1 Driftoff 1,2 rummy 2,0 King 2,1 Bleereye 2,2 gin 3,0 Queen 3,1 Mutter 3,2 bridge 4,0 Earl 4,1 Outovit 4,2 poker } { 0,who Earl 0,op "1 below" 0,whom Mutter 1,who Outovit 1,op "1 above" 1,whom bridge 2,who King 2,op "is not" 2,whom Mutter 3,who King 3,op "is not" 3,whom bridge 4,who hearts 4,op "1 above" 4,whom Bleereye 5,who Bleereye 5,op "2 below" 5,whom King 6,who Princess 6,op "2 above" 6,whom poker } } set PUZZLE([incr n]) { "The Feed'em-Fat Diner" {numCol 3 numElem 5} { t,0 "First Name" t,1 "Surname" t,2 "Occupation" 0,0 "Dave" 0,1 "Jaffa" 0,2 "manager" 1,0 "Connie" 1,1 "Fish" 1,2 "receptionist" 2,0 "Bill" 2,1 "Gateau" 2,2 "chef" 3,0 "Eleanor" 3,1 "Ingest" 3,2 "waiter" 4,0 "Anne" 4,1 "Haddock" 4,2 "dishwasher" } { 0,who "Haddock" 0,op "2 below" 0,whom "manager" 1,who "Dave" 1,op "2 above" 1,whom "Ingest" 2,who "waiter" 2,op "2 above" 2,whom "Anne" 3,who "Eleanor" 3,op "3 below" 3,whom "receptionist" 4,who "Fish" 4,op "is not" 4,whom "first" } } set PUZZLE([incr n]) { "Animal Races" {numCol 3 numElem 6} { t,0 "Animal" t,1 "Name" t,2 "Prize" 0,0 "badger" 0,1 "Karen" 0,2 "Porsche" 1,0 "elephant" 1,1 "Harry" 1,2 "spoon" 2,0 "antelope" 2,1 "Lorna" 2,2 "television" 3,0 "cat" 3,1 "Ian" 3,2 "microwave" 4,0 "dog" 4,1 "George" 4,2 "carrot" 5,0 "frog" 5,1 "Jenny" 5,2 "radiator" } { 0,who "badger" 0,op "is not" 0,whom "sixth" 1,who "George" 1,op "is not" 1,whom "sixth" 2,who "microwave" 2,op "2 below" 2,whom "Harry" 3,who "microwave" 3,op "1 above" 3,whom "elephant" 4,who "Ian" 4,op "is not" 4,whom "carrot" 5,who "Ian" 5,op "not next to" 5,whom "Lorna" 6,who "spoon" 6,op "3 below" 6,whom "Ian" 7,who "spoon" 7,op "2 below" 7,whom "dog" 8,who "Porsche" 8,op "1 below" 8,whom "Lorna" 9,who "Porsche" 9,op "1 above" 9,whom "antelope" } } set PUZZLE([incr n]) { "A Meal Out" {numCol 4 numElem 5} { t,0 "Nickname" t,1 "Name" t,2 "Food" t,3 "Beverage" 0,0 "Doghouse" 0,1 "Steve" 0,2 "pork" 0,3 "milkshake" 1,0 "Bigears" 1,1 "Annie" 1,2 "lamb" 1,3 "latte" 2,0 "Tender" 2,1 "Chris" 2,2 "beef" 2,3 "cappuccino" 3,0 "Simple" 3,1 "Jackie" 3,2 "chicken" 3,3 "mocha" 4,0 "Wimpsy" 4,1 "Georgina" 4,2 "fish" 4,3 "tea" } { 0,who "Annie" 0,op "1 below" 0,whom "beef" 1,who "pork" 1,op "1 above" 1,whom "Tender" 2,who "pork" 2,op "1 below" 2,whom "tea" 3,who "Chris" 3,op "not next to" 3,whom "Steve" 4,who "fish" 4,op "1 below" 4,whom "mocha" 5,who "fish" 5,op "2 below" 5,whom "Steve" 6,who "Annie" 6,op "is not" 6,whom "fifth" 7,who "Doghouse" 7,op "is not" 7,whom "fifth" 8,who "Bigears" 8,op "1 above" 8,whom "latte" 9,who "Bigears" 9,op "1 below" 9,whom "Chris" } } set PUZZLE([incr n]) { "Whodunnit?" { numCol 4 numElem 5} { t,0 "First Name" t,1 Surname t,2 Weapon t,3 Location 0,0 James 0,1 Bracket 0,2 hammer 0,3 kitchen 1,0 Lyn 1,1 Thrust 1,2 rope 1,3 conservatory 2,0 Sid 2,1 Nutter 2,2 gun 2,3 hall 3,0 Alice 3,1 Kilroy 3,2 knife 3,3 library 4,0 Eunice 4,1 Loosenut 4,2 poison 4,3 study } { 0,who conservatory 0,op "1 below" 0,whom Nutter 1,who conservatory 1,op "1 above" 1,whom knife 2,who gun 2,op "below" 2,whom Alice 3,who James 3,op "2 above" 3,whom Kilroy 4,who poison 4,op "above" 4,whom Bracket 5,who hall 5,op "1 below" 5,whom gun 6,who hall 6,op "1 above" 6,whom Eunice 7,who Alice 7,op "is" 7,whom library } } set PUZZLE([incr n]) { "Alien Ages" {numCol 4 numElem 6} { t,0 "Name" t,1 "Race" t,2 "Planet" t,3 "Age" 0,0 "Bleep" 0,1 "Tartan" 0,2 "Parp" 0,3 "213" 1,0 "Ting" 1,1 "Polyp" 1,2 "Dorb" 1,3 "385" 2,0 "Hoot" 2,1 "Bunter" 2,2 "Esther" 2,3 "706" 3,0 "Eek" 3,1 "Crispy" 3,2 "Booper" 3,3 "503" 4,0 "Peep" 4,1 "Winky" 4,2 "Grunt" 4,3 "897" 5,0 "Doodah" 5,1 "Fodder" 5,2 "Flip" 5,3 "32" } { 0,who "Grunt" 0,op "1 above" 0,whom "Fodder" 1,who "Booper" 1,op "3 below" 1,whom "Eek" 2,who "Booper" 2,op "2 below" 2,whom "32" 3,who "Doodah" 3,op "2 above" 3,whom "Tartan" 4,who "Doodah" 4,op "1 below" 4,whom "385" 5,who "706" 5,op "3 above" 5,whom "Ting" 6,who "Peep" 6,op "2 below" 6,whom "Bunter" 7,who "Peep" 7,op "1 above" 7,whom "Parp" 8,who "Esther" 8,op "1 above" 8,whom "213" 9,who "Esther" 9,op "3 above" 9,whom "Polyp" } } set S(numCol) 3 set S(numElem) 4 DoDisplay NewPuzzle 0 return ---- See also [Solving cryptarithms] - [Solving cryptograms] - [Brute force in velvet gloves] ---- [Category Application] | [Category AI]