diff options
| author | Matthias Melcher <github@matthiasm.com> | 2023-08-13 21:28:04 +0200 |
|---|---|---|
| committer | Matthias Melcher <github@matthiasm.com> | 2023-08-15 17:09:51 +0200 |
| commit | 0de186e614db861c2ae0b63459353384da37fe56 (patch) | |
| tree | 4e3422454b34d4592667bfba3818070259dbbda0 /test/sudoku.cxx | |
| parent | a1b55385e3c0d7d5c75178bc1307fba685b0f3d7 (diff) | |
Sudoku into smaller pieces.
Diffstat (limited to 'test/sudoku.cxx')
| -rw-r--r-- | test/sudoku.cxx | 591 |
1 files changed, 5 insertions, 586 deletions
diff --git a/test/sudoku.cxx b/test/sudoku.cxx index 43fe37124..822508b7f 100644 --- a/test/sudoku.cxx +++ b/test/sudoku.cxx @@ -15,6 +15,11 @@ // https://www.fltk.org/bugs.php // +#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> @@ -36,25 +41,6 @@ #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... // @@ -68,570 +54,6 @@ # 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 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_; } -}; - - -// 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(¶ms); - snd_pcm_hw_params_any(handle, params); - snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); - snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16); - snd_pcm_hw_params_set_channels(handle, params, 2); - unsigned rate = 44100; - int dir; - snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir); - snd_pcm_uframes_t period = (int)rate / 4; - snd_pcm_hw_params_set_period_size_near(handle, params, &period, &dir); - - sample_size = rate / 20; - - if (snd_pcm_hw_params(handle, params) < 0) { - sample_size = 0; - snd_pcm_close(handle); - handle = NULL; - } - } -# endif // HAVE_ALSA_ASOUNDLIB_H -#endif // __APPLE__ - - if (sample_size) { - // Make each of the notes using a combination of sine and sawtooth waves - int attack = sample_size / 10; - int decay = 4 * sample_size / 5; - - for (int i = 0; i < 9; i ++) { - sample_data[i] = new short[2 * sample_size]; - - short *sample_ptr = sample_data[i]; - - for (int j = 0; j < sample_size; j ++, sample_ptr += 2) { - double theta = 0.05 * frequencies[i] * j / sample_size; - double val = 0.5 * sin(2.0 * M_PI * theta) + theta - (int)theta - 0.5; - - if (j < attack) { - *sample_ptr = (int)(32767 * val * j / attack); - } else if (j > decay) { - *sample_ptr = (int)(32767 * val * (sample_size - j + decay) / - sample_size); - } else *sample_ptr = (int)(32767 * val); - - sample_ptr[1] = *sample_ptr; - } - } - } -} - - -// Cleanup the SudokuSound class -SudokuSound::~SudokuSound() { -#ifdef __APPLE__ - if (sample_size) { -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - AudioDeviceStop(device, audio_proc_id); - AudioDeviceDestroyIOProcID(device, audio_proc_id); -# else - AudioDeviceStop(device, audio_cb); - AudioDeviceRemoveIOProc(device, audio_cb); -# endif - } - -#elif defined(_WIN32) - if (sample_size) { - waveOutClose(device); - - GlobalUnlock(header_handle); - GlobalFree(header_handle); - - GlobalUnlock(data_handle); - GlobalFree(data_handle); - } - -#else -# ifdef HAVE_ALSA_ASOUNDLIB_H - if (handle) { - snd_pcm_drain(handle); - snd_pcm_close(handle); - } -# endif // HAVE_ALSA_ASOUNDLIB_H -#endif // __APPLE__ - - if (sample_size) { - for (int i = 0; i < 9; i ++) { - delete[] sample_data[i]; - } - } -} - - -#ifdef __APPLE__ -// Callback function for writing audio data... -OSStatus -SudokuSound::audio_cb(AudioDeviceID device, - const AudioTimeStamp *current_time, - const AudioBufferList *data_in, - const AudioTimeStamp *time_in, - AudioBufferList *data_out, - const AudioTimeStamp *time_out, - void *client_data) { - SudokuSound *ss = (SudokuSound *)client_data; - int count; - float *buffer; - - if (!ss->remaining) return noErr; - - for (count = data_out->mBuffers[0].mDataByteSize / sizeof(float), - buffer = (float*) data_out->mBuffers[0].mData; - ss->remaining > 0 && count > 0; - count --, ss->data ++, ss->remaining --) { - *buffer++ = *(ss->data) / 32767.0; - } - - while (count > 0) { - *buffer++ = 0.0; - count --; - } - - return noErr; -} -#endif // __APPLE__ - -#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), - 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... - 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()*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; -} - // Sudoku class globals... Fl_Help_Dialog *Sudoku::help_dialog_ = (Fl_Help_Dialog *)0; @@ -1081,9 +503,6 @@ Sudoku::new_cb(Fl_Widget *widget, void *) { s->new_game(time(NULL)); } - -extern int generate_sudoku(int grid_data[81], int minHints, int maxHints); - // Create a new game... void Sudoku::new_game(time_t seed) { |
