In the Windows frontend, stop tab navigation from activating buttons.
[sgt/puzzles] / rect.c
diff --git a/rect.c b/rect.c
index a7113af..1fe873b 100644 (file)
--- a/rect.c
+++ b/rect.c
@@ -60,7 +60,11 @@ struct game_params {
 
 #define PREFERRED_TILE_SIZE 24
 #define TILE_SIZE (ds->tilesize)
+#ifdef SMALL_SCREEN
+#define BORDER (2)
+#else
 #define BORDER (TILE_SIZE * 3 / 4)
+#endif
 
 #define CORNER_TOLERANCE 0.15F
 #define CENTRE_TOLERANCE 0.15F
@@ -102,8 +106,10 @@ static int game_fetch_preset(int i, char **name, game_params **params)
       case 2: w = 11, h = 11; break;
       case 3: w = 13, h = 13; break;
       case 4: w = 15, h = 15; break;
+#ifndef SMALL_SCREEN
       case 5: w = 17, h = 17; break;
       case 6: w = 19, h = 19; break;
+#endif
       default: return FALSE;
     }
 
@@ -323,6 +329,11 @@ static void remove_number_placement(int w, int h, struct numberdata *number,
     number->npoints--;
 }
 
+/*
+ * Returns 0 for failure to solve due to inconsistency; 1 for
+ * success; 2 for failure to complete a solution due to either
+ * ambiguity or it being too difficult.
+ */
 static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
                        unsigned char *hedge, unsigned char *vedge,
                       random_state *rs)
@@ -428,6 +439,14 @@ static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
      * Indexing of this array is by the formula
      * 
      *   overlaps[(rectindex * h + y) * w + x]
+     * 
+     * A positive or zero value indicates what it sounds as if it
+     * should; -1 indicates that this square _cannot_ be part of
+     * this rectangle; and -2 indicates that it _definitely_ is
+     * (which is distinct from 1, because one might very well know
+     * that _if_ square S is part of rectangle R then it must be
+     * because R is placed in a certain position without knowing
+     * that it definitely _is_).
      */
     overlaps = snewn(nrects * w * h, int);
     memset(overlaps, 0, nrects * w * h * sizeof(int));
