chiark / gitweb /
Fix completion checking in Killer Solo.
[sgt-puzzles.git] / undead.c
index dab3f504e6abca4a303916295d3d72ee9d46e654..ad0ab79d74613c741d3e40b54e4340e2eb816a72 100644 (file)
--- a/undead.c
+++ b/undead.c
@@ -49,12 +49,13 @@ enum {
     COL_GHOST,
     COL_ZOMBIE,
     COL_VAMPIRE,
+    COL_DONE,
     NCOLOURS
 };
 
-#define DIFFLIST(A) \
-    A(EASY,Easy,e) \
-    A(NORMAL,Normal,n) \
+#define DIFFLIST(A)                             \
+    A(EASY,Easy,e)                              \
+    A(NORMAL,Normal,n)                          \
     A(TRICKY,Tricky,t)
 #define ENUM(upper,title,lower) DIFF_ ## upper,
 #define TITLE(upper,title,lower) #title,
@@ -113,7 +114,8 @@ static void free_params(game_params *params) {
     sfree(params);
 }
 
-static game_params *dup_params(game_params *params) {
+static game_params *dup_params(const game_params *params)
+{
     game_params *ret = snew(game_params);
     *ret = *params;            /* structure copy */
     return ret;
@@ -142,7 +144,8 @@ static void decode_params(game_params *params, char const *string) {
     return;
 }
 
-static char *encode_params(game_params *params, int full) {
+static char *encode_params(const game_params *params, int full)
+{
     char buf[256];
     sprintf(buf, "%dx%d", params->w, params->h);
     if (full)
@@ -150,7 +153,8 @@ static char *encode_params(game_params *params, int full) {
     return dupstr(buf);
 }
 
-static config_item *game_configure(game_params *params) {
+static config_item *game_configure(const game_params *params)
+{
     config_item *ret;
     char buf[64];
 
@@ -181,7 +185,8 @@ static config_item *game_configure(game_params *params) {
     return ret;
 }
 
-static game_params *custom_params(config_item *cfg) {
+static game_params *custom_params(const config_item *cfg)
+{
     game_params *ret = snew(game_params);
 
     ret->w = atoi(cfg[0].sval);
@@ -190,7 +195,8 @@ static game_params *custom_params(config_item *cfg) {
     return ret;
 }
 
-static char *validate_params(game_params *params, int full) {
+static char *validate_params(const game_params *params, int full)
+{
     if ((params->w * params->h ) > 54)  return "Grid is too big";
     if (params->w < 3)                  return "Width must be at least 3";
     if (params->h < 3)                  return "Height must be at least 3";
@@ -232,12 +238,13 @@ struct game_state {
     unsigned char *pencils;
     unsigned char *cell_errors;
     unsigned char *hint_errors;
+    unsigned char *hints_done;
     unsigned char count_errors[3];
     int solved;
     int cheated;
 };
 
-static game_state *new_state(game_params *params) {
+static game_state *new_state(const game_params *params) {
     int i;
     game_state *state = snew(game_state);
     state->common = snew(struct game_common);
@@ -284,6 +291,9 @@ static game_state *new_state(game_params *params) {
     state->hint_errors = snewn(2*state->common->num_paths, unsigned char);
     for (i=0;i<2*state->common->num_paths;i++)
         state->hint_errors[i] = FALSE;
+    state->hints_done = snewn(2 * state->common->num_paths, unsigned char);
+    memset(state->hints_done, 0,
+           2 * state->common->num_paths * sizeof(unsigned char));
     for (i=0;i<3;i++)
         state->count_errors[i] = FALSE;
 
@@ -293,7 +303,8 @@ static game_state *new_state(game_params *params) {
     return state;
 }
 
-static game_state *dup_game(game_state *state) {
+static game_state *dup_game(const game_state *state)
+{
     game_state *ret = snew(game_state);
 
     ret->common = state->common;
@@ -326,6 +337,13 @@ static game_state *dup_game(game_state *state) {
     }
     else ret->hint_errors = NULL;
 
+    if (state->hints_done != NULL) {
+        ret->hints_done = snewn(2 * state->common->num_paths, unsigned char);
+        memcpy(ret->hints_done, state->hints_done,
+               2 * state->common->num_paths * sizeof(unsigned char));
+    }
+    else ret->hints_done = NULL;
+
     ret->count_errors[0] = state->count_errors[0];
     ret->count_errors[1] = state->count_errors[1];
     ret->count_errors[2] = state->count_errors[2];
@@ -352,6 +370,7 @@ static void free_game(game_state *state) {
         if (state->common->fixed != NULL) sfree(state->common->fixed);
         sfree(state->common);
     }
+    if (state->hints_done != NULL) sfree(state->hints_done);
     if (state->hint_errors != NULL) sfree(state->hint_errors);
     if (state->cell_errors != NULL) sfree(state->cell_errors);
     if (state->pencils != NULL) sfree(state->pencils);
@@ -576,8 +595,9 @@ int next_list(struct guess *g, int pos) {
 
 void get_unique(game_state *state, int counter, random_state *rs) {
 
-    int p,i,c,count_uniques;
+    int p,i,c,pathlimit,count_uniques;
     struct guess path_guess;
+    int *view_count;
     
     struct entry {
         struct entry *link;
@@ -589,10 +609,10 @@ void get_unique(game_state *state, int counter, random_state *rs) {
     struct {
         struct entry *head;
         struct entry *node;
-    } views, single_views, loop_views, test_views;
+    } views, single_views, test_views;
 
     struct entry test_entry;
-    
+
     path_guess.length = state->common->paths[counter].num_monsters;
     path_guess.guess = snewn(path_guess.length,int);
     path_guess.possible = snewn(path_guess.length,int);
@@ -603,32 +623,29 @@ void get_unique(game_state *state, int counter, random_state *rs) {
         path_guess.possible[p] =
             state->guess[state->common->paths[counter].mapping[p]];
         switch (path_guess.possible[p]) {
-            case 1: path_guess.guess[p] = 1; break;
-            case 2: path_guess.guess[p] = 2; break;
-            case 3: path_guess.guess[p] = 1; break;
-            case 4: path_guess.guess[p] = 4; break;
-            case 5: path_guess.guess[p] = 1; break;
-            case 6: path_guess.guess[p] = 2; break;
-            case 7: path_guess.guess[p] = 1; break;
+          case 1: path_guess.guess[p] = 1; break;
+          case 2: path_guess.guess[p] = 2; break;
+          case 3: path_guess.guess[p] = 1; break;
+          case 4: path_guess.guess[p] = 4; break;
+          case 5: path_guess.guess[p] = 1; break;
+          case 6: path_guess.guess[p] = 2; break;
+          case 7: path_guess.guess[p] = 1; break;
         }
     }
 
     views.head = NULL;
     views.node = NULL;
+
+    pathlimit = state->common->paths[counter].length + 1;
+    view_count = snewn(pathlimit*pathlimit, int);
+    for (i = 0; i < pathlimit*pathlimit; i++)
+        view_count[i] = 0;
     
     do {
-        int mirror;
+        int mirror, start_view, end_view;
         
-        views.node = snewn(1,struct entry);
-        views.node->link = views.head;
-        views.node->guess = snewn(path_guess.length,int);
-        views.head = views.node;
-        views.node->start_view = 0;
-        views.node->end_view = 0;
-        memcpy(views.node->guess, path_guess.guess,
-               path_guess.length*sizeof(int));
-
         mirror = FALSE;
+        start_view = 0;
         for (p=0;p<state->common->paths[counter].length;p++) {
             if (state->common->paths[counter].p[p] == -1) mirror = TRUE;
             else {
@@ -636,17 +653,18 @@ void get_unique(game_state *state, int counter, random_state *rs) {
                     if (state->common->paths[counter].p[p] ==
                         state->common->paths[counter].mapping[i]) {
                         if (path_guess.guess[i] == 1 && mirror == TRUE)
-                            views.node->start_view++;
+                            start_view++;
                         if (path_guess.guess[i] == 2 && mirror == FALSE)
-                            views.node->start_view++;
+                            start_view++;
                         if (path_guess.guess[i] == 4)
-                            views.node->start_view++;
+                            start_view++;
                         break;
                     }
                 }
             }
         }
         mirror = FALSE;
+        end_view = 0;
         for (p=state->common->paths[counter].length-1;p>=0;p--) {
             if (state->common->paths[counter].p[p] == -1) mirror = TRUE;
             else {
@@ -654,16 +672,31 @@ void get_unique(game_state *state, int counter, random_state *rs) {
                     if (state->common->paths[counter].p[p] ==
                         state->common->paths[counter].mapping[i]) {
                         if (path_guess.guess[i] == 1 && mirror == TRUE)
-                            views.node->end_view++;
+                            end_view++;
                         if (path_guess.guess[i] == 2 && mirror == FALSE)
-                            views.node->end_view++;
+                            end_view++;
                         if (path_guess.guess[i] == 4)
-                            views.node->end_view++;
+                            end_view++;
                         break;
                     }
                 }
             }
         }
+
+        assert(start_view >= 0 && start_view < pathlimit);
+        assert(end_view >= 0 && end_view < pathlimit);
+        i = start_view * pathlimit + end_view;
+        view_count[i]++;
+        if (view_count[i] == 1) {
+            views.node = snewn(1,struct entry);
+            views.node->link = views.head;
+            views.node->guess = snewn(path_guess.length,int);
+            views.head = views.node;
+            views.node->start_view = start_view;
+            views.node->end_view = end_view;
+            memcpy(views.node->guess, path_guess.guess,
+                   path_guess.length*sizeof(int));
+        }
     } while (next_list(&path_guess, path_guess.length-1));
 
     /*  extract single entries from view list */
@@ -680,17 +713,8 @@ void get_unique(game_state *state, int counter, random_state *rs) {
     while (test_views.head != NULL) {
         test_views.node = test_views.head;
         test_views.head = test_views.head->link;
-        c = 0;
-        loop_views.head = views.head;
-        loop_views.node = views.node;
-        while (loop_views.head != NULL) {
-            loop_views.node = loop_views.head;
-            loop_views.head = loop_views.head->link;
-            if (test_views.node->start_view == loop_views.node->start_view &&
-                test_views.node->end_view == loop_views.node->end_view)
-                c++;
-        }
-        if (c == 1) {
+        i = test_views.node->start_view * pathlimit + test_views.node->end_view;
+        if (view_count[i] == 1) {
             single_views.node = snewn(1,struct entry);
             single_views.node->link = single_views.head;
             single_views.node->guess = snewn(path_guess.length,int);
@@ -703,6 +727,8 @@ void get_unique(game_state *state, int counter, random_state *rs) {
         }
     }
 
+    sfree(view_count);
+
     if (count_uniques > 0) {
         test_entry.start_view = 0;
         test_entry.end_view = 0;
@@ -841,13 +867,13 @@ int solve_iterative(game_state *state, struct path *paths) {
 
             for (i=0;i<paths[p].num_monsters;i++) {
                 switch (state->guess[paths[p].mapping[i]]) {
-                    case 1: loop.guess[i] = 1; break;
-                    case 2: loop.guess[i] = 2; break;
-                    case 3: loop.guess[i] = 1; break;
-                    case 4: loop.guess[i] = 4; break;
-                    case 5: loop.guess[i] = 1; break;
-                    case 6: loop.guess[i] = 2; break;
-                    case 7: loop.guess[i] = 1; break;
+                  case 1: loop.guess[i] = 1; break;
+                  case 2: loop.guess[i] = 2; break;
+                  case 3: loop.guess[i] = 1; break;
+                  case 4: loop.guess[i] = 4; break;
+                  case 5: loop.guess[i] = 1; break;
+                  case 6: loop.guess[i] = 2; break;
+                  case 7: loop.guess[i] = 1; break;
                 }
                 loop.possible[i] = state->guess[paths[p].mapping[i]];
                 possible[paths[p].mapping[i]] = 0;
@@ -900,13 +926,13 @@ int solve_bruteforce(game_state *state, struct path *paths) {
     for (i=0;i<state->common->num_total;i++) {
         loop.possible[i] = state->guess[i];
         switch (state->guess[i]) {
-            case 1: loop.guess[i] = 1; break;
-            case 2: loop.guess[i] = 2; break;
-            case 3: loop.guess[i] = 1; break;
-            case 4: loop.guess[i] = 4; break;
-            case 5: loop.guess[i] = 1; break;
-            case 6: loop.guess[i] = 2; break;
-            case 7: loop.guess[i] = 1; break;
+          case 1: loop.guess[i] = 1; break;
+          case 2: loop.guess[i] = 2; break;
+          case 3: loop.guess[i] = 1; break;
+          case 4: loop.guess[i] = 4; break;
+          case 5: loop.guess[i] = 1; break;
+          case 6: loop.guess[i] = 2; break;
+          case 7: loop.guess[i] = 1; break;
         }
     }
 
@@ -949,7 +975,7 @@ int path_cmp(const void *a, const void *b) {
     return pa->num_monsters - pb->num_monsters;
 }
 
-static char *new_game_desc(game_params *params, random_state *rs,
+static char *new_game_desc(const game_params *params, random_state *rs,
                            char **aux, int interactive) {
     int i,count,c,w,h,r,p,g;
     game_state *new;
@@ -981,23 +1007,23 @@ static char *new_game_desc(game_params *params, random_state *rs,
          * empty monster cells */
         count = 0;
         for (h=1;h<new->common->params.h+1;h++)
-        for (w=1;w<new->common->params.w+1;w++) {
-            c = random_upto(rs,5);
-            if (c >= 2) {
-                new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY;
-                new->common->xinfo[w+h*(new->common->params.w+2)] = count++;
-            }
-            else if (c == 0) {
-                new->common->grid[w+h*(new->common->params.w+2)] = 
-                    CELL_MIRROR_L;
-                new->common->xinfo[w+h*(new->common->params.w+2)] = -1;
-            }
-            else {
-                new->common->grid[w+h*(new->common->params.w+2)] =
-                    CELL_MIRROR_R;
-                new->common->xinfo[w+h*(new->common->params.w+2)] = -1;         
+            for (w=1;w<new->common->params.w+1;w++) {
+                c = random_upto(rs,5);
+                if (c >= 2) {
+                    new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY;
+                    new->common->xinfo[w+h*(new->common->params.w+2)] = count++;
+                }
+                else if (c == 0) {
+                    new->common->grid[w+h*(new->common->params.w+2)] = 
+                        CELL_MIRROR_L;
+                    new->common->xinfo[w+h*(new->common->params.w+2)] = -1;
+                }
+                else {
+                    new->common->grid[w+h*(new->common->params.w+2)] =
+                        CELL_MIRROR_R;
+                    new->common->xinfo[w+h*(new->common->params.w+2)] = -1;         
+                }
             }
-        }
         new->common->num_total = count; /* Total number of monsters in maze */
 
         /* Puzzle is boring if it has too few monster cells. Discard
@@ -1050,10 +1076,10 @@ static char *new_game_desc(game_params *params, random_state *rs,
         /* Grid is invalid if max. path length > threshold. Discard
          * grid, make new one */
         switch (new->common->params.diff) {
-            case DIFF_EASY:     max_length = min(new->common->params.w,new->common->params.h) + 1; break;
-            case DIFF_NORMAL:   max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break;
-            case DIFF_TRICKY:   max_length = 9; break;
-            default:            max_length = 9; break;
+          case DIFF_EASY:     max_length = min(new->common->params.w,new->common->params.h) + 1; break;
+          case DIFF_NORMAL:   max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break;
+          case DIFF_TRICKY:   max_length = 9; break;
+          default:            max_length = 9; break;
         }
 
         for (p=0;p<new->common->num_paths;p++) {
@@ -1077,10 +1103,10 @@ static char *new_game_desc(game_params *params, random_state *rs,
             even less paths with unique solutions */
 
         switch (new->common->params.diff) {
-            case DIFF_EASY:   filling = 2; break;
-            case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
-            case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
-            default:          filling = 0; break;
+          case DIFF_EASY:   filling = 2; break;
+          case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
+          case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
+          default:          filling = 0; break;
         }
 
         count = 0;
@@ -1125,14 +1151,14 @@ static char *new_game_desc(game_params *params, random_state *rs,
         }
 
         for (w=1;w<new->common->params.w+1;w++)
-        for (h=1;h<new->common->params.h+1;h++) {
-            c = new->common->xinfo[w+h*(new->common->params.w+2)];
-            if (c >= 0) {
-                if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST;
-                if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE;
-                if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE;                 
+            for (h=1;h<new->common->params.h+1;h++) {
+                c = new->common->xinfo[w+h*(new->common->params.w+2)];
+                if (c >= 0) {
+                    if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST;
+                    if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE;
+                    if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE;                 
+                }
             }
-        }
 
         /* Prepare path information needed by the solver (containing all hints) */  
         for (p=0;p<new->common->num_paths;p++) {
@@ -1249,26 +1275,26 @@ static char *new_game_desc(game_params *params, random_state *rs,
     /* Encode grid */
     count = 0;
     for (y=1;y<new->common->params.h+1;y++)
-    for (x=1;x<new->common->params.w+1;x++) {
-        c = new->common->grid[x+y*(new->common->params.w+2)];
-        if (count > 25) {
-            *e++ = 'z';
-            count -= 26;
-        }
-        if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) {
-            count++;
-        }
-        else if (c == CELL_MIRROR_L) {
-            if (count > 0) *e++ = (count-1 + 'a');
-            *e++ = 'L';
-            count = 0;
-        }
-        else {
-            if (count > 0) *e++ = (count-1 + 'a');
-            *e++ = 'R';
-            count = 0;          
+        for (x=1;x<new->common->params.w+1;x++) {
+            c = new->common->grid[x+y*(new->common->params.w+2)];
+            if (count > 25) {
+                *e++ = 'z';
+                count -= 26;
+            }
+            if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) {
+                count++;
+            }
+            else if (c == CELL_MIRROR_L) {
+                if (count > 0) *e++ = (count-1 + 'a');
+                *e++ = 'L';
+                count = 0;
+            }
+            else {
+                if (count > 0) *e++ = (count-1 + 'a');
+                *e++ = 'R';
+                count = 0;          
+            }
         }
-    }
     if (count > 0) *e++ = (count-1 + 'a');
 
     /* Encode hints */
@@ -1292,7 +1318,9 @@ void num2grid(int num, int width, int height, int *x, int *y) {
     return;
 }
 
-static game_state *new_game(midend *me, game_params *params, char *desc) {
+static game_state *new_game(midend *me, const game_params *params,
+                            const char *desc)
+{
     int i;
     int n;
     int count;
@@ -1412,14 +1440,15 @@ static game_state *new_game(midend *me, game_params *params, char *desc) {
     return state;
 }
 
-static char *validate_desc(game_params *params, char *desc) {
+static char *validate_desc(const game_params *params, const char *desc)
+{
     int i;
     int w = params->w, h = params->h;
     int wh = w*h;
     int area;
     int monsters;
     int monster_count;
-    char *desc_s = desc;
+    const char *desc_s = desc;
         
     for (i=0;i<3;i++) {
         if (!*desc) return "Faulty game description";
@@ -1463,7 +1492,9 @@ static char *validate_desc(game_params *params, char *desc) {
     return NULL;
 }
 
-static char *solve_game(game_state *state_start, game_state *currstate, char *aux, char **error) {
+static char *solve_game(const game_state *state_start, const game_state *currstate,
+                        const char *aux, char **error)
+{
     int p;
     int *old_guess;
     int iterative_depth;
@@ -1546,11 +1577,13 @@ static char *solve_game(game_state *state_start, game_state *currstate, char *au
     return move;
 }
 
-static int game_can_format_as_text_now(game_params *params) {
+static int game_can_format_as_text_now(const game_params *params)
+{
     return TRUE;
 }
 
-static char *game_text_format(game_state *state) {
+static char *game_text_format(const game_state *state)
+{
     int w,h,c,r,xi,g;
     char *ret;
     char buf[120];
@@ -1595,7 +1628,8 @@ struct game_ui {
     int ascii;
 };
 
-static game_ui *new_ui(game_state *state) {
+static game_ui *new_ui(const game_state *state)
+{
     game_ui *ui = snew(game_ui);
     ui->hx = ui->hy = 0;
     ui->hpencil = ui->hshow = ui->hcursor = 0;
@@ -1608,15 +1642,19 @@ static void free_ui(game_ui *ui) {
     return;
 }
 
-static char *encode_ui(game_ui *ui) {
+static char *encode_ui(const game_ui *ui)
+{
     return NULL;
 }
 
-static void decode_ui(game_ui *ui, char *encoding) {
+static void decode_ui(game_ui *ui, const char *encoding)
+{
     return;
 }
 
-static void game_changed_state(game_ui *ui, game_state *oldstate, game_state *newstate) {
+static void game_changed_state(game_ui *ui, const game_state *oldstate,
+                               const game_state *newstate)
+{
     /* See solo.c; if we were pencil-mode highlighting and
      * somehow a square has just been properly filled, cancel
      * pencil mode. */
@@ -1637,17 +1675,46 @@ struct game_drawstate {
     unsigned char count_errors[3];
     unsigned char *cell_errors;
     unsigned char *hint_errors;
+    unsigned char *hints_done;
 
     int hx, hy, hshow, hpencil; /* as for game_ui. */
     int hflash;
     int ascii;
 };
 
+static int is_clue(const game_state *state, int x, int y)
+{
+    int h = state->common->params.h, w = state->common->params.w;
+
+    if (((x == 0 || x == w + 1) && y > 0 && y <= h) ||
+        ((y == 0 || y == h + 1) && x > 0 && x <= w))
+        return TRUE;
+
+    return FALSE;
+}
+
+static int clue_index(const game_state *state, int x, int y)
+{
+    int h = state->common->params.h, w = state->common->params.w;
+
+    if (y == 0)
+        return x - 1;
+    else if (x == w + 1)
+        return w + y - 1;
+    else if (y == h + 1)
+        return 2 * w + h - x;
+    else if (x == 0)
+        return 2 * (w + h) - y;
+
+    return -1;
+}
+
 #define TILESIZE (ds->tilesize)
-#define BORDER (TILESIZE/2)
+#define BORDER (TILESIZE/4)
 
-static char *interpret_move(game_state *state, game_ui *ui,
-                            const game_drawstate *ds, int x, int y, int button)
+static char *interpret_move(const game_state *state, game_ui *ui,
+                            const game_drawstate *ds,
+                            int x, int y, int button)
 {
     int gx,gy;
     int g,xi;
@@ -1660,6 +1727,10 @@ static char *interpret_move(game_state *state, game_ui *ui,
         ui->ascii = !ui->ascii;
         return "";      
     }
+
+    if (button == 'm' || button == 'M') {
+        return dupstr("M");
+    }
     
     if (ui->hshow == 1 && ui->hpencil == 0) {
         xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
@@ -1694,11 +1765,11 @@ static char *interpret_move(game_state *state, game_ui *ui,
             ui->hy = 1;
         }
         else switch (button) {
-            case CURSOR_UP:     ui->hy -= (ui->hy > 1)     ? 1 : 0; break;
-            case CURSOR_DOWN:   ui->hy += (ui->hy < ds->h) ? 1 : 0; break;
-            case CURSOR_RIGHT:  ui->hx += (ui->hx < ds->w) ? 1 : 0; break;
-            case CURSOR_LEFT:   ui->hx -= (ui->hx > 1)     ? 1 : 0; break;
-        }
+              case CURSOR_UP:     ui->hy -= (ui->hy > 1)     ? 1 : 0; break;
+              case CURSOR_DOWN:   ui->hy += (ui->hy < ds->h) ? 1 : 0; break;
+              case CURSOR_RIGHT:  ui->hx += (ui->hx < ds->w) ? 1 : 0; break;
+              case CURSOR_LEFT:   ui->hx -= (ui->hx > 1)     ? 1 : 0; break;
+            }
         ui->hshow = ui->hcursor = 1;
         return "";
     }
@@ -1713,24 +1784,23 @@ static char *interpret_move(game_state *state, game_ui *ui,
         if (xi >= 0 && !state->common->fixed[xi]) {
             if (button == 'g' || button == 'G' || button == '1') {
                 sprintf(buf,"g%d",xi);
-                                ui->hpencil = ui->hshow = 0;
+                if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
                 return dupstr(buf);
             }
             if (button == 'v' || button == 'V' || button == '2') {
                 sprintf(buf,"v%d",xi);
-                                ui->hpencil = ui->hshow = 0;
+                if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
                 return dupstr(buf);
             }
             if (button == 'z' || button == 'Z' || button == '3') {
                 sprintf(buf,"z%d",xi);
-                                ui->hpencil = ui->hshow = 0;
+                if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
                 return dupstr(buf);
             }
             if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
                 button == '0' || button == '\b') {
-                if (!ui->hcursor) ui->hshow = 0;
                 sprintf(buf,"E%d",xi);
-                                ui->hpencil = ui->hshow = 0;
+                if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
                 return dupstr(buf);
             }
         }       
@@ -1793,6 +1863,11 @@ static char *interpret_move(game_state *state, game_ui *ui,
                 }
             }
         }
+    } else if (button == LEFT_BUTTON) {
+        if (is_clue(state, gx, gy)) {
+            sprintf(buf, "D%d,%d", gx, gy);
+            return dupstr(buf);
+        }
     }
 
     return NULL;
@@ -1819,36 +1894,36 @@ int check_numbers_draw(game_state *state, int *guess) {
         valid = FALSE; 
         state->count_errors[0] = TRUE; 
         for (x=1;x<state->common->params.w+1;x++)
-        for (y=1;y<state->common->params.h+1;y++) {
-            xy = x+y*(state->common->params.w+2);
-            if (state->common->xinfo[xy] >= 0 &&
-                guess[state->common->xinfo[xy]] == 1)
-                state->cell_errors[xy] = TRUE;
-        }
+            for (y=1;y<state->common->params.h+1;y++) {
+                xy = x+y*(state->common->params.w+2);
+                if (state->common->xinfo[xy] >= 0 &&
+                    guess[state->common->xinfo[xy]] == 1)
+                    state->cell_errors[xy] = TRUE;
+            }
     }
     if (count_vampires > state->common->num_vampires ||
         (filled && count_vampires != state->common->num_vampires) ) {
         valid = FALSE; 
         state->count_errors[1] = TRUE; 
         for (x=1;x<state->common->params.w+1;x++)
-        for (y=1;y<state->common->params.h+1;y++) {
-            xy = x+y*(state->common->params.w+2);
-            if (state->common->xinfo[xy] >= 0 &&
-                guess[state->common->xinfo[xy]] == 2)
-                state->cell_errors[xy] = TRUE;
-        }
+            for (y=1;y<state->common->params.h+1;y++) {
+                xy = x+y*(state->common->params.w+2);
+                if (state->common->xinfo[xy] >= 0 &&
+                    guess[state->common->xinfo[xy]] == 2)
+                    state->cell_errors[xy] = TRUE;
+            }
     }
     if (count_zombies > state->common->num_zombies ||
         (filled && count_zombies != state->common->num_zombies) )  {
         valid = FALSE; 
         state->count_errors[2] = TRUE; 
         for (x=1;x<state->common->params.w+1;x++)
-        for (y=1;y<state->common->params.h+1;y++) {
-            xy = x+y*(state->common->params.w+2);
-            if (state->common->xinfo[xy] >= 0 &&
-                guess[state->common->xinfo[xy]] == 4)
-                state->cell_errors[xy] = TRUE;
-        }
+            for (y=1;y<state->common->params.h+1;y++) {
+                xy = x+y*(state->common->params.w+2);
+                if (state->common->xinfo[xy] >= 0 &&
+                    guess[state->common->xinfo[xy]] == 4)
+                    state->cell_errors[xy] = TRUE;
+            }
     }
 
     return valid;
@@ -1880,7 +1955,9 @@ int check_path_solution(game_state *state, int p) {
         }
     }
 
-    if (unfilled == 0 && count != state->common->paths[p].sightings_start) {
+    if (count            > state->common->paths[p].sightings_start ||
+        count + unfilled < state->common->paths[p].sightings_start)
+    {
         correct = FALSE;
         state->hint_errors[state->common->paths[p].grid_start] = TRUE;
     }
@@ -1902,7 +1979,9 @@ int check_path_solution(game_state *state, int p) {
         }
     }
 
-    if (unfilled == 0 && count != state->common->paths[p].sightings_end) {
+    if (count            > state->common->paths[p].sightings_end ||
+        count + unfilled < state->common->paths[p].sightings_end)
+    {
         correct = FALSE;
         state->hint_errors[state->common->paths[p].grid_end] = TRUE;
     }
@@ -1915,8 +1994,9 @@ int check_path_solution(game_state *state, int p) {
     return correct;
 }
 
-static game_state *execute_move(game_state *state, char *move) {
-    int x,n,p,i;
+static game_state *execute_move(const game_state *state, const char *move)
+{
+    int x,y,n,p,i;
     char c;
     int correct; 
     int solver; 
@@ -1943,6 +2023,23 @@ static game_state *execute_move(game_state *state, char *move) {
             if (c == 'z') ret->pencils[x] ^= 4;
             move += n;
         }
+        if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 &&
+            is_clue(ret, x, y)) {
+            ret->hints_done[clue_index(ret, x, y)] ^= 1;
+            move += n + 1;
+        }
+        if (c == 'M') {
+            /*
+             * Fill in absolutely all pencil marks in unfilled
+             * squares, for those who like to play by the rigorous
+             * approach of starting off in that state and eliminating
+             * things.
+             */
+            for (i = 0; i < ret->common->wh; i++)
+                if (ret->guess[i] == 7)
+                    ret->pencils[i] = 7;
+            move++;
+        }
         if (*move == ';') move++;
     }
 
@@ -1973,22 +2070,29 @@ static game_state *execute_move(game_state *state, char *move) {
 
 #define PREFERRED_TILE_SIZE 64
 
-static void game_compute_size(game_params *params, int tilesize,
-                              int *x, int *y) {
-    *x = tilesize + (2 + params->w) * tilesize;
-    *y = tilesize + (3 + params->h) * tilesize;
+static void game_compute_size(const game_params *params, int tilesize,
+                              int *x, int *y)
+{
+    /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+    struct { int tilesize; } ads, *ds = &ads;
+    ads.tilesize = tilesize;
+
+    *x = 2*BORDER+(params->w+2)*TILESIZE;
+    *y = 2*BORDER+(params->h+3)*TILESIZE;
     return;
 }
 
 static void game_set_size(drawing *dr, game_drawstate *ds,
-                          game_params *params, int tilesize) {
+                          const game_params *params, int tilesize)
+{
     ds->tilesize = tilesize;
     return;
 }
 
 #define COLOUR(ret, i, r, g, b)     ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b)))
 
-static float *game_colours(frontend *fe, int *ncolours) {
+static float *game_colours(frontend *fe, int *ncolours)
+{
     float *ret = snewn(3 * NCOLOURS, float);
 
     frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
@@ -2025,11 +2129,16 @@ static float *game_colours(frontend *fe, int *ncolours) {
     ret[COL_VAMPIRE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
     ret[COL_VAMPIRE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
 
+    ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F;
+    ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F;
+    ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F;
+
     *ncolours = NCOLOURS;
     return ret;
 }
 
-static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) {
+static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
+{
     int i;
     struct game_drawstate *ds = snew(struct game_drawstate);
 
@@ -2056,6 +2165,9 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) {
     ds->hint_errors = snewn(2*state->common->num_paths,unsigned char);
     for (i=0;i<2*state->common->num_paths;i++)
         ds->hint_errors[i] = FALSE;
+    ds->hints_done = snewn(2 * state->common->num_paths, unsigned char);
+    memset(ds->hints_done, 0,
+           2 * state->common->num_paths * sizeof(unsigned char));
 
     ds->hshow = ds->hpencil = ds->hflash = 0;
     ds->hx = ds->hy = 0;
@@ -2063,6 +2175,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) {
 }
 
 static void game_free_drawstate(drawing *dr, game_drawstate *ds) {
+    sfree(ds->hints_done);
     sfree(ds->hint_errors);
     sfree(ds->cell_errors);
     sfree(ds->pencils);
@@ -2072,7 +2185,8 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds) {
 }
 
 static void draw_cell_background(drawing *dr, game_drawstate *ds,
-                                 game_state *state, game_ui *ui, int x, int y) {
+                                 const game_state *state, const game_ui *ui,
+                                 int x, int y) {
 
     int hon;
     int dx,dy;
@@ -2237,73 +2351,88 @@ static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y,
 }
 
 static void draw_monster_count(drawing *dr, game_drawstate *ds,
-                               game_state *state, int c, int hflash) {
-    int dx,dy,dh;
+                               const game_state *state, int c, int hflash) {
+    int dx,dy;
     char buf[8];
     char bufm[8];
     
-    dy = TILESIZE/2;
-    dh = TILESIZE;
+    dy = TILESIZE/4;
     dx = BORDER+(ds->w+2)*TILESIZE/2+TILESIZE/4;
     switch (c) {
-        case 0: 
-            sprintf(buf,"%d",state->common->num_ghosts);
-            sprintf(bufm,"G");
-            dx -= 3*TILESIZE/2;
-            break;
-        case 1: 
-            sprintf(buf,"%d",state->common->num_vampires); 
-            sprintf(bufm,"V");
-            break;
-        case 2: 
-            sprintf(buf,"%d",state->common->num_zombies); 
-            sprintf(bufm,"Z");
-            dx += 3*TILESIZE/2;
-            break;
-    }
-
+      case 0: 
+        sprintf(buf,"%d",state->common->num_ghosts);
+        sprintf(bufm,"G");
+        dx -= 3*TILESIZE/2;
+        break;
+      case 1: 
+        sprintf(buf,"%d",state->common->num_vampires); 
+        sprintf(bufm,"V");
+        break;
+      case 2: 
+        sprintf(buf,"%d",state->common->num_zombies); 
+        sprintf(bufm,"Z");
+        dx += 3*TILESIZE/2;
+        break;
+    }
+
+    draw_rect(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE,
+              COL_BACKGROUND);
     if (!ds->ascii) { 
-        draw_rect(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh,COL_BACKGROUND);
-        draw_monster(dr,ds,dx-TILESIZE/3,dh,2*TILESIZE/3,hflash,1<<c);
-        draw_text(dr,dx,dh,FONT_VARIABLE,dy-1,ALIGN_HLEFT|ALIGN_VCENTRE,
-                  (state->count_errors[c] ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT), buf);
-        draw_update(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh);
-    }
-    else {
-        draw_rect(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh,COL_BACKGROUND);
-        draw_text(dr,dx-TILESIZE/3,dh,FONT_VARIABLE,dy-1,
+        draw_monster(dr, ds, dx-TILESIZE/3, dy+TILESIZE/2,
+                     2*TILESIZE/3, hflash, 1<<c);
+    } else {
+        draw_text(dr, dx-TILESIZE/3,dy+TILESIZE/2,FONT_VARIABLE,TILESIZE/2,
                   ALIGN_HCENTRE|ALIGN_VCENTRE,
                   hflash ? COL_FLASH : COL_TEXT, bufm);
-        draw_text(dr,dx,dh,FONT_VARIABLE,dy-1,ALIGN_HLEFT|ALIGN_VCENTRE,
-                  (state->count_errors[c] ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT), buf);        
-        draw_update(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh);
     }
+    draw_text(dr, dx, dy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2,
+              ALIGN_HLEFT|ALIGN_VCENTRE,
+              (state->count_errors[c] ? COL_ERROR :
+               hflash ? COL_FLASH : COL_TEXT), buf);
+    draw_update(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE);
 
     return;
 }
 
-static void draw_path_hint(drawing *dr, game_drawstate *ds, game_state *state,
-                           int i, int hflash, int start) {
-    int dx,dy,x,y;
-    int p,error;
-    char buf[80];
-
-    p = start ? state->common->paths[i].grid_start : state->common->paths[i].grid_end;
-    range2grid(p,state->common->params.w,state->common->params.h,&x,&y);
-    error = ds->hint_errors[p];
-
-    dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
-    dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
-    sprintf(buf,"%d", start ? state->common->paths[i].sightings_start : state->common->paths[i].sightings_end);
-    draw_rect(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3,TILESIZE-3,COL_BACKGROUND);
-    draw_text(dr,dx,dy,FONT_FIXED,TILESIZE/2,ALIGN_HCENTRE|ALIGN_VCENTRE, error ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT,buf);
-    draw_update(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3,TILESIZE-3);
+static void draw_path_hint(drawing *dr, game_drawstate *ds,
+                           const struct game_params *params,
+                           int hint_index, int hflash, int hint) {
+    int x, y, color, dx, dy, text_dx, text_dy, text_size;
+    char buf[4];
+
+    if (ds->hint_errors[hint_index])
+        color = COL_ERROR;
+    else if (hflash)
+        color = COL_FLASH;
+    else if (ds->hints_done[hint_index])
+        color = COL_DONE;
+    else
+        color = COL_TEXT;
+
+    range2grid(hint_index, params->w, params->h, &x, &y);
+    /* Upper-left corner of the "tile" */
+    dx = BORDER + x * TILESIZE;
+    dy = BORDER + y * TILESIZE + TILESIZE;
+    /* Center of the "tile" */
+    text_dx = dx + TILESIZE / 2;
+    text_dy = dy +  TILESIZE / 2;
+    /* Avoid wiping out the borders of the puzzle */
+    dx += 2;
+    dy += 2;
+    text_size = TILESIZE - 3;
+
+    sprintf(buf,"%d", hint);
+    draw_rect(dr, dx, dy, text_size, text_size, COL_BACKGROUND);
+    draw_text(dr, text_dx, text_dy, FONT_FIXED, TILESIZE / 2,
+              ALIGN_HCENTRE | ALIGN_VCENTRE, color, buf);
+    draw_update(dr, dx, dy, text_size, text_size);
 
     return;
 }
 
-static void draw_mirror(drawing *dr, game_drawstate *ds, game_state *state,
-                        int x, int y, int hflash, int mirror) {
+static void draw_mirror(drawing *dr, game_drawstate *ds,
+                        const game_state *state, int x, int y,
+                        int hflash, int mirror) {
     int dx,dy,mx1,my1,mx2,my2;
     dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
     dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
@@ -2327,8 +2456,9 @@ static void draw_mirror(drawing *dr, game_drawstate *ds, game_state *state,
     return;
 }
 
-static void draw_big_monster(drawing *dr, game_drawstate *ds, game_state *state,
-                             int x, int y, int hflash, int monster)
+static void draw_big_monster(drawing *dr, game_drawstate *ds,
+                             const game_state *state, int x, int y,
+                             int hflash, int monster)
 {
     int dx,dy;
     char buf[10];
@@ -2350,40 +2480,41 @@ static void draw_big_monster(drawing *dr, game_drawstate *ds, game_state *state,
     return;
 }
 
-static void draw_pencils(drawing *dr, game_drawstate *ds, game_state *state,
-                         int x, int y, int pencil) {
+static void draw_pencils(drawing *dr, game_drawstate *ds,
+                         const game_state *state, int x, int y, int pencil)
+{
     int dx, dy;
-        int monsters[4];
-        int i, j, px, py;
+    int monsters[4];
+    int i, j, px, py;
     char buf[10];
     dx = BORDER+(x* ds->tilesize)+(TILESIZE/4);
     dy = BORDER+(y* ds->tilesize)+(TILESIZE/4)+TILESIZE;
 
-        for (i = 0, j = 1; j < 8; j *= 2)
-            if (pencil & j)
-                monsters[i++] = j;
-        while (i < 4)
-            monsters[i++] = 0;
-
-        for (py = 0; py < 2; py++)
-            for (px = 0; px < 2; px++)
-                if (monsters[py*2+px]) {
-                    if (!ds->ascii) {
-                        draw_monster(dr, ds,
-                                     dx + TILESIZE/2 * px, dy + TILESIZE/2 * py,
-                                     TILESIZE/2, 0, monsters[py*2+px]);
-                    }
-                    else {
-                        switch (monsters[py*2+px]) {
-                            case 1: sprintf(buf,"G"); break;
-                            case 2: sprintf(buf,"V"); break;
-                            case 4: sprintf(buf,"Z"); break;
-                        }
-                        draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py,
-                                  FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE,
-                                  COL_TEXT,buf);
+    for (i = 0, j = 1; j < 8; j *= 2)
+        if (pencil & j)
+            monsters[i++] = j;
+    while (i < 4)
+        monsters[i++] = 0;
+
+    for (py = 0; py < 2; py++)
+        for (px = 0; px < 2; px++)
+            if (monsters[py*2+px]) {
+                if (!ds->ascii) {
+                    draw_monster(dr, ds,
+                                 dx + TILESIZE/2 * px, dy + TILESIZE/2 * py,
+                                 TILESIZE/2, 0, monsters[py*2+px]);
+                }
+                else {
+                    switch (monsters[py*2+px]) {
+                      case 1: sprintf(buf,"G"); break;
+                      case 2: sprintf(buf,"V"); break;
+                      case 4: sprintf(buf,"Z"); break;
                     }
+                    draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py,
+                              FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE,
+                              COL_TEXT,buf);
                 }
+            }
     draw_update(dr,dx-(TILESIZE/4)+2,dy-(TILESIZE/4)+2,
                 (TILESIZE/2)-3,(TILESIZE/2)-3);
 
@@ -2392,9 +2523,31 @@ static void draw_pencils(drawing *dr, game_drawstate *ds, game_state *state,
 
 #define FLASH_TIME 0.7F
 
-static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
-            game_state *state, int dir, game_ui *ui,
-            float animtime, float flashtime) {
+static int is_hint_stale(const game_drawstate *ds, int hflash,
+                         const game_state *state, int index)
+{
+    int ret = FALSE;
+    if (!ds->started) ret = TRUE;
+    if (ds->hflash != hflash) ret = TRUE;
+
+    if (ds->hint_errors[index] != state->hint_errors[index]) {
+        ds->hint_errors[index] = state->hint_errors[index];
+        ret = TRUE;
+    }
+
+    if (ds->hints_done[index] != state->hints_done[index]) {
+        ds->hints_done[index] = state->hints_done[index];
+        ret = TRUE;
+    }
+
+    return ret;
+}
+
+static void game_redraw(drawing *dr, game_drawstate *ds,
+                        const game_state *oldstate, const game_state *state,
+                        int dir, const game_ui *ui,
+                        float animtime, float flashtime)
+{
     int i,j,x,y,xy;
     int stale, xi, c, hflash, hchanged, changed_ascii;
 
@@ -2411,8 +2564,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
                 draw_rect(dr, BORDER+(ds->tilesize*(i+1))+1,
                           BORDER+(ds->tilesize*(j+2))+1, ds->tilesize-1,
                           ds->tilesize-1, COL_BACKGROUND);
-        draw_update(dr,BORDER+TILESIZE-1,BORDER+2*TILESIZE-1,
-                    (ds->w)*TILESIZE+3, (ds->h)*TILESIZE+3);
+        draw_update(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE,
+                    2*BORDER+(ds->h+3)*TILESIZE);
     }
 
     hchanged = FALSE;
@@ -2423,7 +2576,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
     if (ds->ascii != ui->ascii) {
         ds->ascii = ui->ascii;
         changed_ascii = TRUE;
-    }
+    } else
+        changed_ascii = FALSE;
 
     /* Draw monster count hints */
 
@@ -2444,83 +2598,63 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
 
     /* Draw path count hints */
     for (i=0;i<state->common->num_paths;i++) {
-        int p;
-        stale = FALSE;
-
-        if (!ds->started) stale = TRUE;
-        if (ds->hflash != hflash) stale = TRUE;
+        struct path *path = &state->common->paths[i];
         
-        p = state->common->paths[i].grid_start;
-        if (ds->hint_errors[p] != state->hint_errors[p]) {
-            stale = TRUE;
-            ds->hint_errors[p] = state->hint_errors[p];
-        }
-
-        if (stale) {
-            draw_path_hint(dr, ds, state, i, hflash, TRUE);
+        if (is_hint_stale(ds, hflash, state, path->grid_start)) {
+            draw_path_hint(dr, ds, &state->common->params, path->grid_start,
+                           hflash, path->sightings_start);
         }
 
-        stale = FALSE;
-
-        if (!ds->started) stale = TRUE;
-        if (ds->hflash != hflash) stale = TRUE;
-
-        p = state->common->paths[i].grid_end;
-        if (ds->hint_errors[p] != state->hint_errors[p]) {
-            stale = TRUE;
-            ds->hint_errors[p] = state->hint_errors[p];
+        if (is_hint_stale(ds, hflash, state, path->grid_end)) {
+            draw_path_hint(dr, ds, &state->common->params, path->grid_end,
+                           hflash, path->sightings_end);
         }
-
-        if (stale) {
-            draw_path_hint(dr, ds, state, i, hflash, FALSE);
-        }
-
     }
 
     /* Draw puzzle grid contents */
     for (x = 1; x < ds->w+1; x++)
-    for (y = 1; y < ds->h+1; y++) {
-        stale = FALSE;
-        xy = x+y*(state->common->params.w+2);
-        xi = state->common->xinfo[xy];
-        c = state->common->grid[xy];
+        for (y = 1; y < ds->h+1; y++) {
+            stale = FALSE;
+            xy = x+y*(state->common->params.w+2);
+            xi = state->common->xinfo[xy];
+            c = state->common->grid[xy];
     
-        if (!ds->started) stale = TRUE;
-        if (ds->hflash != hflash) stale = TRUE;
-        if (changed_ascii) stale = TRUE;
+            if (!ds->started) stale = TRUE;
+            if (ds->hflash != hflash) stale = TRUE;
+            if (changed_ascii) stale = TRUE;
         
-        if (hchanged) {
-            if ((x == ui->hx && y == ui->hy) ||
-                (x == ds->hx && y == ds->hy))
-                stale = TRUE;
-        }
+            if (hchanged) {
+                if ((x == ui->hx && y == ui->hy) ||
+                    (x == ds->hx && y == ds->hy))
+                    stale = TRUE;
+            }
 
-        if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) {
-            stale = TRUE;
-            ds->monsters[xi] = state->guess[xi];
-        }
+            if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) {
+                stale = TRUE;
+                ds->monsters[xi] = state->guess[xi];
+            }
         
-        if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) {
-            stale = TRUE;
-            ds->pencils[xi] = state->pencils[xi];
-        }
+            if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) {
+                stale = TRUE;
+                ds->pencils[xi] = state->pencils[xi];
+            }
 
-        if (state->cell_errors[xy] != ds->cell_errors[xy]) {
-            stale = TRUE;
-            ds->cell_errors[xy] = state->cell_errors[xy];
-        }
+            if (state->cell_errors[xy] != ds->cell_errors[xy]) {
+                stale = TRUE;
+                ds->cell_errors[xy] = state->cell_errors[xy];
+            }
                 
-        if (stale) {
-            draw_cell_background(dr, ds, state, ui, x, y);
-            if (xi < 0) 
-                draw_mirror(dr, ds, state, x, y, hflash, c);
-            else if (state->guess[xi] == 1 || state->guess[xi] == 2 ||
-                     state->guess[xi] == 4)
-                draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]);
-            else 
-                draw_pencils(dr, ds, state, x, y, state->pencils[xi]);
+            if (stale) {
+                draw_cell_background(dr, ds, state, ui, x, y);
+                if (xi < 0) 
+                    draw_mirror(dr, ds, state, x, y, hflash, c);
+                else if (state->guess[xi] == 1 || state->guess[xi] == 2 ||
+                         state->guess[xi] == 4)
+                    draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]);
+                else 
+                    draw_pencils(dr, ds, state, x, y, state->pencils[xi]);
+            }
         }
-    }
 
     ds->hx = ui->hx; ds->hy = ui->hy;
     ds->hshow = ui->hshow;
@@ -2530,29 +2664,35 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
     return;
 }
 
-static float game_anim_length(game_state *oldstate, game_state *newstate,
-                              int dir, game_ui *ui) {
+static float game_anim_length(const game_state *oldstate,
+                              const game_state *newstate, int dir, game_ui *ui)
+{
     return 0.0F;
 }
 
-static float game_flash_length(game_state *oldstate, game_state *newstate,
-                               int dir, game_ui *ui) {
+static float game_flash_length(const game_state *oldstate,
+                               const game_state *newstate, int dir, game_ui *ui)
+{
     return (!oldstate->solved && newstate->solved && !oldstate->cheated &&
             !newstate->cheated) ? FLASH_TIME : 0.0F;
 }
 
-static int game_status(game_state *state) {
+static int game_status(const game_state *state)
+{
     return state->solved;
 }
 
-static int game_timing_state(game_state *state, game_ui *ui) {
+static int game_timing_state(const game_state *state, game_ui *ui)
+{
     return TRUE;
 }
 
-static void game_print_size(game_params *params, float *x, float *y) {
+static void game_print_size(const game_params *params, float *x, float *y)
+{
 }
 
-static void game_print(drawing *dr, game_state *state, int tilesize) {
+static void game_print(drawing *dr, const game_state *state, int tilesize)
+{
 }
 
 #ifdef COMBINED
@@ -2596,4 +2736,3 @@ const struct game thegame = {
     FALSE, game_timing_state,
     0,                     /* flags */
 };
-