More serialisation changes: the game_aux_info structure has now been
[sgt/puzzles] / guess.c
diff --git a/guess.c b/guess.c
index 3a53f4b..613bc59 100644 (file)
--- a/guess.c
+++ b/guess.c
@@ -60,11 +60,6 @@ static game_params *default_params(void)
     return ret;
 }
 
-static int game_fetch_preset(int i, char **name, game_params **params)
-{
-    return FALSE;
-}
-
 static void free_params(game_params *params)
 {
     sfree(params);
@@ -77,6 +72,32 @@ static game_params *dup_params(game_params *params)
     return ret;
 }
 
+static const struct {
+    char *name;
+    game_params params;
+} guess_presets[] = {
+    {"Standard", {6, 4, 10, FALSE, TRUE}},
+    {"Super", {8, 5, 12, FALSE, TRUE}},
+};
+
+
+static int game_fetch_preset(int i, char **name, game_params **params)
+{
+    if (i < 0 || i >= lenof(guess_presets))
+        return FALSE;
+
+    *name = dupstr(guess_presets[i].name);
+    /*
+     * get round annoying const issues
+     */
+    {
+        game_params tmp = guess_presets[i].params;
+        *params = dup_params(&tmp);
+    }
+
+    return TRUE;
+}
+
 static void decode_params(game_params *params, char const *string)
 {
     char const *p = string;
@@ -221,12 +242,9 @@ static pegrow new_pegrow(int npegs)
 
 static pegrow dup_pegrow(pegrow pegs)
 {
-    pegrow newpegs = snew(struct pegrow);
+    pegrow newpegs = new_pegrow(pegs->npegs);
 
-    newpegs->npegs = pegs->npegs;
-    newpegs->pegs = snewn(newpegs->npegs, int);
     memcpy(newpegs->pegs, pegs->pegs, newpegs->npegs * sizeof(int));
-    newpegs->feedback = snewn(newpegs->npegs, int);
     memcpy(newpegs->feedback, pegs->feedback, newpegs->npegs * sizeof(int));
 
     return newpegs;
@@ -246,7 +264,7 @@ static void free_pegrow(pegrow pegs)
 }
 
 static char *new_game_desc(game_params *params, random_state *rs,
-                          game_aux_info **aux, int interactive)
+                          char **aux, int interactive)
 {
     unsigned char *bmp = snewn(params->npegs, unsigned char);
     char *ret;
@@ -268,11 +286,6 @@ newcol:
     return ret;
 }
 
-static void game_free_aux_info(game_aux_info *aux)
-{
-    assert(!"Shouldn't happen");
-}
-
 static char *validate_desc(game_params *params, char *desc)
 {
     unsigned char *bmp;
@@ -325,6 +338,7 @@ static game_state *dup_game(game_state *state)
     int i;
 
     *ret = *state;
+
     ret->guesses = snewn(state->params.nguesses, pegrow);
     for (i = 0; i < state->params.nguesses; i++)
        ret->guesses[i] = dup_pegrow(state->guesses[i]);
@@ -345,12 +359,10 @@ static void free_game(game_state *state)
     sfree(state);
 }
 
-static game_state *solve_game(game_state *state, game_state *currstate,
-                              game_aux_info *aux, char **error)
+static char *solve_game(game_state *state, game_state *currstate,
+                       char *aux, char **error)
 {
-    game_state *ret = dup_game(currstate);
-    ret->solved = 1;
-    return ret;
+    return dupstr("S");
 }
 
 static char *game_text_format(game_state *state)
@@ -387,6 +399,15 @@ static void free_ui(game_ui *ui)
     sfree(ui);
 }
 
+char *encode_ui(game_ui *ui)
+{
+    return NULL;
+}
+
+void decode_ui(game_ui *ui, char *encoding)
+{
+}
+
 static void game_changed_state(game_ui *ui, game_state *oldstate,
                                game_state *newstate)
 {
@@ -395,6 +416,7 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
     /* just clear the row-in-progress when we have an undo/redo. */
     for (i = 0; i < ui->curr_pegs->npegs; i++)
        ui->curr_pegs->pegs[i] = 0;
+    ui->markable = FALSE;
 }
 
 #define PEGSZ   (ds->pegsz)
