/*
* 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 <stdio.h>
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 */
debug_state(state);
assert(!"place_lights failed to resolve overlapping lights!");
}
+ sfree(numindices);
}
/* Fills in all black squares with numbers of adjacent lights. */
get_surrounds(state, x, y, &s);
for (i = 0; i < s.npoints; i++) {
- if (!GRID(state,flags,s.points[i].x,s.points[i].y) & F_NUMBERED)
+ if (!(GRID(state,flags,s.points[i].x,s.points[i].y) & F_NUMBERED))
continue;
- /* we have an adjacent clue square; find /it's/ surrounds
+ /* we have an adjacent clue square; find /its/ surrounds
* and count the remaining lights it needs. */
get_surrounds(state,s.points[i].x,s.points[i].y,&ss);
curr_lights = 0;
}
}
}
+ debug(("Stripped %d unused numbers.\n", n));
return n;
}
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;
/* 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);
/* 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:
(pc)) -1 (nil)
(nil))
*/
-static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
+static char *interpret_move(game_state *state, game_ui *ui, const game_drawstate *ds,
int x, int y, int button)
{
enum { NONE, FLIP_LIGHT, FLIP_IMPOSSIBLE } action = NONE;
cx = FROMCOORD(x);
cy = FROMCOORD(y);
action = (button == LEFT_BUTTON) ? FLIP_LIGHT : FLIP_IMPOSSIBLE;
- } else if (button == CURSOR_SELECT || button == CURSOR_SELECT2 ||
+ } else if (IS_CURSOR_SELECT(button) ||
button == 'i' || button == 'I' ||
button == ' ' || button == '\r' || button == '\n') {
- ui->cur_visible = 1;
- cx = ui->cur_x;
- cy = ui->cur_y;
- action = (button == 'i' || button == 'I' || button == CURSOR_SELECT2) ?
- FLIP_IMPOSSIBLE : FLIP_LIGHT;
- } else if (button == CURSOR_UP || button == CURSOR_DOWN ||
- button == CURSOR_RIGHT || button == CURSOR_LEFT) {
- int dx = 0, dy = 0;
- switch (button) {
- case CURSOR_UP: dy = -1; break;
- case CURSOR_DOWN: dy = 1; break;
- case CURSOR_RIGHT: dx = 1; break;
- case CURSOR_LEFT: dx = -1; break;
- default: assert(!"shouldn't get here");
+ if (ui->cur_visible) {
+ /* Only allow cursor-effect operations if the cursor is visible
+ * (otherwise you have no idea which square it might be affecting) */
+ cx = ui->cur_x;
+ cy = ui->cur_y;
+ action = (button == 'i' || button == 'I' || button == CURSOR_SELECT2) ?
+ FLIP_IMPOSSIBLE : FLIP_LIGHT;
}
- ui->cur_x += dx; ui->cur_y += dy;
- ui->cur_x = min(max(ui->cur_x, 0), state->w - 1);
- ui->cur_y = min(max(ui->cur_y, 0), state->h - 1);
+ ui->cur_visible = 1;
+ } else if (IS_CURSOR_MOVE(button)) {
+ move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, 0);
ui->cur_visible = 1;
nullret = empty;
} else
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;
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
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;
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,
game_redraw,
game_anim_length,
game_flash_length,
+ game_status,
TRUE, FALSE, game_print_size, game_print,
FALSE, /* wants_statusbar */
FALSE, game_timing_state,