#ident "$Id: cursesGui.c,v 1.5 2011/05/03 09:34:06 pwh Exp $"
/*
 * Sudoku Puzzle Solver, curses GUI logic.
 */

#if	defined(DEBUG)
#include	<stdio.h>
extern FILE *logFile;
#endif

#include	<sys/stat.h>
#include	<stdlib.h>
#include	<curses.h>	/* Use the ncurses library. */
#include	"cursesGui.h"
#include	"twiddleBits.h"

#define		BIT_MASK	0x1FF

const char	*gameName = PUZZLE_TITLE;
const char	*instructions1 = "Enter clues";
const char	*instructions2 = "'s' = solve";
const char	*quit = "Quit (Y/N)? ";
const char	*thinking = "Thinking...";
const char	*paused = "Paused...";
const char	*stumped = "Multiple Solutions";
const char	*noSolution = "No Solution!";
const char	*solved = "Ta Daaaa!!!";
const char	*toExit = "Press any key ...";
const char	*solveAnyway = "Solve (Y/N)?";


int confirm ( DISPLAY *display, const char *message )

{
   int  y = display->y0 - 1;
   int	x;
   int	c;
   int	stringLength = strlen ( message );

#if	defined(DEBUG)
   fprintf ( logFile, "confirm ( %p, \"%s\" ) {\n", display, message );
   fflush ( logFile );
#endif

   x = display->x0 + ( ( isBig ( display ) ? 19 : 13 ) - stringLength ) / 2;

   move ( y, x );

   while ( *message ) addch ( *message++ );

   refresh ();

   while ( ( c = getch () ) == ERR );

   move ( y, 0 );

   clrtoeol ();

#if	defined(DEBUG)
   fprintf ( logFile, "} = '%c' /* confirm () */\n", c );
   fflush ( logFile );
#endif

   return ( c );
}


void displayCaptions ( DISPLAY *display, const char *line1, const char *line2 )

