I found a slightly odd-looking line of code in this file a few days
[sgt/puzzles] / midend.c
index 1be8ca9..833cbef 100644 (file)
--- a/midend.c
+++ b/midend.c
@@ -80,7 +80,7 @@ struct midend {
 
     int pressed_mouse_button;
 
-    int tilesize, winwidth, winheight;
+    int preferred_tilesize, tilesize, winwidth, winheight;
 };
 
 #define ensure(me) do { \
@@ -130,6 +130,26 @@ midend *midend_new(frontend *fe, const game *ourgame,
     else
        me->drawing = NULL;
 
+    me->preferred_tilesize = ourgame->preferred_tilesize;
+    {
+        /*
+         * Allow an environment-based override for the default tile
+         * size by defining a variable along the lines of
+         * `NET_TILESIZE=15'.
+         */
+
+       char buf[80], *e;
+       int j, k, ts;
+
+       sprintf(buf, "%s_TILESIZE", me->ourgame->name);
+       for (j = k = 0; buf[j]; j++)
+           if (!isspace((unsigned char)buf[j]))
+               buf[k++] = toupper((unsigned char)buf[j]);
+       buf[k] = '\0';
+       if ((e = getenv(buf)) != NULL && sscanf(e, "%d", &ts) == 1 && ts > 0)
+           me->preferred_tilesize = ts;
+    }
+
     sfree(randseed);
 
     return me;
@@ -198,6 +218,17 @@ void midend_size(midend *me, int *x, int *y, int expand)
     int rx, ry;
 
     /*
+     * We can't set the size on the same drawstate twice. So if
+     * we've already sized one drawstate, we must throw it away and
+     * create a new one.
+     */
+    if (me->drawstate && me->tilesize > 0) {
+        me->ourgame->free_drawstate(me->drawing, me->drawstate);
+        me->drawstate = me->ourgame->new_drawstate(me->drawing,
+                                                   me->states[0].state);
+    }
+
+    /*
      * Find the tile size that best fits within the given space. If
      * `expand' is TRUE, we must actually find the _largest_ such
      * tile size; otherwise, we bound above at the game's preferred
@@ -210,7 +241,7 @@ void midend_size(midend *me, int *x, int *y, int expand)
            me->ourgame->compute_size(me->params, max, &rx, &ry);
        } while (rx <= *x && ry <= *y);
     } else
-       max = me->ourgame->preferred_tilesize + 1;
+       max = me->preferred_tilesize + 1;
     min = 1;
 
     /*
@@ -325,8 +356,24 @@ void midend_new_game(midend *me)
     }
 
     ensure(me);
+
+    /*
+     * It might seem a bit odd that we're using me->params to
+     * create the initial game state, rather than me->curparams
+     * which is better tailored to this specific game and which we
+     * always know.
+     * 
+     * It's supposed to be an invariant in the midend that
+     * me->params and me->curparams differ in no aspect that is
+     * important after generation (i.e. after new_desc()). By
+     * deliberately passing the _less_ specific of these two
+     * parameter sets, we provoke play-time misbehaviour in the
+     * case where a game has failed to encode a play-time parameter
+     * in the non-full version of encode_params().
+     */
     me->states[me->nstates].state =
        me->ourgame->new_game(me, me->params, me->desc);
+
     me->states[me->nstates].movestr = NULL;
     me->states[me->nstates].movetype = NEWGAME;
     me->nstates++;
@@ -1136,12 +1183,15 @@ char *midend_solve(midend *me)
     if (me->statepos < 1)
        return "No game set up to solve";   /* _shouldn't_ happen! */
 
-    msg = "Solve operation failed";    /* game _should_ overwrite on error */
+    msg = NULL;
     movestr = me->ourgame->solve(me->states[0].state,
                                 me->states[me->statepos-1].state,
                                 me->aux_info, &msg);
-    if (!movestr)
+    if (!movestr) {
+       if (!msg)
+           msg = "Solve operation failed";   /* _shouldn't_ happen, but can */
        return msg;
+    }
     s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr);
     assert(s);
 
@@ -1149,8 +1199,11 @@ char *midend_solve(midend *me)
      * Now enter the solved state as the next move.
      */
     midend_stop_anim(me);
-    while (me->nstates > me->statepos)
+    while (me->nstates > me->statepos) {
        me->ourgame->free_game(me->states[--me->nstates].state);
+        if (me->states[me->nstates].movestr)
+            sfree(me->states[me->nstates].movestr);
+    }
     ensure(me);
     me->states[me->nstates].state = s;
     me->states[me->nstates].movestr = movestr;