Sudoku into smaller pieces.

This commit is contained in:
Matthias Melcher 2023-08-13 21:28:04 +02:00
parent a1b55385e3
commit 0de186e614
9 changed files with 776 additions and 633 deletions

View File

@ -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_generator.cxx;sudoku.plist;sudoku.icns;sudoku.rc" "fltk_images;fltk;${AUDIOLIBS}")
CREATE_EXAMPLE (sudoku "sudoku.cxx;sudoku_cell.cxx;sudoku_sound.cxx;sudoku_generator.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)

View File

@ -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(&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),
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) {

70
test/sudoku.h Normal file
View File

@ -0,0 +1,70 @@
//
// 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;
// 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_

174
test/sudoku_cell.cxx Normal file
View File

@ -0,0 +1,174 @@
//
// 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...
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;
}

49
test/sudoku_cell.h Normal file
View File

@ -0,0 +1,49 @@
//
// 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_

View File

@ -25,6 +25,8 @@
// and adapted to the FLTK naming scheme.
//
#include "sudoku_generator.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
@ -32,33 +34,6 @@
#define UNASSIGNED 0
typedef int GridData[9][9];
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();
};
void Sudoku_Generator::restoreWorkGrid()
{
for(int i=0;i<9;i++)
@ -72,7 +47,6 @@ 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.
@ -88,11 +62,6 @@ void random_shuffle(int *data, int n)
data[j] = tmp;
}
}
// printf("Rand: ");
// for (int j=0; j<n; j++) {
// printf("%d ", data[j]);
// }
// printf("\n");
}
// Helper functions for solving grid
@ -562,19 +531,19 @@ int generate_sudoku(int grid_data[81], int minHints, int maxHints)
// 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);
// 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

60
test/sudoku_generator.h Normal file
View File

@ -0,0 +1,60 @@
//
// 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_

305
test/sudoku_sound.cxx Normal file
View File

@ -0,0 +1,305 @@
//
// 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>
// 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__
}

97
test/sudoku_sound.h Normal file
View File

@ -0,0 +1,97 @@
//
// 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_