@@ -520,7 +539,10 @@ static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
                 if (overlaps[(i * h + y) * w + x] >= -1) {
                     int j;
 
-                    assert(overlaps[(i * h + y) * w + x] > 0);
+                    if (overlaps[(i * h + y) * w + x] <= 0) {
+                        ret = 0;       /* inconsistency */
+                        goto cleanup;
+                    }
 #ifdef SOLVER_DIAGNOSTICS
                     printf("marking %d,%d as known for rect %d"
                            " (sole remaining number position)\n", x, y, i);
@@ -562,7 +584,10 @@ static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
             for (yy = miny; yy < maxy; yy++)
                 for (xx = minx; xx < maxx; xx++)
                     if (overlaps[(i * h + yy) * w + xx] >= -1) {
-                        assert(overlaps[(i * h + yy) * w + xx] > 0);
+                        if (overlaps[(i * h + yy) * w + xx] <= 0) {
+                            ret = 0;   /* inconsistency */
+                            goto cleanup;
+                        }
 #ifdef SOLVER_DIAGNOSTICS
                         printf("marking %d,%d as known for rect %d"
                                " (intersection of all placements)\n",
@@ -841,15 +866,17 @@ static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
         }
     }
 
-    ret = TRUE;
+    cleanup:
+    ret = 1;
     for (i = 0; i < nrects; i++) {
 #ifdef SOLVER_DIAGNOSTICS
         printf("rect %d has %d possible placements\n",
                i, rectpositions[i].n);
 #endif
-        assert(rectpositions[i].n > 0);
-        if (rectpositions[i].n > 1) {
-            ret = FALSE;
+        if (rectpositions[i].n <= 0) {
+            ret = 0;                   /* inconsistency */
+        } else if (rectpositions[i].n > 1) {
+            ret = 2;                   /* remaining uncertainty */
         } else if (hedge && vedge) {
             /*
              * Place the rectangle in its only possible position.
@@ -1348,6 +1375,8 @@ static char *new_game_desc(game_params *params, random_state *rs,
                                 r1.x++;
                             r1.w--;
                             break;
+                          default:     /* should never happen */
+                            assert(!"invalid direction");
                         }
                         if (r1.h > 0 && r1.w > 0)
                             place_rect(params2, grid, r1);
@@ -1631,9 +1660,9 @@ static char *new_game_desc(game_params *params, random_state *rs,
                ret = rect_solver(params->w, params->h, nnumbers, nd,
                                  NULL, NULL, rs);
            else
-               ret = TRUE;            /* allow any number placement at all */
+               ret = 1;               /* allow any number placement at all */
 
-            if (ret) {
+            if (ret == 1) {
                 /*
                  * Now place the numbers according to the solver's
                  * recommendations.
@@ -1663,7 +1692,7 @@ static char *new_game_desc(game_params *params, random_state *rs,
             /*
              * If we've succeeded, then terminate the loop.
              */
-            if (ret)
+            if (ret == 1)
                 break;
         }
 
@@ -1867,7 +1896,7 @@ static unsigned char *get_correct(game_state *state)
     return ret;
 }
 
-static game_state *new_game(midend_data *me, game_params *params, char *desc)
+static game_state *new_game(midend *me, game_params *params, char *desc)
 {
     game_state *state = snew(game_state);
     int x, y, i, area;
@@ -2520,13 +2549,13 @@ static void game_compute_size(game_params *params, int tilesize,
     *y = params->h * TILE_SIZE + 2*BORDER + 1;
 }
 
-static void game_set_size(game_drawstate *ds, game_params *params,
-                         int tilesize)
+static void game_set_size(drawing *dr, game_drawstate *ds,
+                         game_params *params, int tilesize)
 {
     ds->tilesize = tilesize;
 }
 
-static float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
 
@@ -2556,7 +2585,7 @@ static float *game_colours(frontend *fe, game_state *state, int *ncolours)
     return ret;
 }
 
-static game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
     int i;
@@ -2572,26 +2601,26 @@ static game_drawstate *game_new_drawstate(game_state *state)
     return ds;
 }
 
-static void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(drawing *dr, game_drawstate *ds)
 {
     sfree(ds->visible);
     sfree(ds);
 }
 
-static void draw_tile(frontend *fe, game_drawstate *ds, game_state *state,
+static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
                       int x, int y, unsigned char *hedge, unsigned char *vedge,
                       unsigned char *corners, int correct)
 {
     int cx = COORD(x), cy = COORD(y);
     char str[80];
 
-    draw_rect(fe, cx, cy, TILE_SIZE+1, TILE_SIZE+1, COL_GRID);
-    draw_rect(fe, cx+1, cy+1, TILE_SIZE-1, TILE_SIZE-1,
+    draw_rect(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1, COL_GRID);
+    draw_rect(dr, cx+1, cy+1, TILE_SIZE-1, TILE_SIZE-1,
              correct ? COL_CORRECT : COL_BACKGROUND);
 
     if (grid(state,x,y)) {
        sprintf(str, "%d", grid(state,x,y));
-       draw_text(fe, cx+TILE_SIZE/2, cy+TILE_SIZE/2, FONT_VARIABLE,
+       draw_text(dr, cx+TILE_SIZE/2, cy+TILE_SIZE/2, FONT_VARIABLE,
                  TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, COL_TEXT, str);
     }
 
@@ -2599,19 +2628,19 @@ static void draw_tile(frontend *fe, game_drawstate *ds, game_state *state,
      * Draw edges.
      */
     if (!HRANGE(state,x,y) || index(state,hedge,x,y))
-       draw_rect(fe, cx, cy, TILE_SIZE+1, 2,
+       draw_rect(dr, cx, cy, TILE_SIZE+1, 2,
                   HRANGE(state,x,y) ? COLOUR(index(state,hedge,x,y)) :
                   COL_LINE);
     if (!HRANGE(state,x,y+1) || index(state,hedge,x,y+1))
-       draw_rect(fe, cx, cy+TILE_SIZE-1, TILE_SIZE+1, 2,
+       draw_rect(dr, cx, cy+TILE_SIZE-1, TILE_SIZE+1, 2,
                   HRANGE(state,x,y+1) ? COLOUR(index(state,hedge,x,y+1)) :
                   COL_LINE);
     if (!VRANGE(state,x,y) || index(state,vedge,x,y))
-       draw_rect(fe, cx, cy, 2, TILE_SIZE+1,
+       draw_rect(dr, cx, cy, 2, TILE_SIZE+1,
                   VRANGE(state,x,y) ? COLOUR(index(state,vedge,x,y)) :
                   COL_LINE);
     if (!VRANGE(state,x+1,y) || index(state,vedge,x+1,y))
-       draw_rect(fe, cx+TILE_SIZE-1, cy, 2, TILE_SIZE+1,
+       draw_rect(dr, cx+TILE_SIZE-1, cy, 2, TILE_SIZE+1,
                   VRANGE(state,x+1,y) ? COLOUR(index(state,vedge,x+1,y)) :
                   COL_LINE);
 
@@ -2619,22 +2648,22 @@ static void draw_tile(frontend *fe, game_drawstate *ds, game_state *state,
      * Draw corners.
      */
     if (index(state,corners,x,y))
-       draw_rect(fe, cx, cy, 2, 2,
+       draw_rect(dr, cx, cy, 2, 2,
                   COLOUR(index(state,corners,x,y)));
     if (x+1 < state->w && index(state,corners,x+1,y))
-       draw_rect(fe, cx+TILE_SIZE-1, cy, 2, 2,
+       draw_rect(dr, cx+TILE_SIZE-1, cy, 2, 2,
                   COLOUR(index(state,corners,x+1,y)));
     if (y+1 < state->h && index(state,corners,x,y+1))
-       draw_rect(fe, cx, cy+TILE_SIZE-1, 2, 2,
+       draw_rect(dr, cx, cy+TILE_SIZE-1, 2, 2,
                   COLOUR(index(state,corners,x,y+1)));
     if (x+1 < state->w && y+1 < state->h && index(state,corners,x+1,y+1))
-       draw_rect(fe, cx+TILE_SIZE-1, cy+TILE_SIZE-1, 2, 2,
+       draw_rect(dr, cx+TILE_SIZE-1, cy+TILE_SIZE-1, 2, 2,
                   COLOUR(index(state,corners,x+1,y+1)));
 
-    draw_update(fe, cx, cy, TILE_SIZE+1, TILE_SIZE+1);
+    draw_update(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1);
 }
 
-static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui,
                  float animtime, float flashtime)
 {
@@ -2675,13 +2704,13 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
        }
 
     if (!ds->started) {
-       draw_rect(fe, 0, 0,
+       draw_rect(dr, 0, 0,
                  state->w * TILE_SIZE + 2*BORDER + 1,
                  state->h * TILE_SIZE + 2*BORDER + 1, COL_BACKGROUND);
-       draw_rect(fe, COORD(0)-1, COORD(0)-1,
+       draw_rect(dr, COORD(0)-1, COORD(0)-1,
                  ds->w*TILE_SIZE+3, ds->h*TILE_SIZE+3, COL_LINE);
        ds->started = TRUE;
-       draw_update(fe, 0, 0,
+       draw_update(dr, 0, 0,
                    state->w * TILE_SIZE + 2*BORDER + 1,
                    state->h * TILE_SIZE + 2*BORDER + 1);
     }
@@ -2710,7 +2739,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                c |= CORRECT;
 
            if (index(ds,ds->visible,x,y) != c) {
-               draw_tile(fe, ds, state, x, y, hedge, vedge, corners,
+               draw_tile(dr, ds, state, x, y, hedge, vedge, corners,
                           (c & CORRECT) ? 1 : 0);
                index(ds,ds->visible,x,y) = c;
            }
@@ -2733,7 +2762,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
         else if (state->completed)
             strcat(buf, "COMPLETED!");
 
-        status_bar(fe, buf);
+        status_bar(dr, buf);
     }
 
     if (hedge != state->hedge) {
@@ -2759,14 +2788,74 @@ static float game_flash_length(game_state *oldstate,
     return 0.0F;
 }
 
-static int game_wants_statusbar(void)
+static int game_timing_state(game_state *state, game_ui *ui)
 {
     return TRUE;
 }
 
-static int game_timing_state(game_state *state, game_ui *ui)
+static void game_print_size(game_params *params, float *x, float *y)
 {
-    return TRUE;
+    int pw, ph;
+
+    /*
+     * I'll use 5mm squares by default.
+     */
+    game_compute_size(params, 500, &pw, &ph);
+    *x = pw / 100.0;
+    *y = ph / 100.0;
+}
+
+static void game_print(drawing *dr, game_state *state, int tilesize)
+{
+    int w = state->w, h = state->h;
+    int ink = print_mono_colour(dr, 0);
+    int x, y;
+
+    /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+    game_drawstate ads, *ds = &ads;
+    game_set_size(dr, ds, NULL, tilesize);
+
+    /*
+     * Border.
+     */
+    print_line_width(dr, TILE_SIZE / 10);
+    draw_rect_outline(dr, COORD(0), COORD(0), w*TILE_SIZE, h*TILE_SIZE, ink);
+
+    /*
+     * Grid. We have to make the grid lines particularly thin,
+     * because users will be drawing lines _along_ them and we want
+     * those lines to be visible.
+     */
+    print_line_width(dr, TILE_SIZE / 256);
+    for (x = 1; x < w; x++)
+       draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink);
+    for (y = 1; y < h; y++)
+       draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink);
+
+    /*
+     * Solution.
+     */
+    print_line_width(dr, TILE_SIZE / 10);
+    for (y = 0; y <= h; y++)
+       for (x = 0; x <= w; x++) {
+           if (HRANGE(state,x,y) && hedge(state,x,y))
+               draw_line(dr, COORD(x), COORD(y), COORD(x+1), COORD(y), ink);
+           if (VRANGE(state,x,y) && vedge(state,x,y))
+               draw_line(dr, COORD(x), COORD(y), COORD(x), COORD(y+1), ink);
+       }
+
+    /*
+     * Clues.
+     */
+    for (y = 0; y < h; y++)
+       for (x = 0; x < w; x++)
+           if (grid(state,x,y)) {
+               char str[80];
+               sprintf(str, "%d", grid(state,x,y));
+               draw_text(dr, COORD(x)+TILE_SIZE/2, COORD(y)+TILE_SIZE/2,
+                         FONT_VARIABLE, TILE_SIZE/2,
+                         ALIGN_HCENTRE | ALIGN_VCENTRE, ink, str);
+           }
 }
 
 #ifdef COMBINED
@@ -2774,7 +2863,7 @@ static int game_timing_state(game_state *state, game_ui *ui)
 #endif
 
 const struct game thegame = {
-    "Rectangles", "games.rectangles",
+    "Rectangles", "games.rectangles", "rectangles",
     default_params,
     game_fetch_preset,
     decode_params,
@@ -2804,7 +2893,8 @@ const struct game thegame = {
     game_redraw,
     game_anim_length,
     game_flash_length,
-    game_wants_statusbar,
+    TRUE, FALSE, game_print_size, game_print,
+    TRUE,                             /* wants_statusbar */
     FALSE, game_timing_state,
-    0,                                /* mouse_priorities */
+    0,                                /* flags */
 };