#ident "$Id: sudoku.c,v 1.20 2009/07/30 16:58:40 pwh Exp $"
/*
* Sudoku Puzzle Solver.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <curses.h> /* Use the ncurses library. */
#define PUZZLE_TITLE "Sudoku Solver"
/* Useful character definitions. */
#define ESC 27 /* Escape character. */
#define HT 0x09 /* Horizontal tab. */
#define CR 0x0D /* Carriage return. */
#define DEL 0x7F /* Delete. */
/* Flag bits. */
#define BIG 0x1
#define DISPLAYED 0x2
/* Technique flags. */
#define NAKED_TUPLE_LEVEL 0xF
#define HIDDEN_TUPLE_LEVEL 0xF0
#define CROSS_HATCH 0x100
#define CROSS_HATCH_2 0x200
#define MISSING_DIGIT 0x400
#define NAKED_TUPLE 0x800
#define HIDDEN_TUPLE 0x1000
#define SET_INTERSECTION 0x2000
#define WHAT_IF 0x4000
#define SET_NAKED_TUPLE_LEVEL(a,b) ((a)=((a)&~NAKED_TUPLE_LEVEL)|(b))
#define GET_NAKED_TUPLE_LEVEL(a) ((a)&NAKED_TUPLE_LEVEL)
#define SET_HIDDEN_TUPLE_LEVEL(a,b) ((a)=((a)&~HIDDEN_TUPLE_LEVEL)|((b)<<4))
#define GET_HIDDEN_TUPLE_LEVEL(a) (((a)&HIDDEN_TUPLE_LEVEL)>>4)
/* Test flag bits. */
#define isBig(a) ((a)->flags&BIG)
#define isDisplayed(a) ((a)->flags&DISPLAYED)
/*
* Sudoku puzzle object.
*/
typedef struct {
int y0; /* Upper left hand corner of game board. */
int x0; /* Upper left hand corner of game board. */
int flags; /* Puzzle big, displayed. */
int delay;
int difficulty;
int technique;
char board [9][9];
short masks [9][9];
short rowMasks [9];
short columnMasks [9];
short blockMasks [3][3];
} PUZZLE;
/* Puzzle solution function. */
static int solvePuzzle ( PUZZLE *puzzle );
static const char gameName[] = PUZZLE_TITLE;
static const char instructions1[] = "Enter clues";
static const char instructions2[] = "'s' = solve";
static const char quit[] = "Quit (Y/N)? ";
static const char thinking[] = "Thinking...";
static const char paused[] = "Paused...";
static const char stumped[] = "?Ambiguous?";
static const char noSolution[] = "No Solution!";
static const char solved[] = "Ta Daaaa!!!";
#define BIT_MASK 0x1FF
#define SQUARE_1 0x1C0
#define SQUARE_2 0x038
#define SQUARE_3 0x007
#define ROW_1 SQUARE_1
#define ROW_2 SQUARE_2
#define ROW_3 SQUARE_3
#define COLUMN_1 0x124
#define COLUMN_2 0x092
#define COLUMN_3 0x049
#define bitIndex16(x) ((int)b2dLookup[((((x)&0xFFFF)*0xBCD)>>12)&0xF])
#define bit2digit(x) ((x)&&(x)==((x)&-(x))?((unsigned)(x)>0xFFFF?\
bitIndex16((x)>>16)+16:bitIndex16(x)):0)
#define digit2bit(x) ((x)>0&&(x)<33?d2bLookup[(x)-1]:0)
static char b2dLookup [] = { ( char ) 1, ( char ) 2, ( char ) 3,
( char ) 11, ( char ) 15, ( char ) 4,
( char ) 12, ( char ) 6, ( char ) 16,
( char ) 10, ( char ) 14, ( char ) 5,
( char ) 9, ( char ) 13, ( char ) 8,
( char ) 7 };
static int d2bLookup [] = { 0x0001, 0x0002, 0x0004, 0x0008,
0x0010, 0x0020, 0x0040, 0x0080,
0x0100, 0x0200, 0x0400, 0x0800,
0x1000, 0x2000, 0x4000, 0x8000,
0x00010000, 0x00020000, 0x00040000, 0x00080000,
0x00100000, 0x00200000, 0x00400000, 0x00800000,
0x01000000, 0x02000000, 0x04000000, 0x08000000,
0x10000000, 0x20000000, 0x40000000, 0x80000000 };
static int countBits ( unsigned int bits )
{
bits -= ( bits >> 1 ) & 0x55555555;
bits = ( bits & 0x33333333 ) + ( ( bits >> 2 ) & 0x33333333 );
bits = ( bits + ( bits >> 4 ) ) & 0xF0F0F0F;
bits += ( bits >> 8 );
return ( ( bits + ( bits >> 16 ) ) & 0xFF );
}
static int confirm ( PUZZLE *puzzle, const char *message )
{
int y = puzzle->y0 - 1;
int x;
int c;
int stringLength = strlen ( message );
x = puzzle->x0 + ( ( isBig ( puzzle ) ? 19 : 13 ) - stringLength ) / 2;
move ( y, x );
while ( *message ) addch ( *message++ );
refresh ();
while ( ( c = getch () ) == ERR );
move ( y, 0 );
clrtoeol ();
return ( c );
}
static void displayCaptions ( PUZZLE *puzzle, const char *line1,
const char *line2 )
{
int y;
const char *c;
y = puzzle->y0 + ( isBig ( puzzle ) ? 20 : 14 );
if ( c = line1 ) {
int stringLength;
move ( y, 0 );
clrtoeol ();
if ( stringLength = strlen ( c ) ) {
int x;
x = puzzle->x0 + ( ( isBig ( puzzle ) ? 19 : 13 )
- stringLength ) / 2;
move ( y, x );
while ( *c ) addch ( *c++ );
}
}
++y;
if ( c = line2 ) {
int stringLength;
move ( y, 0 );
clrtoeol ();
if ( stringLength = strlen ( c ) ) {
int x;
x = puzzle->x0 + ( ( isBig ( puzzle ) ? 19 : 13 )
- stringLength ) / 2;
move ( y, x );
while ( *c ) addch ( *c++ );
}
}
move ( puzzle->y0 + 1, puzzle->x0 + 1 );
}
static void displayEntry ( PUZZLE *puzzle, int i, int j, int entry )
{
if ( isDisplayed ( puzzle ) ) {
int y;
int x;
if ( isBig ( puzzle ) ) {
x = ( j << 1 ) + 1;
y = ( i << 1 ) + 1;
} else {
x = ( ( j << 2 ) + 3 ) / 3;
y = ( ( i << 2 ) + 3 ) / 3;
}
if ( puzzle->delay ) {
int c;
timeout ( puzzle->delay );
if ( ( c = getch () ) != ERR ) {
timeout ( -1 );
if ( c == 'q' || c == 'Q' ) {
c = confirm ( puzzle, quit );
if ( c == 'y' || c == 'Y' ) {
puzzle->flags = 0;
puzzle->delay = 0;
}
} else {
displayCaptions ( puzzle, paused, "" );
if ( ( c = getch () ) == 'q' || c == 'Q' ) {
c = confirm ( puzzle, quit );
if ( c == 'y' || c == 'Y' ) {
puzzle->flags = 0;
puzzle->delay = 0;
} else c = ' ';
} else c = ' ';
if ( c == ' ' ) displayCaptions ( puzzle,
thinking, "" );
}
} else timeout ( -1 );
}
move ( puzzle->y0 + y, puzzle->x0 + x );
addch ( ( entry + '0' ) | A_UNDERLINE );
move ( puzzle->y0 + y, puzzle->x0 + x );
if ( puzzle->delay ) refresh ();
}
}
static void deleteEntry ( PUZZLE *puzzle, int i, int j )
{
int entry = puzzle->board [i][j];
if ( entry != 0 ) {
int bit = digit2bit ( entry );
puzzle->rowMasks [i] |= bit;
puzzle->columnMasks [j] |= bit;
puzzle->blockMasks [i/3][j/3] |= bit;
puzzle->board [i][j] = 0;
}
}
static int makeEntry ( PUZZLE *puzzle, int i, int j, int entry )
{
int status = 1;
/* Is this a necessary entry? */
if ( entry != puzzle->board [i][j] ) {
int bit = digit2bit ( entry );
status = 0;
/* Is this a legal entry? */
if ( puzzle->rowMasks [i] & puzzle->columnMasks [j]
& puzzle->blockMasks [i/3][j/3] & bit ) {
deleteEntry ( puzzle, i, j );
status = 1;
puzzle->rowMasks [i] ^= bit;
puzzle->columnMasks [j] ^= bit;
puzzle->blockMasks [i/3][j/3] ^= bit;
puzzle->board [i][j] = entry;
puzzle->masks [i][j] = 0;
displayEntry ( puzzle, i, j, entry );
}
}
return ( status );
}
static int enterClues ( PUZZLE *puzzle )
{
int status = -1;
int y = 1;
int x = 1;
int y_count = 0;
int x_count = 0;
while ( status < 0 ) {
int c;
switch ( c = getch () ) {
case 's': /* Solve. */
case 'S': /* Solve. */
c = 0;
status = 1;
break;
case 'q': /* Quit. */
case 'Q': /* Quit. */
case ESC: /* Quit. */
c = confirm ( puzzle, quit );
if ( c == 'y' || c == 'Y' ) {
c = 0;
status = 0;
}
break;
case KEY_NPAGE:
if ( isBig ( puzzle ) ) {
if ( ( y += 6 ) > 17 ) y -= 18;
} else {
if ( ( y += 4 ) > 11 ) y -= 12;
}
break;
case KEY_PPAGE:
if ( isBig ( puzzle ) ) {
if ( ( y -= 6 ) < 1 ) y += 18;
} else {
if ( ( y -= 4 ) < 1 ) y += 12;
}
break;
case KEY_HOME:
x = 1;
break;
case KEY_END:
x = isBig ( puzzle ) ? 17 : 11;
break;
case KEY_DC: /* DEL */
break;
default:
if ( isdigit ( c ) && c != '0' ) {
int i;
int j;
if ( isBig ( puzzle ) ) {
i = ( y - 1 ) >> 1;
j = ( x - 1 ) >> 1;
} else {
i = ( y * 3 ) >> 2;
j = ( x * 3 ) >> 2;
}
if ( makeEntry ( puzzle, i, j, c - '0' ) ) {
addch ( c | A_BOLD );
} else {
beep ();
break;
}
} else if ( isspace ( c ) || c == '-' ) {
if ( c == HT ) {
int remainder;
if ( isBig ( puzzle ) )
remainder = 5 - ( x % 6 );
else remainder = 3 - ( x % 4 );
x_count = 2;
x += remainder;
} else if ( c == CR ) {
x = isBig ( puzzle ) ? 17 : 11;
x_count = 2;
} else {
int i;
int j;
if ( isBig ( puzzle ) ) {
i = ( y - 1 ) >> 1;
j = ( x - 1 ) >> 1;
} else {
i = ( y * 3 ) >> 2;
j = ( x * 3 ) >> 2;
}
deleteEntry ( puzzle, i, j );
addch ( '-' );
}
} else {
c = 0;
beep ();
break;
}
case KEY_RIGHT:
if ( isBig ( puzzle ) ) {
if ( x == 17 ) x = 1;
else {
x += 2;
break;
}
} else {
if ( x == 11 ) {
x = 1;
x_count = 0;
} else {
x_count += 4;
while ( x_count > 2 ) {
x_count -= 3;
x++;
}
break;
}
}
case KEY_DOWN:
if ( isBig ( puzzle ) ) {
if ( y == 17 ) y = 1;
else y += 2;
} else {
if ( y == 11 ) {
y = 1;
y_count = 0;
} else {
y_count += 4;
while ( y_count > 2 ) {
y_count -= 3;
y++;
}
}
}
break;
case KEY_BACKSPACE:
case KEY_LEFT:
if ( isBig ( puzzle ) ) {
if ( x == 1 ) x = 17;
else {
x -= 2;
break;
}
} else {
if ( x == 1 ) {
x = 11;
x_count = 2;
} else {
x_count -= 4;
while ( x_count < 0 ) {
x_count += 3;
--x;
}
break;
}
}
case KEY_UP:
if ( isBig ( puzzle ) ) {
if ( y == 1 ) y = 17;
else y -= 2;
} else {
if ( y == 1 ) {
y_count = 2;
y = 11;
} else {
y_count -= 4;
while ( y_count < 0 ) {
y_count += 3;
--y;
}
}
}
break;
}
move ( puzzle->y0 + y, puzzle->x0 + x );
if ( c == KEY_BACKSPACE || c == KEY_DC ) {
int i;
int j;
if ( isBig ( puzzle ) ) {
i = ( y - 1 ) >> 1;
j = ( x - 1 ) >> 1;
} else {
i = ( y * 3 ) >> 2;
j = ( x * 3 ) >> 2;
}
deleteEntry ( puzzle, i, j );
addch ( '-' );
move ( puzzle->y0 + y, puzzle->x0 + x );
}
refresh ();
}
return ( status );
}
static void drawTitle ( PUZZLE *puzzle )
{
int titleLen = strlen ( gameName );
int x;
x = puzzle->x0 + ( ( isBig ( puzzle ) ? 19 : 13 ) - titleLen ) / 2;
move ( puzzle->y0 - 2, x );
x = 0;
while ( x < titleLen ) addch ( gameName [ x++] | A_BOLD | A_UNDERLINE );
}
static PUZZLE *drawBoard ( void )
{
PUZZLE *puzzle = NULL;
int y;
int x;
getmaxyx ( stdscr, y, x );
if ( y < 18 || x < 14 ) {
if ( ! isendwin () ) endwin ();
fprintf ( stderr,
"Your screen is not big enough to display the Sudoku puzzle.\n" );
} else if ( puzzle = malloc ( sizeof ( PUZZLE ) ) ) {
int yBorder;
int bigBox;
int littleBox;
puzzle->flags = ( y < 24 || x < 20 ) ? 0 : BIG;
if ( isBig ( puzzle ) ) {
puzzle->y0 = ( ( y - 20 ) >> 1 );
puzzle->x0 = ( ( x - 20 ) >> 1 );
yBorder = littleBox = 5;
bigBox = 19;
} else {
puzzle->y0 = ( ( y - 14 ) >> 1 );
puzzle->x0 = ( ( x - 14 ) >> 1 );
yBorder = littleBox = 3;
bigBox = 13;
}
clear ();
drawTitle ( puzzle );
for ( y = 0; y < bigBox; ++y ) {
int xBorder = littleBox;
move ( puzzle->y0 + y, puzzle->x0 );
if ( yBorder == littleBox ) {
for ( x = 0; x < bigBox; ++x ) {
if ( xBorder == littleBox ) {
int c = ACS_PLUS;
if ( y == 0 ) {
c = ACS_TTEE;
if ( x == 0 ) c = ACS_ULCORNER;
else if ( x == ( bigBox - 1 ) )
c = ACS_URCORNER;
} else if ( y == ( bigBox - 1 ) ) {
c = ACS_BTEE;
if ( x == 0 ) c = ACS_LLCORNER;
else if ( x == ( bigBox - 1 ) )
c = ACS_LRCORNER;
} else {
if ( x == 0 ) c = ACS_LTEE;
else if ( x == ( bigBox - 1 ) )
c = ACS_RTEE;
}
addch ( c | A_BOLD );
xBorder = 0;
} else {
addch ( ACS_HLINE | A_BOLD );
++xBorder;
}
}
yBorder = 0;
} else {
for ( x = 0; x < bigBox; ++x ) {
if ( xBorder == littleBox ) {
addch ( ACS_VLINE | A_BOLD );
xBorder = 0;
} else {
int c = '-';
if ( isBig ( puzzle ) && ( ! ( x & 1 )
|| ! ( y & 1 ) ) )
c = ' ';
addch ( c );
++xBorder;
}
}
++yBorder;
}
}
displayCaptions ( puzzle, instructions1, instructions2 );
move ( puzzle->y0 + 1, puzzle->x0 + 1 );
refresh ();
}
return ( puzzle );
}
static void closePuzzle ( PUZZLE *puzzle )
{
if ( puzzle ) {
if ( ! isendwin () ) endwin ();
free ( puzzle );
}
}
static void copyPuzzle ( PUZZLE *src, PUZZLE *dest )
{
int puzzleSize = sizeof (PUZZLE);
char *p = ( char * ) src;
char *q = ( char * ) dest;
while ( puzzleSize-- ) *q++ = *p++;
dest->flags = 0;
dest->delay = 0;
}
static PUZZLE *openPuzzle ( int delay )
{
PUZZLE *puzzle = NULL;
struct stat st_buff;
/* Check if standard out is a terminal. */
if ( fstat ( 1, &st_buff ) < 0 ) {
perror ( "Standard out not available" );
/* Test if standard out is a terminal. */
} else if ( S_ISCHR ( st_buff.st_mode ) ) {
if ( initscr () ) {
cbreak ();
timeout ( -1 );
noecho ();
nonl ();
intrflush ( stdscr, FALSE );
keypad ( stdscr, TRUE );
/* Draw the puzzle and allocate the object. */
if ( puzzle = drawBoard () ) {
int i;
int j;
puzzle->technique = 0;
puzzle->difficulty = 0;
puzzle->delay = delay;
for ( i = 0; i < 9; ++i ) {
puzzle->rowMasks [i] = BIT_MASK;
puzzle->columnMasks [i] = BIT_MASK;
for ( j = 0; j < 9; ++j ) {
puzzle->board [i][j] = 0;
puzzle->masks [i][j] = BIT_MASK;
}
}
for ( i = 0; i < 3; ++i )
for ( j = 0; j < 3; ++j )
puzzle->blockMasks [i][j] = BIT_MASK;
if ( enterClues ( puzzle ) ) {
puzzle->flags |= DISPLAYED;
for ( i = 0; i < 9; ++i ) {
for ( j = 0; j < 9; ++j ) {
if ( puzzle->board [i][j] == 0 )
puzzle->masks [i][j]
= ( puzzle->rowMasks
[i]
& puzzle->columnMasks
[j]
& puzzle->blockMasks
[i/3][j/3] );
}
}
} else {
closePuzzle ( puzzle );
puzzle = NULL;
}
}
} else fprintf ( stderr, "Cannot take control of terminal.\n" );
} else fprintf ( stderr, "Standard out not a terminal" );
return ( puzzle );
}
static int countUnsolved ( PUZZLE *puzzle )
{
int unsolved = 0;
int i;
for ( i = 0; i < 9; ++i ) {
int j;
for ( j = 0; j < 9; ++j ) {
if ( puzzle->board [i][j] == 0 ) ++unsolved;
}
}
return ( unsolved );
}
static void updateRowMasks ( PUZZLE *puzzle, int row, int entry )
{
int column;
int mask = ~digit2bit ( entry );
for ( column = 0; column < 9; ++column ) {
puzzle->masks [row][column] &= mask;
}
}
static void updateColumnMasks ( PUZZLE *puzzle, int column, int entry )
{
int row;
int mask = ~digit2bit ( entry );
for ( row = 0; row < 9; ++row ) {
puzzle->masks [row][column] &= mask;
}
}
static void updateBlockMasks ( PUZZLE *puzzle, int row, int column, int entry )
{
int rowMax = row + 3 - ( row % 3 );
int columnMax = column + 3 - ( column % 3 );
int mask = ~digit2bit ( entry );
for ( row = rowMax - 3; row < rowMax; ++row ) {
for ( column = columnMax - 3; column < columnMax; ++column ) {
puzzle->masks [row][column] &= mask;
}
}
}
static int missingDigitScan ( PUZZLE *puzzle )
{
int progress = 0;
int i;
puzzle->difficulty += 1;
for ( i = 0; i < 9 && progress > -1; ++i ) {
int j;
for ( j = 0; j < 9 && progress > -1; ++j ) {
if ( puzzle->board [i][j] == 0 ) {
int solution;
if ( solution = bit2digit ( puzzle->masks [i][j] ) ) {
++progress;
makeEntry ( puzzle, i, j, solution );
updateRowMasks ( puzzle, i, solution );
updateColumnMasks ( puzzle, j, solution );
updateBlockMasks ( puzzle, i, j, solution );
} else if ( ! puzzle->masks [i][j] ) progress = -1;
}
}
}
if ( progress > 0 ) puzzle->technique |= MISSING_DIGIT;
return ( progress );
}
static int crossHatch ( int *masks )
{
int progress = 0;
int solutions [9];
int i;
for ( i = 0; i < 9; ++i ) {
int j;
solutions [i] = masks [i];
for ( j = 0; j < 9; ++j ) if ( j != i ) solutions [i] &= ~masks [j];
}
for ( i = 0; i < 9; ++i ) {
if ( masks [i] = bit2digit ( solutions [i] ) ) ++progress;
}
return ( progress );
}
static int getRowMasks ( PUZZLE *puzzle, int row, int *masks )
{
int status = 0;
if ( puzzle->rowMasks [row] ) {
int mask = 0;
int column;
status = 1;
/* Make masks for the row. */
for ( column = 0; column < 9; ++column ) {
masks [column] = puzzle->masks [row][column];
mask |= masks [column];
}
if ( mask != puzzle->rowMasks [row] ) status = -1;
}
return ( status );
}
static int displayRowSolutions ( PUZZLE *puzzle, int row, int *solutions )
{
int progress = 0;
int column;
/* Display the solutions. */
for ( column = 0; column < 9; ++column ) {
if ( solutions [column] ) {
if ( makeEntry ( puzzle, row, column, solutions [column] ) ) {
++progress;
updateColumnMasks ( puzzle, column,
solutions [column] );
updateBlockMasks ( puzzle, row, column,
solutions [column] );
}
}
}
return ( progress );
}
static int getColumnMasks ( PUZZLE *puzzle, int column, int *masks )
{
int status = 0;
/* Make sure the column is not already filled. */
if ( puzzle->columnMasks [column] ) {
int mask = 0;
int row;
status = 1;
/* Make masks for the column. */
for ( row = 0; row < 9; ++row ) {
masks [row] = puzzle->masks [row][column];
mask |= masks [row];
}
if ( mask != puzzle->columnMasks [column] ) status = -1;
}
return ( status );
}
static int displayColumnSolutions ( PUZZLE *puzzle, int column, int *solutions )
{
int progress = 0;
int row;
/* Enter the results of cross hatching the column. */
for ( row = 0; row < 9; ++row ) {
if ( solutions [row] ) {
if ( makeEntry ( puzzle, row, column, solutions [row] ) ) {
++progress;
updateRowMasks ( puzzle, row, solutions [row] );
updateBlockMasks ( puzzle, row, column,
solutions [row] );
}
}
}
return ( progress );
}
static int getBlockMasks ( PUZZLE *puzzle, int i, int j, int *masks )
{
int status = 0;
/* Make sure the 3x3 block is not already full. */
if ( puzzle->blockMasks [i][j] ) {
int rowMax = ( i + 1 ) * 3;
int columnMax = ( j + 1 ) * 3;
int k = 0;
int mask = 0;
int row;
status = 1;
/* Make masks for the 3x3 block. */
for ( row = rowMax - 3; row < rowMax; ++row ) {
int column;
for ( column = columnMax - 3; column < columnMax; ++column ) {
masks [k] = puzzle->masks [row][column];
mask |= masks [k];
++k;
}
}
if ( mask != puzzle->blockMasks [i][j] ) status = -1;
}
return ( status );
}
static int displayBlockSolutions ( PUZZLE *puzzle, int i, int j,
int *solutions )
{
int progress = 0;
int rowMax = ( i + 1 ) * 3;
int columnMax = ( j + 1 ) * 3;
int k = 0;
int row;
for ( row = rowMax - 3; row < rowMax; ++row ) {
int column;
for ( column = columnMax - 3; column < columnMax; ++column ) {
if ( solutions [k] ) {
if ( makeEntry ( puzzle, row, column,
solutions [k] ) ) {
++progress;
updateRowMasks ( puzzle, row, solutions [k] );
updateColumnMasks ( puzzle, column,
solutions [k] );
}
}
++k;
}
}
return ( progress );
}
static int crossHatchScan ( PUZZLE *puzzle )
{
int progress = 0;
int i;
puzzle->difficulty += 1;
/* Cross hatch the 3x3 blocks. */
for ( i = 0; i < 3 && progress > -1; i++ ) {
int j;
for ( j = 0; j < 3 && progress > -1; j++ ) {
int masks [9];
int status;
if ( ( status = getBlockMasks ( puzzle, i, j, masks ) ) > 0 ) {
if ( crossHatch ( masks ) ) {
progress += displayBlockSolutions ( puzzle,
i, j, masks );
}
} else if ( status < 0 ) progress = -1;
}
}
if ( ! progress ) {
puzzle->difficulty += 1;
/* Cross hatch the rows. */
for ( i = 0; i < 9 && progress > -1; ++i ) {
int masks [9];
int status;
if ( ( status = getRowMasks ( puzzle, i, masks ) ) > 0 ) {
if ( crossHatch ( masks ) ) {
progress += displayRowSolutions ( puzzle, i,
masks );
}
} else if ( status < 0 ) progress = -1;
}
/* Cross hatch the columns. */
for ( i = 0; i < 9 && progress > -1; ++i ) {
int masks [9];
int status;
if ( ( status = getColumnMasks ( puzzle, i, masks ) ) > 0 ) {
if ( crossHatch ( masks ) ) {
progress += displayColumnSolutions ( puzzle, i,
masks );
}
} else if ( status < 0 ) progress = -1;
}
if ( progress > 0 ) puzzle->technique |= CROSS_HATCH_2;
} else if ( progress > 0 ) puzzle->technique |= CROSS_HATCH;
return ( progress );
}
static int findRowTuple ( PUZZLE *puzzle, int row, int mask, int bitCount )
{
int progress = 0;
int tupleMask = 0;
int tupleCount = 0;
int tupleBit = 0x100;
int exactCount = 0;
int rowMax = row + 3 - ( row % 3 );
int columnMax = 0;
int column;
for ( column = 0; column < 9; ++column ) {
int cellMask = puzzle->masks [row][column];
if ( mask & cellMask ) {
++tupleCount;
tupleMask |= tupleBit;
if ( ! ( ~mask & cellMask ) ) ++exactCount;
}
tupleBit >>= 1;
}
if ( exactCount == bitCount ) {
tupleBit = 0x100;
tupleMask = 0;
for ( column = 0; column < 9; ++column ) {
int cellMask = puzzle->masks [row][column];
if ( cellMask ) {
if ( ! ( ~mask & cellMask ) ) tupleMask |= tupleBit;
else if ( mask & cellMask ) {
puzzle->masks [row][column] &= ~mask;
++progress;
}
}
tupleBit >>= 1;
}
if ( bitCount > 1 ) {
puzzle->technique |= NAKED_TUPLE;
if ( GET_NAKED_TUPLE_LEVEL ( puzzle->technique ) < bitCount )
SET_NAKED_TUPLE_LEVEL ( puzzle->technique, bitCount );
}
} else if ( tupleCount == bitCount ) {
for ( column = 0; column < 9; ++column ) {
int cellMask = puzzle->masks [row][column];
if ( ( mask & cellMask ) && ( ~mask & cellMask ) ) {
puzzle->masks [row][column] &= mask;
++progress;
}
}
if ( bitCount > 1 ) {
puzzle->technique |= HIDDEN_TUPLE;
if ( GET_HIDDEN_TUPLE_LEVEL ( puzzle->technique ) < bitCount )
SET_HIDDEN_TUPLE_LEVEL ( puzzle->technique, bitCount );
}
}
if ( bitCount < 4 ) {
if ( ! ( tupleMask & ~SQUARE_1 ) ) columnMax = 3;
else if ( ! ( tupleMask & ~SQUARE_2 ) ) columnMax = 6;
else if ( ! ( tupleMask & ~SQUARE_3 ) ) columnMax = 9;
if ( columnMax ) {
int i;
for ( i = rowMax - 3; i < rowMax; ++i ) {
if ( i != row ) {
for ( column = columnMax - 3;
column < columnMax; ++column ) {
if ( puzzle->masks [i][column]
& mask ) {
puzzle->masks [i][column]
&= ~mask;
++progress;
puzzle->technique
|= SET_INTERSECTION;
}
}
}
}
}
}
return ( progress );
}
static int rowTuples ( PUZZLE *puzzle, int row, int mask, int bitCount,
int skip )
{
int progress = 0;
int maskBits = countBits ( mask );
if ( maskBits > bitCount && skip < maskBits ) {
int digitBit = 0x100;
int skipped = 0;
--maskBits;
while ( ! progress && digitBit ) {
if ( ( digitBit & mask ) && skipped++ == skip ) {
int digitMask = mask ^ digitBit;
if ( maskBits != bitCount )
progress += rowTuples ( puzzle, row, digitMask,
bitCount, skip );
else
progress += findRowTuple ( puzzle, row,
digitMask, bitCount );
++skip;
}
digitBit >>= 1;
}
}
return ( progress );
}
static int findColumnTuple ( PUZZLE *puzzle, int column, int mask,
int bitCount )
{
int progress = 0;
int tupleCount = 0;
int tupleMask = 0;
int tupleBit = 0x100;
int exactCount = 0;
int rowMax = 0;
int columnMax = column + 3 - ( column % 3 );
int row;
for ( row = 0; row < 9; ++row ) {
int cellMask = puzzle->masks [row][column];
if ( mask & cellMask ) {
++tupleCount;
tupleMask |= tupleBit;
if ( ! ( ~mask & cellMask ) ) ++exactCount;
}
tupleBit >>= 1;
}
if ( exactCount == bitCount ) {
tupleMask = 0;
tupleBit = 0x100;
for ( row = 0; row < 9; ++row ) {
int cellMask = puzzle->masks [row][column];
if ( cellMask ) {
if ( ! ( ~mask & cellMask ) ) tupleMask |= tupleBit;
else if ( mask & cellMask ) {
puzzle->masks [row][column] &= ~mask;
++progress;
}
}
tupleBit >>= 1;
}
if ( bitCount > 1 ) {
if ( progress ) puzzle->technique |= NAKED_TUPLE;
if ( GET_NAKED_TUPLE_LEVEL ( puzzle->technique ) < bitCount )
SET_NAKED_TUPLE_LEVEL ( puzzle->technique, bitCount );
}
} else if ( tupleCount == bitCount ) {
for ( row = 0; row < 9; ++row ) {
int cellMask = puzzle->masks [row][column];
if ( ( mask & cellMask ) && ( ~mask & cellMask ) ) {
puzzle->masks [row][column] &= mask;
++progress;
}
}
if ( bitCount > 1 ) {
if ( progress ) puzzle->technique |= HIDDEN_TUPLE;
if ( GET_HIDDEN_TUPLE_LEVEL ( puzzle->technique ) < bitCount )
SET_HIDDEN_TUPLE_LEVEL ( puzzle->technique, bitCount );
}
}
if ( bitCount < 4 ) {
if ( ! ( tupleMask & ~SQUARE_1 ) ) rowMax = 3;
else if ( ! ( tupleMask & ~SQUARE_2 ) ) rowMax = 6;
else if ( ! ( tupleMask & ~SQUARE_3 ) ) rowMax = 9;
if ( rowMax ) {
int j;
for ( j = columnMax - 3; j < columnMax; ++j ) {
if ( j != column ) {
for ( row = rowMax - 3; row < rowMax; ++row ) {
if ( puzzle->masks [row][j] & mask ) {
puzzle->masks [row][j] &= ~mask;
++progress;
puzzle->technique
|= SET_INTERSECTION;
}
}
}
}
}
}
return ( progress );
}
static int columnTuples ( PUZZLE *puzzle, int column, int mask, int bitCount,
int skip )
{
int progress = 0;
int maskBits = countBits ( mask );
if ( maskBits > bitCount && skip < maskBits ) {
int digitBit = 0x100;
int skipped = 0;
--maskBits;
while ( ! progress && digitBit ) {
if ( ( digitBit & mask ) && skipped++ == skip ) {
int digitMask = mask ^ digitBit;
if ( maskBits != bitCount )
progress += columnTuples ( puzzle, column,
digitMask, bitCount, skip );
else
progress += findColumnTuple ( puzzle, column,
digitMask, bitCount );
++skip;
}
digitBit >>= 1;
}
}
return ( progress );
}
static findBlockTuple ( PUZZLE *puzzle, int top, int left, int mask,
int bitCount )
{
int progress = 0;
int rowMax = ( top + 1 ) * 3;
int columnMax = ( left + 1 ) * 3;
int tupleCount = 0;
int tupleMask = 0;
int tupleBit = 0x100;
int exactCount = 0;
int i = -1;
int j = -1;
int row;
for ( row = rowMax - 3; row < rowMax; ++row ) {
int column;
for ( column = columnMax - 3; column < columnMax; ++column ) {
int cellMask = puzzle->masks [row][column];
if ( mask & cellMask ) {
++tupleCount;
tupleMask |= tupleBit;
if ( ! ( ~mask & cellMask ) ) ++exactCount;
}
tupleBit >>= 1;
}
}
if ( exactCount == bitCount ) {
tupleMask = 0;
tupleBit = 0x100;
for ( row = rowMax - 3; row < rowMax; ++row ) {
int column;
for ( column = columnMax - 3; column < columnMax; ++column ) {
int cellMask = puzzle->masks [row][column];
if ( cellMask ) {
if ( ! ( ~mask & cellMask ) )
tupleMask |= tupleBit;
else if ( mask & cellMask ) {
puzzle->masks [row][column] &= ~mask;
++progress;
}
}
tupleBit >>= 1;
}
}
if ( bitCount > 1 ) {
if ( progress ) puzzle->technique |= NAKED_TUPLE;
if ( GET_NAKED_TUPLE_LEVEL ( puzzle->technique ) < bitCount )
SET_NAKED_TUPLE_LEVEL ( puzzle->technique, bitCount );
}
} else if ( tupleCount == bitCount ) {
for ( row = rowMax - 3; row < rowMax; ++row ) {
int column;
for ( column = columnMax - 3; column < columnMax; ++column ) {
int cellMask = puzzle->masks [row][column];
if ( ( mask & cellMask ) && ( ~mask & cellMask ) ) {
puzzle->masks [row][column] &= mask;
++progress;
}
}
}
if ( bitCount > 1 ) {
if ( progress ) puzzle->technique |= HIDDEN_TUPLE;
if ( GET_HIDDEN_TUPLE_LEVEL ( puzzle->technique ) < bitCount )
SET_HIDDEN_TUPLE_LEVEL ( puzzle->technique, bitCount );
}
}
if ( bitCount < 4 ) {
if ( ! ( tupleMask & ~ROW_1 ) ) i = rowMax - 3;
else if ( ! ( tupleMask & ~ROW_2 ) ) i = rowMax - 2;
else if ( ! ( tupleMask & ~ROW_3 ) ) i = rowMax - 1;
else if ( ! ( tupleMask & ~COLUMN_1 ) ) j = columnMax - 3;
else if ( ! ( tupleMask & ~COLUMN_2 ) ) j = columnMax - 2;
else if ( ! ( tupleMask & ~COLUMN_3 ) ) j = columnMax - 1;
if ( i > -1 ) {
int jMax = columnMax - 3;
for ( j = 0; j < jMax; ++j ) {
if ( puzzle->masks [i][j] & mask ) {
puzzle->masks [i][j] &= ~mask;
++progress;
puzzle->technique |= SET_INTERSECTION;
}
}
for ( j = columnMax; j < 9; ++j ) {
if ( puzzle->masks [i][j] & mask ) {
puzzle->masks [i][j] &= ~mask;
++progress;
puzzle->technique |= SET_INTERSECTION;
}
}
} else if ( j > -1 ) {
int iMax = rowMax - 3;
for ( i = 0; i < iMax; ++i ) {
if ( puzzle->masks [i][j] & mask ) {
puzzle->masks [i][j] &= ~mask;
++progress;
puzzle->technique |= SET_INTERSECTION;
}
}
for ( i = rowMax; i < 9; ++i ) {
if ( puzzle->masks [i][j] & mask ) {
puzzle->masks [i][j] &= ~mask;
++progress;
puzzle->technique |= SET_INTERSECTION;
}
}
}
}
return ( progress );
}
static int blockTuples ( PUZZLE *puzzle, int top, int left, int mask,
int bitCount, int skip )
{
int progress = 0;
int maskBits = countBits ( mask );
if ( maskBits > bitCount && skip < maskBits ) {
int digitBit = 0x100;
int skipped = 0;
--maskBits;
while ( digitBit ) {
if ( ( digitBit & mask ) && skipped++ == skip ) {
int digitMask = mask ^ digitBit;
if ( maskBits != bitCount )
progress += blockTuples ( puzzle, top,
left, digitMask, bitCount, skip );
else
progress += findBlockTuple ( puzzle,
top, left, digitMask, bitCount );
++skip;
}
digitBit >>= 1;
}
}
return ( progress );
}
static int tuplesAnalysis ( PUZZLE *puzzle )
{
int progress = 0;
int bitCount = 1;
int i;
while ( ! progress && bitCount < 9 ) {
puzzle->difficulty += 1;
for ( i = 0; i < 9; ++i ) {
int mask = puzzle->rowMasks [i];
if ( mask ) progress += rowTuples ( puzzle, i, mask,
bitCount, 0 );
}
for ( i = 0; i < 9; ++i ) {
int mask = puzzle->columnMasks [i];
if ( mask ) progress += columnTuples ( puzzle, i, mask,
bitCount, 0 );
}
for ( i = 0; i < 3; ++i ) {
int j;
for ( j = 0; j < 3; ++j ) {
int mask = puzzle->blockMasks [i][j];
if ( mask ) progress += blockTuples ( puzzle, i, j,
mask, bitCount, 0 );
}
}
++bitCount;
}
return ( progress );
}
static int contradictions ( PUZZLE *puzzle )
{
int progress = 0;
int bitCount = 2;
puzzle->difficulty += 1;
while ( ! progress ) {
int row;
int minBitCount = 9;
for ( row = 0; ! progress && row < 9; row++ ) {
int column;
for ( column = 0; ! progress && column < 9; column++ ) {
int bits = 0;
if ( puzzle->masks [row][column]
&& ( bits
= countBits ( puzzle->masks [row][column] ) )
== bitCount ) {
int bit = 0x100;
int ambiguous = 0;
int mask = puzzle->masks [row][column];
while ( bit ) {
if ( bit & mask ) {
PUZZLE puzzle2;
int entry
= bit2digit ( bit );
int unsolved;
copyPuzzle ( puzzle, &puzzle2 );
makeEntry ( &puzzle2, row,
column, entry );
updateRowMasks ( &puzzle2, row,
entry );
updateColumnMasks ( &puzzle2,
column, entry );
updateBlockMasks ( &puzzle2,
row, column,
entry );
if ( ( unsolved
= solvePuzzle ( &puzzle2 ) )
< 0 ) {
puzzle->masks
[row][column]
^= bit;
puzzle->technique
= puzzle2.technique;
++progress;
break;
} else if ( unsolved
|| ambiguous ) {
progress = -1;
break;
} else ++ambiguous;
}
bit >>= 1;
}
} else {
if ( bits > bitCount && bits < minBitCount )
minBitCount = bits;
}
}
}
bitCount = minBitCount;
}
if ( progress > 0 ) puzzle->technique |= WHAT_IF;
return ( progress );
}
static int solvePuzzle ( PUZZLE *puzzle )
{
int unsolved;
int progress = 1;
int i;
if ( isDisplayed ( puzzle ) && puzzle->delay ) {
displayCaptions ( puzzle, thinking, "" );
refresh ();
}
unsolved = countUnsolved ( puzzle );
while ( unsolved && progress > 0 ) {
if ( ( progress = crossHatchScan ( puzzle ) )
|| ( progress = missingDigitScan ( puzzle ) ) ) {
if ( progress > 0 ) unsolved -= progress;
else {
unsolved = -1;
progress = 0;
}
} else if ( ! ( progress = tuplesAnalysis ( puzzle ) ) )
progress = contradictions ( puzzle );
}
if ( isDisplayed ( puzzle ) ) {
if ( unsolved ) {
if ( puzzle->delay ) {
timeout ( puzzle->delay );
if ( getch () != ERR ) {
displayCaptions ( puzzle, paused, "" );
timeout ( -1 );
getch ();
} else timeout ( -1 );
}
if ( unsolved > 0 ) displayCaptions ( puzzle, stumped, "" );
else displayCaptions ( puzzle, noSolution, "" );
} else displayCaptions ( puzzle, solved, "" );
beep ();
while ( ( i = getch () ) == ERR );
}
return ( unsolved );
}
static char * tupleNames [] = { "pair", "triplet", "quadruplet", "quintuplet",
"sextuplet", "septuplet", "octuplet" };
static char tupleNameBuffer [100];
static char *printTuple ( int tuple )
{
char *tupleName = tupleNameBuffer;
if ( tuple > 1 && tuple < 9 ) tupleName = tupleNames [tuple-2];
else sprintf ( tupleNameBuffer, "%d", tuple );
return ( tupleName );
}
void printHelp ( char *programName )
{
fprintf ( stderr, "\nUse: %s [-afsnhi]\n\nWhere:\n", programName );
fprintf ( stderr, "\t-a Displays only the analysis and difficuly score.\n" );
fprintf ( stderr, "\t-f Fast solution.\n" );
fprintf ( stderr, "\t-s Slow solution.\n" );
fprintf ( stderr, "\t-n No delay solution.\n" );
fprintf ( stderr, "\t-h Help screen.\n" );
fprintf ( stderr, "\t-i Instruction screen.\n" );
}
void printInstructions ( void )
{
fprintf ( stderr, "\nEnter clues (digits 1-9). Use arrow keys,\n" );
fprintf ( stderr, "space, backspace, tab and enter keys to\n" );
fprintf ( stderr, "position the cursor. Press 's' to end\n" );
fprintf ( stderr, "entering clues and start the solution\nlogic.\n" );
fprintf ( stderr, "\nDuring the solution stage, press any key\n" );
fprintf ( stderr, "to pause and then press any key to resume.\n" );
fprintf ( stderr, "Press 'q' at any time to quit.\n" );
}
#define DEFAULT_DELAY 1000
#define FAST_DELAY 500
#define SLOW_DELAY 2000
int main ( int argc, char **argv )
{
int status = 1;
PUZZLE *puzzle;
char *programName;
char *revision;
int delay = DEFAULT_DELAY;
int analysis = 0;
int warning = 0;
int help = 0;
programName = *argv;
revision = strdup ( "$Revision: 1.20 $" );
revision += 1;
revision [ strlen ( revision ) - 1 ] = '\0';
while ( *++argv ) {
if ( **argv == '-' ) {
int index = 1;
while ( argv [0][index] ) {
switch ( argv [0][index] ) {
case 'a':
if ( delay != DEFAULT_DELAY ) {
if ( ! ( warning & 2 ) ) {
fprintf ( stderr,
"Warning - speed setting has no effect when analysis option selected.\n" );
warning |= 2;
}
}
analysis = 1;
break;
case 'f':
if ( analysis ) {
if ( ! ( warning & 2 ) ) {
fprintf ( stderr,
"Warning - speed setting has no effect when analysis option selected.\n" );
warning |= 2;
}
} else if ( delay == DEFAULT_DELAY ) delay
= FAST_DELAY;
else if ( ! ( warning & 1 ) ) {
warning |= 1;
fprintf ( stderr,
"Warning - speed already set, discarding last speed setting.\n" );
}
break;
case 's':
if ( analysis ) {
if ( ! ( warning & 2 ) ) {
fprintf ( stderr,
"Warning - speed setting has no effect when analysis option selected.\n" );
warning |= 2;
}
} else if ( delay == DEFAULT_DELAY ) delay
= SLOW_DELAY;
else if ( ! ( warning & 1 ) ) {
warning |= 1;
fprintf ( stderr,
"Warning - speed already set, discarding last speed setting.\n" );
}
break;
case 'n':
if ( analysis ) {
if ( ! ( warning & 2 ) ) {
fprintf ( stderr,
"Warning - speed setting has no effect when analysis option selected.\n" );
warning |= 2;
}
} else if ( delay == DEFAULT_DELAY ) delay = 0;
else if ( ! ( warning & 1 ) ) {
warning |= 1;
fprintf ( stderr,
"Warning - speed already set, discarding last speed setting.\n" );
}
break;
case 'h':
help |= 1;
break;
case 'i':
help |= 2;
break;
default:
warning += 4;
fprintf ( stderr,
"Unknown option letter (-%c) ignored.\n",
argv [0][index] );
break;
}
++index;
}
} else fprintf ( stderr,
"Illegal command line parameter (%s) ignored.\n",
*argv );
}
if ( warning ) sleep ( ( ( warning + 3 ) >> 1 ) + 1 );
if ( help ) {
fprintf ( stderr, "%s\n%s\n", PUZZLE_TITLE, revision );
if ( help & 1 ) printHelp ( programName );
if ( help & 2 ) printInstructions ();
status = 0;
} else if ( puzzle = openPuzzle ( delay ) ) {
int technique = 0;
int difficulty = 0;
if ( analysis ) {
puzzle->flags = 0;
puzzle->delay = 0;
}
status = solvePuzzle ( puzzle );
if ( ! status ) {
difficulty = puzzle->difficulty;
technique = puzzle->technique;
}
closePuzzle ( puzzle );
if ( ! status ) {
fprintf ( stdout, "Solution techniques used:\n" );
if ( technique & CROSS_HATCH ) fprintf ( stdout,
"\tBlock cross hatch scan\n" );
if ( technique & CROSS_HATCH_2 ) fprintf ( stdout,
"\tRow/column cross hatch scan\n" );
if ( technique & MISSING_DIGIT ) fprintf ( stdout,
"\tMissing digit scan\n" );
if ( technique & SET_INTERSECTION ) fprintf ( stdout,
"\tRegion intersection analysis\n" );
if ( technique & NAKED_TUPLE ) fprintf ( stdout,
"\tNaked %s analysis\n",
printTuple ( GET_NAKED_TUPLE_LEVEL ( technique ) ) );
if ( technique & HIDDEN_TUPLE ) fprintf ( stdout,
"\tHidden %s analysis\n",
printTuple ( GET_HIDDEN_TUPLE_LEVEL ( technique ) ) );
if ( technique & WHAT_IF ) fprintf ( stdout,
"\tWhat if analysis\n" );
fprintf ( stdout, "Difficulty score = %d\n", difficulty );
} else if ( analysis ) {
if ( status < 0 ) fprintf ( stdout, "No solution possible.\n" );
else fprintf ( stdout, "Multiple solutions possible.\n" );
}
}
return ( status );
}
A special word of thanks to Eric for drawing my attention to this web page of bit twiddling hacks. I made a few changes to my bit twiddling routines based on what I learned there.