{amin, omin, \
"Width and height for this grid type must both be at least " #amin, \
"At least one of width and height for this grid type must be at least " #omin,},
-enum { GRIDLIST(GRID_LOOPYTYPE) };
+enum { GRIDLIST(GRID_LOOPYTYPE) LOOPY_GRID_DUMMY_TERMINATOR };
static char const *const gridnames[] = { GRIDLIST(GRID_NAME) };
#define GRID_CONFIGS GRIDLIST(GRID_CONFIG)
static grid_type grid_types[] = { GRIDLIST(GRID_GRIDTYPE) };
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
{ 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)
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;
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)
const struct game thegame = {
"Loopy", "games.loopy", "loopy",
default_params,
- game_fetch_preset, NULL,
+ NULL, game_preset_menu,
decode_params,
encode_params,
free_params,