X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/94cfbe9dd4c59b36f41b7ef85b6da9d20274e23d..64010fc4cd5dc8a15c022f3e22bc008a4edea7fd:/lightup.c diff --git a/lightup.c b/lightup.c index a4e983d..8f09263 100644 --- a/lightup.c +++ b/lightup.c @@ -1,5 +1,45 @@ /* * lightup.c: Implementation of the Nikoli game 'Light Up'. + * + * Possible future solver enhancements: + * + * - In a situation where two clues are diagonally adjacent, you can + * deduce bounds on the number of lights shared between them. For + * instance, suppose a 3 clue is diagonally adjacent to a 1 clue: + * of the two squares adjacent to both clues, at least one must be + * a light (or the 3 would be unsatisfiable) and yet at most one + * must be a light (or the 1 would be overcommitted), so in fact + * _exactly_ one must be a light, and hence the other two squares + * adjacent to the 3 must also be lights and the other two adjacent + * to the 1 must not. Likewise if the 3 is replaced with a 2 but + * one of its other two squares is known not to be a light, and so + * on. + * + * - In a situation where two clues are orthogonally separated (not + * necessarily directly adjacent), you may be able to deduce + * something about the squares that align with each other. For + * instance, suppose two clues are vertically adjacent. Consider + * the pair of squares A,B horizontally adjacent to the top clue, + * and the pair C,D horizontally adjacent to the bottom clue. + * Assuming no intervening obstacles, A and C align with each other + * and hence at most one of them can be a light, and B and D + * likewise, so we must have at most two lights between the four + * squares. So if the clues indicate that there are at _least_ two + * lights in those four squares because the top clue requires at + * least one of AB to be a light and the bottom one requires at + * least one of CD, then we can in fact deduce that there are + * _exactly_ two lights between the four squares, and fill in the + * other squares adjacent to each clue accordingly. For instance, + * if both clues are 3s, then we instantly deduce that all four of + * the squares _vertically_ adjacent to the two clues must be + * lights. (For that to happen, of course, there'd also have to be + * a black square in between the clues, so the two inner lights + * don't light each other.) + * + * - I haven't thought it through carefully, but there's always the + * possibility that both of the above deductions are special cases + * of some more general pattern which can be made computationally + * feasible... */ #include @@ -215,6 +255,11 @@ static void decode_params(game_params *params, char const *string) if (*string == 's') { string++; EATNUM(params->symm); + } else { + /* cope with user input such as '18x10' by ensuring symmetry + * is not selected by default to be incompatible with dimensions */ + if (params->symm == SYMM_ROT4 && params->w != params->h) + params->symm = SYMM_ROT2; } params->difficulty = 0; /* cope with old params */ @@ -727,6 +772,7 @@ static void place_lights(game_state *state, random_state *rs) debug_state(state); assert(!"place_lights failed to resolve overlapping lights!"); } + sfree(numindices); } /* Fills in all black squares with numbers of adjacent lights. */ @@ -1401,6 +1447,7 @@ static int strip_unused_nums(game_state *state) } } } + debug(("Stripped %d unused numbers.\n", n)); return n; } @@ -1474,7 +1521,7 @@ static char *new_game_desc(game_params *params, random_state *rs, char **aux, int interactive) { game_state *news = new_state(params), *copys; - int nsol, i, j, run, x, y, wh = params->w*params->h, num; + int i, j, run, x, y, wh = params->w*params->h, num; char *ret, *p; int *numindices; @@ -1498,8 +1545,7 @@ static char *new_game_desc(game_params *params, random_state *rs, /* Take a copy, remove numbers we didn't use and check there's * still a unique solution; if so, use the copy subsequently. */ copys = dup_game(news); - nsol = strip_unused_nums(copys); - debug(("Stripped %d unused numbers.\n", nsol)); + strip_unused_nums(copys); if (!puzzle_is_good(copys, params->difficulty)) { debug(("Stripped grid is not good, reverting.\n")); free_game(copys); @@ -1671,7 +1717,7 @@ static char *solve_game(game_state *state, game_state *currstate, /* That didn't work; try solving from the clean puzzle. */ solved = dup_game(state); if (dosolve(solved, sflags, NULL) > 0) goto solved; - *error = "Puzzle is not self-consistent."; + *error = "Unable to find a solution to this puzzle."; goto done; solved: @@ -1868,11 +1914,19 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, if (flags & F_BLACK) return nullret; if (action == FLIP_LIGHT) { +#ifdef STYLUS_BASED + if (flags & F_IMPOSSIBLE || flags & F_LIGHT) c = 'I'; else c = 'L'; +#else if (flags & F_IMPOSSIBLE) return nullret; c = 'L'; +#endif } else { +#ifdef STYLUS_BASED + if (flags & F_IMPOSSIBLE || flags & F_LIGHT) c = 'L'; else c = 'I'; +#else if (flags & F_LIGHT) return nullret; c = 'I'; +#endif } sprintf(buf, "%c%d,%d", (int)c, cx, cy); break; @@ -2057,7 +2111,7 @@ static void tile_redraw(drawing *dr, game_drawstate *ds, game_state *state, draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_BLACK); if (ds_flags & DF_NUMBERED) { int ccol = (ds_flags & DF_NUMBERWRONG) ? COL_ERROR : COL_LIGHT; - char str[10]; + char str[32]; /* We know that this won't change over the course of the game * so it's OK to ignore this when calculating whether or not @@ -2151,6 +2205,11 @@ static float game_flash_length(game_state *oldstate, game_state *newstate, return 0.0F; } +static int game_status(game_state *state) +{ + return state->completed ? +1 : 0; +} + static int game_timing_state(game_state *state, game_ui *ui) { return TRUE; @@ -2205,7 +2264,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize) if (ds_flags & DF_BLACK) { draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, ink); if (ds_flags & DF_NUMBERED) { - char str[10]; + char str[32]; sprintf(str, "%d", GRID(state, lights, x, y)); draw_text(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE*3/5, @@ -2253,6 +2312,7 @@ const struct game thegame = { game_redraw, game_anim_length, game_flash_length, + game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state,