Sudoku into smaller pieces.
This commit is contained in:
parent
a1b55385e3
commit
0de186e614
@ -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)
|
||||
|
591
test/sudoku.cxx
591
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) {
|
||||
|
70
test/sudoku.h
Normal file
70
test/sudoku.h
Normal 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
174
test/sudoku_cell.cxx
Normal 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
49
test/sudoku_cell.h
Normal 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_
|
@ -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
60
test/sudoku_generator.h
Normal 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
305
test/sudoku_sound.cxx
Normal 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(¶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__
|
||||
}
|
||||
|
97
test/sudoku_sound.h
Normal file
97
test/sudoku_sound.h
Normal 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_
|
Loading…
Reference in New Issue
Block a user