ref: 2d91261eb3a37b919f48450888f8f5bfd226c053
parent: c0bd524848f98e5c4a495c4bc31dd55087a28aaa
author: Simon Tatham <anakin@pobox.com>
date: Sun Apr 23 10:02:39 EDT 2023
Net: preference for how loop highlighting interacts with locking. Net's loop highlighting detects any loop in the current state of the grid. I've occasionally found that to be a bit of a spoiler, since sometimes it can point out a deduction I should make before I've figured it out for myself - e.g. when I've just locked all but two of the squares involved in the loop, and the last two _just happen_ to be oriented so as to complete the loop. In that situation I'd prefer if the loop _didn't_ immediately light up and point out to me that I need to arrange that those squares aren't connected to each other. The simple answer is to only count edges connecting two _locked_ squares, for the purposes of loop detection. But this is obviously unacceptable to some players - in particular, those who play without the locking feature at all. So it should be a user preference.
--- a/net.c
+++ b/net.c
@@ -1135,7 +1135,8 @@
static int *compute_loops_inner(int w, int h, bool wrapping,
const unsigned char *tiles,
- const unsigned char *barriers);
+ const unsigned char *barriers,
+ bool include_unlocked_squares);
static char *new_game_desc(const game_params *params, random_state *rs,
char **aux, bool interactive)
@@ -1464,7 +1465,8 @@
*/
prev_loopsquares = w*h+1;
while (1) {
- loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL);
+ loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL,
+ true);
this_loopsquares = 0;
for (i = 0; i < w*h; i++) {
if (loops[i]) {
@@ -1919,6 +1921,7 @@
int w, h;
const unsigned char *tiles, *barriers;
int i, n, neighbours[4];
+ bool include_unlocked_squares;
};
static int net_neighbour(int vertex, void *vctx)
{
@@ -1939,6 +1942,9 @@
continue;
OFFSETWH(x1, y1, x, y, dir, ctx->w, ctx->h);
v1 = y1 * ctx->w + x1;
+ if (!ctx->include_unlocked_squares &&
+ !(tile & ctx->tiles[v1] & LOCKED))
+ continue;
if (ctx->tiles[v1] & F(dir))
ctx->neighbours[ctx->n++] = v1;
}
@@ -1952,12 +1958,13 @@
static int *compute_loops_inner(int w, int h, bool wrapping,
const unsigned char *tiles,
- const unsigned char *barriers)
+ const unsigned char *barriers,
+ bool include_unlocked_squares)
{
struct net_neighbour_ctx ctx;
struct findloopstate *fls;
int *loops;
- int x, y;
+ int x, y, v;
fls = findloop_new_state(w*h);
ctx.w = w;
@@ -1964,6 +1971,7 @@
ctx.h = h;
ctx.tiles = tiles;
ctx.barriers = barriers;
+ ctx.include_unlocked_squares = include_unlocked_squares;
findloop_run(fls, w*h, net_neighbour, &ctx);
loops = snewn(w*h, int);
@@ -1970,14 +1978,19 @@
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
- int x1, y1, dir;
+ int x1, y1, v1, dir;
int flags = 0;
+ v = y * w + x;
for (dir = 1; dir < 0x10; dir <<= 1) {
- if ((tiles[y*w+x] & dir) &&
+ if ((tiles[v] & dir) &&
!(barriers && (barriers[y*w+x] & dir))) {
OFFSETWH(x1, y1, x, y, dir, w, h);
- if ((tiles[y1*w+x1] & F(dir)) &&
+ v1 = y1 * w + x1;
+ if (!include_unlocked_squares &&
+ !(tiles[v] & tiles[v1] & LOCKED))
+ continue;
+ if ((tiles[v1] & F(dir)) &&
findloop_is_loop_edge(fls, y*w+x, y1*w+x1))
flags |= ERR(dir);
}
@@ -1990,10 +2003,12 @@
return loops;
}
-static int *compute_loops(const game_state *state)
+static int *compute_loops(const game_state *state,
+ bool include_unlocked_squares)
{
return compute_loops_inner(state->width, state->height, state->wrapping,
- state->tiles, state->imm->barriers);
+ state->tiles, state->imm->barriers,
+ include_unlocked_squares);
}
struct game_ui {
@@ -2006,6 +2021,8 @@
int dragtilex, dragtiley, dragstartx, dragstarty;
bool dragged;
#endif
+
+ bool unlocked_loops;
};
static game_ui *new_ui(const game_state *state)
@@ -2013,20 +2030,28 @@
void *seed;
int seedsize;
game_ui *ui = snew(game_ui);
- ui->org_x = ui->org_y = 0;
- ui->cur_x = ui->cx = state->width / 2;
- ui->cur_y = ui->cy = state->height / 2;
- ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
- get_random_seed(&seed, &seedsize);
- ui->rs = random_new(seed, seedsize);
- sfree(seed);
+ ui->unlocked_loops = true;
+
+ if (state) {
+ ui->org_x = ui->org_y = 0;
+ ui->cur_x = ui->cx = state->width / 2;
+ ui->cur_y = ui->cy = state->height / 2;
+ ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
+ get_random_seed(&seed, &seedsize);
+ ui->rs = random_new(seed, seedsize);
+ sfree(seed);
+ } else {
+ ui->rs = NULL;
+ }
+
return ui;
}
static void free_ui(game_ui *ui)
{
- random_free(ui->rs);
+ if (ui->rs)
+ random_free(ui->rs);
sfree(ui);
}
@@ -2060,6 +2085,28 @@
}
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Highlight loops involving unlocked squares";
+ ret[0].kw = "unlocked-loops";
+ ret[0].type = C_BOOLEAN;
+ ret[0].u.boolean.bval = ui->unlocked_loops;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->unlocked_loops = cfg[0].u.boolean.bval;
+}
+
static void game_changed_state(game_ui *ui, const game_state *oldstate,
const game_state *newstate)
{
@@ -2891,7 +2938,7 @@
* of barriers.
*/
active = compute_active(state, ui->cx, ui->cy);
- loops = compute_loops(state);
+ loops = compute_loops(state, ui->unlocked_loops);
for (dy = -1; dy < ds->height+1; dy++) {
for (dx = -1; dx < ds->width+1; dx++) {
@@ -3270,7 +3317,7 @@
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
- NULL, NULL, /* get_prefs, set_prefs */
+ get_prefs, set_prefs,
new_ui,
free_ui,
encode_ui,