You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1347 lines
32KB

  1. //
  2. // "$Id: sudoku.cxx 7903 2010-11-28 21:06:39Z matt $"
  3. //
  4. // Sudoku game using the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 2005-2010 by Michael Sweet.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems on the following page:
  24. //
  25. // http://www.fltk.org/str.php
  26. //
  27. #include <FL/Fl.H>
  28. #include <FL/Enumerations.H>
  29. #include <FL/Fl_Double_Window.H>
  30. #include <FL/Fl_Button.H>
  31. #include <FL/Fl_Group.H>
  32. #include <FL/fl_ask.H>
  33. #include <FL/fl_draw.H>
  34. #include <FL/Fl_Help_Dialog.H>
  35. #include <FL/Fl_Preferences.H>
  36. #include <FL/Fl_Sys_Menu_Bar.H>
  37. #include <FL/x.H>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <time.h>
  42. #include <FL/math.h>
  43. #ifdef WIN32
  44. # include "sudokurc.h"
  45. #elif !defined(__APPLE__)
  46. # include "pixmaps/sudoku.xbm"
  47. #endif // WIN32
  48. // Audio headers...
  49. #include <config.h>
  50. #ifndef WIN32
  51. # include <unistd.h>
  52. #endif // !WIN32
  53. #ifdef HAVE_ALSA_ASOUNDLIB_H
  54. # define ALSA_PCM_NEW_HW_PARAMS_API
  55. # include <alsa/asoundlib.h>
  56. #endif // HAVE_ALSA_ASOUNDLIB_H
  57. #ifdef __APPLE__
  58. # include <CoreAudio/AudioHardware.h>
  59. #endif // __APPLE__
  60. #ifdef WIN32
  61. # include <mmsystem.h>
  62. #endif // WIN32
  63. //
  64. // Default sizes...
  65. //
  66. #define GROUP_SIZE 160
  67. #define CELL_SIZE 50
  68. #define CELL_OFFSET 5
  69. #ifdef __APPLE__
  70. # define MENU_OFFSET 0
  71. #else
  72. # define MENU_OFFSET 25
  73. #endif // __APPLE__
  74. // Sound class for Sudoku...
  75. //
  76. // There are MANY ways to implement sound in a FLTK application.
  77. // The approach we are using here is to conditionally compile OS-
  78. // specific code into the application - CoreAudio for MacOS X, the
  79. // standard Win32 API stuff for Windows, ALSA or X11 for Linux, and
  80. // X11 for all others. We have to support ALSA on Linux because the
  81. // current Xorg releases no longer support XBell() or the PC speaker.
  82. //
  83. // There are several good cross-platform audio libraries we could also
  84. // use, such as OpenAL, PortAudio, and SDL, however they were not chosen
  85. // for this application because of our limited use of sound.
  86. //
  87. // Many thanks to Ian MacArthur who provided sample code that led to
  88. // the CoreAudio implementation you see here!
  89. class SudokuSound {
  90. // Private, OS-specific data...
  91. #ifdef __APPLE__
  92. AudioDeviceID device;
  93. # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  94. AudioDeviceIOProcID audio_proc_id;
  95. # endif
  96. AudioStreamBasicDescription format;
  97. short *data;
  98. int remaining;
  99. static OSStatus audio_cb(AudioDeviceID device,
  100. const AudioTimeStamp *current_time,
  101. const AudioBufferList *data_in,
  102. const AudioTimeStamp *time_in,
  103. AudioBufferList *data_out,
  104. const AudioTimeStamp *time_out,
  105. void *client_data);
  106. #elif defined(WIN32)
  107. HWAVEOUT device;
  108. HGLOBAL header_handle;
  109. LPWAVEHDR header_ptr;
  110. HGLOBAL data_handle;
  111. LPSTR data_ptr;
  112. #else
  113. # ifdef HAVE_ALSA_ASOUNDLIB_H
  114. snd_pcm_t *handle;
  115. # endif // HAVE_ALSA_ASOUNDLIB_H
  116. #endif // __APPLE__
  117. // Common data...
  118. static int frequencies[9];
  119. static short *sample_data[9];
  120. static int sample_size;
  121. public:
  122. SudokuSound();
  123. ~SudokuSound();
  124. void play(char note);
  125. };
  126. // Sudoku cell class...
  127. class SudokuCell : public Fl_Widget {
  128. bool readonly_;
  129. int value_;
  130. int test_value_[9];
  131. public:
  132. SudokuCell(int X, int Y, int W, int H);
  133. void draw();
  134. int handle(int event);
  135. void readonly(bool r) { readonly_ = r; redraw(); }
  136. bool readonly() const { return readonly_; }
  137. void test_value(int v, int n) { test_value_[n] = v; redraw(); }
  138. int test_value(int n) const { return test_value_[n]; }
  139. void value(int v) {
  140. value_ = v;
  141. for (int i = 0; i < 8; i ++) test_value_[i] = 0;
  142. redraw();
  143. }
  144. int value() const { return value_; }
  145. };
  146. // Sudoku window class...
  147. class Sudoku : public Fl_Double_Window {
  148. Fl_Sys_Menu_Bar *menubar_;
  149. Fl_Group *grid_;
  150. time_t seed_;
  151. char grid_values_[9][9];
  152. SudokuCell *grid_cells_[9][9];
  153. Fl_Group *grid_groups_[3][3];
  154. int difficulty_;
  155. SudokuSound *sound_;
  156. static void check_cb(Fl_Widget *widget, void *);
  157. static void close_cb(Fl_Widget *widget, void *);
  158. static void diff_cb(Fl_Widget *widget, void *d);
  159. static void update_helpers_cb(Fl_Widget *, void *);
  160. static void help_cb(Fl_Widget *, void *);
  161. static void mute_cb(Fl_Widget *widget, void *);
  162. static void new_cb(Fl_Widget *widget, void *);
  163. static void reset_cb(Fl_Widget *widget, void *);
  164. static void restart_cb(Fl_Widget *widget, void *);
  165. void set_title();
  166. static void solve_cb(Fl_Widget *widget, void *);
  167. static Fl_Help_Dialog *help_dialog_;
  168. static Fl_Preferences prefs_;
  169. public:
  170. Sudoku();
  171. ~Sudoku();
  172. void check_game(bool highlight = true);
  173. void load_game();
  174. void new_game(time_t seed);
  175. int next_value(SudokuCell *c);
  176. void resize(int X, int Y, int W, int H);
  177. void save_game();
  178. void solve_game();
  179. void update_helpers();
  180. };
  181. // Sound class globals...
  182. int SudokuSound::frequencies[9] = {
  183. 880, // A(5)
  184. 988, // B(5)
  185. 1046, // C(5)
  186. 1174, // D(5)
  187. 1318, // E(5)
  188. 1396, // F(5)
  189. 1568, // G(5)
  190. 1760, // H (A6)
  191. 1976 // I (B6)
  192. };
  193. short *SudokuSound::sample_data[9] = { 0 };
  194. int SudokuSound::sample_size = 0;
  195. // Initialize the SudokuSound class
  196. SudokuSound::SudokuSound() {
  197. sample_size = 0;
  198. #ifdef __APPLE__
  199. remaining = 0;
  200. UInt32 size = sizeof(device);
  201. if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
  202. &size, (void *)&device) != noErr) return;
  203. size = sizeof(format);
  204. if (AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyStreamFormat,
  205. &size, &format) != noErr) return;
  206. // Set up a format we like...
  207. format.mSampleRate = 44100.0; // 44.1kHz
  208. format.mChannelsPerFrame = 2; // stereo
  209. if (AudioDeviceSetProperty(device, NULL, 0, false,
  210. kAudioDevicePropertyStreamFormat,
  211. sizeof(format), &format) != noErr) return;
  212. // Check we got linear pcm - what to do if we did not ???
  213. if (format.mFormatID != kAudioFormatLinearPCM) return;
  214. // Attach the callback and start the device
  215. # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  216. if (AudioDeviceCreateIOProcID(device, audio_cb, (void *)this, &audio_proc_id) != noErr) return;
  217. AudioDeviceStart(device, audio_proc_id);
  218. # else
  219. if (AudioDeviceAddIOProc(device, audio_cb, (void *)this) != noErr) return;
  220. AudioDeviceStart(device, audio_cb);
  221. # endif
  222. sample_size = (int)format.mSampleRate / 20;
  223. #elif defined(WIN32)
  224. WAVEFORMATEX format;
  225. memset(&format, 0, sizeof(format));
  226. format.cbSize = sizeof(format);
  227. format.wFormatTag = WAVE_FORMAT_PCM;
  228. format.nChannels = 2;
  229. format.nSamplesPerSec = 44100;
  230. format.nAvgBytesPerSec = 44100 * 4;
  231. format.nBlockAlign = 4;
  232. format.wBitsPerSample = 16;
  233. data_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, format.nSamplesPerSec / 5);
  234. if (!data_handle) return;
  235. data_ptr = (LPSTR)GlobalLock(data_handle);
  236. header_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
  237. if (!header_handle) return;
  238. header_ptr = (WAVEHDR *)GlobalLock(header_handle);
  239. header_ptr->lpData = data_ptr;
  240. header_ptr->dwBufferLength = format.nSamplesPerSec / 5;
  241. header_ptr->dwFlags = 0;
  242. header_ptr->dwLoops = 0;
  243. if (waveOutOpen(&device, WAVE_MAPPER, &format, 0, 0, WAVE_ALLOWSYNC)
  244. != MMSYSERR_NOERROR) return;
  245. waveOutPrepareHeader(device, header_ptr, sizeof(WAVEHDR));
  246. sample_size = 44100 / 20;
  247. #else
  248. # ifdef HAVE_ALSA_ASOUNDLIB_H
  249. handle = NULL;
  250. if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) >= 0) {
  251. // Initialize PCM sound stuff...
  252. snd_pcm_hw_params_t *params;
  253. snd_pcm_hw_params_alloca(&params);
  254. snd_pcm_hw_params_any(handle, params);
  255. snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
  256. snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16);
  257. snd_pcm_hw_params_set_channels(handle, params, 2);
  258. unsigned rate = 44100;
  259. int dir;
  260. snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
  261. snd_pcm_uframes_t period = (int)rate / 4;
  262. snd_pcm_hw_params_set_period_size_near(handle, params, &period, &dir);
  263. sample_size = rate / 20;
  264. if (snd_pcm_hw_params(handle, params) < 0) {
  265. sample_size = 0;
  266. snd_pcm_close(handle);
  267. handle = NULL;
  268. }
  269. }
  270. # endif // HAVE_ALSA_ASOUNDLIB_H
  271. #endif // __APPLE__
  272. if (sample_size) {
  273. // Make each of the notes using a combination of sine and sawtooth waves
  274. int attack = sample_size / 10;
  275. int decay = 4 * sample_size / 5;
  276. for (int i = 0; i < 9; i ++) {
  277. sample_data[i] = new short[2 * sample_size];
  278. short *sample_ptr = sample_data[i];
  279. for (int j = 0; j < sample_size; j ++, sample_ptr += 2) {
  280. double theta = 0.05 * frequencies[i] * j / sample_size;
  281. double val = 0.5 * sin(2.0 * M_PI * theta) + theta - (int)theta - 0.5;
  282. if (j < attack) {
  283. *sample_ptr = (int)(32767 * val * j / attack);
  284. } else if (j > decay) {
  285. *sample_ptr = (int)(32767 * val * (sample_size - j + decay) /
  286. sample_size);
  287. } else *sample_ptr = (int)(32767 * val);
  288. sample_ptr[1] = *sample_ptr;
  289. }
  290. }
  291. }
  292. }
  293. // Cleanup the SudokuSound class
  294. SudokuSound::~SudokuSound() {
  295. #ifdef __APPLE__
  296. if (sample_size) {
  297. # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  298. AudioDeviceStop(device, audio_proc_id);
  299. AudioDeviceDestroyIOProcID(device, audio_proc_id);
  300. # else
  301. AudioDeviceStop(device, audio_cb);
  302. AudioDeviceRemoveIOProc(device, audio_cb);
  303. # endif
  304. }
  305. #elif defined(WIN32)
  306. if (sample_size) {
  307. waveOutClose(device);
  308. GlobalUnlock(header_handle);
  309. GlobalFree(header_handle);
  310. GlobalUnlock(data_handle);
  311. GlobalFree(data_handle);
  312. }
  313. #else
  314. # ifdef HAVE_ALSA_ASOUNDLIB_H
  315. if (handle) {
  316. snd_pcm_drain(handle);
  317. snd_pcm_close(handle);
  318. }
  319. # endif // HAVE_ALSA_ASOUNDLIB_H
  320. #endif // __APPLE__
  321. if (sample_size) {
  322. for (int i = 0; i < 9; i ++) {
  323. delete[] sample_data[i];
  324. }
  325. }
  326. }
  327. #ifdef __APPLE__
  328. // Callback function for writing audio data...
  329. OSStatus
  330. SudokuSound::audio_cb(AudioDeviceID device,
  331. const AudioTimeStamp *current_time,
  332. const AudioBufferList *data_in,
  333. const AudioTimeStamp *time_in,
  334. AudioBufferList *data_out,
  335. const AudioTimeStamp *time_out,
  336. void *client_data) {
  337. SudokuSound *ss = (SudokuSound *)client_data;
  338. int count;
  339. float *buffer;
  340. if (!ss->remaining) return noErr;
  341. for (count = data_out->mBuffers[0].mDataByteSize / sizeof(float),
  342. buffer = (float*) data_out->mBuffers[0].mData;
  343. ss->remaining > 0 && count > 0;
  344. count --, ss->data ++, ss->remaining --) {
  345. *buffer++ = *(ss->data) / 32767.0;
  346. }
  347. while (count > 0) {
  348. *buffer++ = 0.0;
  349. count --;
  350. }
  351. return noErr;
  352. }
  353. #endif // __APPLE__
  354. // Play a note for 250ms...
  355. void SudokuSound::play(char note) {
  356. Fl::check();
  357. #ifdef __APPLE__
  358. // Point to the next note...
  359. data = sample_data[note - 'A'];
  360. remaining = sample_size * 2;
  361. // Wait for the sound to complete...
  362. usleep(50000);
  363. #elif defined(WIN32)
  364. if (sample_size) {
  365. memcpy(data_ptr, sample_data[note - 'A'], sample_size * 4);
  366. waveOutWrite(device, header_ptr, sizeof(WAVEHDR));
  367. Sleep(50);
  368. } else Beep(frequencies[note - 'A'], 50);
  369. #else
  370. # ifdef HAVE_ALSA_ASOUNDLIB_H
  371. if (handle) {
  372. // Use ALSA to play the sound...
  373. if (snd_pcm_writei(handle, sample_data[note - 'A'], sample_size) < 0) {
  374. snd_pcm_prepare(handle);
  375. snd_pcm_writei(handle, sample_data[note - 'A'], sample_size);
  376. }
  377. usleep(50000);
  378. return;
  379. }
  380. # endif // HAVE_ALSA_ASOUNDLIB_H
  381. // Just use standard X11 stuff...
  382. XKeyboardState state;
  383. XKeyboardControl control;
  384. // Get original pitch and duration...
  385. XGetKeyboardControl(fl_display, &state);
  386. // Sound a tone for the given note...
  387. control.bell_percent = 100;
  388. control.bell_pitch = frequencies[note - 'A'];
  389. control.bell_duration = 50;
  390. XChangeKeyboardControl(fl_display,
  391. KBBellPercent | KBBellPitch | KBBellDuration,
  392. &control);
  393. XBell(fl_display, 100);
  394. XFlush(fl_display);
  395. // Restore original pitch and duration...
  396. control.bell_percent = state.bell_percent;
  397. control.bell_pitch = state.bell_pitch;
  398. control.bell_duration = state.bell_duration;
  399. XChangeKeyboardControl(fl_display,
  400. KBBellPercent | KBBellPitch | KBBellDuration,
  401. &control);
  402. #endif // __APPLE__
  403. }
  404. // Create a cell widget
  405. SudokuCell::SudokuCell(int X, int Y, int W, int H)
  406. : Fl_Widget(X, Y, W, H, 0) {
  407. value(0);
  408. }
  409. // Draw cell
  410. void
  411. SudokuCell::draw() {
  412. static Fl_Align align[8] = {
  413. FL_ALIGN_TOP_LEFT,
  414. FL_ALIGN_TOP,
  415. FL_ALIGN_TOP_RIGHT,
  416. FL_ALIGN_RIGHT,
  417. FL_ALIGN_BOTTOM_RIGHT,
  418. FL_ALIGN_BOTTOM,
  419. FL_ALIGN_BOTTOM_LEFT,
  420. FL_ALIGN_LEFT
  421. };
  422. // Draw the cell box...
  423. if (readonly()) fl_draw_box(FL_UP_BOX, x(), y(), w(), h(), color());
  424. else fl_draw_box(FL_DOWN_BOX, x(), y(), w(), h(), color());
  425. // Draw the cell background...
  426. if (Fl::focus() == this) {
  427. Fl_Color c = fl_color_average(FL_SELECTION_COLOR, color(), 0.5f);
  428. fl_color(c);
  429. fl_rectf(x() + 4, y() + 4, w() - 8, h() - 8);
  430. fl_color(fl_contrast(labelcolor(), c));
  431. } else fl_color(labelcolor());
  432. // Draw the cell value...
  433. char s[2];
  434. s[1] = '\0';
  435. if (value_) {
  436. s[0] = value_ + '0';
  437. fl_font(FL_HELVETICA_BOLD, h() - 10);
  438. fl_draw(s, x(), y(), w(), h(), FL_ALIGN_CENTER);
  439. }
  440. fl_font(FL_HELVETICA_BOLD, h() / 5);
  441. for (int i = 0; i < 8; i ++) {
  442. if (test_value_[i]) {
  443. s[0] = test_value_[i] + '0';
  444. fl_draw(s, x() + 5, y() + 5, w() - 10, h() - 10, align[i]);
  445. }
  446. }
  447. }
  448. // Handle events in cell
  449. int
  450. SudokuCell::handle(int event) {
  451. switch (event) {
  452. case FL_FOCUS :
  453. Fl::focus(this);
  454. redraw();
  455. return 1;
  456. case FL_UNFOCUS :
  457. redraw();
  458. return 1;
  459. case FL_PUSH :
  460. if (!readonly() && Fl::event_inside(this)) {
  461. if (Fl::event_clicks()) {
  462. // 2+ clicks increments/sets value
  463. if (value()) {
  464. if (value() < 9) value(value() + 1);
  465. else value(1);
  466. } else value(((Sudoku *)window())->next_value(this));
  467. }
  468. Fl::focus(this);
  469. redraw();
  470. return 1;
  471. }
  472. break;
  473. case FL_KEYDOWN :
  474. if (Fl::event_state() & FL_CTRL) break;
  475. int key = Fl::event_key() - '0';
  476. if (key < 0 || key > 9) key = Fl::event_key() - FL_KP - '0';
  477. if (key > 0 && key <= 9) {
  478. if (readonly()) {
  479. fl_beep(FL_BEEP_ERROR);
  480. return 1;
  481. }
  482. if (Fl::event_state() & (FL_SHIFT | FL_CAPS_LOCK)) {
  483. int i;
  484. for (i = 0; i < 8; i ++)
  485. if (test_value_[i] == key) {
  486. test_value_[i] = 0;
  487. break;
  488. }
  489. if (i >= 8) {
  490. for (i = 0; i < 8; i ++)
  491. if (!test_value_[i]) {
  492. test_value_[i] = key;
  493. break;
  494. }
  495. }
  496. if (i >= 8) {
  497. for (i = 0; i < 7; i ++) test_value_[i] = test_value_[i + 1];
  498. test_value_[i] = key;
  499. }
  500. redraw();
  501. } else {
  502. value(key);
  503. do_callback();
  504. }
  505. return 1;
  506. } else if (key == 0 || Fl::event_key() == FL_BackSpace ||
  507. Fl::event_key() == FL_Delete) {
  508. if (readonly()) {
  509. fl_beep(FL_BEEP_ERROR);
  510. return 1;
  511. }
  512. value(0);
  513. do_callback();
  514. return 1;
  515. }
  516. break;
  517. }
  518. return Fl_Widget::handle(event);
  519. }
  520. // Sudoku class globals...
  521. Fl_Help_Dialog *Sudoku::help_dialog_ = (Fl_Help_Dialog *)0;
  522. Fl_Preferences Sudoku::prefs_(Fl_Preferences::USER, "fltk.org", "sudoku");
  523. // Create a Sudoku game window...
  524. Sudoku::Sudoku()
  525. : Fl_Double_Window(GROUP_SIZE * 3, GROUP_SIZE * 3 + MENU_OFFSET, "Sudoku")
  526. {
  527. int j, k;
  528. Fl_Group *g;
  529. SudokuCell *cell;
  530. static Fl_Menu_Item items[] = {
  531. { "&Game", 0, 0, 0, FL_SUBMENU },
  532. { "&New Game", FL_COMMAND | 'n', new_cb, 0, FL_MENU_DIVIDER },
  533. { "&Check Game", FL_COMMAND | 'c', check_cb, 0, 0 },
  534. { "&Restart Game", FL_COMMAND | 'r', restart_cb, 0, 0 },
  535. { "&Solve Game", FL_COMMAND | 's', solve_cb, 0, FL_MENU_DIVIDER },
  536. { "&Update Helpers", 0, update_helpers_cb, 0, 0 },
  537. { "&Mute Sound", FL_COMMAND | 'm', mute_cb, 0, FL_MENU_TOGGLE | FL_MENU_DIVIDER },
  538. { "&Quit", FL_COMMAND | 'q', close_cb, 0, 0 },
  539. { 0 },
  540. { "&Difficulty", 0, 0, 0, FL_SUBMENU },
  541. { "&Easy", 0, diff_cb, (void *)"0", FL_MENU_RADIO },
  542. { "&Medium", 0, diff_cb, (void *)"1", FL_MENU_RADIO },
  543. { "&Hard", 0, diff_cb, (void *)"2", FL_MENU_RADIO },
  544. { "&Impossible", 0, diff_cb, (void *)"3", FL_MENU_RADIO },
  545. { 0 },
  546. { "&Help", 0, 0, 0, FL_SUBMENU },
  547. { "&About Sudoku", FL_F + 1, help_cb, 0, 0 },
  548. { 0 },
  549. { 0 }
  550. };
  551. // Setup sound output...
  552. prefs_.get("mute_sound", j, 0);
  553. if (j) {
  554. // Mute sound?
  555. sound_ = NULL;
  556. items[6].flags |= FL_MENU_VALUE;
  557. } else sound_ = new SudokuSound();
  558. // Menubar...
  559. prefs_.get("difficulty", difficulty_, 0);
  560. if (difficulty_ < 0 || difficulty_ > 3) difficulty_ = 0;
  561. items[10 + difficulty_].flags |= FL_MENU_VALUE;
  562. menubar_ = new Fl_Sys_Menu_Bar(0, 0, 3 * GROUP_SIZE, 25);
  563. menubar_->menu(items);
  564. // Create the grids...
  565. grid_ = new Fl_Group(0, MENU_OFFSET, 3 * GROUP_SIZE, 3 * GROUP_SIZE);
  566. for (j = 0; j < 3; j ++)
  567. for (k = 0; k < 3; k ++) {
  568. g = new Fl_Group(k * GROUP_SIZE, j * GROUP_SIZE + MENU_OFFSET,
  569. GROUP_SIZE, GROUP_SIZE);
  570. g->box(FL_BORDER_BOX);
  571. if ((int)(j == 1) ^ (int)(k == 1)) g->color(FL_DARK3);
  572. else g->color(FL_DARK2);
  573. g->end();
  574. grid_groups_[j][k] = g;
  575. }
  576. for (j = 0; j < 9; j ++)
  577. for (k = 0; k < 9; k ++) {
  578. cell = new SudokuCell(k * CELL_SIZE + CELL_OFFSET +
  579. (k / 3) * (GROUP_SIZE - 3 * CELL_SIZE),
  580. j * CELL_SIZE + CELL_OFFSET + MENU_OFFSET +
  581. (j / 3) * (GROUP_SIZE - 3 * CELL_SIZE),
  582. CELL_SIZE, CELL_SIZE);
  583. cell->callback(reset_cb);
  584. grid_cells_[j][k] = cell;
  585. }
  586. // Set icon for window (MacOS uses app bundle for icon...)
  587. #ifdef WIN32
  588. icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON)));
  589. #elif !defined(__APPLE__)
  590. fl_open_display();
  591. icon((char *)XCreateBitmapFromData(fl_display, DefaultRootWindow(fl_display),
  592. (char *)sudoku_bits, sudoku_width,
  593. sudoku_height));
  594. #endif // WIN32
  595. // Catch window close events...
  596. callback(close_cb);
  597. // Make the window resizable...
  598. resizable(grid_);
  599. size_range(3 * GROUP_SIZE, 3 * GROUP_SIZE + MENU_OFFSET, 0, 0, 5, 5, 1);
  600. // Restore the previous window dimensions...
  601. int X, Y, W, H;
  602. if (prefs_.get("x", X, -1)) {
  603. prefs_.get("y", Y, -1);
  604. prefs_.get("width", W, 3 * GROUP_SIZE);
  605. prefs_.get("height", H, 3 * GROUP_SIZE + MENU_OFFSET);
  606. resize(X, Y, W, H);
  607. }
  608. set_title();
  609. }
  610. // Destroy the sudoku window...
  611. Sudoku::~Sudoku() {
  612. if (sound_) delete sound_;
  613. }
  614. // Check for a solution to the game...
  615. void
  616. Sudoku::check_cb(Fl_Widget *widget, void *) {
  617. ((Sudoku *)(widget->window()))->check_game();
  618. }
  619. // Check if the user has correctly solved the game...
  620. void
  621. Sudoku::check_game(bool highlight) {
  622. bool empty = false;
  623. bool correct = true;
  624. int j, k, m;
  625. // Check the game for right/wrong answers...
  626. for (j = 0; j < 9; j ++)
  627. for (k = 0; k < 9; k ++) {
  628. SudokuCell *cell = grid_cells_[j][k];
  629. int val = cell->value();
  630. if (cell->readonly()) continue;
  631. if (!val) empty = true;
  632. else {
  633. for (m = 0; m < 9; m ++)
  634. if ((j != m && grid_cells_[m][k]->value() == val) ||
  635. (k != m && grid_cells_[j][m]->value() == val)) break;
  636. if (m < 9) {
  637. if (highlight) {
  638. cell->color(FL_YELLOW);
  639. cell->redraw();
  640. }
  641. correct = false;
  642. } else if (highlight) {
  643. cell->color(FL_LIGHT3);
  644. cell->redraw();
  645. }
  646. }
  647. }
  648. // Check subgrids for duplicate numbers...
  649. for (j = 0; j < 9; j += 3)
  650. for (k = 0; k < 9; k += 3)
  651. for (int jj = 0; jj < 3; jj ++)
  652. for (int kk = 0; kk < 3; kk ++) {
  653. SudokuCell *cell = grid_cells_[j + jj][k + kk];
  654. int val = cell->value();
  655. if (cell->readonly() || !val) continue;
  656. int jjj;
  657. for (jjj = 0; jjj < 3; jjj ++) {
  658. int kkk;
  659. for (kkk = 0; kkk < 3; kkk ++)
  660. if (jj != jjj && kk != kkk &&
  661. grid_cells_[j + jjj][k + kkk]->value() == val) break;
  662. if (kkk < 3) break;
  663. }
  664. if (jjj < 3) {
  665. if (highlight) {
  666. cell->color(FL_YELLOW);
  667. cell->redraw();
  668. }
  669. correct = false;
  670. }
  671. }
  672. if (!empty && correct) {
  673. // Success!
  674. for (j = 0; j < 9; j ++) {
  675. for (k = 0; k < 9; k ++) {
  676. SudokuCell *cell = grid_cells_[j][k];
  677. cell->color(FL_GREEN);
  678. cell->readonly(1);
  679. }
  680. if (sound_) sound_->play('A' + grid_cells_[j][8]->value() - 1);
  681. }
  682. }
  683. }
  684. // Close the window, saving the game first...
  685. void
  686. Sudoku::close_cb(Fl_Widget *widget, void *) {
  687. Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
  688. s->save_game();
  689. s->hide();
  690. if (help_dialog_) help_dialog_->hide();
  691. }
  692. // Set the level of difficulty...
  693. void
  694. Sudoku::diff_cb(Fl_Widget *widget, void *d) {
  695. Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
  696. int diff = atoi((char *)d);
  697. if (diff != s->difficulty_) {
  698. s->difficulty_ = diff;
  699. s->new_game(s->seed_);
  700. s->set_title();
  701. if (diff > 1)
  702. {
  703. // Display a message about the higher difficulty levels for the
  704. // Sudoku zealots of the world...
  705. int val;
  706. prefs_.get("difficulty_warning", val, 0);
  707. if (!val)
  708. {
  709. prefs_.set("difficulty_warning", 1);
  710. fl_alert("Note: 'Hard' and 'Impossible' puzzles may have more than "
  711. "one possible solution.\n"
  712. "This is not an error or bug.");
  713. }
  714. }
  715. prefs_.set("difficulty", s->difficulty_);
  716. }
  717. }
  718. // Update the little marker numbers in all cells
  719. void
  720. Sudoku::update_helpers_cb(Fl_Widget *widget, void *) {
  721. Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
  722. s->update_helpers();
  723. }
  724. void
  725. Sudoku::update_helpers() {
  726. int j, k, m;
  727. // First we delete any entries that the user may have made
  728. for (j = 0; j < 9; j ++) {
  729. for (k = 0; k < 9; k ++) {
  730. SudokuCell *cell = grid_cells_[j][k];
  731. for (m = 0; m < 8; m ++) {
  732. cell->test_value(0, m);
  733. }
  734. }
  735. }
  736. // Now go through all cells and find out, what we can not be
  737. for (j = 0; j < 81; j ++) {
  738. char taken[10] = { 0 };
  739. // Find our destination cell
  740. int row = j / 9;
  741. int col = j % 9;
  742. SudokuCell *dst_cell = grid_cells_[row][col];
  743. if (dst_cell->value()) continue;
  744. // Find all values already taken in this row
  745. for (k = 0; k < 9; k ++) {
  746. SudokuCell *cell = grid_cells_[row][k];
  747. int v = cell->value();
  748. if (v) taken[v] = 1;
  749. }
  750. // Find all values already taken in this column
  751. for (k = 0; k < 9; k ++) {
  752. SudokuCell *cell = grid_cells_[k][col];
  753. int v = cell->value();
  754. if (v) taken[v] = 1;
  755. }
  756. // Now find all values already taken in this square
  757. int ro = (row / 3) * 3;
  758. int co = (col / 3) * 3;
  759. for (k = 0; k < 3; k ++) {
  760. for (m = 0; m < 3; m ++) {
  761. SudokuCell *cell = grid_cells_[ro + k][co + m];
  762. int v = cell->value();
  763. if (v) taken[v] = 1;
  764. }
  765. }
  766. // transfer our findings to the markers
  767. for (m = 1, k = 0; m <= 9; m ++) {
  768. if (!taken[m])
  769. dst_cell->test_value(m, k ++);
  770. }
  771. }
  772. }
  773. // Show the on-line help...
  774. void
  775. Sudoku::help_cb(Fl_Widget *, void *) {
  776. if (!help_dialog_) {
  777. help_dialog_ = new Fl_Help_Dialog();
  778. help_dialog_->value(
  779. "<HTML>\n"
  780. "<HEAD>\n"
  781. "<TITLE>Sudoku Help</TITLE>\n"
  782. "</HEAD>\n"
  783. "<BODY BGCOLOR='#ffffff'>\n"
  784. "<H2>About the Game</H2>\n"
  785. "<P>Sudoku (pronounced soo-dough-coo with the emphasis on the\n"
  786. "first syllable) is a simple number-based puzzle/game played on a\n"
  787. "9x9 grid that is divided into 3x3 subgrids. The goal is to enter\n"
  788. "a number from 1 to 9 in each cell so that each number appears\n"
  789. "only once in each column and row. In addition, each 3x3 subgrid\n"
  790. "may only contain one of each number.</P>\n"
  791. "<P>This version of the puzzle is copyright 2005-2010 by Michael R\n"
  792. "Sweet.</P>\n"
  793. "<P><B>Note:</B> The 'Hard' and 'Impossible' difficulty\n"
  794. "levels generate Sudoku puzzles with multiple possible solutions.\n"
  795. "While some purists insist that these cannot be called 'Sudoku'\n"
  796. "puzzles, the author (me) has personally solved many such puzzles\n"
  797. "in published/printed Sudoku books and finds them far more\n"
  798. "interesting than the simple single solution variety. If you don't\n"
  799. "like it, don't play with the difficulty set to 'High' or\n"
  800. "'Impossible'.</P>\n"
  801. "<H2>How to Play the Game</H2>\n"
  802. "<P>At the start of a new game, Sudoku fills in a random selection\n"
  803. "of cells for you - the number of cells depends on the difficulty\n"
  804. "level you use. Click in any of the empty cells or use the arrow\n"
  805. "keys to highlight individual cells and press a number from 1 to 9\n"
  806. "to fill in the cell. To clear a cell, press 0, Delete, or\n"
  807. "Backspace. When you have successfully completed all subgrids, the\n"
  808. "entire puzzle is highlighted in green until you start a new\n"
  809. "game.</P>\n"
  810. "<P>As you work to complete the puzzle, you can display possible\n"
  811. "solutions inside each cell by holding the Shift key and pressing\n"
  812. "each number in turn. Repeat the process to remove individual\n"
  813. "numbers, or press a number without the Shift key to replace them\n"
  814. "with the actual number to use.</P>\n"
  815. "</BODY>\n"
  816. );
  817. }
  818. help_dialog_->show();
  819. }
  820. // Load the game from saved preferences...
  821. void
  822. Sudoku::load_game() {
  823. // Load the current values and state of each grid...
  824. memset(grid_values_, 0, sizeof(grid_values_));
  825. bool solved = true;
  826. for (int j = 0; j < 9; j ++)
  827. for (int k = 0; k < 9; k ++) {
  828. char name[255];
  829. int val;
  830. SudokuCell *cell = grid_cells_[j][k];
  831. sprintf(name, "value%d.%d", j, k);
  832. if (!prefs_.get(name, val, 0)) {
  833. j = 9;
  834. grid_values_[0][0] = 0;
  835. break;
  836. }
  837. grid_values_[j][k] = val;
  838. sprintf(name, "state%d.%d", j, k);
  839. prefs_.get(name, val, 0);
  840. cell->value(val);
  841. sprintf(name, "readonly%d.%d", j, k);
  842. prefs_.get(name, val, 0);
  843. cell->readonly(val);
  844. if (val) cell->color(FL_GRAY);
  845. else {
  846. cell->color(FL_LIGHT3);
  847. solved = false;
  848. }
  849. for (int m = 0; m < 8; m ++) {
  850. sprintf(name, "test%d%d.%d", m, j, k);
  851. prefs_.get(name, val, 0);
  852. cell->test_value(val, m);
  853. }
  854. }
  855. // If we didn't load any values or the last game was solved, then
  856. // create a new game automatically...
  857. if (solved || !grid_values_[0][0]) new_game(time(NULL));
  858. else check_game(false);
  859. }
  860. // Mute/unmute sound...
  861. void
  862. Sudoku::mute_cb(Fl_Widget *widget, void *) {
  863. Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
  864. if (s->sound_) {
  865. delete s->sound_;
  866. s->sound_ = NULL;
  867. prefs_.set("mute_sound", 1);
  868. } else {
  869. s->sound_ = new SudokuSound();
  870. prefs_.set("mute_sound", 0);
  871. }
  872. }
  873. // Create a new game...
  874. void
  875. Sudoku::new_cb(Fl_Widget *widget, void *) {
  876. Sudoku *s = (Sudoku *)(widget->window() ? widget->window() : widget);
  877. if (s->grid_cells_[0][0]->color() != FL_GREEN) {
  878. if (!fl_choice("Are you sure you want to change the difficulty level and "
  879. "discard the current game?", "Keep Current Game", "Start New Game",
  880. NULL)) return;
  881. }
  882. s->new_game(time(NULL));
  883. }
  884. // Create a new game...
  885. void
  886. Sudoku::new_game(time_t seed) {
  887. int j, k, m, n, t, count;
  888. // Generate a new (valid) Sudoku grid...
  889. seed_ = seed;
  890. srand(seed);
  891. memset(grid_values_, 0, sizeof(grid_values_));
  892. for (j = 0; j < 9; j += 3) {
  893. for (k = 0; k < 9; k += 3) {
  894. for (t = 1; t <= 9; t ++) {
  895. for (count = 0; count < 20; count ++) {
  896. m = j + (rand() % 3);
  897. n = k + (rand() % 3);
  898. if (!grid_values_[m][n]) {
  899. int mm;
  900. for (mm = 0; mm < m; mm ++)
  901. if (grid_values_[mm][n] == t) break;
  902. if (mm < m) continue;
  903. int nn;
  904. for (nn = 0; nn < n; nn ++)
  905. if (grid_values_[m][nn] == t) break;
  906. if (nn < n) continue;
  907. grid_values_[m][n] = t;
  908. break;
  909. }
  910. }
  911. if (count == 20) {
  912. // Unable to find a valid puzzle so far, so start over...
  913. k = 9;
  914. j = -3;
  915. memset(grid_values_, 0, sizeof(grid_values_));
  916. }
  917. }
  918. }
  919. }
  920. // Start by making all cells editable
  921. SudokuCell *cell;
  922. for (j = 0; j < 9; j ++)
  923. for (k = 0; k < 9; k ++) {
  924. cell = grid_cells_[j][k];
  925. cell->value(0);
  926. cell->readonly(0);
  927. cell->color(FL_LIGHT3);
  928. }
  929. // Show N cells...
  930. count = 11 * (5 - difficulty_);
  931. int numbers[9];
  932. for (j = 0; j < 9; j ++) numbers[j] = j + 1;
  933. while (count > 0) {
  934. for (j = 0; j < 20; j ++) {
  935. k = rand() % 9;
  936. m = rand() % 9;
  937. t = numbers[k];
  938. numbers[k] = numbers[m];
  939. numbers[m] = t;
  940. }
  941. for (j = 0; count > 0 && j < 9; j ++) {
  942. t = numbers[j];
  943. for (k = 0; count > 0 && k < 9; k ++) {
  944. cell = grid_cells_[j][k];
  945. if (grid_values_[j][k] == t && !cell->readonly()) {
  946. cell->value(grid_values_[j][k]);
  947. cell->readonly(1);
  948. cell->color(FL_GRAY);
  949. count --;
  950. break;
  951. }
  952. }
  953. }
  954. }
  955. }
  956. // Return the next available value for a cell...
  957. int
  958. Sudoku::next_value(SudokuCell *c) {
  959. int j, k, m, n;
  960. for (j = 0; j < 9; j ++) {
  961. for (k = 0; k < 9; k ++)
  962. if (grid_cells_[j][k] == c) break;
  963. if (k < 9) break;
  964. }
  965. if (j == 9) return 1;
  966. j -= j % 3;
  967. k -= k % 3;
  968. int numbers[9];
  969. memset(numbers, 0, sizeof(numbers));
  970. for (m = 0; m < 3; m ++)
  971. for (n = 0; n < 3; n ++) {
  972. c = grid_cells_[j + m][k + n];
  973. if (c->value()) numbers[c->value() - 1] = 1;
  974. }
  975. for (j = 0; j < 9; j ++)
  976. if (!numbers[j]) return j + 1;
  977. return 1;
  978. }
  979. // Reset widget color to gray...
  980. void
  981. Sudoku::reset_cb(Fl_Widget *widget, void *) {
  982. widget->color(FL_LIGHT3);
  983. widget->redraw();
  984. ((Sudoku *)(widget->window()))->check_game(false);
  985. }
  986. // Resize the window...
  987. void
  988. Sudoku::resize(int X, int Y, int W, int H) {
  989. // Resize the window...
  990. Fl_Double_Window::resize(X, Y, W, H);
  991. // Save the new window geometry...
  992. prefs_.set("x", X);
  993. prefs_.set("y", Y);
  994. prefs_.set("width", W);
  995. prefs_.set("height", H);
  996. }
  997. // Restart game from beginning...
  998. void
  999. Sudoku::restart_cb(Fl_Widget *widget, void *) {
  1000. Sudoku *s = (Sudoku *)(widget->window());
  1001. bool solved = true;
  1002. for (int j = 0; j < 9; j ++)
  1003. for (int k = 0; k < 9; k ++) {
  1004. SudokuCell *cell = s->grid_cells_[j][k];
  1005. if (!cell->readonly()) {
  1006. solved = false;
  1007. int v = cell->value();
  1008. cell->value(0);
  1009. cell->color(FL_LIGHT3);
  1010. if (v && s->sound_) s->sound_->play('A' + v - 1);
  1011. }
  1012. }
  1013. if (solved) s->new_game(s->seed_);
  1014. }
  1015. // Save the current game state...
  1016. void
  1017. Sudoku::save_game() {
  1018. // Save the current values and state of each grid...
  1019. for (int j = 0; j < 9; j ++)
  1020. for (int k = 0; k < 9; k ++) {
  1021. char name[255];
  1022. SudokuCell *cell = grid_cells_[j][k];
  1023. sprintf(name, "value%d.%d", j, k);
  1024. prefs_.set(name, grid_values_[j][k]);
  1025. sprintf(name, "state%d.%d", j, k);
  1026. prefs_.set(name, cell->value());
  1027. sprintf(name, "readonly%d.%d", j, k);
  1028. prefs_.set(name, cell->readonly());
  1029. for (int m = 0; m < 8; m ++) {
  1030. sprintf(name, "test%d%d.%d", m, j, k);
  1031. prefs_.set(name, cell->test_value(m));
  1032. }
  1033. }
  1034. }
  1035. // Set title of window...
  1036. void
  1037. Sudoku::set_title() {
  1038. static const char * const titles[] = {
  1039. "Sudoku - Easy",
  1040. "Sudoku - Medium",
  1041. "Sudoku - Hard",
  1042. "Sudoku - Impossible"
  1043. };
  1044. label(titles[difficulty_]);
  1045. }
  1046. // Solve the puzzle...
  1047. void
  1048. Sudoku::solve_cb(Fl_Widget *widget, void *) {
  1049. ((Sudoku *)(widget->window()))->solve_game();
  1050. }
  1051. // Solve the puzzle...
  1052. void
  1053. Sudoku::solve_game() {
  1054. int j, k;
  1055. for (j = 0; j < 9; j ++) {
  1056. for (k = 0; k < 9; k ++) {
  1057. SudokuCell *cell = grid_cells_[j][k];
  1058. cell->value(grid_values_[j][k]);
  1059. cell->readonly(1);
  1060. cell->color(FL_GRAY);
  1061. }
  1062. if (sound_) sound_->play('A' + grid_cells_[j][8]->value() - 1);
  1063. }
  1064. }
  1065. // Main entry for game...
  1066. int
  1067. main(int argc, char *argv[]) {
  1068. Sudoku s;
  1069. // Show the game...
  1070. s.show(argc, argv);
  1071. // Load the previous game...
  1072. s.load_game();
  1073. // Run until the user quits...
  1074. return (Fl::run());
  1075. }
  1076. //
  1077. // End of "$Id: sudoku.cxx 7903 2010-11-28 21:06:39Z matt $".
  1078. //