{
   int		y;
   const char	*c;

#if	defined(DEBUG)
   fprintf ( logFile, "displayCaptions ( %p, \"%s\", \"%s\" ) {\n", display,
								line1, line2 );
   fflush ( logFile );
#endif

   y = display->y0 + ( isBig ( display ) ? 20 : 14 );

   if ( c = line1 ) {

	int	stringLength;

	move ( y, 0 );
	clrtoeol ();

	if ( stringLength = strlen ( c ) ) {

		int	x;

		x = display->x0 + ( ( isBig ( display ) ? 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 = display->x0 + ( ( isBig ( display ) ? 19 : 13 )
							- stringLength ) / 2;
		move ( y, x );
		while ( *c ) addch ( *c++ );
	}
   }

   move ( display->y0 + 1, display->x0 + 1 );

#if	defined(DEBUG)
   fprintf ( logFile, "} /* displayCaptions () */\n" );
   fflush ( logFile );
#endif
}


int displayEntry ( DISPLAY *display, int i, int j, int entry )

{
   int	status = 1;
   int	y;
   int	x;

#if	defined(DEBUG)
   fprintf ( logFile, "displayEntry ( %p, %d, %d, %d ) {\n", display, i, j,
									entry );
   fflush ( logFile );
#endif

   display->board [i][j] = entry;

   if ( isBig ( display ) ) {

	x = ( j << 1 ) + 1;
	y = ( i << 1 ) + 1;

   } else {

	x = ( ( j << 2 ) + 3 ) / 3;
	y = ( ( i << 2 ) + 3 ) / 3;
   }

   if ( display->delay ) {

	int	c;

	timeout ( display->delay );

	if ( ( c = getch () ) != ERR ) {

		timeout ( -1 );

		if ( c == 'q' || c == 'Q' ) {

			c = confirm ( display, quit );

			if ( c == 'y' || c == 'Y' ) {

				display->flags = 0;
				display->delay = 0;
				status = 0;
			}

		} else {

			displayCaptions ( display, paused, "" );

			if ( ( c = getch () ) == 'q' || c == 'Q' ) {

				c = confirm ( display, quit );

				if ( c == 'y' || c == 'Y' ) {

					display->flags = 0;
					display->delay = 0;
					status = 0;

				} else c = ' ';

			} else c = ' ';

			if ( c == ' ' ) displayCaptions ( display, thinking,
									"" );
		}

	} else timeout ( -1 );
   }

   move ( display->y0 + y, display->x0 + x );
   addch ( ( entry + '0' ) | A_UNDERLINE );
   move ( display->y0 + y, display->x0 + x );

   if ( display->delay ) refresh ();

#if	defined(DEBUG)
   fprintf ( logFile, "} = %d /* displayEntry () */\n", status );
   fflush ( logFile );
#endif

   return ( status );
}


static void deleteClue ( DISPLAY *display, int i, int j )

{
   int	entry = display->board [i][j];

#if	defined(DEBUG)
   fprintf ( logFile, "deleteClue ( %p, %d, %d ) {\n", display, i, j );
   fflush ( logFile );
#endif

   if ( entry != 0 ) {

	int	bit = digit2bit ( entry );

	display->rowMasks [i] |= bit;
	display->columnMasks [j] |= bit;
	display->blockMasks [i/3][j/3] |= bit;
	display->board [i][j] = 0;
   }

#if	defined(DEBUG)
   fprintf ( logFile, "} /* deleteClue () */\n" );
   fflush ( logFile );
#endif
}


static int enterClue ( DISPLAY *display, int i, int j, int entry )

{
   int	status = 1;

#if	defined(DEBUG)
   fprintf ( logFile, "enterClue ( %p, %d, %d, %d ) {\n", display, i, j,
									entry );
   fflush ( logFile );
#endif

   /* Is this a necessary entry? */
   if ( entry != display->board [i][j] ) {

	int	bit = digit2bit ( entry );

	status = 0;

	/* Is this a legal entry? */
	if ( display->rowMasks [i] & display->columnMasks [j]
				& display->blockMasks [i/3][j/3] & bit ) {

		deleteClue ( display, i, j );

		status = 1;
		display->rowMasks [i] ^= bit;
		display->columnMasks [j] ^= bit;
		display->blockMasks [i/3][j/3] ^= bit;
		displayEntry ( display, i, j, entry );
	}
   }

#if	defined(DEBUG)
   fprintf ( logFile, "} = %d /* enterClue () */\n", status );
   fflush ( logFile );
#endif

   return ( status );
}


static int getClues ( DISPLAY *display )

{
   int	status = -1;
   int	y = 1;
   int	x = 1;
   int  y_count = 0;
   int  x_count = 0;

#if	defined(DEBUG)
   fprintf ( logFile, "getClues ( %p ) {\n", display );
   fflush ( logFile );
#endif

   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 ( display, quit );

		if ( c == 'y' || c == 'Y' ) {

			c = 0;
			status = 0;
		}

		break;

	  case KEY_NPAGE:

		if ( isBig ( display ) ) {

			if ( ( y += 6 ) > 17 ) y -= 18;

		} else {

			if ( ( y += 4 ) > 11 ) y -= 12;
		}

		break;

	  case KEY_PPAGE:

		if ( isBig ( display ) ) {

			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 ( display ) ? 17 : 11;
		break;

	  case KEY_DC:	/* DEL */

		break;

	  default:

		if ( isdigit ( c ) && c != '0' ) {

			int	i;
			int	j;

			if ( isBig ( display ) ) {

				i = ( y - 1 ) >> 1;
				j = ( x - 1 ) >> 1;

			} else {

				i = ( y * 3 ) >> 2;
				j = ( x * 3 ) >> 2;
			}

			if ( enterClue ( display, i, j, c - '0' ) ) {

				addch ( c | A_BOLD );

			} else {

				beep ();
				break;
			}

		} else if ( isspace ( c ) || c == '-' ) {

			if ( c == HT ) {

				int	remainder;

				if ( isBig ( display ) )
						remainder = 5 - ( x % 6 );
				else remainder = 3 - ( x % 4 );

				x_count = 2;

				x += remainder;

			} else if ( c == CR ) {

				x = isBig ( display ) ? 17 : 11;
				x_count = 2;

			} else {

				int	i;
				int	j;

				if ( isBig ( display ) ) {

					i = ( y - 1 ) >> 1;
					j = ( x - 1 ) >> 1;

				} else {

					i = ( y * 3 ) >> 2;
					j = ( x * 3 ) >> 2;
				}

				deleteClue ( display, i, j );
				addch ( '-' );
			}

		} else {

			c = 0;
			beep ();
			break;
		}

	  case KEY_RIGHT:

		if ( isBig ( display ) ) {

			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 ( display ) ) {

			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 ( display ) ) {

			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 ( display ) ) {

			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 ( display->y0 + y, display->x0 + x );

	if ( c == KEY_BACKSPACE || c == KEY_DC ) {

		int	i;
		int	j;

		if ( isBig ( display ) ) {

			i = ( y - 1 ) >> 1;
			j = ( x - 1 ) >> 1;

		} else {

			i = ( y * 3 ) >> 2;
			j = ( x * 3 ) >> 2;
		}

		deleteClue ( display, i, j );
		addch ( '-' );
		move ( display->y0 + y, display->x0 + x );
	}

	refresh ();
   }

#if	defined(DEBUG)
   fprintf ( logFile, "} = %d /* getClues () */\n", status );
   fflush ( logFile );
#endif

   return ( status );
}


static void drawTitle ( DISPLAY *display )

{
#if	defined(DEBUG)
   fprintf ( logFile, "drawTitle ( %p ) {\n", display );
   fflush ( logFile );
#endif

   int	titleLen = strlen ( gameName );
   int	x;

   x = display->x0 + ( ( isBig ( display ) ? 19 : 13 ) - titleLen ) / 2;

   move ( display->y0 - 2, x );

   x = 0;

   while ( x < titleLen ) addch ( gameName [ x++] | A_BOLD | A_UNDERLINE );

#if	defined(DEBUG)
   fprintf ( logFile, "} /* drawTitle () */\n" );
   fflush ( logFile );
#endif
}


static DISPLAY *drawBoard ( void )

{
   DISPLAY	*display = NULL;
   int		y;
   int		x;

#if	defined(DEBUG)
   fprintf ( logFile, "drawBoard ( void ) {\n" );
   fflush ( logFile );
#endif

   getmaxyx ( stdscr, y, x );

   if ( y < 18 || x < 18 ) {

	if ( ! isendwin () ) endwin ();

	fprintf ( stderr,
	"Your screen is not big enough to display the Sudoku puzzle.\n" );

   } else if ( display = malloc ( sizeof ( DISPLAY ) ) ) {

	int	yBorder;
	int	bigBox;
	int	littleBox;

	display->flags = ( y < 24 || x < 20 ) ? 0 : BIG;

	if ( isBig ( display ) ) {

		display->y0 = ( ( y - 20 ) >> 1 );
		display->x0 = ( ( x - 20 ) >> 1 );

		yBorder = littleBox = 5;
		bigBox = 19;

	} else {

		display->y0 = ( ( y - 14 ) >> 1 );
		display->x0 = ( ( x - 14 ) >> 1 );

		yBorder = littleBox = 3;
		bigBox = 13;
	}

	clear ();
	drawTitle ( display );

	for ( y = 0; y < bigBox; ++y ) {

		int	xBorder = littleBox;

		move ( display->y0 + y, display->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 ( display ) && ( ! ( x & 1 )
							|| ! ( y & 1 ) ) )
									c = ' ';
					addch ( c );
					++xBorder;
				}
			}

			++yBorder;
		}
	}

	displayCaptions ( display, instructions1, instructions2 );
	move ( display->y0 + 1, display->x0 + 1 );
	refresh ();
   }

#if	defined(DEBUG)
   fprintf ( logFile, "} = %p /* drawBoard () */\n", display );
   fflush ( logFile );
#endif

   return ( display );
}


