diff options
| author | Matthias Melcher <github@matthiasm.com> | 2023-08-15 22:59:07 +0200 |
|---|---|---|
| committer | Matthias Melcher <github@matthiasm.com> | 2023-08-15 22:59:07 +0200 |
| commit | e8b378302cdb582a09dd5a577541ecf32029e93f (patch) | |
| tree | c48995b09151eaf9b12be691d58cc6163183b984 /test/sudoku.cxx | |
| parent | cfe5b2d6e109a8b0bedb4f2ad83a0803f888cd5b (diff) | |
Undoing previous changes
Diffstat (limited to 'test/sudoku.cxx')
| -rw-r--r-- | test/sudoku.cxx | 788 |
1 files changed, 638 insertions, 150 deletions
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(¶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) { + 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; |
