summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt2
-rw-r--r--test/Makefile9
-rw-r--r--test/sudoku.cxx788
-rw-r--r--test/sudoku.h80
-rw-r--r--test/sudoku_cell.cxx176
-rw-r--r--test/sudoku_cell.h49
-rw-r--r--test/sudoku_generator.cxx599
-rw-r--r--test/sudoku_generator.h60
-rw-r--r--test/sudoku_puzzle.cxx846
-rw-r--r--test/sudoku_puzzle.h76
-rw-r--r--test/sudoku_sound.cxx308
-rw-r--r--test/sudoku_sound.h97
12 files changed, 643 insertions, 2447 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 30b9ff542..6fb3b5105 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -133,7 +133,7 @@ CREATE_EXAMPLE (resize-example5c "resize-example5c.cxx;resize-arrows.cxx" fltk)
CREATE_EXAMPLE (rotated_text rotated_text.cxx fltk)
CREATE_EXAMPLE (scroll scroll.cxx fltk)
CREATE_EXAMPLE (subwindow subwindow.cxx fltk)
-CREATE_EXAMPLE (sudoku "sudoku.cxx;sudoku_puzzle.cxx;sudoku_cell.cxx;sudoku_sound.cxx;sudoku_generator.cxx;sudoku.plist;sudoku.icns;sudoku.rc" "fltk_images;fltk;${AUDIOLIBS}")
+CREATE_EXAMPLE (sudoku "sudoku.cxx;sudoku.plist;sudoku.icns;sudoku.rc" "fltk_images;fltk;${AUDIOLIBS}")
CREATE_EXAMPLE (symbols symbols.cxx fltk)
CREATE_EXAMPLE (tabs tabs.fl fltk)
CREATE_EXAMPLE (table table.cxx fltk)
diff --git a/test/Makefile b/test/Makefile
index 51af5bbcb..f1187caf5 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -131,7 +131,6 @@ CPPFILES =\
shape.cxx \
subwindow.cxx \
sudoku.cxx \
- sudoku_generator.cxx \
symbols.cxx \
table.cxx \
tabs.cxx \
@@ -597,19 +596,19 @@ scroll$(EXEEXT): scroll.o
subwindow$(EXEEXT): subwindow.o
-sudoku: sudoku.o sudoku_generator.o
+sudoku: sudoku.o
echo Linking $@...
- $(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) sudoku.o sudoku_generator.o -o $@ $(AUDIOLIBS) $(LINKFLTKIMG) $(LDLIBS)
+ $(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) sudoku.o -o $@ $(AUDIOLIBS) $(LINKFLTKIMG) $(LDLIBS)
$(OSX_ONLY) $(RM) -f -r sudoku.app
$(OSX_ONLY) mkdir -p sudoku.app/Contents/MacOS sudoku.app/Contents/Resources
$(OSX_ONLY) $(INSTALL_BIN) sudoku$(EXEEXT) sudoku.app/Contents/MacOS
$(OSX_ONLY) $(INSTALL_BIN) mac-resources/sudoku.icns sudoku.app/Contents/Resources/
$(OSX_ONLY) $(INSTALL_BIN) mac-resources/sudoku.plist sudoku.app/Contents/Info.plist
-sudoku.exe: sudoku.o sudoku_generator.o sudoku.rc
+sudoku.exe: sudoku.o sudoku.rc
echo Linking $@...
$(RC) sudoku.rc sudokures.o
- $(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) sudoku.o sudoku_generator.o sudokures.o -o $@ $(AUDIOLIBS) $(LINKFLTKIMG) $(LDLIBS)
+ $(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) sudoku.o sudokures.o -o $@ $(AUDIOLIBS) $(LINKFLTKIMG) $(LDLIBS)
symbols$(EXEEXT): symbols.o
diff --git a/test/sudoku.cxx b/test/sudoku.cxx
index fc2003dc3..97766ac2f 100644
--- a/test/sudoku.cxx
+++ b/test/sudoku.cxx
@@ -15,43 +15,6 @@
// https://www.fltk.org/bugs.php
//
-// A Sudoku (i.e. the puzzle) is a partially completed grid. A grid has 9 rows,
-// 9 columns and 9 boxes, each having 9 cells (81 total). Boxes can also be
-// called blocks or regions.[1] Three horizontally adjacent blocks are a band,
-// and three vertically adjacent blocks are a stack.[2] The initially defined
-// values are clues or givens. An ordinary Sudoku (i.e. a proper Sudoku) has
-// one solution. Rows, columns and regions can be collectively referred to as
-// groups, of which the grid has 27. The One Rule encapsulates the three prime
-// rules, i.e. each digit (or number) can occur only once in each row, column,
-// and box; and can be compactly stated as: "Each digit appears once in each
-// group."
-// - wikipedia.org
-
-// Wishlist:
-// - [ ] new easy to new hard, etc.
-// - [ ] store current puzzle in preferences
-// - [ ] undo, redo
-// - [ ] update hints now
-// - [ ] always update hints
-// - [ ] highlight row, column, and box
-// - [ ] highlight other cells with same value
-// - [ ] verify current solution
-// - [ ] hint/flag/note vs. solve mode and button 1..9, erase
-// - [ ] gift one field
-// - [ ] bg is white and bright blue
-// - [ ] selected field bg is green, boxed
-// - [ ] same number is yellow
-// - [ ] conflicts can use arrows and crosses
-// - [ ] fixed numbers are bold, user values are not
-// - [ ] timer
-// - [ ] game hamburge menu
-
-#include "sudoku.h"
-#include "sudoku_puzzle.h"
-#include "sudoku_cell.h"
-#include "sudoku_sound.h"
-#include "sudoku_generator.h"
-
#include <FL/Fl.H>
#include <FL/Enumerations.H>
#include <FL/Fl_Double_Window.H>
@@ -73,11 +36,29 @@
#include "pixmaps/sudoku.xbm"
+// 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
@@ -87,6 +68,560 @@
# 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;
+#ifndef MAC_OS_X_VERSION_10_5
+#define MAC_OS_X_VERSION_10_5 1050
+#endif
+# 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() FL_OVERRIDE;
+ int handle(int event) FL_OVERRIDE;
+ 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) FL_OVERRIDE;
+ 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(&params);
+ 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__
+
+#define NOTE_DURATION 50
+
+// Play a note for <NOTE_DURATION> ms...
+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(NOTE_DURATION*1000);
+
+#elif defined(_WIN32)
+ if (sample_size) {
+ memcpy(data_ptr, sample_data[note - 'A'], sample_size * 4);
+
+ waveOutWrite(device, header_ptr, sizeof(WAVEHDR));
+
+ Sleep(NOTE_DURATION);
+ } else Beep(frequencies[note - 'A'], NOTE_DURATION);
+
+#elif defined(FLTK_USE_X11)
+# 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(NOTE_DURATION*1000);
+ 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 = NOTE_DURATION;
+
+ 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;
@@ -95,9 +630,7 @@ Fl_Preferences Sudoku::prefs_(Fl_Preferences::USER_L, "fltk.org", "sudoku");
// Create a Sudoku game window...
Sudoku::Sudoku()
- : Fl_Double_Window(kPuzzleSize + 2*kPadding,
- kPuzzleSize + 2*kPadding + kMenuOffset,
- "FLTK Sudoku")
+ : Fl_Double_Window(GROUP_SIZE * 3, GROUP_SIZE * 3 + MENU_OFFSET, "Sudoku")
{
int j, k;
Fl_Group *g;
@@ -108,7 +641,7 @@ Sudoku::Sudoku()
{ "&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", FL_COMMAND | 'u', update_helpers_cb, 0, 0 },
+ { "&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 },
@@ -143,38 +676,28 @@ Sudoku::Sudoku()
menubar_->menu(items);
// Create the grids...
- grid_ = new SudokuPuzzle(kPadding,
- kPadding + kMenuOffset,
- kPuzzleSize,
- kPuzzleSize);
- Fl_Group *rsgrid = new Fl_Group(grid_->x()+1, grid_->y()+1,
- grid_->w()-2, grid_->h()-2);
- grid_->resizable(rsgrid);
+ 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 ++) {
- Fl_Group *group = new Fl_Group(grid_->x() + 1 + k*kGroupSize,
- grid_->y() + 1 + j*kGroupSize,
- kGroupSize,
- kGroupSize);
- group->box(FL_BORDER_BOX);
- Fl_Group *rsgroup = new Fl_Group(group->x()+1, group->y()+1,
- group->w()-2, group->h()-2);
- rsgroup->box(FL_NO_BOX);
- new Fl_Box(FL_BORDER_BOX, group->x()+1, group->y()+1, kCellSize, kCellSize, NULL);
- new Fl_Box(FL_BORDER_BOX, group->x()+1+kCellSize, group->y()+1, kCellSize, kCellSize, NULL);
- new Fl_Box(FL_BORDER_BOX, group->x()+1+2*kCellSize, group->y()+1, kCellSize, kCellSize, NULL);
- group->resizable(rsgroup);
- group->end();
+ 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(grid_->x() + 2 + k*kCellSize + (k/3),
- grid_->y() + 2 + j*kCellSize + (j/3),
- kCellSize, kCellSize);
+ 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);
- cell->hide();
grid_cells_[j][k] = cell;
}
@@ -362,12 +885,12 @@ Sudoku::update_helpers() {
int j, k, m;
// First we delete any entries that the user may have made
- // TODO: set all marks if none were set
- // TODO: clear marks if value is used, don't set individual marks
for (j = 0; j < 9; j ++) {
for (k = 0; k < 9; k ++) {
SudokuCell *cell = grid_cells_[j][k];
- cell->clear_marks();
+ for (m = 0; m < 8; m ++) {
+ cell->test_value(0, m);
+ }
}
}
@@ -402,11 +925,10 @@ Sudoku::update_helpers() {
}
}
// transfer our findings to the markers
- for (m = 1; m <= 9; m ++) {
+ for (m = 1, k = 0; m <= 9; m ++) {
if (!taken[m])
- dst_cell->mark(m, true);
+ dst_cell->test_value(m, k ++);
}
- dst_cell->redraw();
}
}
@@ -472,44 +994,47 @@ Sudoku::help_cb(Fl_Widget *, void *) {
// 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;
- bool empty = false;
- for (int j = 0; j < 9; j ++) {
- Fl_Preferences row(prefs_, Fl_Preferences::Name("Row%d", j));
+ for (int j = 0; j < 9; j ++)
for (int k = 0; k < 9; k ++) {
- Fl_Preferences p(row, Fl_Preferences::Name("Col%d", k));
- int v;
+ char name[255];
+ int val;
SudokuCell *cell = grid_cells_[j][k];
- p.get("value", v, 0);
- grid_values_[j][k] = v;
- if (v) empty = false;
+ snprintf(name, sizeof(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;
- p.get("state", v, 0);
- cell->value(v);
+ snprintf(name, sizeof(name), "state%d.%d", j, k);
+ prefs_.get(name, val, 0);
+ cell->value(val);
- p.set("readonly", cell->readonly());
- cell->readonly(v != 0);
- if (v) {
- cell->color(FL_GRAY);
- } else {
+ snprintf(name, sizeof(name), "readonly%d.%d", j, k);
+ prefs_.get(name, val, 0);
+ cell->readonly(val != 0);
+
+ if (val) cell->color(FL_GRAY);
+ else {
cell->color(FL_LIGHT3);
solved = false;
}
- for (int m = 1; m <= 9; m ++) {
- p.get(Fl_Preferences::Name("m%d", m), v, 0);
- cell->mark(m, v);
+ for (int m = 0; m < 8; m ++) {
+ snprintf(name, sizeof(name), "test%d%d.%d", m, j, k);
+ prefs_.get(name, val, 0);
+ cell->test_value(val, m);
}
- cell->redraw();
}
- }
// If we didn't load any values or the last game was solved, then
// create a new game automatically...
@@ -539,47 +1064,19 @@ 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;
-// }
+ 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 grid_data[81];
- int *g = grid_data;
- generate_sudoku(grid_data, 22, 31);
- SudokuCell *cell;
- for (int j = 0; j < 9; j ++) {
- for (int k = 0; k < 9; k ++) {
- int v = *g++;
- int vv = v; if (vv<0) vv = -vv;
- grid_values_[j][k] = vv;
- cell = grid_cells_[j][k];
- if (v<0) {
- cell->value(0);
- cell->readonly(0);
- cell->color(FL_LIGHT3);
- } else {
- cell->value(vv);
- cell->readonly(1);
- cell->color(FL_GRAY);
- }
- }
- }
- return;
- }
-
-
-
-
int j, k, m, n, t, count;
@@ -759,23 +1256,25 @@ Sudoku::restart_cb(Fl_Widget *widget, void *) {
void
Sudoku::save_game() {
// Save the current values and state of each grid...
- for (int j = 0; j < 9; j ++) {
- Fl_Preferences row(prefs_, Fl_Preferences::Name("Row%d", j));
+ for (int j = 0; j < 9; j ++)
for (int k = 0; k < 9; k ++) {
- Fl_Preferences p(row, Fl_Preferences::Name("Col%d", k));
char name[255];
SudokuCell *cell = grid_cells_[j][k];
- p.set("value", grid_values_[j][k]);
- p.set("state", cell->value());
- p.set("readonly", cell->readonly());
- for (int m = 1; m <= 9; m ++) {
- if (cell->mark(m))
- p.set(Fl_Preferences::Name("m%d", m), 1);
- else
- p.deleteEntry(Fl_Preferences::Name("m%d", m));
+
+ snprintf(name, sizeof(name), "value%d.%d", j, k);
+ prefs_.set(name, grid_values_[j][k]);
+
+ snprintf(name, sizeof(name), "state%d.%d", j, k);
+ prefs_.set(name, cell->value());
+
+ snprintf(name, sizeof(name), "readonly%d.%d", j, k);
+ prefs_.set(name, cell->readonly());
+
+ for (int m = 0; m < 8; m ++) {
+ snprintf(name, sizeof(name), "test%d%d.%d", m, j, k);
+ prefs_.set(name, cell->test_value(m));
}
}
- }
}
@@ -820,17 +1319,6 @@ Sudoku::solve_game() {
// Main entry for game...
-// Note 21-17 (proven minimum) clues can be set
-// easy: 30-36
-// expert: 25-30
-// algo: 22 (rare) to 25
-
-// extremely easy: 46+
-// easy: 36-46
-// medium: 32-35
-// difficult: 28-31
-// evil: 17-27
-
int
main(int argc, char *argv[]) {
Sudoku s;
diff --git a/test/sudoku.h b/test/sudoku.h
deleted file mode 100644
index 5b3cebe24..000000000
--- a/test/sudoku.h
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// Sudoku game using the Fast Light Tool Kit (FLTK).
-//
-// Copyright 2005-2018 by Michael Sweet.
-// Copyright 2019-2021 by Bill Spitzak and others.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-#ifndef _SUDOKU_H_
-#define _SUDOKU_H_
-
-#include <FL/Fl_Double_Window.H>
-#include <FL/Fl_Preferences.H>
-
-class SudokuCell;
-class SudokuSound;
-class Fl_Sys_Menu_Bar;
-class Fl_Help_Dialog;
-
-#ifdef __APPLE__
-const int kMenuOffset = 0;
-#else
-const int kMenuOffset = 25;
-#endif // __APPLE__
-const int kPadding = 5;
-const int kCellSize = 50;
-const int kGroupSize = 3*kCellSize + 2;
-const int kPuzzleSize = 3*kGroupSize + 2;
-
-// 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) FL_OVERRIDE;
- void save_game();
- void solve_game();
- void update_helpers();
-};
-
-
-#endif // _SUDOKU_H_
diff --git a/test/sudoku_cell.cxx b/test/sudoku_cell.cxx
deleted file mode 100644
index a718ecf5c..000000000
--- a/test/sudoku_cell.cxx
+++ /dev/null
@@ -1,176 +0,0 @@
-//
-// Sudoku game using the Fast Light Tool Kit (FLTK).
-//
-// Copyright 2005-2018 by Michael Sweet.
-// Copyright 2019-2021 by Bill Spitzak and others.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-#include "sudoku_cell.h"
-#include "sudoku.h"
-
-#include <FL/fl_draw.H>
-#include <FL/fl_ask.H>
-
-// Create a cell widget
-SudokuCell::SudokuCell(int X, int Y, int W, int H)
-: Fl_Widget(X, Y, W, H, 0),
- marks_(0)
-{
- value(0);
-}
-
-
-// Draw cell
-void
-SudokuCell::draw() {
- static Fl_Align align[10] = {
- 0,
- FL_ALIGN_TOP_LEFT,
- FL_ALIGN_TOP,
- FL_ALIGN_TOP_RIGHT,
- FL_ALIGN_LEFT,
- 0,
- FL_ALIGN_RIGHT,
- FL_ALIGN_BOTTOM_LEFT,
- FL_ALIGN_BOTTOM,
- FL_ALIGN_BOTTOM_RIGHT,
- };
-
-
- // Draw the cell box...
- fl_draw_box(FL_BORDER_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';
-
- if (readonly())
- fl_font(FL_HELVETICA_BOLD, h() - 10);
- else
- fl_font(FL_HELVETICA, h() - 10);
- fl_draw(s, x(), y(), w(), h(), FL_ALIGN_CENTER);
- }
-
- fl_font(FL_HELVETICA_BOLD, h()*2/9);
-
- for (int i = 1; i <= 9; i ++) {
- if (mark(i)) {
- s[0] = 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)) {
- toggle_mark(key);
- value_ = 0;
- 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);
-}
-
-void SudokuCell::mark(int n, bool set) {
- if (n<1 || n>9) return;
- if (set) {
- marks_ |= (1<<n);
- } else {
- marks_ &= ~(1<<n);
- }
-}
-
-void SudokuCell::toggle_mark(int n) {
- if (n<1 || n>9) return;
- marks_ ^= (1<<n);
-}
-
-bool SudokuCell::mark(int n) {
- if (n<1 || n>9) return 0;
- return (marks_>>n) & 1;
-}
-
-void SudokuCell::clear_marks() {
- marks_ = 0;
-}
-
-
-
diff --git a/test/sudoku_cell.h b/test/sudoku_cell.h
deleted file mode 100644
index 2fc2b9cf7..000000000
--- a/test/sudoku_cell.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// Sudoku game cell using the Fast Light Tool Kit (FLTK).
-//
-// Copyright 2005-2018 by Michael Sweet.
-// Copyright 2019-2021 by Bill Spitzak and others.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-#ifndef _SUDOKU_CELL_H_
-#define _SUDOKU_CELL_H_
-
-#include <FL/Fl_Button.H>
-
-// Sudoku cell class...
-class SudokuCell : public Fl_Widget {
- bool readonly_;
- int value_;
- int marks_;
-
- public:
-
- SudokuCell(int X, int Y, int W, int H);
- void draw() FL_OVERRIDE;
- int handle(int event) FL_OVERRIDE;
- void readonly(bool r) { readonly_ = r; redraw(); }
- bool readonly() const { return readonly_; }
- void mark(int n, bool set);
- void toggle_mark(int n);
- bool mark(int n);
- void clear_marks();
- void value(int v) {
- value_ = v;
- clear_marks();
- redraw();
- }
- int value() const { return value_; }
-};
-
-
-#endif // _SUDOKU_CELL_H_
diff --git a/test/sudoku_generator.cxx b/test/sudoku_generator.cxx
deleted file mode 100644
index 36a74850f..000000000
--- a/test/sudoku_generator.cxx
+++ /dev/null
@@ -1,599 +0,0 @@
-//
-// Sudoku game generator using the Fast Light Tool Kit (FLTK).
-//
-// Copyright (c) 2018 Vaibhav Thakkar.
-// Copyright 2023 by Vaibhav Thakkar and Matthias Melcher.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-//
-// This solver is based on the work of Vaibhav Thakkar
-// from https://github.com/vaithak/Sudoku-Generator
-// vaithak/Sudoku-Generator is licensed under the MIT License
-// Copyright (c) 2018 Vaibhav Thakkar
-//
-// The solver was modified to fit FLTKs requirements of using minimal C++
-// and adapted to the FLTK naming scheme.
-//
-
-#include "sudoku_generator.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <FL/Fl_Int_Vector.H>
-
-#define UNASSIGNED 0
-
-void Sudoku_Generator::restoreWorkGrid()
-{
- for(int i=0;i<9;i++)
- for(int j=0;j<9;j++)
- this->grid[i][j] = this->solnGrid[i][j];
-}
-
-// Generate a random number between 0 and maxLimit-1
-int genRandNum(int maxLimit)
-{
- return rand()%maxLimit;
-}
-
-// We take an integer array of the length n and swap the content around randomly.
-// The function was part of the c++11 standard, but was removed in C++17 because
-// it was regarded as flawed.
-// This implementation is a minimal hack without care about random dstribution.
-// For additional randomness, we do that three times.
-void random_shuffle(int *data, int n)
-{
- for (int z = 3; z>0; --z) {
- for (int i = n-1; i > 0; --i) {
- int j = rand() % (i+1);
- int tmp = data[i];
- data[i] = data[j];
- data[j] = tmp;
- }
- }
-}
-
-// Helper functions for solving grid
-bool FindUnassignedLocation(const GridData &grid, int &row, int &col)
-{
- for (row = 0; row < 9; row++)
- {
- for (col = 0; col < 9; col++)
- {
- if (grid[row][col] == UNASSIGNED)
- return true;
- }
- }
- return false;
-}
-
-// Return true if num exists in row of grid.
-bool UsedInRow(const GridData &grid, int row, int num)
-{
- for (int col = 0; col < 9; col++)
- {
- if (grid[row][col] == num)
- return true;
- }
- return false;
-}
-
-// Return true if num exists in col of grid.
-bool UsedInCol(const GridData &grid, int col, int num)
-{
- for (int row = 0; row < 9; row++)
- {
- if (grid[row][col] == num)
- return true;
- }
- return false;
-}
-
-// Return true if num exists in box at row, col of grid.
-bool UsedInBox(const GridData grid, int boxStartRow, int boxStartCol, int num)
-{
- for (int row = 0; row < 3; row++)
- {
- for (int col = 0; col < 3; col++)
- {
- if (grid[row+boxStartRow][col+boxStartCol] == num)
- return true;
- }
- }
- return false;
-}
-
-// Return true if a number can be used at row, col and does not appear
-// yet in the rom, column, or box
-bool isSafe(GridData &grid, int row, int col, int num)
-{
- return !UsedInRow(grid, row, num) && !UsedInCol(grid, col, num) && !UsedInBox(grid, row - row%3 , col - col%3, num);
-}
-
-// Fill the box at idx*3, idx*3 with random values
-void Sudoku_Generator::fillEmptyDiagonalBox(int idx)
-{
- int start = idx*3;
- random_shuffle(guessNum, 9);
- for (int i = 0; i < 3; ++i)
- {
- for (int j = 0; j < 3; ++j)
- {
- this->grid[start+i][start+j] = guessNum[i*3+j];
- }
- }
-}
-
-// Create a solved Sudoku puzzle
-void Sudoku_Generator::createSeed()
-{
- /* Fill diagonal boxes to form:
- x | . | .
- . | x | .
- . | . | x
- */
- this->fillEmptyDiagonalBox(0);
- this->fillEmptyDiagonalBox(1);
- this->fillEmptyDiagonalBox(2);
-
- /* Fill the remaining blocks:
- x | x | x
- x | x | x
- x | x | x
- */
- this->solveGrid(); // Not truly random, but still good enough because we generate random diagonals.
-
- // Saving the solution grid
- for(int i=0;i<9;i++)
- {
- for(int j=0;j<9;j++)
- {
- this->solnGrid[i][j] = this->grid[i][j];
- }
- }
-}
-
-// Initialize the egenrator
-Sudoku_Generator::Sudoku_Generator()
-{
- // initialize difficulty level
- this->difficultyLevel = 0;
-
- // Randomly shuffling the array of removing grid positions
- for(int i=0;i<81;i++)
- {
- this->gridPos[i] = i;
- }
-
- random_shuffle(gridPos, 81);
-
- // Randomly shuffling the guessing number array
- for(int i=0;i<9;i++)
- {
- this->guessNum[i]=i+1;
- }
-
- random_shuffle(guessNum, 9);
-
- // Initialising the grid
- for(int i=0;i<9;i++)
- {
- for(int j=0;j<9;j++)
- {
- this->grid[i][j]=0;
- }
- }
-
- grid_status = true;
-}
-
-// Custom Initialising with grid passed as argument
-Sudoku_Generator::Sudoku_Generator(int grid_data[81], bool row_major)
-{
- // First pass: Check if all cells are valid
- for(int i=0; i<81; ++i)
- {
- int curr_num = grid_data[i];
- if(!((curr_num == UNASSIGNED) || (curr_num > 0 && curr_num < 10)))
- {
- grid_status=false;
- return;
- }
-
- if(row_major) grid[i/9][i%9] = curr_num;
- else grid[i%9][i/9] = curr_num;
- }
-
- // Second pass: Check if all columns are valid
- for (int col_num=0; col_num<9; ++col_num)
- {
- bool nums[10]={false};
- for (int row_num=0; row_num<9; ++row_num)
- {
- int curr_num = grid[row_num][col_num];
- if(curr_num!=UNASSIGNED && nums[curr_num]==true)
- {
- grid_status=false;
- return;
- }
- nums[curr_num] = true;
- }
- }
-
- // Third pass: Check if all rows are valid
- for (int row_num=0; row_num<9; ++row_num)
- {
- bool nums[10]={false};
- for (int col_num=0; col_num<9; ++col_num)
- {
- int curr_num = grid[row_num][col_num];
- if(curr_num!=UNASSIGNED && nums[curr_num]==true)
- {
- grid_status=false;
- return;
- }
- nums[curr_num] = true;
- }
- }
-
- // Fourth pass: Check if all blocks are valid
- for (int block_num=0; block_num<9; ++block_num)
- {
- bool nums[10]={false};
- for (int cell_num=0; cell_num<9; ++cell_num)
- {
- int curr_num = grid[((int)(block_num/3))*3 + (cell_num/3)][((int)(block_num%3))*3 + (cell_num%3)];
- if(curr_num!=UNASSIGNED && nums[curr_num]==true)
- {
- grid_status=false;
- return;
- }
- nums[curr_num] = true;
- }
- }
-
- // Randomly shuffling the guessing number array
- for(int i=0;i<9;i++)
- {
- this->guessNum[i]=i+1;
- }
-
- random_shuffle(guessNum, 9);
-
- grid_status = true;
-}
-
-// Return status of the custom grid passed.
-bool Sudoku_Generator::gridStatus()
-{
- return grid_status;
-}
-
-// Printing the grid
-void Sudoku_Generator::printGrid()
-{
- for(int i=0;i<9;i++)
- {
- for(int j=0;j<9;j++)
- {
- if(grid[i][j] == 0)
- printf(" .");
- else
- printf(" %d", grid[i][j]);
- if (((j%3) == 2) && (j < 8))
- printf(" |");
- }
- printf("\n");
- if (((i%3) == 2) && (i < 8))
- printf("-------+-------+-------\n");
- }
- printf("\nDifficulty of current sudoku(0 being easiest): %d\n", difficultyLevel);
-}
-
-// Modified Sudoku solver
-bool Sudoku_Generator::solveGrid()
-{
- int row, col;
-
- // If there is no unassigned location, we are done
- if (!FindUnassignedLocation(this->grid, row, col))
- return true; // success!
-
- // Consider digits 1 to 9
- for (int num = 0; num < 9; num++)
- {
- // if looks promising
- if (isSafe(this->grid, row, col, this->guessNum[num]))
- {
- // make tentative assignment
- this->grid[row][col] = this->guessNum[num];
-
- // return, if success, yay!
- if (solveGrid())
- return true;
-
- // failure, unmake & try again
- this->grid[row][col] = UNASSIGNED;
- }
- }
-
- return false; // this triggers backtracking
-}
-
-// Check if the grid is uniquely solvable
-void Sudoku_Generator::countSoln(int &number)
-{
- int row, col;
-
- if(!FindUnassignedLocation(this->grid, row, col))
- {
- number++;
- return ;
- }
-
-
- for(int i=0;i<9 && number<2;i++)
- {
- if( isSafe(this->grid, row, col, this->guessNum[i]) )
- {
- this->grid[row][col] = this->guessNum[i];
- countSoln(number);
- }
-
- this->grid[row][col] = UNASSIGNED;
- }
-
-}
-// END: Check if the grid is uniquely solvable
-
-
-// START: Generate puzzle
-void Sudoku_Generator::genPuzzle(int minHints)
-{
- int numHints = 81;
- for(int i=0;i<81;i++)
- {
- int x = (this->gridPos[i])/9;
- int y = (this->gridPos[i])%9;
- int temp = this->grid[x][y];
- this->grid[x][y] = UNASSIGNED;
-
- // If now more than 1 solution , replace the removed cell back.
- int check=0;
- countSoln(check);
- if(check!=1)
- {
- this->grid[x][y] = temp;
-// printf("Can't remove cell %d\n", i);
- } else {
- numHints--;
- if (numHints <= minHints) break;
- }
- }
- printf("Found %d hints\n", numHints);
-}
-// END: Generate puzzle
-
-
-// START: Calculate branch difficulty score
-int Sudoku_Generator::branchDifficultyScore()
-{
- int emptyPositions = -1;
- GridData tempGrid;
- int sum=0;
-
- for(int i=0;i<9;i++)
- {
- for(int j=0;j<9;j++)
- {
- tempGrid[i][j] = this->grid[i][j];
- }
- }
-
- while(emptyPositions!=0)
- {
- Fl_Int_Vector empty[81];
- int empty_n = 0;
-
- for(int i=0;i<81;i++)
- {
- if(tempGrid[(int)(i/9)][(int)(i%9)] == 0)
- {
- Fl_Int_Vector temp;
- temp.push_back(i);
-
- for(int num=1;num<=9;num++)
- {
- if(isSafe(tempGrid,i/9,i%9,num))
- {
- temp.push_back(num);
- }
- }
-
- empty[empty_n++] = temp;
- }
-
- }
-
- if(empty_n == 0)
- {
- return sum;
- }
-
- int minIndex = 0;
-
- int check = empty_n;
- for(int i=0;i<check;i++)
- {
- if(empty[i].size() < empty[minIndex].size())
- minIndex = i;
- }
-
- int branchFactor=empty[minIndex].size();
- int rowIndex = empty[minIndex][0]/9;
- int colIndex = empty[minIndex][0]%9;
-
- tempGrid[rowIndex][colIndex] = this->solnGrid[rowIndex][colIndex];
- sum = sum + ((branchFactor-2) * (branchFactor-2)) ;
-
- emptyPositions = empty_n - 1;
- }
-
- return sum;
-
-}
-// END: Finish branch difficulty score
-
-
-// START: Calculate difficulty level of current grid
-void Sudoku_Generator::calculateDifficulty()
-{
- int B = branchDifficultyScore();
- int emptyCells = 0;
-
- for(int i=0;i<9;i++)
- {
- for(int j=0;j<9;j++)
- {
- if(this->grid[i][j] == 0)
- emptyCells++;
- }
- }
-
- this->difficultyLevel = B*100 + emptyCells;
-}
-// END: calculating difficulty level
-
-
-// START: The main function
-int generate_sudoku(int grid_data[81], int minHints, int maxHints)
-{
-#if 0
- int i, j;
- FILE *f = fopen("/Users/matt/dev/su.cxx", "wb");
- fprintf(f, "// all horizontal chains\n");
- for (i=0; i<9; i++) {
- fprintf(f, "{ ");
- for (j=0; j<9; j++) {
- fprintf(f, "%2d, ", i*9+j);
- }
- fprintf(f, "},\n");
- }
- fprintf(f, "// all vertical chains\n");
- for (i=0; i<9; i++) {
- fprintf(f, "{ ");
- for (j=0; j<9; j++) {
- fprintf(f, "%2d, ", j*9+i);
- }
- fprintf(f, "},\n");
- }
- fprintf(f, "// all squares\n");
- for (i=0; i<9; i++) {
- fprintf(f, "{ ");
- for (j=0; j<9; j++) {
- fprintf(f, "%2d, ", ((i%3)*3) + ((i/3)*3*9) + (j%3) + (j/3)*9);
- }
- fprintf(f, "},\n");
- }
- fprintf(f, "// every field is part of 3 chains\n");
- for (i=0; i<81; i++) {
- fprintf(f, "{ ");
- int col = i % 9;
- int row = i / 9;
- fprintf(f, " %2d, %2d, %2d ",
- i/9, i%9, (col/3) + (row/3)*3
- );
- fprintf(f, "},\n");
- }
- fclose(f);
-#endif
-
-
- // Initialising seed for random number generation
- srand((unsigned int)time(NULL));
-
- // Creating an instance of Sudoku
- Sudoku_Generator *puzzle = new Sudoku_Generator();
-
- // Creating a seed for puzzle generation
- puzzle->createSeed();
-
- // Generating the puzzle
- puzzle->genPuzzle(minHints);
-// int minDiff = 100, maxDiff = 0;
-// for (int zz=0; zz<100; zz++) {
-// time_t start; time(&start);
-// random_shuffle(puzzle->gridPos, 81);
-// puzzle->restoreWorkGrid();
-// puzzle->genPuzzle(minHints);
-// time_t end; time(&end);
-// puzzle->calculateDifficulty();
-// printf("--- in %ld, difficulty is %d\n", end-start, puzzle->difficultyLevel);
-// if (puzzle->difficultyLevel < minDiff) minDiff = puzzle->difficultyLevel;
-// if (puzzle->difficultyLevel > maxDiff) maxDiff = puzzle->difficultyLevel;
-// }
-// printf("Difficulty range is %d to %d\n", minDiff, maxDiff);
- // 22: 55 to 1658
- // 25: 56 to 1456
- // 28: 53 to 953, 53 to 1153, 53 to 1253
- // 31: 50 to 850, 50 to 950, 50 to 850
- // 40: 41 to 141
- // 45: 36 to 136
-
-
- // Calculating difficulty of puzzle
- puzzle->calculateDifficulty();
-
- // testing by printing the grid
- puzzle->printGrid();
-
-// // Printing the grid into SVG file
-// string rem = "sudokuGen";
-// string path = argv[0];
-// path = path.substr(0,path.size() - rem.size());
-// puzzle->printSVG(path);
-// cout<<"The above sudoku puzzle has been stored in puzzles.svg in current folder\n";
-// // freeing the memory
-
-// puzzle->printGrid();
- printf("Difficulty: %d\n", puzzle->difficultyLevel);
-// for (int d = 0; d<9; d++) {
-// int x = 0, y = 0;
-// for (;;) {
-// x = genRandNum(9);
-// y = genRandNum(9);
-// if (puzzle->grid[x][y] == 0) break;
-// }
-// puzzle->grid[x][y] = puzzle->solnGrid[x][y];
-// printf(" %d %d\n", x, y);
-// puzzle->calculateDifficulty();
-// printf("Difficulty: %d\n", puzzle->difficultyLevel);
-// }
-
- int *g = grid_data;
- for(int i=0;i<9;i++) {
- for(int j=0;j<9;j++) {
- if (puzzle->grid[i][j] == UNASSIGNED) {
- *g++ = -puzzle->solnGrid[i][j];
- } else {
- *g++ = puzzle->solnGrid[i][j];
- }
- }
- }
-
- delete puzzle;
-
- return 0;
-}
-// END: The main function
diff --git a/test/sudoku_generator.h b/test/sudoku_generator.h
deleted file mode 100644
index 4510adc67..000000000
--- a/test/sudoku_generator.h
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// Sudoku game generator using the Fast Light Tool Kit (FLTK).
-//
-// Copyright (c) 2018 Vaibhav Thakkar.
-// Copyright 2023 by Vaibhav Thakkar and Matthias Melcher.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-//
-// This solver is based on the work of Vaibhav Thakkar
-// from https://github.com/vaithak/Sudoku-Generator
-// vaithak/Sudoku-Generator is licensed under the MIT License
-// Copyright (c) 2018 Vaibhav Thakkar
-//
-// The solver was modified to fit FLTKs requirements of using minimal C++
-// and adapted to the FLTK naming scheme.
-//
-
-#ifndef _SUDOKU_GENERATOR_H_
-#define _SUDOKU_GENERATOR_H_
-
-typedef int GridData[9][9];
-
-extern int generate_sudoku(int grid_data[81], int minHints, int maxHints);
-
-class Sudoku_Generator {
-private:
-public:
- GridData grid;
- GridData solnGrid;
- int guessNum[9];
- int gridPos[81];
- int difficultyLevel;
- bool grid_status;
-
-public:
- Sudoku_Generator ();
- Sudoku_Generator(int[81], bool row_major=true);
- void fillEmptyDiagonalBox(int);
- void createSeed();
- void printGrid();
- bool solveGrid();
- void countSoln(int &number);
- void genPuzzle(int minHints);
- bool gridStatus();
- void calculateDifficulty();
- void restoreWorkGrid();
- int branchDifficultyScore();
-};
-
-#endif // _SUDOKU_GENERATOR_H_
diff --git a/test/sudoku_puzzle.cxx b/test/sudoku_puzzle.cxx
deleted file mode 100644
index cc693b583..000000000
--- a/test/sudoku_puzzle.cxx
+++ /dev/null
@@ -1,846 +0,0 @@
-//
-// Sudoku game puzzle using the Fast Light Tool Kit (FLTK).
-//
-// Copyright 2005-2018 by Michael Sweet.
-// Copyright 2019-2021 by Bill Spitzak and others.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-#include "sudoku_puzzle.h"
-#include "sudoku.h"
-
-#include <FL/fl_draw.H>
-
-SudokuPuzzle::SudokuPuzzle(int x, int y, int w, int h, const char *label)
-: Fl_Group(x, y, w, h, label)
-{
- box(FL_BORDER_BOX);
-}
-
-#if 0
-// A Sudoku (i.e. the puzzle) is a partially completed grid. A grid has 9 rows,
-// 9 columns and 9 boxes, each having 9 cells (81 total). Boxes can also be
-// called blocks or regions.[1] Three horizontally adjacent blocks are a band,
-// and three vertically adjacent blocks are a stack.[2] The initially defined
-// values are clues or givens. An ordinary Sudoku (i.e. a proper Sudoku) has
-// one solution. Rows, columns and regions can be collectively referred to as
-// groups, of which the grid has 27. The One Rule encapsulates the three prime
-// rules, i.e. each digit (or number) can occur only once in each row, column,
-// and box; and can be compactly stated as: "Each digit appears once in each
-// group."
-// - wikipedia.org
-
-// Wishlist:
-// - [ ] new easy to new hard, etc.
-// - [ ] store current puzzle in preferences
-// - [ ] undo, redo
-// - [ ] update hints now
-// - [ ] always update hints
-// - [ ] highlight row, column, and box
-// - [ ] highlight other cells with same value
-// - [ ] verify current solution
-// - [ ] hint/flag/note vs. solve mode and button 1..9, erase
-// - [ ] gift one field
-// - [ ] bg is white and bright blue
-// - [ ] selected field bg is green, boxed
-// - [ ] same number is yellow
-// - [ ] conflicts can use arrows and crosses
-// - [ ] fixed numbers are bold, user values are not
-// - [ ] timer
-// - [ ] game hamburge menu
-
-#include "sudoku.h"
-#include "sudoku_cell.h"
-#include "sudoku_sound.h"
-#include "sudoku_generator.h"
-
-#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/platform.H>
-#include <FL/Fl_Image_Surface.H>
-#include <FL/Fl_Bitmap.H>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <FL/math.h>
-
-#include "pixmaps/sudoku.xbm"
-
-//
-// 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__
-
-
-// Sudoku class globals...
-Fl_Help_Dialog *Sudoku::help_dialog_ = (Fl_Help_Dialog *)0;
-Fl_Preferences Sudoku::prefs_(Fl_Preferences::USER_L, "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", FL_COMMAND | 'u', 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
- Fl_Bitmap bm(sudoku_bits, sudoku_width, sudoku_height);
- Fl_Image_Surface surf(sudoku_width, sudoku_height, 1);
- Fl_Surface_Device::push_current(&surf);
- fl_color(FL_WHITE);
- fl_rectf(0, 0, sudoku_width, sudoku_height);
- fl_color(FL_BLACK);
- bm.draw(0, 0);
- Fl_Surface_Device::pop_current();
- icon(surf.image());
-
- // 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
- // TODO: set all marks if none were set
- // TODO: clear marks if value is used, don't set individual marks
- for (j = 0; j < 9; j ++) {
- for (k = 0; k < 9; k ++) {
- SudokuCell *cell = grid_cells_[j][k];
- cell->clear_marks();
- }
- }
-
- // 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; m <= 9; m ++) {
- if (!taken[m])
- dst_cell->mark(m, true);
- }
- dst_cell->redraw();
- }
-}
-
-
-// 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;
- bool empty = false;
-
- for (int j = 0; j < 9; j ++) {
- Fl_Preferences row(prefs_, Fl_Preferences::Name("Row%d", j));
- for (int k = 0; k < 9; k ++) {
- Fl_Preferences p(row, Fl_Preferences::Name("Col%d", k));
- int v;
-
- SudokuCell *cell = grid_cells_[j][k];
-
- p.get("value", v, 0);
- grid_values_[j][k] = v;
- if (v) empty = false;
-
- p.get("state", v, 0);
- cell->value(v);
-
- p.set("readonly", cell->readonly());
- cell->readonly(v != 0);
- if (v) {
- cell->color(FL_GRAY);
- } else {
- cell->color(FL_LIGHT3);
- solved = false;
- }
-
- for (int m = 1; m <= 9; m ++) {
- p.get(Fl_Preferences::Name("m%d", m), v, 0);
- cell->mark(m, v);
- }
- cell->redraw();
- }
- }
-
- // 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 grid_data[81];
- int *g = grid_data;
- generate_sudoku(grid_data, 22, 31);
- SudokuCell *cell;
- for (int j = 0; j < 9; j ++) {
- for (int k = 0; k < 9; k ++) {
- int v = *g++;
- int vv = v; if (vv<0) vv = -vv;
- grid_values_[j][k] = vv;
- cell = grid_cells_[j][k];
- if (v<0) {
- cell->value(0);
- cell->readonly(0);
- cell->color(FL_LIGHT3);
- } else {
- cell->value(vv);
- cell->readonly(1);
- cell->color(FL_GRAY);
- }
- }
- }
- return;
- }
-
-
-
-
- int j, k, m, n, t, count;
-
-
- // Generate a new (valid) Sudoku grid...
- seed_ = seed;
- srand((unsigned int)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 = 0, k = 0, m = 0, n = 0;
-
-
- 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 ++) {
- Fl_Preferences row(prefs_, Fl_Preferences::Name("Row%d", j));
- for (int k = 0; k < 9; k ++) {
- Fl_Preferences p(row, Fl_Preferences::Name("Col%d", k));
- char name[255];
- SudokuCell *cell = grid_cells_[j][k];
- p.set("value", grid_values_[j][k]);
- p.set("state", cell->value());
- p.set("readonly", cell->readonly());
- for (int m = 1; m <= 9; m ++) {
- if (cell->mark(m))
- p.set(Fl_Preferences::Name("m%d", m), 1);
- else
- p.deleteEntry(Fl_Preferences::Name("m%d", 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...
-// Note 21-17 (proven minimum) clues can be set
-// easy: 30-36
-// expert: 25-30
-// algo: 22 (rare) to 25
-
-// extremely easy: 46+
-// easy: 36-46
-// medium: 32-35
-// difficult: 28-31
-// evil: 17-27
-
-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());
-}
-
-#endif
diff --git a/test/sudoku_puzzle.h b/test/sudoku_puzzle.h
deleted file mode 100644
index b1e5609c3..000000000
--- a/test/sudoku_puzzle.h
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-// Sudoku game puzzle using the Fast Light Tool Kit (FLTK).
-//
-// Copyright 2005-2018 by Michael Sweet.
-// Copyright 2019-2021 by Bill Spitzak and others.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-#ifndef _SUDOKU_PUZZLE_H_
-#define _SUDOKU_PUZZLE_H_
-
-#include <FL/Fl_Group.H>
-
-class SudokuPuzzle : public Fl_Group {
-public:
- SudokuPuzzle(int x, int y, int w, int h, const char *label=NULL);
-};
-
-#if 0
-class SudokuCell;
-class SudokuSound;
-class Fl_Sys_Menu_Bar;
-class Fl_Help_Dialog;
-
-// 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) FL_OVERRIDE;
- void save_game();
- void solve_game();
- void update_helpers();
-};
-
-#endif
-
-#endif // _SUDOKU_PUZZLE_H_
diff --git a/test/sudoku_sound.cxx b/test/sudoku_sound.cxx
deleted file mode 100644
index 887313298..000000000
--- a/test/sudoku_sound.cxx
+++ /dev/null
@@ -1,308 +0,0 @@
-//
-// Sudoku game using the Fast Light Tool Kit (FLTK).
-//
-// Copyright 2005-2018 by Michael Sweet.
-// Copyright 2019-2021 by Bill Spitzak and others.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-#include "sudoku_sound.h"
-
-#include <FL/Fl.H>
-#include <FL/platform.H>
-
-#include <math.h>
-
-// 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(&params);
- 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__
-
-#define NOTE_DURATION 50
-
-// Play a note for <NOTE_DURATION> ms...
-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(NOTE_DURATION*1000);
-
-#elif defined(_WIN32)
- if (sample_size) {
- memcpy(data_ptr, sample_data[note - 'A'], sample_size * 4);
-
- waveOutWrite(device, header_ptr, sizeof(WAVEHDR));
-
- Sleep(NOTE_DURATION);
- } else Beep(frequencies[note - 'A'], NOTE_DURATION);
-
-#elif defined(FLTK_USE_X11)
-# 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(NOTE_DURATION*1000);
- 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 = NOTE_DURATION;
-
- 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__
-}
-
diff --git a/test/sudoku_sound.h b/test/sudoku_sound.h
deleted file mode 100644
index dd7f6fe3c..000000000
--- a/test/sudoku_sound.h
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// Sudoku game sound using the Fast Light Tool Kit (FLTK).
-//
-// Copyright 2005-2018 by Michael Sweet.
-// Copyright 2019-2023 by Bill Spitzak and others.
-//
-// This library is free software. Distribution and use rights are outlined in
-// the file "COPYING" which should have been included with this file. If this
-// file is missing or damaged, see the license at:
-//
-// https://www.fltk.org/COPYING.php
-//
-// Please see the following page on how to report bugs and issues:
-//
-// https://www.fltk.org/bugs.php
-//
-
-#ifndef _SUDOKU_SOUND_H_
-#define _SUDOKU_SOUND_H_
-
-// Audio headers...
-#include <config.h>
-
-#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
-
-// 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;
-#ifndef MAC_OS_X_VERSION_10_5
-#define MAC_OS_X_VERSION_10_5 1050
-#endif
-# 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);
-};
-
-#endif // _SUDOKU_SOUND_H_