@@ -402,6 +424,9 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
 #define HINTSZ  (ds->hintsz)
 #define HINTOFF (ds->hintsz + ds->gapsz)
 
+#define GAP     (ds->gapsz)
+#define CGAP    (ds->gapsz / 2)
+
 #define PEGRAD  (ds->pegrad)
 #define HINTRAD (ds->hintrad)
 
@@ -423,7 +448,7 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
 #define HINT_OY         (GUESS_OY + (PEGSZ - HINTOFF - HINTSZ) / 2)
 #define HINT_X(g)       HINT_OX
 #define HINT_Y(g)       (HINT_OY + (g)*PEGOFF)
-#define HINT_W          (ds->hintw*HINTOFF)
+#define HINT_W          ((ds->hintw*HINTOFF) - GAP)
 #define HINT_H          GUESS_H
 
 #define SOLN_OX         GUESS_OX
@@ -437,8 +462,6 @@ struct game_drawstate {
     pegrow solution;    /* only displayed if state->solved */
     pegrow colours;     /* length ncolours, not npegs */
 
-    int *holds;
-
     int pegsz, hintsz, gapsz; /* peg size (diameter), etc. */
     int pegrad, hintrad;      /* radius of peg, hint */
     int border;
@@ -448,7 +471,6 @@ struct game_drawstate {
     int hintw;          /* no. of hint tiles we're wide per row */
     int w, h, started, solved;
 
-    int colour_cur, peg_cur, display_cur; /* as in game_ui. */
     int next_go;
 
     blitter *blit_peg;
@@ -463,8 +485,9 @@ static int is_markable(game_params *params, pegrow pegs)
     nrequired = params->allow_blank ? 1 : params->npegs;
 
     for (i = 0; i < params->npegs; i++) {
-        if (pegs->pegs[i] > 0) {
-            colcount->pegs[pegs->pegs[i]]++;
+        int c = pegs->pegs[i];
+        if (c > 0) {
+            colcount->pegs[c-1]++;
             nset++;
         }
     }
@@ -527,47 +550,52 @@ static int mark_pegs(pegrow guess, pegrow solution, int ncols)
     return nc_place;
 }
 
-static game_state *mark_move(game_state *from, game_ui *ui)
+static char *encode_move(game_state *from, game_ui *ui)
 {
-    int i, ncleared = 0, nc_place;
-    game_state *to = dup_game(from);
-
-    for (i = 0; i < to->solution->npegs; i++) {
-        to->guesses[from->next_go]->pegs[i] = ui->curr_pegs->pegs[i];
-    }
-    nc_place = mark_pegs(to->guesses[from->next_go], to->solution, to->params.ncolours);
-
-    if (nc_place == to->solution->npegs) {
-        to->solved = 1; /* win! */
-    } else {
-        to->next_go = from->next_go + 1;
-        if (to->next_go >= to->params.nguesses)
-            to->solved = 1; /* 'lose' so we show the pegs. */
+    char *buf, *p, *sep;
+    int len, i, solved;
+    pegrow tmppegs;
+
+    len = ui->curr_pegs->npegs * 20 + 2;
+    buf = snewn(len, char);
+    p = buf;
+    *p++ = 'G';
+    sep = "";
+    for (i = 0; i < ui->curr_pegs->npegs; i++) {
+       p += sprintf(p, "%s%d", sep, ui->curr_pegs->pegs[i]);
+       sep = ",";
     }
-
-    for (i = 0; i < to->solution->npegs; i++) {
-        if (!ui->holds[i] || to->solved) {
-            ui->curr_pegs->pegs[i] = 0;
-            ncleared++;
-        }
-        if (to->solved) ui->holds[i] = 0;
+    *p++ = '\0';
+    assert(p - buf <= len);
+    buf = sresize(buf, len, char);
+
+    tmppegs = dup_pegrow(ui->curr_pegs);
+    solved = mark_pegs(tmppegs, from->solution, from->params.ncolours);
+    solved = (solved == from->params.ncolours);
+    free_pegrow(tmppegs);
+
+    for (i = 0; i < from->solution->npegs; i++) {
+       if (!ui->holds[i] || solved) {
+           ui->curr_pegs->pegs[i] = 0;
+       }
+       if (solved) ui->holds[i] = 0;
     }
     ui->markable = is_markable(&from->params, ui->curr_pegs);
-    if (!ui->markable && ui->peg_cur == to->solution->npegs)
-        ui->peg_cur--;
+    if (!ui->markable && ui->peg_cur == from->solution->npegs)
+       ui->peg_cur--;
 
-    return to;
+    return buf;
 }
 
-static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
-                             int x, int y, int button)
+static char *interpret_move(game_state *from, game_ui *ui, game_drawstate *ds,
+                           int x, int y, int button)
 {
     int over_col = 0;           /* one-indexed */
     int over_guess = -1;        /* zero-indexed */
     int over_past_guess_y = -1; /* zero-indexed */
     int over_past_guess_x = -1; /* zero-indexed */
     int over_hint = 0;          /* zero or one */
-    game_state *ret = NULL;
+    char *ret = NULL;
 
     int guess_ox = GUESS_X(from->next_go, 0);
     int guess_oy = GUESS_Y(from->next_go, 0);
@@ -622,13 +650,13 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
             ui->drag_y = y;
             debug(("Start dragging, col = %d, (%d,%d)",
                    ui->drag_col, ui->drag_x, ui->drag_y));
-            ret = from;
+            ret = "";
         }
     } else if (button == LEFT_DRAG && ui->drag_col) {
         ui->drag_x = x;
         ui->drag_y = y;
         debug(("Keep dragging, (%d,%d)", ui->drag_x, ui->drag_y));
-        ret = from;
+        ret = "";
     } else if (button == LEFT_RELEASE && ui->drag_col) {
         if (over_guess > -1) {
             debug(("Dropping colour %d onto guess peg %d",
@@ -643,19 +671,20 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
         }
         ui->drag_col = 0;
         ui->drag_opeg = -1;
+        ui->display_cur = 0;
         debug(("Stop dragging."));
-        ret = from;
+        ret = "";
     } else if (button == RIGHT_BUTTON) {
         if (over_guess > -1) {
             /* we use ths feedback in the game_ui to signify
              * 'carry this peg to the next guess as well'. */
             ui->holds[over_guess] = 1 - ui->holds[over_guess];
-            ret = from;
+            ret = "";
         }
     } else if (button == LEFT_RELEASE && over_hint && ui->markable) {
         /* NB this won't trigger if on the end of a drag; that's on
          * purpose, in case you drop by mistake... */
-        ret = mark_move(from, ui);
+        ret = encode_move(from, ui);
     }
 
     /* keyboard input */
@@ -665,7 +694,7 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
             ui->colour_cur++;
         if (button == CURSOR_UP && ui->colour_cur > 0)
             ui->colour_cur--;
-        ret = from;
+        ret = "";
     } else if (button == CURSOR_LEFT || button == CURSOR_RIGHT) {
         int maxcur = from->params.npegs;
         if (ui->markable) maxcur++;
@@ -675,22 +704,65 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
             ui->peg_cur++;
         if (button == CURSOR_LEFT && ui->peg_cur > 0)
             ui->peg_cur--;
-        ret = from;
+        ret = "";
     } else if (button == CURSOR_SELECT || button == ' ' || button == '\r' ||
                button == '\n') {
+        ui->display_cur = 1;
         if (ui->peg_cur == from->params.npegs) {
-            ret = mark_move(from, ui);
+            ret = encode_move(from, ui);
         } else {
             set_peg(&from->params, ui, ui->peg_cur, ui->colour_cur+1);
-            ret = from;
+            ret = "";
         }
     } else if (button == 'H' || button == 'h') {
+        ui->display_cur = 1;
         ui->holds[ui->peg_cur] = 1 - ui->holds[ui->peg_cur];
-        ret = from;
+        ret = "";
     }
     return ret;
 }
 
+static game_state *execute_move(game_state *from, char *move)
+{
+    int i, nc_place;
+    game_state *ret;
+    char *p;
+
+    if (!strcmp(move, "S")) {
+       ret = dup_game(from);
+       ret->solved = 1;
+       return ret;
+    } else if (move[0] == 'G') {
+       p = move+1;
+
+       ret = dup_game(from);
+
+       for (i = 0; i < from->solution->npegs; i++) {
+           int val = atoi(p);
+           if (val <= 0 || val > from->params.ncolours) {
+               free_game(ret);
+               return NULL;
+           }
+           ret->guesses[from->next_go]->pegs[i] = atoi(p);
+           while (*p && isdigit((unsigned char)*p)) p++;
+           if (*p == ',') p++;
+       }
+
+       nc_place = mark_pegs(ret->guesses[from->next_go], ret->solution, ret->params.ncolours);
+
+       if (nc_place == ret->solution->npegs) {
+           ret->solved = 1; /* win! */
+       } else {
+           ret->next_go = from->next_go + 1;
+           if (ret->next_go >= ret->params.nguesses)
+               ret->solved = 1; /* 'lose' so we show the pegs. */
+       }
+
+       return ret;
+    } else
+       return NULL;
+}
+
 /* ----------------------------------------------------------------------
  * Drawing routines.
  */
@@ -765,7 +837,8 @@ static void game_size(game_params *params, game_drawstate *ds,
 
 static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
-    float *ret = snewn(3 * NCOLOURS, float);
+    float *ret = snewn(3 * NCOLOURS, float), max;
+    int i;
 
     frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
 
@@ -835,10 +908,6 @@ static float *game_colours(frontend *fe, game_state *state, int *ncolours)
     ret[COL_HOLD * 3 + 1] = 0.5F;
     ret[COL_HOLD * 3 + 2] = 0.5F;
 
-    ret[COL_EMPTY * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0 / 3.0;
-    ret[COL_EMPTY * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0 / 3.0;
-    ret[COL_EMPTY * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0 / 3.0;
-
     ret[COL_CORRECTPLACE*3 + 0] = 0.0F;
     ret[COL_CORRECTPLACE*3 + 1] = 0.0F;
     ret[COL_CORRECTPLACE*3 + 2] = 0.0F;
@@ -847,6 +916,25 @@ static float *game_colours(frontend *fe, game_state *state, int *ncolours)
     ret[COL_CORRECTCOLOUR*3 + 1] = 1.0F;
     ret[COL_CORRECTCOLOUR*3 + 2] = 1.0F;
 
+    /* We want to make sure we can distinguish COL_CORRECTCOLOUR
+     * (which we hard-code as white) from COL_BACKGROUND (which
+     * could default to white on some platforms).
+     * Code borrowed from fifteen.c. */
+    max = ret[COL_BACKGROUND*3];
+    for (i = 1; i < 3; i++)
+        if (ret[COL_BACKGROUND*3+i] > max)
+            max = ret[COL_BACKGROUND*3+i];
+    if (max * 1.2F > 1.0F) {
+        for (i = 0; i < 3; i++)
+            ret[COL_BACKGROUND*3+i] /= (max * 1.2F);
+    }
+
+    /* We also want to be able to tell the difference between BACKGROUND
+     * and EMPTY, for similar distinguishing-hint reasons. */
+    ret[COL_EMPTY * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0 / 3.0;
+    ret[COL_EMPTY * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0 / 3.0;
+    ret[COL_EMPTY * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0 / 3.0;
+
     *ncolours = NCOLOURS;
     return ret;
 }
@@ -871,9 +959,6 @@ static game_drawstate *game_new_drawstate(game_state *state)
 
     ds->hintw = (state->params.npegs+1)/2; /* must round up */
 
-    ds->holds = snewn(state->params.npegs, int);
-    memset(ds->holds, 0, state->params.npegs*sizeof(int));
-
     ds->blit_peg = NULL;
 
     return ds;
@@ -888,7 +973,6 @@ static void game_free_drawstate(game_drawstate *ds)
     free_pegrow(ds->solution);
     for (i = 0; i < ds->nguesses; i++)
         free_pegrow(ds->guesses[i]);
-    sfree(ds->holds);
     sfree(ds->guesses);
     sfree(ds);
 }
@@ -905,17 +989,25 @@ static void draw_peg(frontend *fe, game_drawstate *ds, int cx, int cy,
      * behind it.
      */
     if (!moving)
-       draw_rect(fe, cx-1, cy-1, PEGSZ+2, PEGSZ+2, COL_BACKGROUND);
+        draw_rect(fe, cx-CGAP, cy-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2,
+                  COL_BACKGROUND);
     if (PEGRAD > 0) {
         draw_circle(fe, cx+PEGRAD, cy+PEGRAD, PEGRAD, 1, COL_EMPTY + col);
         draw_circle(fe, cx+PEGRAD, cy+PEGRAD, PEGRAD, 0, COL_EMPTY + col);
     } else
         draw_rect(fe, cx, cy, PEGSZ, PEGSZ, COL_EMPTY + col);
-    draw_update(fe, cx, cy, PEGSZ, PEGSZ);
+    draw_update(fe, cx-CGAP, cy-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2);
+}
+
+static void draw_cursor(frontend *fe, game_drawstate *ds, int x, int y)
+{
+    draw_circle(fe, x+PEGRAD, y+PEGRAD, PEGRAD+CGAP, 0, COL_CURSOR);
+
+    draw_update(fe, x-CGAP, y-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2);
 }
 
 static void guess_redraw(frontend *fe, game_drawstate *ds, int guess,
-                         pegrow src, int force)
+                         pegrow src, int *holds, int cur_col, int force)
 {
     pegrow dest;
     int rowx, rowy, i, scol;
@@ -933,27 +1025,72 @@ static void guess_redraw(frontend *fe, game_drawstate *ds, int guess,
 
     for (i = 0; i < dest->npegs; i++) {
         scol = src ? src->pegs[i] : 0;
-        if ((dest->pegs[i] != scol) || force)
-           draw_peg(fe, ds, rowx + PEGOFF * i, rowy, FALSE, scol);
+        if (i == cur_col)
+            scol |= 0x1000;
+        if (holds && holds[i])
+            scol |= 0x2000;
+        if ((dest->pegs[i] != scol) || force) {
+           draw_peg(fe, ds, rowx + PEGOFF * i, rowy, FALSE, scol &~ 0x3000);
+            /*
+             * Hold marker.
+             */
+            draw_rect(fe, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2,
+                      PEGSZ, 2, (scol & 0x2000 ? COL_HOLD : COL_BACKGROUND));
+            draw_update(fe, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2,
+                        PEGSZ, 2);
+            if (scol & 0x1000)
+                draw_cursor(fe, ds, rowx + PEGOFF * i, rowy);
+        }
         dest->pegs[i] = scol;
     }
 }
 
 static void hint_redraw(frontend *fe, game_drawstate *ds, int guess,
-                        pegrow src, int force, int emptycol)
+                        pegrow src, int force, int cursor, int markable)
 {
     pegrow dest = ds->guesses[guess];
     int rowx, rowy, i, scol, col, hintlen;
+    int need_redraw;
+    int emptycol = (markable ? COL_FLASH : COL_EMPTY);
 
     if (src) assert(src->npegs == dest->npegs);
 
     hintlen = (dest->npegs + 1)/2;
 
+    /*
+     * Because of the possible presence of the cursor around this
+     * entire section, we redraw all or none of it but never part.
+     */
+    need_redraw = FALSE;
+
     for (i = 0; i < dest->npegs; i++) {
         scol = src ? src->feedback[i] : 0;
-        col = (scol == FEEDBACK_CORRECTPLACE) ? COL_CORRECTPLACE :
-              (scol == FEEDBACK_CORRECTCOLOUR) ? COL_CORRECTCOLOUR : emptycol;
+        if (i == 0 && cursor)
+            scol |= 0x1000;
+        if (i == 0 && markable)
+            scol |= 0x2000;
         if ((scol != dest->feedback[i]) || force) {
+            need_redraw = TRUE;
+        }
+        dest->feedback[i] = scol;
+    }
+
+    if (need_redraw) {
+        int hinth = HINTSZ + GAP + HINTSZ;
+        int hx,hy,hw,hh;
+
+        hx = HINT_X(guess)-GAP; hy = HINT_Y(guess)-GAP;
+        hw = HINT_W+GAP*2; hh = hinth+GAP*2;
+
+        /* erase a large background rectangle */
+        draw_rect(fe, hx, hy, hw, hh, COL_BACKGROUND);
+
+        for (i = 0; i < dest->npegs; i++) {
+            scol = src ? src->feedback[i] : 0;
+            col = ((scol == FEEDBACK_CORRECTPLACE) ? COL_CORRECTPLACE :
+                   (scol == FEEDBACK_CORRECTCOLOUR) ? COL_CORRECTCOLOUR :
+                   emptycol);
+
             rowx = HINT_X(guess);
             rowy = HINT_Y(guess);
             if (i < hintlen) {
@@ -962,38 +1099,24 @@ static void hint_redraw(frontend *fe, game_drawstate *ds, int guess,
                 rowx += HINTOFF * (i - hintlen);
                 rowy += HINTOFF;
             }
-           /* erase background for antialiasing platforms */
-           draw_rect(fe, rowx-1, rowy-1, HINTSZ+2, HINTSZ+2, COL_BACKGROUND);
             if (HINTRAD > 0) {
                 draw_circle(fe, rowx+HINTRAD, rowy+HINTRAD, HINTRAD, 1, col);
                 draw_circle(fe, rowx+HINTRAD, rowy+HINTRAD, HINTRAD, 0, col);
             } else {
                 draw_rect(fe, rowx, rowy, HINTSZ, HINTSZ, col);
             }
-            draw_update(fe, rowx, rowy, HINTSZ, HINTSZ);
         }
-        dest->feedback[i] = scol;
-    }
-}
-
-static void hold_redraw(frontend *fe, game_drawstate *ds, int guess, int *src, int force)
-{
-    int shold, col, ox, oy, i;
-
-    if (guess >= ds->nguesses)
-        return;
-
-    for (i = 0; i < ds->solution->npegs; i++) {
-        shold = src ? src[i] : 0;
-        col = shold ? COL_HOLD : COL_BACKGROUND;
-        if ((shold != ds->holds[i]) || force) {
-            ox = GUESS_X(guess, i);
-            oy = GUESS_Y(guess, i) + PEGSZ + ds->gapsz/2;
-
-            draw_rect(fe, ox, oy, PEGSZ, 2, col);
-            draw_update(fe, ox, oy, PEGSZ, 2);
+        if (cursor) {
+            int x1,y1,x2,y2;
+            x1 = hx + CGAP; y1 = hy + CGAP;
+            x2 = hx + hw - CGAP; y2 = hy + hh - CGAP;
+            draw_line(fe, x1, y1, x2, y1, COL_CURSOR);
+            draw_line(fe, x2, y1, x2, y2, COL_CURSOR);
+            draw_line(fe, x2, y2, x1, y2, COL_CURSOR);
+            draw_line(fe, x1, y2, x1, y1, COL_CURSOR);
         }
-        if (src) ds->holds[i] = shold;
+
+        draw_update(fe, hx, hy, hw, hh);
     }
 }
 
@@ -1005,22 +1128,11 @@ static void currmove_redraw(frontend *fe, game_drawstate *ds, int guess, int col
     draw_update(fe, ox-off-1, oy, 2, PEGSZ);
 }
 
-static void cur_redraw(frontend *fe, game_drawstate *ds,
-                       int x, int y, int erase)
-{
-    int cgap = ds->gapsz / 2;
-
-    draw_circle(fe, x+PEGRAD, y+PEGRAD, PEGRAD+cgap, 0,
-                erase ? COL_BACKGROUND : COL_CURSOR);
-
-    draw_update(fe, x-cgap, y-cgap, x+PEGSZ+cgap, y+PEGSZ+cgap);
-}
-
 static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                        game_state *state, int dir, game_ui *ui,
                        float animtime, float flashtime)
 {
-    int i, cur_erase = 0, cur_draw = 0, new_move, last_go;
+    int i, new_move, last_go;
 
     new_move = (state->next_go != ds->next_go) || !ds->started;
     last_go = (state->next_go == state->params.nguesses-1);
@@ -1039,9 +1151,14 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
 
     /* draw the colours */
     for (i = 0; i < state->params.ncolours; i++) {
-        if (ds->colours->pegs[i] != i+1) {
+        int val = i+1;
+        if (ui->display_cur && ui->colour_cur == i)
+            val |= 0x1000;
+        if (ds->colours->pegs[i] != val) {
            draw_peg(fe, ds, COL_X(i), COL_Y(i), FALSE, i+1);
-            ds->colours->pegs[i] = i+1;
+            if (val & 0x1000)
+                draw_cursor(fe, ds, COL_X(i), COL_Y(i));
+            ds->colours->pegs[i] = val;
         }
     }
 
@@ -1049,30 +1166,24 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
     for (i = 0; i < state->params.nguesses; i++) {
         if (state->next_go > i || state->solved) {
             /* this info is stored in the game_state already */
-            guess_redraw(fe, ds, i, state->guesses[i], 0);
+            guess_redraw(fe, ds, i, state->guesses[i], NULL, -1, 0);
             hint_redraw(fe, ds, i, state->guesses[i],
-                        i == (state->next_go-1) ? 1 : 0, COL_EMPTY);
+                        i == (state->next_go-1) ? 1 : 0, FALSE, FALSE);
         } else if (state->next_go == i) {
             /* this is the one we're on; the (incomplete) guess is
              * stored in the game_ui. */
-            guess_redraw(fe, ds, i, ui->curr_pegs, 0);
-            hint_redraw(fe, ds, i, NULL, 1, ui->markable ? COL_FLASH : COL_EMPTY);
+            guess_redraw(fe, ds, i, ui->curr_pegs,
+                         ui->holds, ui->display_cur ? ui->peg_cur : -1, 0);
+            hint_redraw(fe, ds, i, NULL, 1,
+                        ui->display_cur && ui->peg_cur == state->params.npegs,
+                        ui->markable);
         } else {
             /* we've not got here yet; it's blank. */
-            guess_redraw(fe, ds, i, NULL, 0);
-            hint_redraw(fe, ds, i, NULL, 0, COL_EMPTY);
+            guess_redraw(fe, ds, i, NULL, NULL, -1, 0);
+            hint_redraw(fe, ds, i, NULL, 0, FALSE, FALSE);
         }
     }
 
-    /* draw the 'hold' markers */
-    if (state->solved) {
-        hold_redraw(fe, ds, state->next_go, NULL, 1);
-    } else if (new_move) {
-        hold_redraw(fe, ds, ds->next_go, NULL, 1);
-        hold_redraw(fe, ds, state->next_go, last_go ? NULL : ui->holds, 1);
-    } else
-        hold_redraw(fe, ds, state->next_go, last_go ? NULL : ui->holds, 0);
-
     /* draw the 'current move' and 'able to mark' sign. */
     if (new_move)
         currmove_redraw(fe, ds, ds->next_go, COL_BACKGROUND);
@@ -1086,34 +1197,9 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
         draw_update(fe, SOLN_OX, SOLN_OY, SOLN_W, SOLN_H);
     }
     if (state->solved)
-        guess_redraw(fe, ds, -1, state->solution, !ds->solved);
+        guess_redraw(fe, ds, -1, state->solution, NULL, -1, !ds->solved);
     ds->solved = state->solved;
 
-    if (ui->display_cur && !ds->display_cur)
-        cur_draw = 1;
-    else if (!ui->display_cur && ds->display_cur)
-        cur_erase = 1;
-    else if (ui->display_cur) {
-        if ((state->next_go != ds->next_go) ||
-            (ui->peg_cur != ds->peg_cur) ||
-            (ui->colour_cur != ds->colour_cur)) {
-            cur_erase = 1;
-            cur_draw = 1;
-        }
-    }
-    if (cur_erase) {
-        cur_redraw(fe, ds, COL_X(ds->colour_cur), COL_Y(ds->colour_cur), 1);
-        cur_redraw(fe, ds,
-                   GUESS_X(ds->next_go, ds->peg_cur), GUESS_Y(ds->next_go, ds->peg_cur), 1);
-    }
-    if (cur_draw) {
-        cur_redraw(fe, ds, COL_X(ui->colour_cur), COL_Y(ui->colour_cur), 0);
-        cur_redraw(fe, ds,
-                   GUESS_X(state->next_go, ui->peg_cur), GUESS_Y(state->next_go, ui->peg_cur), 0);
-    }
-    ds->display_cur = ui->display_cur;
-    ds->peg_cur = ui->peg_cur;
-    ds->colour_cur = ui->colour_cur;
     ds->next_go = state->next_go;
 
     /* if ui->drag_col != 0, save the screen to the blitter,
@@ -1169,7 +1255,6 @@ const struct game thegame = {
     TRUE, game_configure, custom_params,
     validate_params,
     new_game_desc,
-    game_free_aux_info,
     validate_desc,
     new_game,
     dup_game,
@@ -1178,8 +1263,11 @@ const struct game thegame = {
     FALSE, game_text_format,
     new_ui,
     free_ui,
+    encode_ui,
+    decode_ui,
     game_changed_state,
-    make_move,
+    interpret_move,
+    execute_move,
     game_size,
     game_colours,
     game_new_drawstate,