From 2d333750272c3967cfd5cd3677572cddeaad5932 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 24 Apr 2017 18:32:57 +0100 Subject: [PATCH] Loopy: optional 'autofollow' UI feature. This is mostly intended to make play more convenient for grid types like the new Great-Great-Dodecagonal, and other grids with very high-degree faces, in which it's annoying to have to spend half a dozen mouse clicks on filling in a path of edges round the outside of one of those faces which clearly have to all be set (or clear) if any one of them is. For now, the new feature is enabled by yet another of my hacky environment variables, called LOOPY_AUTOFOLLOW. You can set it to "off", "fixed" or "adaptive", where "off" is currently the default (hence, no user-visible change in the default behaviour from this change). If set to 'fixed', then toggling the state of any edge will automatically toggle any further edges which are in the same state and share a degree-2 vertex of the underlying grid design with the original one. In 'adaptive' mode, the game goes even further, and will consider outgoing edges in LINE_NO state not to count for purposes of deciding if a vertex has degree 2. --- loopy.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/loopy.c b/loopy.c index ef8dbb5..652b9ec 100644 --- a/loopy.c +++ b/loopy.c @@ -2947,7 +2947,8 @@ static char *interpret_move(const game_state *state, game_ui *ui, grid *g = state->game_grid; grid_edge *e; int i; - char *ret, buf[80]; + char *movebuf; + int movelen, movesize; char button_char = ' '; enum line_state old_state; @@ -3009,11 +3010,64 @@ static char *interpret_move(const game_state *state, game_ui *ui, return NULL; } + movelen = 0; + movesize = 80; + movebuf = snewn(movesize, char); + movelen = sprintf(movebuf, "%d%c", i, (int)button_char); + { + static enum { OFF, FIXED, ADAPTIVE, DUNNO } autofollow = DUNNO; + if (autofollow == DUNNO) { + const char *env = getenv("LOOPY_AUTOFOLLOW"); + if (env && !strcmp(env, "off")) + autofollow = OFF; + else if (env && !strcmp(env, "fixed")) + autofollow = FIXED; + else if (env && !strcmp(env, "adaptive")) + autofollow = ADAPTIVE; + else + autofollow = OFF; + } + + if (autofollow != OFF) { + int dotid; + for (dotid = 0; dotid < 2; dotid++) { + grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2); + grid_edge *e_this = e; + + while (1) { + int j, n_found; + grid_edge *e_next = NULL; + + for (j = n_found = 0; j < dot->order; j++) { + grid_edge *e_candidate = dot->edges[j]; + int i_candidate = e_candidate - g->edges; + if (e_candidate != e_this && + (autofollow == FIXED || + state->lines[i] == LINE_NO || + state->lines[i_candidate] != LINE_NO)) { + e_next = e_candidate; + n_found++; + } + } - sprintf(buf, "%d%c", i, (int)button_char); - ret = dupstr(buf); + if (n_found != 1 || + state->lines[e_next - g->edges] != state->lines[i]) + break; - return ret; + dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2); + if (movelen > movesize - 40) { + movesize = movesize * 5 / 4 + 128; + movebuf = sresize(movebuf, movesize, char); + } + e_this = e_next; + movelen += sprintf(movebuf+movelen, "%d%c", + (int)(e_this - g->edges), button_char); + } + } + } + } + + return sresize(movebuf, movelen+1, char); } static game_state *execute_move(const game_state *state, const char *move) -- 2.30.2