summaryrefslogtreecommitdiff
path: root/test/sudoku.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'test/sudoku.cxx')
-rw-r--r--test/sudoku.cxx143
1 files changed, 121 insertions, 22 deletions
diff --git a/test/sudoku.cxx b/test/sudoku.cxx
index d92739c5c..8d226a9fa 100644
--- a/test/sudoku.cxx
+++ b/test/sudoku.cxx
@@ -130,6 +130,8 @@ class SudokuSound {
void play(char note);
};
+typedef unsigned int State;
+typedef State GameState[81];
// Sudoku cell class...
class SudokuCell : public Fl_Widget {
@@ -137,6 +139,7 @@ class SudokuCell : public Fl_Widget {
int col_;
bool readonly_;
int value_;
+ int solution_;
int hint_map_;
public:
@@ -156,6 +159,14 @@ class SudokuCell : public Fl_Widget {
bool hint_set(int n) const { return ((hint_map_&(1<<n))!=0); }
void value(int v) { value_ = v; redraw(); }
int value() const { return value_; }
+ State state() { return (hint_map_>>1) | (value_<<12) | (readonly_<<9); }
+ void state(State s) {
+ hint_map_ = (s & 0x000001ff) << 1;
+ readonly_ = (s & 0x00000200) >> 9;
+ value_ = (s & 0x0000f000) >> 12;
+ if (readonly_) color(FL_GRAY); else color(FL_LIGHT3);
+ redraw();
+ }
};
@@ -169,6 +180,8 @@ class Sudoku : public Fl_Double_Window {
Fl_Group *grid_groups_[3][3];
int difficulty_;
SudokuSound *sound_;
+ GameState undo_stack[64];
+ int undo_head_, undo_tail_, redo_head_;
static void check_cb(Fl_Widget *widget, void *);
static void close_cb(Fl_Widget *widget, void *);
@@ -198,6 +211,12 @@ class Sudoku : public Fl_Double_Window {
void solve_game();
void update_helpers();
void clear_hints_for(int row, int col, int val);
+ void save_state(GameState &s);
+ void load_state(GameState &s);
+ void undo();
+ void redo();
+ void clear_undo();
+ void undo_checkpoint();
};
Sudoku *sudoku = NULL;
@@ -217,7 +236,6 @@ int SudokuSound::frequencies[9] = {
short *SudokuSound::sample_data[9] = { 0 };
int SudokuSound::sample_size = 0;
-
// Initialize the SudokuSound class
SudokuSound::SudokuSound() {
sample_size = 0;
@@ -566,7 +584,7 @@ SudokuCell::handle(int event) {
else value(1);
} else value(sudoku->next_value(this));
}
-
+ // TODO: add this to the undo process
Fl::focus(this);
redraw();
return 1;
@@ -589,10 +607,12 @@ SudokuCell::handle(int event) {
} else {
set_hint(key);
}
+ sudoku->undo_checkpoint();
redraw();
} else {
value(key);
sudoku->clear_hints_for(row(), col(), key);
+ sudoku->undo_checkpoint();
do_callback();
}
return 1;
@@ -604,9 +624,11 @@ SudokuCell::handle(int event) {
}
if (Fl::event_state() & (FL_SHIFT | FL_CAPS_LOCK)) {
clear_hints();
+ sudoku->undo_checkpoint();
} else {
value(0);
do_callback();
+ sudoku->undo_checkpoint();
}
return 1;
}
@@ -621,35 +643,49 @@ SudokuCell::handle(int event) {
Fl_Help_Dialog *Sudoku::help_dialog_ = (Fl_Help_Dialog *)0;
Fl_Preferences Sudoku::prefs_(Fl_Preferences::USER_L, "fltk.org", "sudoku");
+static void undo_cb(Fl_Widget*, void*) {
+ sudoku->undo();
+}
+
+static void redo_cb(Fl_Widget*, void*) {
+ sudoku->redo();
+}
// Create a Sudoku game window...
Sudoku::Sudoku()
- : Fl_Double_Window(GROUP_SIZE * 3, GROUP_SIZE * 3 + MENU_OFFSET, "Sudoku")
+ : Fl_Double_Window(GROUP_SIZE * 3, GROUP_SIZE * 3 + MENU_OFFSET, "Sudoku"),
+ undo_head_(0), undo_tail_(0), redo_head_(0)
{
int j, k;
Fl_Group *g;
SudokuCell *cell;
static Fl_Menu_Item items[] = {
{ "&Game", 0, 0, 0, FL_SUBMENU },
- { "&New Game", FL_COMMAND | 'n', new_cb, 0, FL_MENU_DIVIDER },
- { "&Check Game", FL_COMMAND | 'c', check_cb, 0, 0 },
- { "&Restart Game", FL_COMMAND | 'r', restart_cb, 0, 0 },
- { "&Solve Game", FL_COMMAND | 's', solve_cb, 0, FL_MENU_DIVIDER },
- { "&Update Helpers", 0, update_helpers_cb, 0, 0 },
- { "&Mute Sound", FL_COMMAND | 'm', mute_cb, 0, FL_MENU_TOGGLE | FL_MENU_DIVIDER },
+ { "&New Game", FL_COMMAND | 'n', new_cb, 0, FL_MENU_DIVIDER },
+ { "&Check Game", FL_COMMAND | 'c', check_cb, 0, 0 },
+ { "&Restart Game", FL_COMMAND | 'r', restart_cb, 0, 0 },
+ { "&Solve Game", FL_COMMAND | 's', solve_cb, 0, FL_MENU_DIVIDER },
+ { "&Update Helpers", 0, update_helpers_cb, 0, 0 },
+ { "&Mute Sound", FL_COMMAND | 'm', mute_cb, 0, FL_MENU_TOGGLE | FL_MENU_DIVIDER },
#ifndef USE_MACOS
- { "&Quit", FL_COMMAND | 'q', close_cb, 0, 0 },
+ { "&Quit", FL_COMMAND | 'q', close_cb, 0, 0 },
#endif
- { 0 },
+ { 0 },
+ { "&Edit", 0, 0, 0, FL_SUBMENU },
+ { "&Undo", FL_COMMAND | 'z', undo_cb },
+ { "&Redo", FL_COMMAND | 'Z', redo_cb },
+ { 0 },
{ "&Difficulty", 0, 0, 0, FL_SUBMENU },
- { "&Easy", 0, diff_cb, (void *)"0", FL_MENU_RADIO },
- { "&Medium", 0, diff_cb, (void *)"1", FL_MENU_RADIO },
- { "&Hard", 0, diff_cb, (void *)"2", FL_MENU_RADIO },
- { "&Impossible", 0, diff_cb, (void *)"3", FL_MENU_RADIO },
- { 0 },
+ { "&Easy", 0, diff_cb, (void *)"0", FL_MENU_RADIO },
+ { "&Medium", 0, diff_cb, (void *)"1", FL_MENU_RADIO },
+ { "&Hard", 0, diff_cb, (void *)"2", FL_MENU_RADIO },
+ { "&Impossible", 0, diff_cb, (void *)"3", FL_MENU_RADIO },
+ { 0 },
+#ifndef USE_MACOS
{ "&Help", 0, 0, 0, FL_SUBMENU },
- { "&About Sudoku", FL_F + 1, help_cb, 0, 0 },
- { 0 },
+ { "&About Sudoku", FL_F + 1, help_cb, 0, 0 },
+ { 0 },
+#endif
{ 0 }
};
@@ -670,6 +706,9 @@ Sudoku::Sudoku()
menubar_ = new Fl_Sys_Menu_Bar(0, 0, 3 * GROUP_SIZE, 25);
menubar_->menu(items);
+#ifdef USE_MACOS
+ menubar_->about(help_cb, NULL);
+#endif
// Create the grids...
grid_ = new Fl_Group(0, MENU_OFFSET, 3 * GROUP_SIZE, 3 * GROUP_SIZE);
@@ -728,6 +767,7 @@ Sudoku::Sudoku()
}
set_title();
+ clear_undo();
}
@@ -843,7 +883,7 @@ Sudoku::diff_cb(Fl_Widget *widget, void *d) {
if (diff != sudoku->difficulty_) {
sudoku->difficulty_ = diff;
- sudoku->new_game(sudoku->seed_);
+ sudoku->new_cb(widget, NULL);
sudoku->set_title();
if (diff > 1)
@@ -921,6 +961,7 @@ Sudoku::update_helpers() {
dst_cell->set_hint(m);
}
}
+ undo_checkpoint();
}
void Sudoku::clear_hints_for(int row, int col, int val) {
@@ -1044,6 +1085,7 @@ Sudoku::load_game() {
// create a new game automatically...
if (solved || !grid_values_[0][0]) new_game(time(NULL));
else check_game(false);
+ clear_undo();
}
@@ -1127,10 +1169,10 @@ Sudoku::new_game(time_t seed) {
for (j = 0; j < 9; j ++)
for (k = 0; k < 9; k ++) {
cell = grid_cells_[j][k];
-
cell->value(0);
cell->readonly(0);
cell->color(FL_LIGHT3);
+ cell->clear_hints();
}
// Show N cells...
@@ -1166,6 +1208,7 @@ Sudoku::new_game(time_t seed) {
}
}
}
+ clear_undo();
}
@@ -1236,7 +1279,7 @@ Sudoku::restart_cb(Fl_Widget *widget, void *) {
for (int j = 0; j < 9; j ++)
for (int k = 0; k < 9; k ++) {
SudokuCell *cell = sudoku->grid_cells_[j][k];
-
+ cell->clear_hints();
if (!cell->readonly()) {
solved = false;
int v = cell->value();
@@ -1246,7 +1289,10 @@ Sudoku::restart_cb(Fl_Widget *widget, void *) {
}
}
- if (solved) sudoku->new_game(sudoku->seed_);
+ if (solved)
+ sudoku->new_game(sudoku->seed_);
+ else
+ sudoku->clear_undo();
}
@@ -1275,6 +1321,58 @@ Sudoku::save_game() {
}
}
+void Sudoku::save_state(GameState &s) {
+ for (int j = 0; j < 9; j ++) {
+ for (int k = 0; k < 9; k ++) {
+ s[j*9+k] = grid_cells_[j][k]->state();
+ }
+ }
+}
+
+void Sudoku::load_state(GameState &s) {
+ for (int j = 0; j < 9; j ++) {
+ for (int k = 0; k < 9; k ++) {
+ grid_cells_[j][k]->state(s[j*9+k]);
+ }
+ }
+}
+
+void Sudoku::undo() {
+ if (undo_head_ != undo_tail_) {
+ undo_head_ = ((undo_head_-1) & 63);
+ load_state(undo_stack[undo_head_]);
+ redraw();
+ } else {
+ fl_beep(FL_BEEP_ERROR);
+ }
+}
+
+void Sudoku::redo() {
+ if (undo_head_ != redo_head_) {
+ undo_head_ = ((undo_head_+1) & 63);
+ load_state(undo_stack[undo_head_]);
+ redraw();
+ } else {
+ fl_beep(FL_BEEP_ERROR);
+ }
+}
+
+void Sudoku::clear_undo() {
+ undo_head_ = undo_tail_ = redo_head_ = 0;
+ save_state(undo_stack[undo_head_]);
+}
+
+void Sudoku::undo_checkpoint() {
+ if (undo_head_ == redo_head_) {
+ redo_head_ = ((redo_head_+1) & 63);
+ }
+ undo_head_ = ((undo_head_+1) & 63);
+ if (undo_head_ == undo_tail_) {
+ undo_tail_ = ((undo_tail_+1) & 63);
+ }
+ save_state(undo_stack[undo_head_]);
+}
+
// Set title of window...
void
@@ -1313,6 +1411,7 @@ Sudoku::solve_game() {
if (sound_) sound_->play('A' + grid_cells_[j][8]->value() - 1);
}
+ undo_checkpoint();
}