| 
							- //
 - // "$Id: sudoku.cxx 7903 2010-11-28 21:06:39Z matt $"
 - //
 - // Sudoku game using the Fast Light Tool Kit (FLTK).
 - //
 - // Copyright 2005-2010 by Michael Sweet.
 - //
 - // This library is free software; you can redistribute it and/or
 - // modify it under the terms of the GNU Library General Public
 - // License as published by the Free Software Foundation; either
 - // version 2 of the License, or (at your option) any later version.
 - //
 - // This library is distributed in the hope that it will be useful,
 - // but WITHOUT ANY WARRANTY; without even the implied warranty of
 - // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - // Library General Public License for more details.
 - //
 - // You should have received a copy of the GNU Library General Public
 - // License along with this library; if not, write to the Free Software
 - // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 - // USA.
 - //
 - // Please report all bugs and problems on the following page:
 - //
 - //     http://www.fltk.org/str.php
 - //
 - 
 - #include <FL/Fl.H>
 - #include <FL/Enumerations.H>
 - #include <FL/Fl_Double_Window.H>
 - #include <FL/Fl_Button.H>
 - #include <FL/Fl_Group.H>
 - #include <FL/fl_ask.H>
 - #include <FL/fl_draw.H>
 - #include <FL/Fl_Help_Dialog.H>
 - #include <FL/Fl_Preferences.H>
 - #include <FL/Fl_Sys_Menu_Bar.H>
 - #include <FL/x.H>
 - #include <stdio.h>
 - #include <stdlib.h>
 - #include <string.h>
 - #include <time.h>
 - #include <FL/math.h>
 - 
 - #ifdef WIN32
 - #  include "sudokurc.h"
 - #elif !defined(__APPLE__)
 - #  include "pixmaps/sudoku.xbm"
 - #endif // WIN32
 - 
 - // Audio headers...
 - #include <config.h>
 - 
 - #ifndef WIN32
 - #  include <unistd.h>
 - #endif // !WIN32
 - 
 - #ifdef HAVE_ALSA_ASOUNDLIB_H
 - #  define ALSA_PCM_NEW_HW_PARAMS_API
 - #  include <alsa/asoundlib.h>
 - #endif // HAVE_ALSA_ASOUNDLIB_H
 - #ifdef __APPLE__
 - #  include <CoreAudio/AudioHardware.h>
 - #endif // __APPLE__
 - #ifdef WIN32
 - #  include <mmsystem.h>
 - #endif // WIN32
 - 
 - 
 - //
 - // Default sizes...
 - //
 - 
 - #define GROUP_SIZE	160
 - #define CELL_SIZE	50
 - #define CELL_OFFSET	5
 - #ifdef __APPLE__
 - #  define MENU_OFFSET	0
 - #else
 - #  define MENU_OFFSET	25
 - #endif // __APPLE__
 - 
 - // Sound class for Sudoku...
 - //
 - // There are MANY ways to implement sound in a FLTK application.
 - // The approach we are using here is to conditionally compile OS-
 - // specific code into the application - CoreAudio for MacOS X, the
 - // standard Win32 API stuff for Windows, ALSA or X11 for Linux, and
 - // X11 for all others.  We have to support ALSA on Linux because the
 - // current Xorg releases no longer support XBell() or the PC speaker.
 - //
 - // There are several good cross-platform audio libraries we could also
 - // use, such as OpenAL, PortAudio, and SDL, however they were not chosen
 - // for this application because of our limited use of sound.
 - //
 - // Many thanks to Ian MacArthur who provided sample code that led to
 - // the CoreAudio implementation you see here!
 - class SudokuSound {
 -   // Private, OS-specific data...
 - #ifdef __APPLE__
 -   AudioDeviceID device;
 - #  if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
 -   AudioDeviceIOProcID audio_proc_id;
 - #  endif
 -   AudioStreamBasicDescription format;
 -   short *data;
 -   int remaining;
 - 
 -   static OSStatus audio_cb(AudioDeviceID device,
 - 			   const AudioTimeStamp *current_time,
 - 			   const AudioBufferList *data_in,
 - 			   const AudioTimeStamp *time_in,
 - 			   AudioBufferList *data_out,
 - 			   const AudioTimeStamp *time_out,
 - 			   void *client_data);
 - #elif defined(WIN32)
 -   HWAVEOUT	device;
 -   HGLOBAL	header_handle;
 -   LPWAVEHDR	header_ptr;
 -   HGLOBAL	data_handle;
 -   LPSTR		data_ptr;
 - 
 - #else
 - #  ifdef HAVE_ALSA_ASOUNDLIB_H
 -   snd_pcm_t *handle;
 - #  endif // HAVE_ALSA_ASOUNDLIB_H
 - #endif // __APPLE__
 - 
 -   // Common data...
 -   static int frequencies[9];
 -   static short *sample_data[9];
 -   static int sample_size;
 - 
 -   public:
 - 
 -   SudokuSound();
 -   ~SudokuSound();
 - 
 -   void	play(char note);
 - };
 - 
 - 
 - // Sudoku cell class...
 - class SudokuCell : public Fl_Widget {
 -   bool		readonly_;
 -   int		value_;
 -   int		test_value_[9];
 - 
 -   public:
 - 
 - 		SudokuCell(int X, int Y, int W, int H);
 -   void		draw();
 -   int		handle(int event);
 -   void		readonly(bool r) { readonly_ = r; redraw(); }
 -   bool		readonly() const { return readonly_; }
 -   void		test_value(int v, int n) { test_value_[n] = v; redraw(); }
 -   int		test_value(int n) const { return test_value_[n]; }
 -   void		value(int v) {
 - 		  value_ = v;
 - 		  for (int i = 0; i < 8; i ++) test_value_[i] = 0;
 - 		  redraw();
 - 		}
 -   int		value() const { return value_; }
 - };
 - 
 - 
 - // Sudoku window class...
 - class Sudoku : public Fl_Double_Window {
 -   Fl_Sys_Menu_Bar *menubar_;
 -   Fl_Group	*grid_;
 -   time_t	seed_;
 -   char		grid_values_[9][9];
 -   SudokuCell	*grid_cells_[9][9];
 -   Fl_Group	*grid_groups_[3][3];
 -   int		difficulty_;
 -   SudokuSound	*sound_;
 - 
 -   static void	check_cb(Fl_Widget *widget, void *);
 -   static void	close_cb(Fl_Widget *widget, void *);
 -   static void	diff_cb(Fl_Widget *widget, void *d);
 -   static void	update_helpers_cb(Fl_Widget *, void *);
 -   static void	help_cb(Fl_Widget *, void *);
 -   static void	mute_cb(Fl_Widget *widget, void *);
 -   static void	new_cb(Fl_Widget *widget, void *);
 -   static void	reset_cb(Fl_Widget *widget, void *);
 -   static void	restart_cb(Fl_Widget *widget, void *);
 -   void		set_title();
 -   static void	solve_cb(Fl_Widget *widget, void *);
 - 
 -   static Fl_Help_Dialog *help_dialog_;
 -   static Fl_Preferences	prefs_;
 -   public:
 - 
 - 	      	Sudoku();
 - 		~Sudoku();
 - 
 -   void		check_game(bool highlight = true);
 -   void		load_game();
 -   void		new_game(time_t seed);
 -   int		next_value(SudokuCell *c);
 -   void		resize(int X, int Y, int W, int H);
 -   void		save_game();
 -   void		solve_game();
 -   void	        update_helpers();
 - };
 - 
 - 
 - // Sound class globals...
 - int SudokuSound::frequencies[9] = {
 -   880,	// A(5)
 -   988,	// B(5)
 -   1046,	// C(5)
 -   1174,	// D(5)
 -   1318,	// E(5)
 -   1396,	// F(5)
 -   1568,	// G(5)
 -   1760,	// H (A6)
 -   1976	// I (B6)
 - };
 - short *SudokuSound::sample_data[9] = { 0 };
 - int SudokuSound::sample_size = 0;
 - 
 - 
 - // Initialize the SudokuSound class
 - SudokuSound::SudokuSound() {
 -   sample_size = 0;
 - 
 - #ifdef __APPLE__
 -   remaining = 0;
 - 
 -   UInt32 size = sizeof(device);
 - 
 -   if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
 - 			       &size, (void *)&device) != noErr) return;
 - 
 -   size = sizeof(format);
 -   if (AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyStreamFormat,
 - 			     &size, &format) != noErr) return;
 - 
 -   // Set up a format we like...
 -   format.mSampleRate       = 44100.0;	// 44.1kHz
 -   format.mChannelsPerFrame = 2;		// stereo
 - 
 -   if (AudioDeviceSetProperty(device, NULL, 0, false,
 -                              kAudioDevicePropertyStreamFormat,
 - 	                     sizeof(format), &format) != noErr) return;
 - 
 -   // Check we got linear pcm - what to do if we did not ???
 -   if (format.mFormatID != kAudioFormatLinearPCM) return;
 - 
 -   // Attach the callback and start the device
 - #  if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
 -   if (AudioDeviceCreateIOProcID(device, audio_cb, (void *)this, &audio_proc_id) != noErr) return;
 -   AudioDeviceStart(device, audio_proc_id);
 - #  else
 -   if (AudioDeviceAddIOProc(device, audio_cb, (void *)this) != noErr) return;
 -   AudioDeviceStart(device, audio_cb);
 - #  endif
 -   
 -   sample_size = (int)format.mSampleRate / 20;
 - 
 - #elif defined(WIN32)
 -   WAVEFORMATEX	format;
 - 
 -   memset(&format, 0, sizeof(format));
 -   format.cbSize          = sizeof(format);
 -   format.wFormatTag      = WAVE_FORMAT_PCM;
 -   format.nChannels       = 2;
 -   format.nSamplesPerSec  = 44100;
 -   format.nAvgBytesPerSec = 44100 * 4;
 -   format.nBlockAlign     = 4;
 -   format.wBitsPerSample  = 16;
 - 
 -   data_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, format.nSamplesPerSec / 5);
 -   if (!data_handle) return;
 - 
 -   data_ptr = (LPSTR)GlobalLock(data_handle);
 - 
 -   header_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
 -   if (!header_handle) return;
 - 
 -   header_ptr = (WAVEHDR *)GlobalLock(header_handle);
 - 
 -   header_ptr->lpData         = data_ptr;
 -   header_ptr->dwBufferLength = format.nSamplesPerSec / 5;
 -   header_ptr->dwFlags        = 0;
 -   header_ptr->dwLoops        = 0;
 - 
 -   if (waveOutOpen(&device, WAVE_MAPPER, &format, 0, 0, WAVE_ALLOWSYNC)
 -           != MMSYSERR_NOERROR) return;
 - 
 -   waveOutPrepareHeader(device, header_ptr, sizeof(WAVEHDR));
 - 
 -   sample_size = 44100 / 20;
 - 
 - #else
 - #  ifdef HAVE_ALSA_ASOUNDLIB_H
 -   handle = NULL;
 - 
 -   if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) >= 0) {
 -     // Initialize PCM sound stuff...
 -     snd_pcm_hw_params_t *params;
 - 
 -     snd_pcm_hw_params_alloca(¶ms);
 -     snd_pcm_hw_params_any(handle, params);
 -     snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
 -     snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16);
 -     snd_pcm_hw_params_set_channels(handle, params, 2);
 -     unsigned rate = 44100;
 -     int dir;
 -     snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
 -     snd_pcm_uframes_t period = (int)rate / 4;
 -     snd_pcm_hw_params_set_period_size_near(handle, params, &period, &dir);
 - 
 -     sample_size = rate / 20;
 - 
 -     if (snd_pcm_hw_params(handle, params) < 0) {
 -       sample_size = 0;
 -       snd_pcm_close(handle);
 -       handle = NULL;
 -     }
 -   }
 - #  endif // HAVE_ALSA_ASOUNDLIB_H
 - #endif // __APPLE__
 - 
 -   if (sample_size) {
 -     // Make each of the notes using a combination of sine and sawtooth waves
 -     int attack = sample_size / 10;
 -     int decay = 4 * sample_size / 5;
 - 
 -     for (int i = 0; i < 9; i ++) {
 -       sample_data[i] = new short[2 * sample_size];
 - 
 -       short *sample_ptr = sample_data[i];
 - 
 -       for (int j = 0; j < sample_size; j ++, sample_ptr += 2) {
 -         double theta = 0.05 * frequencies[i] * j / sample_size;
 -         double val = 0.5 * sin(2.0 * M_PI * theta) + theta - (int)theta - 0.5;
 - 
 -         if (j < attack) {
 - 	  *sample_ptr = (int)(32767 * val * j / attack);
 - 	} else if (j > decay) {
 - 	  *sample_ptr = (int)(32767 * val * (sample_size - j + decay) /
 - 	                      sample_size);
 - 	} else *sample_ptr = (int)(32767 * val);
 - 
 -         sample_ptr[1] = *sample_ptr;
 -       }
 -     }
 -   }
 - }
 - 
 - 
 - // Cleanup the SudokuSound class
 - SudokuSound::~SudokuSound() {
 - #ifdef __APPLE__
 -   if (sample_size) {
 - #  if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
 -     AudioDeviceStop(device, audio_proc_id);
 -     AudioDeviceDestroyIOProcID(device, audio_proc_id);
 - #  else
 -     AudioDeviceStop(device, audio_cb);
 -     AudioDeviceRemoveIOProc(device, audio_cb);
 - #  endif
 -   }
 - 
 - #elif defined(WIN32)
 -   if (sample_size) {
 -     waveOutClose(device);
 - 
 -     GlobalUnlock(header_handle);
 -     GlobalFree(header_handle);
 - 
 -     GlobalUnlock(data_handle);
 -     GlobalFree(data_handle);
 -   }
 - 
 - #else
 - #  ifdef HAVE_ALSA_ASOUNDLIB_H
 -   if (handle) {
 -     snd_pcm_drain(handle);
 -     snd_pcm_close(handle);
 -   }
 - #  endif // HAVE_ALSA_ASOUNDLIB_H
 - #endif // __APPLE__
 - 
 -   if (sample_size) {
 -     for (int i = 0; i < 9; i ++) {
 -       delete[] sample_data[i];
 -     }
 -   }
 - }
 - 
 - 
 - #ifdef __APPLE__
 - // Callback function for writing audio data...
 - OSStatus
 - SudokuSound::audio_cb(AudioDeviceID device,
 - 		      const AudioTimeStamp *current_time,
 - 		      const AudioBufferList *data_in,
 - 		      const AudioTimeStamp *time_in,
 - 		      AudioBufferList *data_out,
 - 		      const AudioTimeStamp *time_out,
 - 		      void *client_data) {
 -   SudokuSound *ss = (SudokuSound *)client_data;
 -   int count;
 -   float *buffer;
 - 
 -   if (!ss->remaining) return noErr;
 - 
 -   for (count = data_out->mBuffers[0].mDataByteSize / sizeof(float),
 -           buffer = (float*) data_out->mBuffers[0].mData;
 -        ss->remaining > 0 && count > 0;
 -        count --, ss->data ++, ss->remaining --) {
 -     *buffer++ = *(ss->data) / 32767.0;
 -   }
 - 
 -   while (count > 0) {
 -     *buffer++ = 0.0;
 -     count --;
 -   }
 - 
 -   return noErr;
 - }
 - #endif // __APPLE__
 - 
 - 
 - // Play a note for 250ms...
 - void SudokuSound::play(char note) {
 -   Fl::check();
 - 
 - #ifdef __APPLE__
 -   // Point to the next note...
 -   data      = sample_data[note - 'A'];
 -   remaining = sample_size * 2;
 - 
 -   // Wait for the sound to complete...
 -   usleep(50000);
 - 
 - #elif defined(WIN32)
 -   if (sample_size) {
 -     memcpy(data_ptr, sample_data[note - 'A'], sample_size * 4);
 - 
 -     waveOutWrite(device, header_ptr, sizeof(WAVEHDR));
 - 
 -     Sleep(50);
 -   } else Beep(frequencies[note - 'A'], 50);
 - 
 - #else
 - #  ifdef HAVE_ALSA_ASOUNDLIB_H
 -   if (handle) {
 -     // Use ALSA to play the sound...
 -     if (snd_pcm_writei(handle, sample_data[note - 'A'], sample_size) < 0) {
 -       snd_pcm_prepare(handle);
 -       snd_pcm_writei(handle, sample_data[note - 'A'], sample_size);
 -     }
 -     usleep(50000);
 -     return;
 -   }
 - #  endif // HAVE_ALSA_ASOUNDLIB_H
 - 
 -   // Just use standard X11 stuff...
 -   XKeyboardState	state;
 -   XKeyboardControl	control;
 - 
 -   // Get original pitch and duration...
 -   XGetKeyboardControl(fl_display, &state);
 - 
 -   // Sound a tone for the given note...
 -   control.bell_percent  = 100;
 -   control.bell_pitch    = frequencies[note - 'A'];
 -   control.bell_duration = 50;
 - 
 -   XChangeKeyboardControl(fl_display,
 -                          KBBellPercent | KBBellPitch | KBBellDuration,
 - 			 &control);
 -   XBell(fl_display, 100);
 -   XFlush(fl_display);
 - 
 -   // Restore original pitch and duration...
 -   control.bell_percent  = state.bell_percent;
 -   control.bell_pitch    = state.bell_pitch;
 -   control.bell_duration = state.bell_duration;
 - 
 -   XChangeKeyboardControl(fl_display,
 -                          KBBellPercent | KBBellPitch | KBBellDuration,
 - 			 &control);
 - #endif // __APPLE__
 - }
 - 
 - 
 - // Create a cell widget
 - SudokuCell::SudokuCell(int X, int Y, int W, int H)
 -   : Fl_Widget(X, Y, W, H, 0) {
 -   value(0);
 - }
 - 
 - 
 - // Draw cell
 - void
 - SudokuCell::draw() {
 -   static Fl_Align align[8] = {
 -     FL_ALIGN_TOP_LEFT,
 -     FL_ALIGN_TOP,
 -     FL_ALIGN_TOP_RIGHT,
 -     FL_ALIGN_RIGHT,
 -     FL_ALIGN_BOTTOM_RIGHT,
 -     FL_ALIGN_BOTTOM,
 -     FL_ALIGN_BOTTOM_LEFT,
 -     FL_ALIGN_LEFT
 -   };
 - 
 - 
 -   // Draw the cell box...
 -   if (readonly()) fl_draw_box(FL_UP_BOX, x(), y(), w(), h(), color());
 -   else fl_draw_box(FL_DOWN_BOX, x(), y(), w(), h(), color());
 - 
 -   // Draw the cell background...
 -   if (Fl::focus() == this) {
 -     Fl_Color c = fl_color_average(FL_SELECTION_COLOR, color(), 0.5f);
 -     fl_color(c);
 -     fl_rectf(x() + 4, y() + 4, w() - 8, h() - 8);
 -     fl_color(fl_contrast(labelcolor(), c));
 -   } else fl_color(labelcolor());
 - 
 -   // Draw the cell value...
 -   char s[2];
 - 
 -   s[1] = '\0';
 - 
 -   if (value_) {
 -     s[0] = value_ + '0';
 - 
 -     fl_font(FL_HELVETICA_BOLD, h() - 10);
 -     fl_draw(s, x(), y(), w(), h(), FL_ALIGN_CENTER);
 -   }
 - 
 -   fl_font(FL_HELVETICA_BOLD, h() / 5);
 - 
 -   for (int i = 0; i < 8; i ++) {
 -     if (test_value_[i]) {
 -       s[0] = test_value_[i] + '0';
 -       fl_draw(s, x() + 5, y() + 5, w() - 10, h() - 10, align[i]);
 -     }
 -   }
 - }
 - 
 - 
 - // Handle events in cell
 - int
 - SudokuCell::handle(int event) {
 -   switch (event) {
 -     case FL_FOCUS :
 -       Fl::focus(this);
 -       redraw();
 -       return 1;
 - 
 -     case FL_UNFOCUS :
 -       redraw();
 -       return 1;
 - 
 -     case FL_PUSH :
 -       if (!readonly() && Fl::event_inside(this)) {
 -         if (Fl::event_clicks()) {
 - 	  // 2+ clicks increments/sets value
 - 	  if (value()) {
 - 	    if (value() < 9) value(value() + 1);
 - 	    else value(1);
 - 	  } else value(((Sudoku *)window())->next_value(this));
 - 	}
 - 
 -         Fl::focus(this);
 - 	redraw();
 - 	return 1;
 -       }
 -       break;
 - 
 -     case FL_KEYDOWN :
 -       if (Fl::event_state() & FL_CTRL) break;
 -       int key = Fl::event_key() - '0';
 -       if (key < 0 || key > 9) key = Fl::event_key() - FL_KP - '0';
 -       if (key > 0 && key <= 9) {
 -         if (readonly()) {
 -           fl_beep(FL_BEEP_ERROR);
 -           return 1;
 -         }
 - 
 -         if (Fl::event_state() & (FL_SHIFT | FL_CAPS_LOCK)) {
 - 	  int i;
 - 
 - 	  for (i = 0; i < 8; i ++)
 - 	    if (test_value_[i] == key) {
 - 	      test_value_[i] = 0;
 - 	      break;
 - 	    }
 - 
 -           if (i >= 8) {
 - 	    for (i = 0; i < 8; i ++)
 - 	      if (!test_value_[i]) {
 - 		test_value_[i] = key;
 - 		break;
 - 	      }
 - 	  }
 - 
 - 	  if (i >= 8) {
 - 	    for (i = 0; i < 7; i ++) test_value_[i] = test_value_[i + 1];
 - 	    test_value_[i] = key;
 - 	  }
 - 
 - 	  redraw();
 - 	} else {
 - 	  value(key);
 - 	  do_callback();
 - 	}
 - 	return 1;
 -       } else if (key == 0 || Fl::event_key() == FL_BackSpace ||
 -                  Fl::event_key() == FL_Delete) {
 -         if (readonly()) {
 -           fl_beep(FL_BEEP_ERROR);
 -           return 1;
 -         }
 - 
 -         value(0);
 - 	do_callback();
 - 	return 1;
 -       }
 -       break;
 -   }
 - 
 -   return Fl_Widget::handle(event);
 - }
 - 
 - 
 - // Sudoku class globals...
 - Fl_Help_Dialog	*Sudoku::help_dialog_ = (Fl_Help_Dialog *)0;
 - Fl_Preferences	Sudoku::prefs_(Fl_Preferences::USER, "fltk.org", "sudoku");
 - 
 - 
 - // Create a Sudoku game window...
 - Sudoku::Sudoku()
 -   : Fl_Double_Window(GROUP_SIZE * 3, GROUP_SIZE * 3 + MENU_OFFSET, "Sudoku")
 - {
 -   int j, k;
 -   Fl_Group *g;
 -   SudokuCell *cell;
 -   static Fl_Menu_Item	items[] = {
 -     { "&Game", 0, 0, 0, FL_SUBMENU },
 -     { "&New Game", FL_COMMAND | 'n', new_cb, 0, FL_MENU_DIVIDER },
 -     { "&Check Game", FL_COMMAND | 'c', check_cb, 0, 0 },
 -     { "&Restart Game", FL_COMMAND | 'r', restart_cb, 0, 0 },
 -     { "&Solve Game", FL_COMMAND | 's', solve_cb, 0, FL_MENU_DIVIDER },
 -     { "&Update Helpers", 0, update_helpers_cb, 0, 0 },
 -     { "&Mute Sound", FL_COMMAND | 'm', mute_cb, 0, FL_MENU_TOGGLE | FL_MENU_DIVIDER },
 -     { "&Quit", FL_COMMAND | 'q', close_cb, 0, 0 },
 -     { 0 },
 -     { "&Difficulty", 0, 0, 0, FL_SUBMENU },
 -     { "&Easy", 0, diff_cb, (void *)"0", FL_MENU_RADIO },
 -     { "&Medium", 0, diff_cb, (void *)"1", FL_MENU_RADIO },
 -     { "&Hard", 0, diff_cb, (void *)"2", FL_MENU_RADIO },
 -     { "&Impossible", 0, diff_cb, (void *)"3", FL_MENU_RADIO },
 -     { 0 },
 -     { "&Help", 0, 0, 0, FL_SUBMENU },
 -     { "&About Sudoku", FL_F + 1, help_cb, 0, 0 },
 -     { 0 },
 -     { 0 }
 -   };
 - 
 - 
 -   // Setup sound output...
 -   prefs_.get("mute_sound", j, 0);
 -   if (j) {
 -     // Mute sound?
 -     sound_ = NULL;
 -     items[6].flags |= FL_MENU_VALUE;
 -   } else sound_ = new SudokuSound();
 - 
 -   // Menubar...
 -   prefs_.get("difficulty", difficulty_, 0);
 -   if (difficulty_ < 0 || difficulty_ > 3) difficulty_ = 0;
 - 
 -   items[10 + difficulty_].flags |= FL_MENU_VALUE;
 - 
 -   menubar_ = new Fl_Sys_Menu_Bar(0, 0, 3 * GROUP_SIZE, 25);
 -   menubar_->menu(items);
 - 
 -   // Create the grids...
 -   grid_ = new Fl_Group(0, MENU_OFFSET, 3 * GROUP_SIZE, 3 * GROUP_SIZE);
 - 
 -   for (j = 0; j < 3; j ++)
 -     for (k = 0; k < 3; k ++) {
 -       g = new Fl_Group(k * GROUP_SIZE, j * GROUP_SIZE + MENU_OFFSET,
 - 		       GROUP_SIZE, GROUP_SIZE);
 -       g->box(FL_BORDER_BOX);
 -       if ((int)(j == 1) ^ (int)(k == 1)) g->color(FL_DARK3);
 -       else g->color(FL_DARK2);
 -       g->end();
 - 
 -       grid_groups_[j][k] = g;
 -     }
 - 
 -   for (j = 0; j < 9; j ++)
 -     for (k = 0; k < 9; k ++) {
 -       cell = new SudokuCell(k * CELL_SIZE + CELL_OFFSET +
 -                                 (k / 3) * (GROUP_SIZE - 3 * CELL_SIZE),
 - 	                    j * CELL_SIZE + CELL_OFFSET + MENU_OFFSET +
 - 			        (j / 3) * (GROUP_SIZE - 3 * CELL_SIZE),
 - 			    CELL_SIZE, CELL_SIZE);
 -       cell->callback(reset_cb);
 -       grid_cells_[j][k] = cell;
 -     }
 - 
 -   // Set icon for window (MacOS uses app bundle for icon...)
 - #ifdef WIN32
 -   icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON)));
 - #elif !defined(__APPLE__)
 -   fl_open_display();
 -   icon((char *)XCreateBitmapFromData(fl_display, DefaultRootWindow(fl_display),
 -                                      (char *)sudoku_bits, sudoku_width,
 - 				     sudoku_height));
 - #endif // WIN32
 - 
 -   // Catch window close events...
 -   callback(close_cb);
 - 
 -   // Make the window resizable...
 -   resizable(grid_);
 -   size_range(3 * GROUP_SIZE, 3 * GROUP_SIZE + MENU_OFFSET, 0, 0, 5, 5, 1);
 - 
 -   // Restore the previous window dimensions...
 -   int X, Y, W, H;
 - 
 -   if (prefs_.get("x", X, -1)) {
 -     prefs_.get("y", Y, -1);
 -     prefs_.get("width", W, 3 * GROUP_SIZE);
 -     prefs_.get("height", H, 3 * GROUP_SIZE + MENU_OFFSET);
 - 
 -     resize(X, Y, W, H);
 -   }
 - 
 -   set_title();
 - }
 - 
 - 
 - // Destroy the sudoku window...
 - Sudoku::~Sudoku() {
 -   if (sound_) delete sound_;
 - }
 - 
 - 
 - // Check for a solution to the game...
 - void
 - Sudoku::check_cb(Fl_Widget *widget, void *) {
 -   ((Sudoku *)(widget->window()))->check_game();
 - }
 - 
 - 
 - // Check if the user has correctly solved the game...
 - void
 - Sudoku::check_game(bool highlight) {
 -   bool empty = false;
 -   bool correct = true;
 -   int j, k, m;
 - 
 -   // Check the game for right/wrong answers...
 -   for (j = 0; j < 9; j ++)
 -     for (k = 0; k < 9; k ++) {
 -       SudokuCell *cell = grid_cells_[j][k];
 -       int val = cell->value();
 - 
 -       if (cell->readonly()) continue;
 - 
 -       if (!val) empty = true;
 -       else {
 -         for (m = 0; m < 9; m ++)
 -           if ((j != m && grid_cells_[m][k]->value() == val) ||
 - 	      (k != m && grid_cells_[j][m]->value() == val)) break;
 - 
 -         if (m < 9) {
 -           if (highlight) {
 - 	    cell->color(FL_YELLOW);
 - 	    cell->redraw();
 - 	  }
 - 
 - 	  correct = false;
 - 	} else if (highlight) {
 - 	  cell->color(FL_LIGHT3);
 - 	  cell->redraw();
 - 	}
 -       }
 -     }
 - 
 -   // Check subgrids for duplicate numbers...
 -   for (j = 0; j < 9; j += 3)
 -     for (k = 0; k < 9; k += 3)
 -       for (int jj = 0; jj < 3; jj ++)
 -         for (int kk = 0; kk < 3; kk ++) {
 - 	  SudokuCell *cell = grid_cells_[j + jj][k + kk];
 - 	  int val = cell->value();
 - 
 - 	  if (cell->readonly() || !val) continue;
 - 
 -           int jjj;
 - 
 -           for (jjj = 0; jjj < 3; jjj ++) {
 - 	    int kkk;
 - 
 - 	    for (kkk = 0; kkk < 3; kkk ++)
 -               if (jj != jjj && kk != kkk &&
 - 	          grid_cells_[j + jjj][k + kkk]->value() == val) break;
 - 
 -             if (kkk < 3) break;
 - 	  }
 - 
 -           if (jjj < 3) {
 -             if (highlight) {
 - 	      cell->color(FL_YELLOW);
 - 	      cell->redraw();
 - 	    }
 - 
 - 	    correct = false;
 - 	  }
 - 	}
 - 
 -   if (!empty && correct) {
 -     // Success!
 -     for (j = 0; j < 9; j ++) {
 -       for (k = 0; k < 9; k ++) {
 - 	SudokuCell *cell = grid_cells_[j][k];
 - 	cell->color(FL_GREEN);
 - 	cell->readonly(1);
 -       }
 - 
 -       if (sound_) sound_->play('A' + grid_cells_[j][8]->value() - 1);
 -     }
 -   }
 - }
 - 
 - 
 - // Close the window, saving the game first...
 - void
 - Sudoku::close_cb(Fl_Widget *widget, void *) {
 -   Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
 - 
 -   s->save_game();
 -   s->hide();
 - 
 -   if (help_dialog_) help_dialog_->hide();
 - }
 - 
 - 
 - // Set the level of difficulty...
 - void
 - Sudoku::diff_cb(Fl_Widget *widget, void *d) {
 -   Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
 -   int diff = atoi((char *)d);
 - 
 -   if (diff != s->difficulty_) {
 -     s->difficulty_ = diff;
 -     s->new_game(s->seed_);
 -     s->set_title();
 - 
 -     if (diff > 1)
 -     {
 -       // Display a message about the higher difficulty levels for the
 -       // Sudoku zealots of the world...
 -       int val;
 - 
 -       prefs_.get("difficulty_warning", val, 0);
 - 
 -       if (!val)
 -       {
 -         prefs_.set("difficulty_warning", 1);
 - 	fl_alert("Note: 'Hard' and 'Impossible' puzzles may have more than "
 - 	         "one possible solution.\n"
 - 		 "This is not an error or bug.");
 -       }
 -     }
 - 
 -     prefs_.set("difficulty", s->difficulty_);
 -   }
 - }
 - 
 - // Update the little marker numbers in all cells
 - void
 - Sudoku::update_helpers_cb(Fl_Widget *widget, void *) {
 -   Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
 -   s->update_helpers();
 - }
 - 
 - void
 - Sudoku::update_helpers() {
 -   int j, k, m;
 - 
 -   // First we delete any entries that the user may have made
 -   for (j = 0; j < 9; j ++) {
 -     for (k = 0; k < 9; k ++) {
 -       SudokuCell *cell = grid_cells_[j][k];
 -       for (m = 0; m < 8; m ++) {
 -         cell->test_value(0, m);
 -       }
 -     }
 -   }
 - 
 -   // Now go through all cells and find out, what we can not be
 -   for (j = 0; j < 81; j ++) {
 -     char taken[10] = { 0 };
 -     // Find our destination cell
 -     int row = j / 9;
 -     int col = j % 9;
 -     SudokuCell *dst_cell = grid_cells_[row][col];
 -     if (dst_cell->value()) continue;
 -     // Find all values already taken in this row
 -     for (k = 0; k < 9; k ++) {
 -       SudokuCell *cell = grid_cells_[row][k];
 -       int v = cell->value();
 -       if (v) taken[v] = 1;
 -     }
 -     // Find all values already taken in this column
 -     for (k = 0; k < 9; k ++) {
 -       SudokuCell *cell = grid_cells_[k][col];
 -       int v = cell->value();
 -       if (v) taken[v] = 1;
 -     }
 -     // Now find all values already taken in this square
 -     int ro = (row / 3) * 3;
 -     int co = (col / 3) * 3;
 -     for (k = 0; k < 3; k ++) {
 -       for (m = 0; m < 3; m ++) {
 -         SudokuCell *cell = grid_cells_[ro + k][co + m];
 -         int v = cell->value();
 -         if (v) taken[v] = 1;
 -       }
 -     }
 -     // transfer our findings to the markers
 -     for (m = 1, k = 0; m <= 9; m ++) {
 -       if (!taken[m])
 -         dst_cell->test_value(m, k ++);
 -     }
 -   }
 - }
 - 
 - 
 - // Show the on-line help...
 - void
 - Sudoku::help_cb(Fl_Widget *, void *) {
 -   if (!help_dialog_) {
 -     help_dialog_ = new Fl_Help_Dialog();
 - 
 -     help_dialog_->value(
 - 	"<HTML>\n"
 - 	"<HEAD>\n"
 - 	"<TITLE>Sudoku Help</TITLE>\n"
 - 	"</HEAD>\n"
 - 	"<BODY BGCOLOR='#ffffff'>\n"
 - 
 - 	"<H2>About the Game</H2>\n"
 - 
 - 	"<P>Sudoku (pronounced soo-dough-coo with the emphasis on the\n"
 -         "first syllable) is a simple number-based puzzle/game played on a\n"
 - 	"9x9 grid that is divided into 3x3 subgrids. The goal is to enter\n"
 - 	"a number from 1 to 9 in each cell so that each number appears\n"
 - 	"only once in each column and row. In addition, each 3x3 subgrid\n"
 - 	"may only contain one of each number.</P>\n"
 - 
 - 	"<P>This version of the puzzle is copyright 2005-2010 by Michael R\n"
 - 	"Sweet.</P>\n"
 - 
 - 	"<P><B>Note:</B> The 'Hard' and 'Impossible' difficulty\n"
 - 	"levels generate Sudoku puzzles with multiple possible solutions.\n"
 - 	"While some purists insist that these cannot be called 'Sudoku'\n"
 - 	"puzzles, the author (me) has personally solved many such puzzles\n"
 - 	"in published/printed Sudoku books and finds them far more\n"
 - 	"interesting than the simple single solution variety. If you don't\n"
 - 	"like it, don't play with the difficulty set to 'High' or\n"
 - 	"'Impossible'.</P>\n"
 - 
 - 	"<H2>How to Play the Game</H2>\n"
 - 
 - 	"<P>At the start of a new game, Sudoku fills in a random selection\n"
 - 	"of cells for you - the number of cells depends on the difficulty\n"
 - 	"level you use. Click in any of the empty cells or use the arrow\n"
 - 	"keys to highlight individual cells and press a number from 1 to 9\n"
 - 	"to fill in the cell. To clear a cell, press 0, Delete, or\n"
 - 	"Backspace. When you have successfully completed all subgrids, the\n"
 - 	"entire puzzle is highlighted in green until you start a new\n"
 - 	"game.</P>\n"
 - 
 - 	"<P>As you work to complete the puzzle, you can display possible\n"
 - 	"solutions inside each cell by holding the Shift key and pressing\n"
 - 	"each number in turn. Repeat the process to remove individual\n"
 - 	"numbers, or press a number without the Shift key to replace them\n"
 - 	"with the actual number to use.</P>\n"
 - 	"</BODY>\n"
 -     );
 -   }
 - 
 -   help_dialog_->show();
 - }
 - 
 - 
 - // Load the game from saved preferences...
 - void
 - Sudoku::load_game() {
 -   // Load the current values and state of each grid...
 -   memset(grid_values_, 0, sizeof(grid_values_));
 - 
 -   bool solved = true;
 - 
 -   for (int j = 0; j < 9; j ++)
 -     for (int k = 0; k < 9; k ++) {
 -       char name[255];
 -       int val;
 - 
 -       SudokuCell *cell = grid_cells_[j][k];
 - 
 -       sprintf(name, "value%d.%d", j, k);
 -       if (!prefs_.get(name, val, 0)) {
 -         j = 9;
 - 	grid_values_[0][0] = 0;
 - 	break;
 -       }
 - 
 -       grid_values_[j][k] = val;
 - 
 -       sprintf(name, "state%d.%d", j, k);
 -       prefs_.get(name, val, 0);
 -       cell->value(val);
 -  
 -       sprintf(name, "readonly%d.%d", j, k);
 -       prefs_.get(name, val, 0);
 -       cell->readonly(val);
 - 
 -       if (val) cell->color(FL_GRAY);
 -       else {
 -         cell->color(FL_LIGHT3);
 - 	solved = false;
 -       }
 - 
 -       for (int m = 0; m < 8; m ++) {
 - 	sprintf(name, "test%d%d.%d", m, j, k);
 - 	prefs_.get(name, val, 0);
 - 	cell->test_value(val, m);
 -       }
 -     }
 - 
 -   // If we didn't load any values or the last game was solved, then
 -   // create a new game automatically...
 -   if (solved || !grid_values_[0][0]) new_game(time(NULL));
 -   else check_game(false);
 - }
 - 
 - 
 - // Mute/unmute sound...
 - void
 - Sudoku::mute_cb(Fl_Widget *widget, void *) {
 -   Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
 - 
 -   if (s->sound_) {
 -     delete s->sound_;
 -     s->sound_ = NULL;
 -     prefs_.set("mute_sound", 1);
 -   } else {
 -     s->sound_ = new SudokuSound();
 -     prefs_.set("mute_sound", 0);
 -   }
 - }
 - 
 - 
 - // Create a new game...
 - void
 - Sudoku::new_cb(Fl_Widget *widget, void *) {
 -   Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
 - 
 -   if (s->grid_cells_[0][0]->color() != FL_GREEN) {
 -     if (!fl_choice("Are you sure you want to change the difficulty level and "
 -                    "discard the current game?", "Keep Current Game", "Start New Game",
 -                    NULL)) return;
 -   }
 - 
 -   s->new_game(time(NULL));
 - }
 - 
 - 
 - // Create a new game...
 - void
 - Sudoku::new_game(time_t seed) {
 -   int j, k, m, n, t, count;
 - 
 - 
 -   // Generate a new (valid) Sudoku grid...
 -   seed_ = seed;
 -   srand(seed);
 - 
 -   memset(grid_values_, 0, sizeof(grid_values_));
 - 
 -   for (j = 0; j < 9; j += 3) {
 -     for (k = 0; k < 9; k += 3) {
 -       for (t = 1; t <= 9; t ++) {
 - 	for (count = 0; count < 20; count ++) {
 - 	  m = j + (rand() % 3);
 - 	  n = k + (rand() % 3);
 - 	  if (!grid_values_[m][n]) {
 - 	    int mm;
 - 
 - 	    for (mm = 0; mm < m; mm ++)
 - 	      if (grid_values_[mm][n] == t) break;
 - 
 - 	    if (mm < m) continue;
 - 
 - 	    int nn;
 - 
 - 	    for (nn = 0; nn < n; nn ++)
 - 	      if (grid_values_[m][nn] == t) break;
 - 
 - 	    if (nn < n) continue;
 - 
 - 	    grid_values_[m][n] = t;
 - 	    break;
 - 	  }
 - 	}
 - 
 - 	if (count == 20) {
 - 	  // Unable to find a valid puzzle so far, so start over...
 - 	  k = 9;
 - 	  j = -3;
 - 	  memset(grid_values_, 0, sizeof(grid_values_));
 - 	}
 -       }
 -     }
 -   }
 - 
 -   // Start by making all cells editable
 -   SudokuCell *cell;
 - 
 -   for (j = 0; j < 9; j ++)
 -     for (k = 0; k < 9; k ++) {
 -       cell = grid_cells_[j][k];
 - 
 -       cell->value(0);
 -       cell->readonly(0);
 -       cell->color(FL_LIGHT3);
 -     }
 - 
 -   // Show N cells...
 -   count = 11 * (5 - difficulty_);
 - 
 -   int numbers[9];
 - 
 -   for (j = 0; j < 9; j ++) numbers[j] = j + 1;
 - 
 -   while (count > 0) {
 -     for (j = 0; j < 20; j ++) {
 -       k          = rand() % 9;
 -       m          = rand() % 9;
 -       t          = numbers[k];
 -       numbers[k] = numbers[m];
 -       numbers[m] = t;
 -     }
 - 
 -     for (j = 0; count > 0 && j < 9; j ++) {
 -       t = numbers[j];
 - 
 -       for (k = 0; count > 0 && k < 9; k ++) {
 -         cell = grid_cells_[j][k];
 - 
 -         if (grid_values_[j][k] == t && !cell->readonly()) {
 - 	  cell->value(grid_values_[j][k]);
 - 	  cell->readonly(1);
 - 	  cell->color(FL_GRAY);
 - 
 - 	  count --;
 - 	  break;
 - 	}
 -       }
 -     }
 -   }
 - }
 - 
 - 
 - // Return the next available value for a cell...
 - int
 - Sudoku::next_value(SudokuCell *c) {
 -   int	j, k, m, n;
 - 
 - 
 -   for (j = 0; j < 9; j ++) {
 -     for (k = 0; k < 9; k ++)
 -       if (grid_cells_[j][k] == c) break;
 - 
 -     if (k < 9) break;
 -   }
 - 
 -   if (j == 9) return 1;
 - 
 -   j -= j % 3;
 -   k -= k % 3;
 - 
 -   int numbers[9];
 - 
 -   memset(numbers, 0, sizeof(numbers));
 - 
 -   for (m = 0; m < 3; m ++)
 -     for (n = 0; n < 3; n ++) {
 -       c = grid_cells_[j + m][k + n];
 -       if (c->value()) numbers[c->value() - 1] = 1;
 -     }
 - 
 -   for (j = 0; j < 9; j ++)
 -     if (!numbers[j]) return j + 1;
 - 
 -   return 1;
 - }
 - 
 - 
 - // Reset widget color to gray...
 - void
 - Sudoku::reset_cb(Fl_Widget *widget, void *) {
 -   widget->color(FL_LIGHT3);
 -   widget->redraw();
 -   
 -   ((Sudoku *)(widget->window()))->check_game(false);
 - }
 - 
 - 
 - // Resize the window...
 - void
 - Sudoku::resize(int X, int Y, int W, int H) {
 -   // Resize the window...
 -   Fl_Double_Window::resize(X, Y, W, H);
 - 
 -   // Save the new window geometry...
 -   prefs_.set("x", X);
 -   prefs_.set("y", Y);
 -   prefs_.set("width", W);
 -   prefs_.set("height", H);
 - }
 - 
 - 
 - // Restart game from beginning...
 - void
 - Sudoku::restart_cb(Fl_Widget *widget, void *) {
 -   Sudoku *s = (Sudoku *)(widget->window());
 -   bool solved = true;
 - 
 -   for (int j = 0; j < 9; j ++)
 -     for (int k = 0; k < 9; k ++) {
 -       SudokuCell *cell = s->grid_cells_[j][k];
 - 
 -       if (!cell->readonly()) {
 -         solved = false;
 -         int v = cell->value();
 - 	cell->value(0);
 - 	cell->color(FL_LIGHT3);
 - 	if (v && s->sound_) s->sound_->play('A' + v - 1);
 -       }
 -     }
 - 
 -   if (solved) s->new_game(s->seed_);
 - }
 - 
 - 
 - // Save the current game state...
 - void
 - Sudoku::save_game() {
 -   // Save the current values and state of each grid...
 -   for (int j = 0; j < 9; j ++)
 -     for (int k = 0; k < 9; k ++) {
 -       char name[255];
 -       SudokuCell *cell = grid_cells_[j][k];
 - 
 -       sprintf(name, "value%d.%d", j, k);
 -       prefs_.set(name, grid_values_[j][k]);
 - 
 -       sprintf(name, "state%d.%d", j, k);
 -       prefs_.set(name, cell->value());
 - 
 -       sprintf(name, "readonly%d.%d", j, k);
 -       prefs_.set(name, cell->readonly());
 - 
 -       for (int m = 0; m < 8; m ++) {
 - 	sprintf(name, "test%d%d.%d", m, j, k);
 - 	prefs_.set(name, cell->test_value(m));
 -       }
 -     }
 - }
 - 
 - 
 - // Set title of window...
 - void
 - Sudoku::set_title() {
 -   static const char * const titles[] = {
 -     "Sudoku - Easy",
 -     "Sudoku - Medium",
 -     "Sudoku - Hard",
 -     "Sudoku - Impossible"
 -   };
 - 
 -   label(titles[difficulty_]);
 - }
 - 
 - 
 - // Solve the puzzle...
 - void
 - Sudoku::solve_cb(Fl_Widget *widget, void *) {
 -   ((Sudoku *)(widget->window()))->solve_game();
 - }
 - 
 - 
 - // Solve the puzzle...
 - void
 - Sudoku::solve_game() {
 -   int j, k;
 - 
 -   for (j = 0; j < 9; j ++) {
 -     for (k = 0; k < 9; k ++) {
 -       SudokuCell *cell = grid_cells_[j][k];
 - 
 -       cell->value(grid_values_[j][k]);
 -       cell->readonly(1);
 -       cell->color(FL_GRAY);
 -     }
 - 
 -     if (sound_) sound_->play('A' + grid_cells_[j][8]->value() - 1);
 -   }
 - }
 - 
 - 
 - // Main entry for game...
 - int
 - main(int argc, char *argv[]) {
 -   Sudoku s;
 - 
 -   // Show the game...
 -   s.show(argc, argv);
 - 
 -   // Load the previous game...
 -   s.load_game();
 - 
 -   // Run until the user quits...
 -   return (Fl::run());
 - }
 - 
 - 
 - //
 - // End of "$Id: sudoku.cxx 7903 2010-11-28 21:06:39Z matt $".
 - //
 
 
  |