chiark / gitweb /
Fix completion checking in Killer Solo.
[sgt-puzzles.git] / undead.c
index 7d68b8183a152891e6e873dc091ed57946339ca4..ad0ab79d74613c741d3e40b54e4340e2eb816a72 100644 (file)
--- a/undead.c
+++ b/undead.c
@@ -49,6 +49,7 @@ enum {
     COL_GHOST,
     COL_ZOMBIE,
     COL_VAMPIRE,
+    COL_DONE,
     NCOLOURS
 };
 
@@ -237,6 +238,7 @@ 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;
@@ -289,6 +291,9 @@ static game_state *new_state(const 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;
 
@@ -332,6 +337,13 @@ static game_state *dup_game(const 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];
@@ -358,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);
@@ -1662,12 +1675,40 @@ 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/4)
 
@@ -1686,6 +1727,10 @@ static char *interpret_move(const 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)];
@@ -1818,6 +1863,11 @@ static char *interpret_move(const 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;
@@ -1905,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;
     }
@@ -1927,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;
     }
@@ -1942,7 +1996,7 @@ int check_path_solution(game_state *state, int p) {
 
 static game_state *execute_move(const game_state *state, const char *move)
 {
-    int x,n,p,i;
+    int x,y,n,p,i;
     char c;
     int correct; 
     int solver; 
@@ -1969,6 +2023,23 @@ static game_state *execute_move(const game_state *state, const 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++;
     }
 
@@ -2058,6 +2129,10 @@ 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;
 }
@@ -2090,6 +2165,9 @@ static game_drawstate *game_new_drawstate(drawing *dr, const 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;
@@ -2097,6 +2175,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, const 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);
@@ -2316,22 +2395,37 @@ static void draw_monster_count(drawing *dr, game_drawstate *ds,
 }
 
 static void draw_path_hint(drawing *dr, game_drawstate *ds,
-                           const 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);
+                           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;
 }
@@ -2429,6 +2523,26 @@ static void draw_pencils(drawing *dr, game_drawstate *ds,
 
 #define FLASH_TIME 0.7F
 
+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,
@@ -2484,37 +2598,17 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
 
     /* 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);
-        }
-
-        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_start)) {
+            draw_path_hint(dr, ds, &state->common->params, path->grid_start,
+                           hflash, path->sightings_start);
         }
 
-        if (stale) {
-            draw_path_hint(dr, ds, state, i, hflash, FALSE);
+        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);
         }
-
     }
 
     /* Draw puzzle grid contents */