chiark / gitweb /
Fix borders on the HTML menu bar.
[sgt-puzzles.git] / loopy.c
diff --git a/loopy.c b/loopy.c
index bc6ebb306040e7f2387240ef410043a137985b90..7d3436aacbc5862ab2d657f93c4b0c828143e4f6 100644 (file)
--- a/loopy.c
+++ b/loopy.c
@@ -513,22 +513,15 @@ static game_params *dup_params(const game_params *params)
     return ret;
 }
 
-static const game_params presets[] = {
+static const game_params loopy_presets_top[] = {
 #ifdef SMALL_SCREEN
     {  7,  7, DIFF_EASY,   LOOPY_GRID_SQUARE },
     {  7,  7, DIFF_NORMAL, LOOPY_GRID_SQUARE },
     {  7,  7, DIFF_HARD,   LOOPY_GRID_SQUARE },
-    {  7,  7, DIFF_HARD,   LOOPY_GRID_HONEYCOMB },
     {  7,  7, DIFF_HARD,   LOOPY_GRID_TRIANGULAR },
     {  5,  5, DIFF_HARD,   LOOPY_GRID_SNUBSQUARE },
     {  7,  7, DIFF_HARD,   LOOPY_GRID_CAIRO },
-    {  5,  4, DIFF_HARD,   LOOPY_GRID_GREATHEXAGONAL },
-    {  5,  5, DIFF_HARD,   LOOPY_GRID_OCTAGONAL },
     {  5,  5, DIFF_HARD,   LOOPY_GRID_KITE },
-    {  3,  3, DIFF_HARD,   LOOPY_GRID_FLORET },
-    {  3,  3, DIFF_HARD,   LOOPY_GRID_DODECAGONAL },
-    {  3,  3, DIFF_HARD,   LOOPY_GRID_GREATDODECAGONAL },
-    {  3,  2, DIFF_HARD,   LOOPY_GRID_GREATGREATDODECAGONAL },
     {  6,  6, DIFF_HARD,   LOOPY_GRID_PENROSE_P2 },
     {  6,  6, DIFF_HARD,   LOOPY_GRID_PENROSE_P3 },
 #else
@@ -538,38 +531,64 @@ static const game_params presets[] = {
     { 10, 10, DIFF_NORMAL, LOOPY_GRID_SQUARE },
     {  7,  7, DIFF_HARD,   LOOPY_GRID_SQUARE },
     { 10, 10, DIFF_HARD,   LOOPY_GRID_SQUARE },
-    { 10, 10, DIFF_HARD,   LOOPY_GRID_HONEYCOMB },
     { 12, 10, DIFF_HARD,   LOOPY_GRID_TRIANGULAR },
     {  7,  7, DIFF_HARD,   LOOPY_GRID_SNUBSQUARE },
     {  9,  9, DIFF_HARD,   LOOPY_GRID_CAIRO },
+    {  5,  5, DIFF_HARD,   LOOPY_GRID_KITE },
+    { 10, 10, DIFF_HARD,   LOOPY_GRID_PENROSE_P2 },
+    { 10, 10, DIFF_HARD,   LOOPY_GRID_PENROSE_P3 },
+#endif
+};
+
+static const game_params loopy_presets_more[] = {
+#ifdef SMALL_SCREEN
+    {  7,  7, DIFF_HARD,   LOOPY_GRID_HONEYCOMB },
+    {  5,  4, DIFF_HARD,   LOOPY_GRID_GREATHEXAGONAL },
+    {  5,  5, DIFF_HARD,   LOOPY_GRID_OCTAGONAL },
+    {  3,  3, DIFF_HARD,   LOOPY_GRID_FLORET },
+    {  3,  3, DIFF_HARD,   LOOPY_GRID_DODECAGONAL },
+    {  3,  3, DIFF_HARD,   LOOPY_GRID_GREATDODECAGONAL },
+    {  3,  2, DIFF_HARD,   LOOPY_GRID_GREATGREATDODECAGONAL },
+#else
+    { 10, 10, DIFF_HARD,   LOOPY_GRID_HONEYCOMB },
     {  5,  4, DIFF_HARD,   LOOPY_GRID_GREATHEXAGONAL },
     {  7,  7, DIFF_HARD,   LOOPY_GRID_OCTAGONAL },
-    {  5,  5, DIFF_HARD,   LOOPY_GRID_KITE },
     {  5,  5, DIFF_HARD,   LOOPY_GRID_FLORET },
     {  5,  4, DIFF_HARD,   LOOPY_GRID_DODECAGONAL },
     {  5,  4, DIFF_HARD,   LOOPY_GRID_GREATDODECAGONAL },
     {  5,  3, DIFF_HARD,   LOOPY_GRID_GREATGREATDODECAGONAL },
-    { 10, 10, DIFF_HARD,   LOOPY_GRID_PENROSE_P2 },
-    { 10, 10, DIFF_HARD,   LOOPY_GRID_PENROSE_P3 },
 #endif
 };
 
-static int game_fetch_preset(int i, char **name, game_params **params)
+static void preset_menu_add_preset_with_title(struct preset_menu *menu,
+                                              const game_params *params)
 {
-    game_params *tmppar;
     char buf[80];
+    game_params *dup_params;
 
-    if (i < 0 || i >= lenof(presets))
-        return FALSE;
+    sprintf(buf, "%dx%d %s - %s", params->h, params->w,
+            gridnames[params->type], diffnames[params->diff]);
 
-    tmppar = snew(game_params);
-    *tmppar = presets[i];
-    *params = tmppar;
-    sprintf(buf, "%dx%d %s - %s", tmppar->h, tmppar->w,
-            gridnames[tmppar->type], diffnames[tmppar->diff]);
-    *name = dupstr(buf);
+    dup_params = snew(game_params);
+    *dup_params = *params;
 
-    return TRUE;
+    preset_menu_add_preset(menu, dupstr(buf), dup_params);
+}
+
+static struct preset_menu *game_preset_menu(void)
+{
+    struct preset_menu *top, *more;
+    int i;
+
+    top = preset_menu_new();
+    for (i = 0; i < lenof(loopy_presets_top); i++)
+        preset_menu_add_preset_with_title(top, &loopy_presets_top[i]);
+
+    more = preset_menu_add_submenu(top, dupstr("More..."));
+    for (i = 0; i < lenof(loopy_presets_more); i++)
+        preset_menu_add_preset_with_title(more, &loopy_presets_more[i]);
+
+    return top;
 }
 
 static void free_params(game_params *params)
@@ -2928,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;
 
@@ -2990,11 +3010,83 @@ 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;
+
+                    if (e_next == e) {
+                        /*
+                         * Special case: we might have come all the
+                         * way round a loop and found our way back to
+                         * the same edge we started from. In that
+                         * situation, we must terminate not only this
+                         * while loop, but the 'for' outside it that
+                         * was tracing in both directions from the
+                         * starting edge, because if we let it trace
+                         * in the second direction then we'll only
+                         * find ourself traversing the same loop in
+                         * the other order and generate an encoded
+                         * move string that mentions the same set of
+                         * edges twice.
+                         */
+                        goto autofollow_done;
+                    }
 
-    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);
+                }
+            }
+          autofollow_done:;
+        }
+    }
+
+    return sresize(movebuf, movelen+1, char);
 }
 
 static game_state *execute_move(const game_state *state, const char *move)
@@ -3548,7 +3640,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
 const struct game thegame = {
     "Loopy", "games.loopy", "loopy",
     default_params,
-    game_fetch_preset, NULL,
+    NULL, game_preset_menu,
     decode_params,
     encode_params,
     free_params,