diff options
| author | Yuval Adam <yuv.adm@gmail.com> | 2014-08-08 14:42:07 +0300 |
|---|---|---|
| committer | Yuval Adam <yuv.adm@gmail.com> | 2014-08-08 14:42:07 +0300 |
| commit | a6163888f3c56123b1db313743c6147ba498732c (patch) | |
| tree | ff7d15d991d1d09ba6cbc0cec80924f57445bfdd /third_party/windows/fltk-1.1.10/test/sudoku.cxx | |
| parent | c3e4c9a25c2910d2d66d52215b3406b13d5b23d5 (diff) | |
Add third_party libs
Diffstat (limited to 'third_party/windows/fltk-1.1.10/test/sudoku.cxx')
| -rw-r--r-- | third_party/windows/fltk-1.1.10/test/sudoku.cxx | 1335 |
1 files changed, 1335 insertions, 0 deletions
diff --git a/third_party/windows/fltk-1.1.10/test/sudoku.cxx b/third_party/windows/fltk-1.1.10/test/sudoku.cxx new file mode 100644 index 0000000..79fc935 --- /dev/null +++ b/third_party/windows/fltk-1.1.10/test/sudoku.cxx @@ -0,0 +1,1335 @@ +//
+// "$Id: sudoku.cxx 6973 2009-12-21 16:35:06Z AlbrechtS $"
+//
+// Sudoku game using the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2005-2007 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_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;
+ 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_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
+ if (AudioDeviceAddIOProc(device, audio_cb, (void *)this) != noErr) return;
+
+ // Start the device...
+ AudioDeviceStart(device, audio_cb);
+
+ 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) {
+ AudioDeviceStop(device, audio_cb);
+ AudioDeviceRemoveIOProc(device, audio_cb);
+ }
+
+#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_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-2006 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_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 6973 2009-12-21 16:35:06Z AlbrechtS $".
+//
|
