#!/usr/bin/awk -f # # GLOBAL VARIABLES: # grid - Game grid # row - Horizontal slice through the grid # col - Vertical slice through the grid # sector - solved numbers per sector # setorMap - map of sector for each cell # solutions - grid of all possible solutions per unsolved cell # solutionsCnt - count of possible solutions for each unsolved number per sector # BEGIN { for (i=1; i <= 9; i++ ) { for (j=1; j <= 9; j++ ) { grid[i,j]=0 sectorMap[i,j]=toSector(i,j) } } "tput smso" | getline SMSO "tput bold" | getline BOLD "tput rmso" | getline RMSO "tput clear" | getline CLEAR "tput cup 0 0" | getline HOME "tput cup 20 0" | getline LINE20 PROMPT = ">>" printf "%s ", PROMPT } $1 == "help" { print "help, load, loadFile, step, dumpsolutions, dumpSector, dumpSectorMap, dump" } $1 == "list" { LIST="ls puzzle.*" while ( LIST | getline listing >0 ) { print listing } close(LIST) } #################################################################### # # load puzzle from a file # #################################################################### $1 == "loadFile" { fileName = $2 print "loading " fileName clearSector() delete grid delete row delete col r=0; while ( (getline < fileName) > 0 ) { r++ for (c=1; c<=NF; c++) { if ( $c != ".") load(r, c, $c) } } close(fileName) } #################################################################### # # load puzzle from command line # #################################################################### $1 == "load" { fileName="" for (i=2; i<=NF; i++) { split($i,fields,",") load(fields[1], fields[2], fields[3]) } } #################################################################### # # step through automated solution of the puzzle # #################################################################### $1 == "step" { if (solveStep()==0) print "no solution" } #################################################################### # # automatically solve the entire puzzle # #################################################################### $1 == "solve" { printf "%s", CLEAR do { if ( $2 != "" ) { system("sleep " $2) } printf "%s", HOME } while (solveStep() != 0 ) dump() print "Solution Stopped" } #################################################################### # # validate the currently solved puzzle against a solution # in a file # #################################################################### $1 == "check" { fileName=$2 print "checking solution against " fileName r=0 while ( (getline < fileName) > 0 ) { r++ for (c=1; c<=NF; c++) { # print "grid["r","c"]("grid[r,c]")=" $c ## DEBUG if (grid[r,c] != $c) { dump(r,c) print "Error @ cell " r","c close(fileName) next } } } print "Congradulations, your solution is correct!" close(fileName) } #################################################################### # # save the current puzzle in a file # #################################################################### $1 == "save" { for (r=1; r<=9; r++) { for (c=1; c<=9; c++) { v = grid[r,c] if ( v <=0 ) v="." printf "%s ", v > $2 } print "" > $2 } close($2) } $1 == "dumpSolution" { dumpSolutions() } # dump the list of solutions to output $1 == "dumpSectorMap" { dumpSectorMap() } # dump the sector map to output $1 == "dumpSector" { dumpSectors() } # dump all solved numbers per sector to output $1 == "dump" { C=$3; dump($2, $3) } # dump the puzzle grid to output $1 == "quit" { exit 0 } # exit the game { printf "%s ", PROMPT } #################################################################### # # load a value into a (r)ow and (c)olumn of the puzzle grid # #################################################################### function load( r, c, value) { grid[r,c]=value solutions[r,c]="C" row[r,value]=1 col[c,value]=1 addToSector(sectorMap[r, c],value) } #################################################################### # # attempt to find the first solvable cell in the grid. # print the grid with the solved cell highlighted. # #################################################################### function solveStep() { plotSolutions() for ( r=1; r<=9; r++ ) { for ( c=1; c<=9; c++ ) { for ( i=1; i <= 9; i++ ) { if(solutions[r,c] != "C" && solutions[r,c] ~ i && solutions[r,c] !~ /,/) { load(r,c,i) dump(r,c) print " " return 1 } if ( solutions[r,c]!="C" && solutions[r,c] ~ i && solutionsCnt[sectorMap[r,c],i]==1) { load(r,c,i) dump(r,c) print " " return 1 } } } } if ( !gridSolved() ) { plotSolutions() plotTwins() for ( r=1; r<=9; r++ ) { for ( c=1; c<=9; c++ ) { for ( i=1; i <= 9; i++ ) { if(solutions[r,c] != "C" && solutions[r,c] ~ i && solutions[r,c] !~ /,/) { load(r,c,i) dump(r,c) print "Solved using twins..." return 1 } if ( solutions[r,c]!="C" && solutions[r,c] ~ i && solutionsCnt[sectorMap[r,c],i]==1) { load(r,c,i) dump(r,c) print "Solved using twins..." return 1 } } } } } return 0 } #################################################################### # # plot all possible solutions for all unsolved cells # #################################################################### function plotSolutions() { delete solutions delete solutionsCnt ############################################################ # # Mark solved cells in the solution space # ############################################################ for ( r=1; r<=9; r++ ) { for (c=1; c<=9; c++) { if ( grid[r,c] != 0 ) { solutions[r,c]="C" } } } ############################################################ # # plot all possible solutions for the current # puzzle grid # ############################################################ for (i=1; i<=9; i++ ) { for ( r=1; r<=9; r++ ) { for ( c=1; c<=9; c++ ) { if ( solutions[r,c] != "C" ) { if ( row[r,i] != 1 && col[c,i] != 1 && sector[sectorMap[r,c]] !~ i) { if ( solutions[r,c] == 0 ) solutions[r,c]=i else solutions[r,c]=solutions[r,c]","i solutionsCnt[sectorMap[r,c],i]= solutionsCnt[sectorMap[r,c],i]+1 } } } } } } function plotTwins() { ############################################################ # # look for "twins" in the solutions space # ############################################################ for ( r=1; r<=9; r++) { for ( c=1; c<=9; c++ ) { if (split(solutions[r,c],a,",") == 2 ) { if (matchTwinCol(r,c,a)) { extractTwinValuesFromCol(c,a) } if (matchTwinRow(r,c,a)) { extractTwinValuesFromRow(r,a) } } } } } function matchTwinCol(R,C,A){ twinCount=0 ############################################################ # # look for "twins" across column in the solutions space # ############################################################ for ( ; C<=9; C++ ) { if (split(solutions[R,C],b,",") == 2 ) { if( isTwin(A,b) ) { twinCount++ } } } if ( twinCount == 1 ) return 1 else return 0 # false } function matchTwinRow(R,C,A){ twinCount=0 ############################################################ # # look for "twins" across Row in the solutions space # ############################################################ for ( ; R<=9; R++ ) { if (split(solutions[R,C],b,",") == 2 ) { if( isTwin(A,b) ) { twinCount++ } } } if ( twinCount == 1 ) return 1 else return 0 # false } function extractTwinValuesFromCol(C, A) { for ( R=1; R<=9; R++ ) { if ( solutions[R,C] != "C" ) { split(solutions[R,C],B,",") if ( length(B) > 1 && !isTwin(A,B)) { solutions[R,C]="" for ( i=1; i<= length(B); i++ ) { if ( B[i] != A[1] && B[i] != A[2] ) { if ( solutions[R,C]=="" ) { solutions[R,C]=B[i] }else{ solutions[R,C]=solutions[R,C]","B[i] } } } } } } } function extractTwinValuesFromRow(R, A) { for ( C=1; C<=9; C++ ) { if ( solutions[R,C] != "C" ) { split(solutions[R,C],B,",") if ( length(B) > 1 && !isTwin(A,B)) { solutions[R,C]="" for ( i=1; i<= length(B); i++ ) { if ( B[i] != A[1] && B[i] != A[2] ) { if ( solutions[R,C]=="" ) { solutions[R,C]=B[i] }else{ solutions[R,C]=solutions[R,C]","B[i] } } } } } } } function isTwin(A, B) { if ( length(A) == 2 && length(B) == 2 && A[1] == B[1] && A[2] == B[2] ) { return 1 } return 0 } function gridSolved() { for ( r=1 ; r<=9; r++ ) { for ( c=1; c<=9; c++ ) { if ( solutions[r,c]!="C" ) return 0 } } return 1 } #################################################################### # # dump the list of all currently plotted solutions for # unsolved cells # #################################################################### function dumpSolutions() { for ( r=1; r<=9; r++ ) { for ( c=1; c<=9; c++ ) { printf "solutions[%d,%d]=%s\n", r, c, solutions[r,c] } } } #################################################################### # # dump all solved numbers per sector # #################################################################### function dumpSectors() { print "SECTORS {" for ( i=1; i <=9; i++ ) { printf " Sector[%d]: %s\n", i, sector[i] } print "}" } #################################################################### # # map a cell to a sector # #################################################################### function toSector(r, c) { if ( r <= 3 && c <=3 ) { return 1 } else if ( r <= 3 && c <=6 ) { return 2 } else if ( r <= 3 && c <=9 ) { return 3 } else if ( r <= 6 && c <=3 ) { return 4 } else if ( r <= 6 && c <=6 ) { return 5 } else if ( r <= 6 && c <=9 ) { return 6 } else if ( r <= 9 && c <=3 ) { return 7 } else if ( r <= 9 && c <=6 ) { return 8 } else if ( r <= 9 && c <=9 ) { return 9 } } #################################################################### # # dump the puzzle grid to output, highlight the cell # designated by the (r)ow and (c)olumn arguments # #################################################################### function dump(r, c) { print fileName printf "===================\n" for (i=1; i<=9; i++ ) { printf "|" for ( j=1; j<=9; j++) { if( i==r && j==c) { SM=SMSO RM=RMSO }else{ SM="" RM="" } if (grid[i,j]!=0) { printf "%s%d%s",SM,grid[i,j],RM }else{ printf " " } if ( j%3 == 0 ) { printf "|" }else{ printf ":" } } if ( i%3 == 0 ) { printf "\n|=====|=====|=====|\n" }else{ printf "\n|-----|-----|-----|\n" } } } #################################################################### # # dump the pregenerated sector map to output # #################################################################### function dumpSectorMap() { printf "SECTOR MAP {\n ===================\n " for (i=1; i<=9; i++ ) { printf "|" for ( j=1; j<=9; j++) { printf "%d", sectorMap[i,j] if ( j%3 == 0 ) { printf "|" }else{ printf ":" } } if ( i%3 == 0 ) { printf "\n |=====|=====|=====|\n " }else{ printf "\n |-----|-----|-----|\n " } } printf "\n}\n" } #################################################################### # # clear sector solutions # #################################################################### function clearSector() { delete sector delete sectorCnt } #################################################################### # # add a solution (v)alue to a (s)ector # #################################################################### function addToSector(s,v) { if ( sector[s]==0 ) { sector[s]=v } else{ sector[s]=sector[s]","v } sectorCnt[s,v]=sectorCnt[s,v]+1 } #################################################################### # # Program Exit # #################################################################### END { print ">> GOODBYE!" }