X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=undead.c;h=b1f536e8d0233183a3d0ecf553999038670bf120;hb=a0a581c8b5422bf0c5ed3fde6aa25811e4eb89fc;hp=6ce6eaa272bbd3f6fa90904e3295a451a6b5487f;hpb=67ddba7a15573643666f0f4128d43d0095b80da0;p=sgt-puzzles.git diff --git a/undead.c b/undead.c index 6ce6eaa..b1f536e 100644 --- a/undead.c +++ b/undead.c @@ -49,12 +49,13 @@ enum { COL_GHOST, COL_ZOMBIE, COL_VAMPIRE, + COL_DONE, NCOLOURS }; -#define DIFFLIST(A) \ - A(EASY,Easy,e) \ - A(NORMAL,Normal,n) \ +#define DIFFLIST(A) \ + A(EASY,Easy,e) \ + A(NORMAL,Normal,n) \ A(TRICKY,Tricky,t) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, @@ -113,7 +114,8 @@ static void free_params(game_params *params) { sfree(params); } -static game_params *dup_params(game_params *params) { +static game_params *dup_params(const game_params *params) +{ game_params *ret = snew(game_params); *ret = *params; /* structure copy */ return ret; @@ -142,7 +144,8 @@ static void decode_params(game_params *params, char const *string) { return; } -static char *encode_params(game_params *params, int full) { +static char *encode_params(const game_params *params, int full) +{ char buf[256]; sprintf(buf, "%dx%d", params->w, params->h); if (full) @@ -150,7 +153,8 @@ static char *encode_params(game_params *params, int full) { return dupstr(buf); } -static config_item *game_configure(game_params *params) { +static config_item *game_configure(const game_params *params) +{ config_item *ret; char buf[64]; @@ -181,7 +185,8 @@ static config_item *game_configure(game_params *params) { return ret; } -static game_params *custom_params(config_item *cfg) { +static game_params *custom_params(const config_item *cfg) +{ game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); @@ -190,7 +195,8 @@ static game_params *custom_params(config_item *cfg) { return ret; } -static char *validate_params(game_params *params, int full) { +static char *validate_params(const game_params *params, int full) +{ if ((params->w * params->h ) > 54) return "Grid is too big"; if (params->w < 3) return "Width must be at least 3"; if (params->h < 3) return "Height must be at least 3"; @@ -232,12 +238,13 @@ 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; }; -static game_state *new_state(game_params *params) { +static game_state *new_state(const game_params *params) { int i; game_state *state = snew(game_state); state->common = snew(struct game_common); @@ -284,6 +291,9 @@ static game_state *new_state(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; @@ -293,7 +303,8 @@ static game_state *new_state(game_params *params) { return state; } -static game_state *dup_game(game_state *state) { +static game_state *dup_game(const game_state *state) +{ game_state *ret = snew(game_state); ret->common = state->common; @@ -326,6 +337,13 @@ static game_state *dup_game(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]; @@ -352,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); @@ -576,8 +595,9 @@ int next_list(struct guess *g, int pos) { void get_unique(game_state *state, int counter, random_state *rs) { - int p,i,c,count_uniques; + int p,i,c,pathlimit,count_uniques; struct guess path_guess; + int *view_count; struct entry { struct entry *link; @@ -589,10 +609,10 @@ void get_unique(game_state *state, int counter, random_state *rs) { struct { struct entry *head; struct entry *node; - } views, single_views, loop_views, test_views; + } views, single_views, test_views; struct entry test_entry; - + path_guess.length = state->common->paths[counter].num_monsters; path_guess.guess = snewn(path_guess.length,int); path_guess.possible = snewn(path_guess.length,int); @@ -603,32 +623,29 @@ void get_unique(game_state *state, int counter, random_state *rs) { path_guess.possible[p] = state->guess[state->common->paths[counter].mapping[p]]; switch (path_guess.possible[p]) { - case 1: path_guess.guess[p] = 1; break; - case 2: path_guess.guess[p] = 2; break; - case 3: path_guess.guess[p] = 1; break; - case 4: path_guess.guess[p] = 4; break; - case 5: path_guess.guess[p] = 1; break; - case 6: path_guess.guess[p] = 2; break; - case 7: path_guess.guess[p] = 1; break; + case 1: path_guess.guess[p] = 1; break; + case 2: path_guess.guess[p] = 2; break; + case 3: path_guess.guess[p] = 1; break; + case 4: path_guess.guess[p] = 4; break; + case 5: path_guess.guess[p] = 1; break; + case 6: path_guess.guess[p] = 2; break; + case 7: path_guess.guess[p] = 1; break; } } views.head = NULL; views.node = NULL; + + pathlimit = state->common->paths[counter].length + 1; + view_count = snewn(pathlimit*pathlimit, int); + for (i = 0; i < pathlimit*pathlimit; i++) + view_count[i] = 0; do { - int mirror; + int mirror, start_view, end_view; - views.node = snewn(1,struct entry); - views.node->link = views.head; - views.node->guess = snewn(path_guess.length,int); - views.head = views.node; - views.node->start_view = 0; - views.node->end_view = 0; - memcpy(views.node->guess, path_guess.guess, - path_guess.length*sizeof(int)); - mirror = FALSE; + start_view = 0; for (p=0;pcommon->paths[counter].length;p++) { if (state->common->paths[counter].p[p] == -1) mirror = TRUE; else { @@ -636,17 +653,18 @@ void get_unique(game_state *state, int counter, random_state *rs) { if (state->common->paths[counter].p[p] == state->common->paths[counter].mapping[i]) { if (path_guess.guess[i] == 1 && mirror == TRUE) - views.node->start_view++; + start_view++; if (path_guess.guess[i] == 2 && mirror == FALSE) - views.node->start_view++; + start_view++; if (path_guess.guess[i] == 4) - views.node->start_view++; + start_view++; break; } } } } mirror = FALSE; + end_view = 0; for (p=state->common->paths[counter].length-1;p>=0;p--) { if (state->common->paths[counter].p[p] == -1) mirror = TRUE; else { @@ -654,16 +672,31 @@ void get_unique(game_state *state, int counter, random_state *rs) { if (state->common->paths[counter].p[p] == state->common->paths[counter].mapping[i]) { if (path_guess.guess[i] == 1 && mirror == TRUE) - views.node->end_view++; + end_view++; if (path_guess.guess[i] == 2 && mirror == FALSE) - views.node->end_view++; + end_view++; if (path_guess.guess[i] == 4) - views.node->end_view++; + end_view++; break; } } } } + + assert(start_view >= 0 && start_view < pathlimit); + assert(end_view >= 0 && end_view < pathlimit); + i = start_view * pathlimit + end_view; + view_count[i]++; + if (view_count[i] == 1) { + views.node = snewn(1,struct entry); + views.node->link = views.head; + views.node->guess = snewn(path_guess.length,int); + views.head = views.node; + views.node->start_view = start_view; + views.node->end_view = end_view; + memcpy(views.node->guess, path_guess.guess, + path_guess.length*sizeof(int)); + } } while (next_list(&path_guess, path_guess.length-1)); /* extract single entries from view list */ @@ -680,17 +713,8 @@ void get_unique(game_state *state, int counter, random_state *rs) { while (test_views.head != NULL) { test_views.node = test_views.head; test_views.head = test_views.head->link; - c = 0; - loop_views.head = views.head; - loop_views.node = views.node; - while (loop_views.head != NULL) { - loop_views.node = loop_views.head; - loop_views.head = loop_views.head->link; - if (test_views.node->start_view == loop_views.node->start_view && - test_views.node->end_view == loop_views.node->end_view) - c++; - } - if (c == 1) { + i = test_views.node->start_view * pathlimit + test_views.node->end_view; + if (view_count[i] == 1) { single_views.node = snewn(1,struct entry); single_views.node->link = single_views.head; single_views.node->guess = snewn(path_guess.length,int); @@ -703,6 +727,8 @@ void get_unique(game_state *state, int counter, random_state *rs) { } } + sfree(view_count); + if (count_uniques > 0) { test_entry.start_view = 0; test_entry.end_view = 0; @@ -841,13 +867,13 @@ int solve_iterative(game_state *state, struct path *paths) { for (i=0;iguess[paths[p].mapping[i]]) { - case 1: loop.guess[i] = 1; break; - case 2: loop.guess[i] = 2; break; - case 3: loop.guess[i] = 1; break; - case 4: loop.guess[i] = 4; break; - case 5: loop.guess[i] = 1; break; - case 6: loop.guess[i] = 2; break; - case 7: loop.guess[i] = 1; break; + case 1: loop.guess[i] = 1; break; + case 2: loop.guess[i] = 2; break; + case 3: loop.guess[i] = 1; break; + case 4: loop.guess[i] = 4; break; + case 5: loop.guess[i] = 1; break; + case 6: loop.guess[i] = 2; break; + case 7: loop.guess[i] = 1; break; } loop.possible[i] = state->guess[paths[p].mapping[i]]; possible[paths[p].mapping[i]] = 0; @@ -900,13 +926,13 @@ int solve_bruteforce(game_state *state, struct path *paths) { for (i=0;icommon->num_total;i++) { loop.possible[i] = state->guess[i]; switch (state->guess[i]) { - case 1: loop.guess[i] = 1; break; - case 2: loop.guess[i] = 2; break; - case 3: loop.guess[i] = 1; break; - case 4: loop.guess[i] = 4; break; - case 5: loop.guess[i] = 1; break; - case 6: loop.guess[i] = 2; break; - case 7: loop.guess[i] = 1; break; + case 1: loop.guess[i] = 1; break; + case 2: loop.guess[i] = 2; break; + case 3: loop.guess[i] = 1; break; + case 4: loop.guess[i] = 4; break; + case 5: loop.guess[i] = 1; break; + case 6: loop.guess[i] = 2; break; + case 7: loop.guess[i] = 1; break; } } @@ -949,7 +975,7 @@ int path_cmp(const void *a, const void *b) { return pa->num_monsters - pb->num_monsters; } -static char *new_game_desc(game_params *params, random_state *rs, +static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int i,count,c,w,h,r,p,g; game_state *new; @@ -981,23 +1007,23 @@ static char *new_game_desc(game_params *params, random_state *rs, * empty monster cells */ count = 0; for (h=1;hcommon->params.h+1;h++) - for (w=1;wcommon->params.w+1;w++) { - c = random_upto(rs,5); - if (c >= 2) { - new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY; - new->common->xinfo[w+h*(new->common->params.w+2)] = count++; - } - else if (c == 0) { - new->common->grid[w+h*(new->common->params.w+2)] = - CELL_MIRROR_L; - new->common->xinfo[w+h*(new->common->params.w+2)] = -1; - } - else { - new->common->grid[w+h*(new->common->params.w+2)] = - CELL_MIRROR_R; - new->common->xinfo[w+h*(new->common->params.w+2)] = -1; + for (w=1;wcommon->params.w+1;w++) { + c = random_upto(rs,5); + if (c >= 2) { + new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY; + new->common->xinfo[w+h*(new->common->params.w+2)] = count++; + } + else if (c == 0) { + new->common->grid[w+h*(new->common->params.w+2)] = + CELL_MIRROR_L; + new->common->xinfo[w+h*(new->common->params.w+2)] = -1; + } + else { + new->common->grid[w+h*(new->common->params.w+2)] = + CELL_MIRROR_R; + new->common->xinfo[w+h*(new->common->params.w+2)] = -1; + } } - } new->common->num_total = count; /* Total number of monsters in maze */ /* Puzzle is boring if it has too few monster cells. Discard @@ -1050,10 +1076,10 @@ static char *new_game_desc(game_params *params, random_state *rs, /* Grid is invalid if max. path length > threshold. Discard * grid, make new one */ switch (new->common->params.diff) { - case DIFF_EASY: max_length = min(new->common->params.w,new->common->params.h) + 1; break; - case DIFF_NORMAL: max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break; - case DIFF_TRICKY: max_length = 9; break; - default: max_length = 9; break; + case DIFF_EASY: max_length = min(new->common->params.w,new->common->params.h) + 1; break; + case DIFF_NORMAL: max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break; + case DIFF_TRICKY: max_length = 9; break; + default: max_length = 9; break; } for (p=0;pcommon->num_paths;p++) { @@ -1077,10 +1103,10 @@ static char *new_game_desc(game_params *params, random_state *rs, even less paths with unique solutions */ switch (new->common->params.diff) { - case DIFF_EASY: filling = 2; break; - case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; - case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; - default: filling = 0; break; + case DIFF_EASY: filling = 2; break; + case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; + case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; + default: filling = 0; break; } count = 0; @@ -1125,14 +1151,14 @@ static char *new_game_desc(game_params *params, random_state *rs, } for (w=1;wcommon->params.w+1;w++) - for (h=1;hcommon->params.h+1;h++) { - c = new->common->xinfo[w+h*(new->common->params.w+2)]; - if (c >= 0) { - if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST; - if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE; - if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE; + for (h=1;hcommon->params.h+1;h++) { + c = new->common->xinfo[w+h*(new->common->params.w+2)]; + if (c >= 0) { + if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST; + if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE; + if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE; + } } - } /* Prepare path information needed by the solver (containing all hints) */ for (p=0;pcommon->num_paths;p++) { @@ -1249,26 +1275,26 @@ static char *new_game_desc(game_params *params, random_state *rs, /* Encode grid */ count = 0; for (y=1;ycommon->params.h+1;y++) - for (x=1;xcommon->params.w+1;x++) { - c = new->common->grid[x+y*(new->common->params.w+2)]; - if (count > 25) { - *e++ = 'z'; - count -= 26; - } - if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) { - count++; - } - else if (c == CELL_MIRROR_L) { - if (count > 0) *e++ = (count-1 + 'a'); - *e++ = 'L'; - count = 0; - } - else { - if (count > 0) *e++ = (count-1 + 'a'); - *e++ = 'R'; - count = 0; + for (x=1;xcommon->params.w+1;x++) { + c = new->common->grid[x+y*(new->common->params.w+2)]; + if (count > 25) { + *e++ = 'z'; + count -= 26; + } + if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) { + count++; + } + else if (c == CELL_MIRROR_L) { + if (count > 0) *e++ = (count-1 + 'a'); + *e++ = 'L'; + count = 0; + } + else { + if (count > 0) *e++ = (count-1 + 'a'); + *e++ = 'R'; + count = 0; + } } - } if (count > 0) *e++ = (count-1 + 'a'); /* Encode hints */ @@ -1292,7 +1318,9 @@ void num2grid(int num, int width, int height, int *x, int *y) { return; } -static game_state *new_game(midend *me, game_params *params, char *desc) { +static game_state *new_game(midend *me, const game_params *params, + const char *desc) +{ int i; int n; int count; @@ -1412,14 +1440,15 @@ static game_state *new_game(midend *me, game_params *params, char *desc) { return state; } -static char *validate_desc(game_params *params, char *desc) { +static char *validate_desc(const game_params *params, const char *desc) +{ int i; int w = params->w, h = params->h; int wh = w*h; int area; int monsters; int monster_count; - char *desc_s = desc; + const char *desc_s = desc; for (i=0;i<3;i++) { if (!*desc) return "Faulty game description"; @@ -1463,7 +1492,9 @@ static char *validate_desc(game_params *params, char *desc) { return NULL; } -static char *solve_game(game_state *state_start, game_state *currstate, char *aux, char **error) { +static char *solve_game(const game_state *state_start, const game_state *currstate, + const char *aux, char **error) +{ int p; int *old_guess; int iterative_depth; @@ -1546,11 +1577,13 @@ static char *solve_game(game_state *state_start, game_state *currstate, char *au return move; } -static int game_can_format_as_text_now(game_params *params) { +static int game_can_format_as_text_now(const game_params *params) +{ return TRUE; } -static char *game_text_format(game_state *state) { +static char *game_text_format(const game_state *state) +{ int w,h,c,r,xi,g; char *ret; char buf[120]; @@ -1595,7 +1628,8 @@ struct game_ui { int ascii; }; -static game_ui *new_ui(game_state *state) { +static game_ui *new_ui(const game_state *state) +{ game_ui *ui = snew(game_ui); ui->hx = ui->hy = 0; ui->hpencil = ui->hshow = ui->hcursor = 0; @@ -1608,15 +1642,19 @@ static void free_ui(game_ui *ui) { return; } -static char *encode_ui(game_ui *ui) { +static char *encode_ui(const game_ui *ui) +{ return NULL; } -static void decode_ui(game_ui *ui, char *encoding) { +static void decode_ui(game_ui *ui, const char *encoding) +{ return; } -static void game_changed_state(game_ui *ui, game_state *oldstate, game_state *newstate) { +static void game_changed_state(game_ui *ui, const game_state *oldstate, + const game_state *newstate) +{ /* See solo.c; if we were pencil-mode highlighting and * somehow a square has just been properly filled, cancel * pencil mode. */ @@ -1637,17 +1675,47 @@ 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/2) +#define BORDER (TILESIZE/4) -static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, - int x, int y, int button) { +static char *interpret_move(const game_state *state, game_ui *ui, + const game_drawstate *ds, + int x, int y, int button) +{ int gx,gy; int g,xi; char buf[80]; @@ -1656,9 +1724,13 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, gy = ((y-BORDER-2) / TILESIZE ) - 1; if (button == 'a' || button == 'A') { - ds->ascii = ui->ascii ? FALSE : TRUE; + 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)]; @@ -1693,11 +1765,11 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, ui->hy = 1; } else switch (button) { - case CURSOR_UP: ui->hy -= (ui->hy > 1) ? 1 : 0; break; - case CURSOR_DOWN: ui->hy += (ui->hy < ds->h) ? 1 : 0; break; - case CURSOR_RIGHT: ui->hx += (ui->hx < ds->w) ? 1 : 0; break; - case CURSOR_LEFT: ui->hx -= (ui->hx > 1) ? 1 : 0; break; - } + case CURSOR_UP: ui->hy -= (ui->hy > 1) ? 1 : 0; break; + case CURSOR_DOWN: ui->hy += (ui->hy < ds->h) ? 1 : 0; break; + case CURSOR_RIGHT: ui->hx += (ui->hx < ds->w) ? 1 : 0; break; + case CURSOR_LEFT: ui->hx -= (ui->hx > 1) ? 1 : 0; break; + } ui->hshow = ui->hcursor = 1; return ""; } @@ -1712,24 +1784,23 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, if (xi >= 0 && !state->common->fixed[xi]) { if (button == 'g' || button == 'G' || button == '1') { sprintf(buf,"g%d",xi); - ui->hpencil = ui->hshow = 0; + if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } if (button == 'v' || button == 'V' || button == '2') { sprintf(buf,"v%d",xi); - ui->hpencil = ui->hshow = 0; + if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } if (button == 'z' || button == 'Z' || button == '3') { sprintf(buf,"z%d",xi); - ui->hpencil = ui->hshow = 0; + if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 || button == '0' || button == '\b') { - if (!ui->hcursor) ui->hshow = 0; sprintf(buf,"E%d",xi); - ui->hpencil = ui->hshow = 0; + if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } } @@ -1792,6 +1863,11 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, } } } + } else if (button == LEFT_BUTTON) { + if (is_clue(state, gx, gy)) { + sprintf(buf, "D%d,%d", gx, gy); + return dupstr(buf); + } } return NULL; @@ -1818,36 +1894,36 @@ int check_numbers_draw(game_state *state, int *guess) { valid = FALSE; state->count_errors[0] = TRUE; for (x=1;xcommon->params.w+1;x++) - for (y=1;ycommon->params.h+1;y++) { - xy = x+y*(state->common->params.w+2); - if (state->common->xinfo[xy] >= 0 && - guess[state->common->xinfo[xy]] == 1) - state->cell_errors[xy] = TRUE; - } + for (y=1;ycommon->params.h+1;y++) { + xy = x+y*(state->common->params.w+2); + if (state->common->xinfo[xy] >= 0 && + guess[state->common->xinfo[xy]] == 1) + state->cell_errors[xy] = TRUE; + } } if (count_vampires > state->common->num_vampires || (filled && count_vampires != state->common->num_vampires) ) { valid = FALSE; state->count_errors[1] = TRUE; for (x=1;xcommon->params.w+1;x++) - for (y=1;ycommon->params.h+1;y++) { - xy = x+y*(state->common->params.w+2); - if (state->common->xinfo[xy] >= 0 && - guess[state->common->xinfo[xy]] == 2) - state->cell_errors[xy] = TRUE; - } + for (y=1;ycommon->params.h+1;y++) { + xy = x+y*(state->common->params.w+2); + if (state->common->xinfo[xy] >= 0 && + guess[state->common->xinfo[xy]] == 2) + state->cell_errors[xy] = TRUE; + } } if (count_zombies > state->common->num_zombies || (filled && count_zombies != state->common->num_zombies) ) { valid = FALSE; state->count_errors[2] = TRUE; for (x=1;xcommon->params.w+1;x++) - for (y=1;ycommon->params.h+1;y++) { - xy = x+y*(state->common->params.w+2); - if (state->common->xinfo[xy] >= 0 && - guess[state->common->xinfo[xy]] == 4) - state->cell_errors[xy] = TRUE; - } + for (y=1;ycommon->params.h+1;y++) { + xy = x+y*(state->common->params.w+2); + if (state->common->xinfo[xy] >= 0 && + guess[state->common->xinfo[xy]] == 4) + state->cell_errors[xy] = TRUE; + } } return valid; @@ -1879,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; } @@ -1901,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; } @@ -1914,8 +1994,9 @@ int check_path_solution(game_state *state, int p) { return correct; } -static game_state *execute_move(game_state *state, char *move) { - int x,n,p,i; +static game_state *execute_move(const game_state *state, const char *move) +{ + int x,y,n,p,i; char c; int correct; int solver; @@ -1942,6 +2023,23 @@ static game_state *execute_move(game_state *state, 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++; } @@ -1972,22 +2070,29 @@ static game_state *execute_move(game_state *state, char *move) { #define PREFERRED_TILE_SIZE 64 -static void game_compute_size(game_params *params, int tilesize, - int *x, int *y) { - *x = tilesize + (2 + params->w) * tilesize; - *y = tilesize + (3 + params->h) * tilesize; +static void game_compute_size(const game_params *params, int tilesize, + int *x, int *y) +{ + /* Ick: fake up `ds->tilesize' for macro expansion purposes */ + struct { int tilesize; } ads, *ds = &ads; + ads.tilesize = tilesize; + + *x = 2*BORDER+(params->w+2)*TILESIZE; + *y = 2*BORDER+(params->h+3)*TILESIZE; return; } static void game_set_size(drawing *dr, game_drawstate *ds, - game_params *params, int tilesize) { + const game_params *params, int tilesize) +{ ds->tilesize = tilesize; return; } #define COLOUR(ret, i, r, g, b) ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b))) -static float *game_colours(frontend *fe, int *ncolours) { +static float *game_colours(frontend *fe, int *ncolours) +{ float *ret = snewn(3 * NCOLOURS, float); frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); @@ -2024,11 +2129,16 @@ 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; } -static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) { +static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) +{ int i; struct game_drawstate *ds = snew(struct game_drawstate); @@ -2055,6 +2165,9 @@ static game_drawstate *game_new_drawstate(drawing *dr, 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; @@ -2062,6 +2175,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, 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); @@ -2071,7 +2185,8 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds) { } static void draw_cell_background(drawing *dr, game_drawstate *ds, - game_state *state, game_ui *ui, int x, int y) { + const game_state *state, const game_ui *ui, + int x, int y) { int hon; int dx,dy; @@ -2236,73 +2351,88 @@ static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y, } static void draw_monster_count(drawing *dr, game_drawstate *ds, - game_state *state, int c, int hflash) { - int dx,dy,dh; + const game_state *state, int c, int hflash) { + int dx,dy; char buf[8]; char bufm[8]; - dy = TILESIZE/2; - dh = TILESIZE; + dy = TILESIZE/4; dx = BORDER+(ds->w+2)*TILESIZE/2+TILESIZE/4; switch (c) { - case 0: - sprintf(buf,"%d",state->common->num_ghosts); - sprintf(bufm,"G"); - dx -= 3*TILESIZE/2; - break; - case 1: - sprintf(buf,"%d",state->common->num_vampires); - sprintf(bufm,"V"); - break; - case 2: - sprintf(buf,"%d",state->common->num_zombies); - sprintf(bufm,"Z"); - dx += 3*TILESIZE/2; - break; - } - + case 0: + sprintf(buf,"%d",state->common->num_ghosts); + sprintf(bufm,"G"); + dx -= 3*TILESIZE/2; + break; + case 1: + sprintf(buf,"%d",state->common->num_vampires); + sprintf(bufm,"V"); + break; + case 2: + sprintf(buf,"%d",state->common->num_zombies); + sprintf(bufm,"Z"); + dx += 3*TILESIZE/2; + break; + } + + draw_rect(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE, + COL_BACKGROUND); if (!ds->ascii) { - draw_rect(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh,COL_BACKGROUND); - draw_monster(dr,ds,dx-TILESIZE/3,dh,2*TILESIZE/3,hflash,1<count_errors[c] ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT), buf); - draw_update(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh); - } - else { - draw_rect(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh,COL_BACKGROUND); - draw_text(dr,dx-TILESIZE/3,dh,FONT_VARIABLE,dy-1, + draw_monster(dr, ds, dx-TILESIZE/3, dy+TILESIZE/2, + 2*TILESIZE/3, hflash, 1<count_errors[c] ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT), buf); - draw_update(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh); } + draw_text(dr, dx, dy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2, + ALIGN_HLEFT|ALIGN_VCENTRE, + (state->count_errors[c] ? COL_ERROR : + hflash ? COL_FLASH : COL_TEXT), buf); + draw_update(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE); return; } -static void draw_path_hint(drawing *dr, game_drawstate *ds, 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); +static void draw_path_hint(drawing *dr, game_drawstate *ds, + 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; } -static void draw_mirror(drawing *dr, game_drawstate *ds, game_state *state, - int x, int y, int hflash, int mirror) { +static void draw_mirror(drawing *dr, game_drawstate *ds, + const game_state *state, int x, int y, + int hflash, int mirror) { int dx,dy,mx1,my1,mx2,my2; dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; @@ -2326,8 +2456,9 @@ static void draw_mirror(drawing *dr, game_drawstate *ds, game_state *state, return; } -static void draw_big_monster(drawing *dr, game_drawstate *ds, game_state *state, - int x, int y, int hflash, int monster) +static void draw_big_monster(drawing *dr, game_drawstate *ds, + const game_state *state, int x, int y, + int hflash, int monster) { int dx,dy; char buf[10]; @@ -2349,40 +2480,41 @@ static void draw_big_monster(drawing *dr, game_drawstate *ds, game_state *state, return; } -static void draw_pencils(drawing *dr, game_drawstate *ds, game_state *state, - int x, int y, int pencil) { +static void draw_pencils(drawing *dr, game_drawstate *ds, + const game_state *state, int x, int y, int pencil) +{ int dx, dy; - int monsters[4]; - int i, j, px, py; + int monsters[4]; + int i, j, px, py; char buf[10]; dx = BORDER+(x* ds->tilesize)+(TILESIZE/4); dy = BORDER+(y* ds->tilesize)+(TILESIZE/4)+TILESIZE; - for (i = 0, j = 1; j < 8; j *= 2) - if (pencil & j) - monsters[i++] = j; - while (i < 4) - monsters[i++] = 0; - - for (py = 0; py < 2; py++) - for (px = 0; px < 2; px++) - if (monsters[py*2+px]) { - if (!ds->ascii) { - draw_monster(dr, ds, - dx + TILESIZE/2 * px, dy + TILESIZE/2 * py, - TILESIZE/2, 0, monsters[py*2+px]); - } - else { - switch (monsters[py*2+px]) { - case 1: sprintf(buf,"G"); break; - case 2: sprintf(buf,"V"); break; - case 4: sprintf(buf,"Z"); break; - } - draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py, - FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE, - COL_TEXT,buf); + for (i = 0, j = 1; j < 8; j *= 2) + if (pencil & j) + monsters[i++] = j; + while (i < 4) + monsters[i++] = 0; + + for (py = 0; py < 2; py++) + for (px = 0; px < 2; px++) + if (monsters[py*2+px]) { + if (!ds->ascii) { + draw_monster(dr, ds, + dx + TILESIZE/2 * px, dy + TILESIZE/2 * py, + TILESIZE/2, 0, monsters[py*2+px]); + } + else { + switch (monsters[py*2+px]) { + case 1: sprintf(buf,"G"); break; + case 2: sprintf(buf,"V"); break; + case 4: sprintf(buf,"Z"); break; } + draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py, + FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE, + COL_TEXT,buf); } + } draw_update(dr,dx-(TILESIZE/4)+2,dy-(TILESIZE/4)+2, (TILESIZE/2)-3,(TILESIZE/2)-3); @@ -2391,11 +2523,33 @@ static void draw_pencils(drawing *dr, game_drawstate *ds, game_state *state, #define FLASH_TIME 0.7F -static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, - game_state *state, int dir, game_ui *ui, - float animtime, float flashtime) { +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, + float animtime, float flashtime) +{ int i,j,x,y,xy; - int stale, xi, c, hflash, hchanged; + int stale, xi, c, hflash, hchanged, changed_ascii; hflash = (int)(flashtime * 5 / FLASH_TIME) % 2; @@ -2410,8 +2564,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, draw_rect(dr, BORDER+(ds->tilesize*(i+1))+1, BORDER+(ds->tilesize*(j+2))+1, ds->tilesize-1, ds->tilesize-1, COL_BACKGROUND); - draw_update(dr,BORDER+TILESIZE-1,BORDER+2*TILESIZE-1, - (ds->w)*TILESIZE+3, (ds->h)*TILESIZE+3); + draw_update(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE, + 2*BORDER+(ds->h+3)*TILESIZE); } hchanged = FALSE; @@ -2419,13 +2573,19 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, ds->hshow != ui->hshow || ds->hpencil != ui->hpencil) hchanged = TRUE; + if (ds->ascii != ui->ascii) { + ds->ascii = ui->ascii; + changed_ascii = TRUE; + } else + changed_ascii = FALSE; + /* Draw monster count hints */ for (i=0;i<3;i++) { stale = FALSE; if (!ds->started) stale = TRUE; if (ds->hflash != hflash) stale = TRUE; - if (ds->ascii != ui->ascii) stale = TRUE; + if (changed_ascii) stale = TRUE; if (ds->count_errors[i] != state->count_errors[i]) { stale = TRUE; ds->count_errors[i] = state->count_errors[i]; @@ -2438,116 +2598,101 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, /* Draw path count hints */ for (i=0;icommon->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 */ for (x = 1; x < ds->w+1; x++) - for (y = 1; y < ds->h+1; y++) { - stale = FALSE; - xy = x+y*(state->common->params.w+2); - xi = state->common->xinfo[xy]; - c = state->common->grid[xy]; + for (y = 1; y < ds->h+1; y++) { + stale = FALSE; + xy = x+y*(state->common->params.w+2); + xi = state->common->xinfo[xy]; + c = state->common->grid[xy]; - if (!ds->started) stale = TRUE; - if (ds->hflash != hflash) stale = TRUE; - if (ds->ascii != ui->ascii) stale = TRUE; + if (!ds->started) stale = TRUE; + if (ds->hflash != hflash) stale = TRUE; + if (changed_ascii) stale = TRUE; - if (hchanged) { - if ((x == ui->hx && y == ui->hy) || - (x == ds->hx && y == ds->hy)) - stale = TRUE; - } + if (hchanged) { + if ((x == ui->hx && y == ui->hy) || + (x == ds->hx && y == ds->hy)) + stale = TRUE; + } - if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) { - stale = TRUE; - ds->monsters[xi] = state->guess[xi]; - } + if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) { + stale = TRUE; + ds->monsters[xi] = state->guess[xi]; + } - if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) { - stale = TRUE; - ds->pencils[xi] = state->pencils[xi]; - } + if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) { + stale = TRUE; + ds->pencils[xi] = state->pencils[xi]; + } - if (state->cell_errors[xy] != ds->cell_errors[xy]) { - stale = TRUE; - ds->cell_errors[xy] = state->cell_errors[xy]; - } + if (state->cell_errors[xy] != ds->cell_errors[xy]) { + stale = TRUE; + ds->cell_errors[xy] = state->cell_errors[xy]; + } - if (stale) { - draw_cell_background(dr, ds, state, ui, x, y); - if (xi < 0) - draw_mirror(dr, ds, state, x, y, hflash, c); - else if (state->guess[xi] == 1 || state->guess[xi] == 2 || - state->guess[xi] == 4) - draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]); - else - draw_pencils(dr, ds, state, x, y, state->pencils[xi]); + if (stale) { + draw_cell_background(dr, ds, state, ui, x, y); + if (xi < 0) + draw_mirror(dr, ds, state, x, y, hflash, c); + else if (state->guess[xi] == 1 || state->guess[xi] == 2 || + state->guess[xi] == 4) + draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]); + else + draw_pencils(dr, ds, state, x, y, state->pencils[xi]); + } } - } ds->hx = ui->hx; ds->hy = ui->hy; ds->hshow = ui->hshow; ds->hpencil = ui->hpencil; ds->hflash = hflash; - ui->ascii = ds->ascii; ds->started = TRUE; return; } -static float game_anim_length(game_state *oldstate, game_state *newstate, - int dir, game_ui *ui) { +static float game_anim_length(const game_state *oldstate, + const game_state *newstate, int dir, game_ui *ui) +{ return 0.0F; } -static float game_flash_length(game_state *oldstate, game_state *newstate, - int dir, game_ui *ui) { +static float game_flash_length(const game_state *oldstate, + const game_state *newstate, int dir, game_ui *ui) +{ return (!oldstate->solved && newstate->solved && !oldstate->cheated && !newstate->cheated) ? FLASH_TIME : 0.0F; } -static int game_status(game_state *state) { +static int game_status(const game_state *state) +{ return state->solved; } -static int game_timing_state(game_state *state, game_ui *ui) { +static int game_timing_state(const game_state *state, game_ui *ui) +{ return TRUE; } -static void game_print_size(game_params *params, float *x, float *y) { +static void game_print_size(const game_params *params, float *x, float *y) +{ } -static void game_print(drawing *dr, game_state *state, int tilesize) { +static void game_print(drawing *dr, const game_state *state, int tilesize) +{ } #ifdef COMBINED @@ -2557,7 +2702,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize) { const struct game thegame = { "Undead", "games.undead", "undead", default_params, - game_fetch_preset, + game_fetch_preset, NULL, decode_params, encode_params, free_params, @@ -2591,4 +2736,3 @@ const struct game thegame = { FALSE, game_timing_state, 0, /* flags */ }; -