More serialisation changes: the game_aux_info structure has now been
[sgt/puzzles] / guess.c
diff --git a/guess.c b/guess.c
index b58284f..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;
@@ -243,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;
@@ -265,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;
@@ -343,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)
@@ -385,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)
 {
@@ -401,6 +424,7 @@ 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)
@@ -424,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
@@ -526,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];
+    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 = ",";
     }
-    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. */
-    }
-
-    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);
@@ -621,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",
@@ -644,18 +673,18 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
         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;
 }
@@ -988,10 +1076,14 @@ static void hint_redraw(frontend *fe, game_drawstate *ds, int guess,
     }
 
     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, GUESS_X(guess, dest->npegs)-CGAP,
-                  GUESS_Y(guess, dest->npegs)-CGAP,
-                  PEGSZ+CGAP*2, PEGSZ+CGAP*2, COL_BACKGROUND);
+        draw_rect(fe, hx, hy, hw, hh, COL_BACKGROUND);
 
         for (i = 0; i < dest->npegs; i++) {
             scol = src ? src->feedback[i] : 0;
@@ -1014,13 +1106,17 @@ static void hint_redraw(frontend *fe, game_drawstate *ds, int guess,
                 draw_rect(fe, rowx, rowy, HINTSZ, HINTSZ, col);
             }
         }
-        if (cursor)
-            draw_cursor(fe, ds, GUESS_X(guess, dest->npegs),
-                        GUESS_Y(guess, dest->npegs));
+        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);
+        }
 
-        draw_update(fe, GUESS_X(guess, dest->npegs)-CGAP,
-                    GUESS_Y(guess, dest->npegs)-CGAP,
-                    PEGSZ+CGAP*2, PEGSZ+CGAP*2);
+        draw_update(fe, hx, hy, hw, hh);
     }
 }
 
@@ -1159,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,
@@ -1168,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,