void closeDisplay ( DISPLAY *display )

{
#if	defined(DEBUG)
   fprintf ( logFile, "closeDisplay ( %p ) {\n", display );
   fflush ( logFile );
#endif

   if ( display ) {

	if ( ! isendwin () ) endwin ();
	free ( display );
   }

#if	defined(DEBUG)
   fprintf ( logFile, "} /* closeDisplay () */\n" );
   fflush ( logFile );
#endif
}


DISPLAY *openDisplay ( int delay )

{
   DISPLAY	*display = NULL;
   struct stat	st_buff;

#if	defined(DEBUG)
   fprintf ( logFile, "openDisplay ( %d ) {\n", delay );
   fflush ( logFile );
#endif

   /* 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 board and allocate the object. */
		if ( display = drawBoard () ) {

			int	i;
			int	j;

			display->delay = 0;

			for ( i = 0; i < 9; ++i ) {

				display->rowMasks [i] = BIT_MASK;
				display->columnMasks [i] = BIT_MASK;

				for ( j = 0; j < 9; ++j ) {

					display->board [i][j] = 0;
				}
			}

			for ( i = 0; i < 3; ++i )
				for ( j = 0; j < 3; ++j )
					display->blockMasks [i][j] = BIT_MASK;

			if ( ! getClues ( display ) ) {

				closeDisplay ( display );
				display = NULL;

			} else display->delay = delay;
		}

	} else fprintf ( stderr, "Cannot take control of terminal.\n" );

   } else fprintf ( stderr, "Standard out not a terminal" );

#if	defined(DEBUG)
   fprintf ( logFile, "} = %p /* openDisplay () */\n", display );
   fflush ( logFile );
#endif

   return ( display );
}


Last updated: Saturday, August 01, 2009 11:49:29 PM