summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2018-04-17 18:18:40 -0400
committerFranklin Wei <git@fwei.tk>2018-04-17 18:20:12 -0400
commit6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd (patch)
treef6c9abf4a05f640134aa3d248a9498a06f596712 /apps/plugins
parent7a12e796a62b916dae5de1b2986e8cb11ac5be4c (diff)
downloadrockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.zip
rockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.tar.gz
rockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.tar.bz2
rockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.tar.xz
puzzles: enable all the remaining games
They all work now :). I merged in part of Chris Boyle's Android port of Puzzles to give the front end a way to know what keys the back end needs. This also re-syncs to the latest upstream sources. Change-Id: Ie0409bbb32f617ae5abf4f81be3b45d1552db9bb
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/puzzles/README.rockbox3
-rw-r--r--apps/plugins/puzzles/SOURCES.games16
-rw-r--r--apps/plugins/puzzles/rockbox.c216
-rw-r--r--apps/plugins/puzzles/src/blackbox.c1
-rw-r--r--apps/plugins/puzzles/src/bridges.c1
-rw-r--r--apps/plugins/puzzles/src/cube.c1
-rw-r--r--apps/plugins/puzzles/src/dominosa.c1
-rw-r--r--apps/plugins/puzzles/src/fifteen.c1
-rw-r--r--apps/plugins/puzzles/src/filling.c6
-rw-r--r--apps/plugins/puzzles/src/flip.c1
-rw-r--r--apps/plugins/puzzles/src/flood.c1
-rw-r--r--apps/plugins/puzzles/src/galaxies.c1
-rw-r--r--apps/plugins/puzzles/src/guess.c1
-rw-r--r--apps/plugins/puzzles/src/inertia.c1
-rw-r--r--apps/plugins/puzzles/src/keen.c15
-rw-r--r--apps/plugins/puzzles/src/latin.c31
-rw-r--r--apps/plugins/puzzles/src/lightup.c1
-rw-r--r--apps/plugins/puzzles/src/loopy.c1
-rw-r--r--apps/plugins/puzzles/src/magnets.c1
-rw-r--r--apps/plugins/puzzles/src/map.c1
-rw-r--r--apps/plugins/puzzles/src/midend.c173
-rw-r--r--apps/plugins/puzzles/src/mines.c1
-rw-r--r--apps/plugins/puzzles/src/net.c1
-rw-r--r--apps/plugins/puzzles/src/netslide.c1
-rw-r--r--apps/plugins/puzzles/src/nullgame.c1
-rw-r--r--apps/plugins/puzzles/src/palisade.c1
-rw-r--r--apps/plugins/puzzles/src/pattern.c1
-rw-r--r--apps/plugins/puzzles/src/pearl.c1
-rw-r--r--apps/plugins/puzzles/src/pegs.c1
-rw-r--r--apps/plugins/puzzles/src/puzzles.h1
-rw-r--r--apps/plugins/puzzles/src/range.c6
-rw-r--r--apps/plugins/puzzles/src/rect.c1
-rw-r--r--apps/plugins/puzzles/src/samegame.c1
-rw-r--r--apps/plugins/puzzles/src/signpost.c1
-rw-r--r--apps/plugins/puzzles/src/singles.c1
-rw-r--r--apps/plugins/puzzles/src/sixteen.c1
-rw-r--r--apps/plugins/puzzles/src/slant.c1
-rw-r--r--apps/plugins/puzzles/src/solo.c21
-rw-r--r--apps/plugins/puzzles/src/tents.c1
-rw-r--r--apps/plugins/puzzles/src/towers.c30
-rw-r--r--apps/plugins/puzzles/src/tracks.c1
-rw-r--r--apps/plugins/puzzles/src/twiddle.c1
-rw-r--r--apps/plugins/puzzles/src/undead.c6
-rw-r--r--apps/plugins/puzzles/src/unequal.c16
-rw-r--r--apps/plugins/puzzles/src/unruly.c1
-rw-r--r--apps/plugins/puzzles/src/untangle.c1
46 files changed, 470 insertions, 103 deletions
diff --git a/apps/plugins/puzzles/README.rockbox b/apps/plugins/puzzles/README.rockbox
index 012fe78..15bc550 100644
--- a/apps/plugins/puzzles/README.rockbox
+++ b/apps/plugins/puzzles/README.rockbox
@@ -51,3 +51,6 @@ March 2018: Added help styling. Changed from simple_viewer to
display_text for displaying help text. compress.c now does additional
processing on the help text, and also requires a slightly modified
halibut.
+
+April 2018: Finished up the rest of the games. All work now! Surely
+there's still bugs to fix, so stay tuned...
diff --git a/apps/plugins/puzzles/SOURCES.games b/apps/plugins/puzzles/SOURCES.games
index 2fbedae..1b958d0 100644
--- a/apps/plugins/puzzles/SOURCES.games
+++ b/apps/plugins/puzzles/SOURCES.games
@@ -1,17 +1,17 @@
-/* commented-out games cannot be played due to controls and/or bugs */
+/* every game works! :) */
src/blackbox.c
src/bridges.c
src/cube.c
src/dominosa.c
src/fifteen.c
-//src/filling.c /* requires numeric input */
+src/filling.c
src/flip.c
src/flood.c
src/galaxies.c
src/guess.c
src/inertia.c
-//src/keen.c /* numeric input */
+src/keen.c
src/lightup.c
src/magnets.c
src/map.c
@@ -29,11 +29,11 @@ src/singles.c
src/sixteen.c
src/slant.c
src/tents.c
-//src/towers.c /* numeric input */
+src/towers.c
src/tracks.c
src/twiddle.c
-//src/undead.c /* keyboard input */
-//src/unequal.c /* numeric input */
+src/undead.c
+src/unequal.c
src/unruly.c
src/untangle.c
@@ -46,6 +46,6 @@ src/untangle.c
/* no c200v2 */
#if PLUGIN_BUFFER_SIZE > 0x14000
src/pearl.c
-src/loopy.c /* mouse input */
-//src/solo.c /* numeric input */
+src/loopy.c
+src/solo.c
#endif
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index cf0358c..9865407 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2017 Franklin Wei
+ * Copyright (C) 2018 Franklin Wei
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -139,28 +139,36 @@ static int ud_l = 0, ud_u = 0, ud_r = LCD_WIDTH, ud_d = LCD_HEIGHT;
static char *titlebar = NULL;
+/* some games can run in a separate thread for a larger stack (only
+ * Solo for now) */
+static int thread = -1;
+
/* how to process the input (custom, per-game) */
static struct {
- bool want_spacebar, falling_edge, ignore_repeats, rclick_on_hold;
+ bool want_spacebar; /* send spacebar event on long-press of select */
+ bool falling_edge; /* send events upon button release, not initial press */
+ bool ignore_repeats; /* ignore repeated button events (currently in all games but Untangle) */
+ bool rclick_on_hold; /* if in mouse mode, send right-click on long-press of select */
+ bool numerical_chooser; /* repurpose select to activate a numerical chooser */
} input_settings;
static bool accept_input = true;
/* last timer call */
static long last_tstamp;
-static volatile bool timer_on = false;
+static bool timer_on = false;
static bool load_success;
/* debug settings */
-/* did I mention there's a secret debug menu? */
+/* ...did I mention there's a secret debug menu? */
static struct {
int slowmo_factor;
bool timerflash, clipoff, shortcuts, no_aa, polyanim;
} debug_settings;
-/* re-implementations of many rockbox primitives, adapted to draw into
- * a custom framebuffer. */
+/* These are re-implementations of many rockbox drawing functions, adapted to
+ * draw into a custom framebuffer (used for the zoom feature). */
static void zoom_drawpixel(int x, int y)
{
if(y < zoom_clipu || y >= zoom_clipd)
@@ -323,7 +331,7 @@ static void zoom_alpha_bitmap(const unsigned char *bits, int x, int y, int w, in
}
}
-/* font management */
+/* font management routines */
static struct bundled_font {
int status; /* -3 = never tried loading, or unloaded, -2 = failed to load, >= -1: loaded successfully */
@@ -343,8 +351,7 @@ static void unload_fonts(void)
loaded_fonts[i].status = -3;
}
access_counter = -1;
- cur_font = FONT_UI;
- rb->lcd_setfont(cur_font);
+ rb->lcd_setfont(cur_font = FONT_UI);
}
static void init_fonttab(void)
@@ -451,7 +458,7 @@ fallback:
return;
}
-/*** Drawing API ***/
+/*** Drawing API (normal, no zoom) ***/
static void offset_coords(int *x, int *y)
{
@@ -1138,6 +1145,8 @@ static void rb_draw_circle(void *handle, int cx, int cy, int radius,
}
}
+/* blitters allow the game code to save/restore a piece of the
+ * framebuffer */
struct blitter {
bool have_data;
int x, y;
@@ -1341,8 +1350,7 @@ static void draw_title(bool clear_first)
}
int w, h;
- cur_font = FONT_UI;
- rb->lcd_setfont(cur_font);
+ rb->lcd_setfont(cur_font = FONT_UI);
rb->lcd_getstringsize(str, &w, &h);
rb->lcd_set_foreground(BG_COLOR);
@@ -1435,7 +1443,7 @@ const drawing_api rb_drawing = {
NULL,
};
-/** functions exported to puzzles code **/
+/** utility functions exported to puzzles code **/
void fatal(const char *fmt, ...)
{
@@ -1449,7 +1457,10 @@ void fatal(const char *fmt, ...)
rb->splash(HZ * 2, buf);
va_end(ap);
- exit(1);
+ if(rb->thread_self() == thread)
+ rb->thread_exit();
+ else
+ exit(PLUGIN_ERROR);
}
void get_random_seed(void **randseed, int *randseedsize)
@@ -1457,7 +1468,7 @@ void get_random_seed(void **randseed, int *randseedsize)
*randseed = snew(long);
long seed = *rb->current_tick;
rb->memcpy(*randseed, &seed, sizeof(seed));
- *randseedsize = sizeof(long);
+ *randseedsize = sizeof(seed);
}
static void timer_cb(void)
@@ -1512,11 +1523,57 @@ static void send_click(int button, bool release)
midend_process_key(me, x, y, button + 6);
}
+static int choose_key(void)
+{
+ char *game_keys = NULL;
+
+ const game *gm = midend_which_game(me);
+ if(gm->request_keys)
+ game_keys = gm->request_keys(midend_get_params(me));
+
+ if(!game_keys)
+ return;
+
+ int options = strlen(game_keys);
+ int sel = 0;
+
+ while(1)
+ {
+ midend_process_key(me, 0, 0, game_keys[sel]);
+ midend_redraw(me);
+ rb->lcd_update();
+
+ int button = rb->button_get(true);
+ switch(button)
+ {
+ case BTN_LEFT:
+ if(--sel < 0)
+ sel = options - 1;
+ break;
+ case BTN_RIGHT:
+ if(++sel >= options)
+ sel = 0;
+ break;
+ case BTN_PAUSE:
+ return -1;
+ case BTN_FIRE:
+ midend_force_redraw(me);
+ rb->lcd_update();
+ free(game_keys);
+
+ /* the key has already been sent to the game */
+ return 0;
+ }
+ }
+}
+
/* This function handles most user input. It has specific workarounds
* and fixes for certain games to allow them to work well on
* Rockbox. It will either return a positive value that can be passed
- * to the midend, or a negative flag value. Set do_pausemenu to false
- * to just return -1 on BTN_PAUSE and do nothing else. */
+ * to the midend, or a negative flag value. `do_pausemenu' sets how
+ * this function will handle BUTTON_PAUSE: if true, it will handle it
+ * all, otherwise it will simply return -1 and let the caller do the
+ * work (this is used for zoom mode). */
static int process_input(int tmo, bool do_pausemenu)
{
LOGF("process_input start");
@@ -1547,7 +1604,7 @@ static int process_input(int tmo, bool do_pausemenu)
return 0;
}
- /* These games want a spacebar in this event. */
+ /* These games want a spacebar in the event of a long press. */
if(!mouse_mode && input_settings.want_spacebar)
return ' ';
}
@@ -1769,10 +1826,38 @@ static int process_input(int tmo, bool do_pausemenu)
break;
case BTN_FIRE:
- if(!strcmp("Fifteen", midend_which_game(me)->name))
- state = 'h'; /* hint */
+ if(input_settings.numerical_chooser)
+ {
+ if(choose_key() < 0)
+ {
+ if(do_pausemenu)
+ {
+ /* quick hack to preserve the clipping state */
+ bool orig_clipped = clipped;
+ if(orig_clipped)
+ rb_unclip(NULL);
+
+ int rc = pause_menu();
+
+ if(orig_clipped)
+ rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
+
+ last_keystate = 0;
+ accept_input = true;
+
+ return rc;
+ }
+ else
+ return -1;
+ }
+ }
else
- state = CURSOR_SELECT;
+ {
+ if(!strcmp("Fifteen", midend_which_game(me)->name))
+ state = 'h'; /* hint */
+ else
+ state = CURSOR_SELECT;
+ }
break;
default:
@@ -1807,7 +1892,8 @@ static int process_input(int tmo, bool do_pausemenu)
return state;
}
-/* either pan around a zoomed-in image or play zoomed-in */
+/* This function handles zoom mode, where the user can either pan
+ * around a zoomed-in image or play a zoomed-in version of the game. */
static void zoom(void)
{
rb->splash(0, "Please wait...");
@@ -1955,6 +2041,8 @@ static void zoom(void)
}
}
+/** settings/preset code */
+
static const char *config_choices_formatter(int sel, void *data, char *buf, size_t len)
{
/* we can't rely on being called in any particular order */
@@ -2176,8 +2264,7 @@ static bool config_menu(void)
char *title;
config_item *config = midend_get_config(me, CFG_SETTINGS, &title);
- cur_font = FONT_UI;
- rb->lcd_setfont(cur_font);
+ rb->lcd_setfont(cur_font = FONT_UI);
bool success = false;
@@ -2372,13 +2459,15 @@ static void full_help(const char *name)
rb->lcd_set_foreground(LCD_WHITE);
rb->lcd_set_background(LCD_BLACK);
unload_fonts();
- cur_font = FONT_UI;
- rb->lcd_setfont(cur_font);
+ rb->lcd_setfont(cur_font = FONT_UI);
+ /* The help text is stored in compressed format in the help_text[]
+ * array. display_text wants an array of pointers to
+ * null-terminated words, so we create that here. */
char *buf = smalloc(help_text_len);
LZ4_decompress_tiny(help_text, buf, help_text_len);
- /* fill the word_ptrs array to pass to display_text */
+ /* create the word_ptrs array to pass to display_text */
char **word_ptrs = smalloc(sizeof(char*) * help_text_words);
char **ptr = word_ptrs;
bool last_was_null = false;
@@ -2428,6 +2517,7 @@ static void init_default_settings(void)
}
#ifdef DEBUG_MENU
+/* Useless debug code. Mostly a waste of space. */
static void bench_aa(void)
{
rb->sleep(0);
@@ -2738,8 +2828,7 @@ static size_t giant_buffer_len = 0; /* set on start */
static void fix_size(void)
{
int w = LCD_WIDTH, h = LCD_HEIGHT, h_x;
- cur_font = FONT_UI;
- rb->lcd_setfont(cur_font);
+ rb->lcd_setfont(cur_font = FONT_UI);
rb->lcd_getstringsize("X", NULL, &h_x);
h -= h_x;
midend_size(me, &w, &h, TRUE);
@@ -2798,10 +2887,9 @@ static bool string_in_list(const char *target, const char **list)
return false;
}
+/* this function sets game-specific input settings */
static void tune_input(const char *name)
{
- /* game-specific stuff */
-
static const char *want_spacebar[] = {
"Magnets",
"Mines",
@@ -2824,7 +2912,8 @@ static void tune_input(const char *name)
/* wait until a key is released to send an action */
input_settings.falling_edge = string_in_list(name, falling_edge);
- /* in all games but untangle (mouse mode overrides this) */
+ /* ignore repeated keypresses in all games but untangle (mouse
+ * mode overrides this no matter what) */
static const char *ignore_repeats[] = {
"Untangle",
NULL
@@ -2848,6 +2937,18 @@ static void tune_input(const char *name)
};
mouse_mode = string_in_list(name, mouse_games);
+
+ static const char *number_chooser_games[] = {
+ "Filling",
+ "Keen",
+ "Solo",
+ "Towers",
+ "Undead",
+ "Unequal",
+ NULL
+ };
+
+ input_settings.numerical_chooser = string_in_list(name, number_chooser_games);
}
static const char *init_for_game(const game *gm, int load_fd, bool draw)
@@ -3143,21 +3244,10 @@ static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
return action;
}
-enum plugin_status plugin_start(const void *param)
+static void puzzles_main(void)
{
- (void) param;
-
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
- /* boost for init */
- rb->cpu_boost(true);
-#endif
-
- giant_buffer = rb->plugin_get_buffer(&giant_buffer_len);
-
rb_atexit(exit_handler);
- init_tlsf();
-
init_default_settings();
init_fonttab();
@@ -3255,7 +3345,7 @@ enum plugin_status plugin_start(const void *param)
case 7:
/* we don't care about freeing anything because tlsf will
* be wiped out the next time around */
- return PLUGIN_OK;
+ return;
default:
break;
}
@@ -3294,13 +3384,13 @@ enum plugin_status plugin_start(const void *param)
/* quit without saving */
midend_free(me);
sfree(colors);
- exit(PLUGIN_OK);
+ return;
case -3:
/* save and quit */
save_game();
midend_free(me);
sfree(colors);
- exit(PLUGIN_OK);
+ return;
default:
break;
}
@@ -3329,3 +3419,35 @@ enum plugin_status plugin_start(const void *param)
sfree(colors);
}
}
+
+enum plugin_status plugin_start(const void *param)
+{
+ (void) param;
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ /* boost for init */
+ rb->cpu_boost(true);
+#endif
+
+ giant_buffer = rb->plugin_get_buffer(&giant_buffer_len);
+ init_tlsf();
+
+ if(!strcmp(thegame.name, "Solo"))
+ {
+ /* Solo needs a big stack */
+ int stack_sz = 16 * DEFAULT_STACK_SIZE;
+ uintptr_t old = smalloc(stack_sz);
+
+ /* word alignment */
+ long *stack = ((uintptr_t)old & (uintptr_t)(~0x3)) + 4;
+ stack_sz -= ((char*)stack - (char*)old);
+
+ thread = rb->create_thread(puzzles_main, stack, stack_sz, 0, "puzzles"
+ IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
+ rb->thread_wait(thread);
+ }
+ else
+ puzzles_main();
+
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/puzzles/src/blackbox.c b/apps/plugins/puzzles/src/blackbox.c
index ffc7f7c..caaa9eb 100644
--- a/apps/plugins/puzzles/src/blackbox.c
+++ b/apps/plugins/puzzles/src/blackbox.c
@@ -1521,6 +1521,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/bridges.c b/apps/plugins/puzzles/src/bridges.c
index 0a326f2..098fea2 100644
--- a/apps/plugins/puzzles/src/bridges.c
+++ b/apps/plugins/puzzles/src/bridges.c
@@ -3238,6 +3238,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/cube.c b/apps/plugins/puzzles/src/cube.c
index 2a79a81..7491565 100644
--- a/apps/plugins/puzzles/src/cube.c
+++ b/apps/plugins/puzzles/src/cube.c
@@ -1751,6 +1751,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/dominosa.c b/apps/plugins/puzzles/src/dominosa.c
index a072ac6..73b744d 100644
--- a/apps/plugins/puzzles/src/dominosa.c
+++ b/apps/plugins/puzzles/src/dominosa.c
@@ -1723,6 +1723,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/fifteen.c b/apps/plugins/puzzles/src/fifteen.c
index f749e96..35bcb32 100644
--- a/apps/plugins/puzzles/src/fifteen.c
+++ b/apps/plugins/puzzles/src/fifteen.c
@@ -1103,6 +1103,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/filling.c b/apps/plugins/puzzles/src/filling.c
index 231de4c..c331dac 100644
--- a/apps/plugins/puzzles/src/filling.c
+++ b/apps/plugins/puzzles/src/filling.c
@@ -1287,6 +1287,11 @@ static const char *validate_desc(const game_params *params, const char *desc)
return (area < sz) ? "Not enough data to fill grid" : NULL;
}
+static char *game_request_keys(const game_params *params)
+{
+ return dupstr("1234567890\b");
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
@@ -2125,6 +2130,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/flip.c b/apps/plugins/puzzles/src/flip.c
index 9e5716a..481c4b5 100644
--- a/apps/plugins/puzzles/src/flip.c
+++ b/apps/plugins/puzzles/src/flip.c
@@ -1327,6 +1327,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/flood.c b/apps/plugins/puzzles/src/flood.c
index 854469a..840ff64 100644
--- a/apps/plugins/puzzles/src/flood.c
+++ b/apps/plugins/puzzles/src/flood.c
@@ -1350,6 +1350,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/galaxies.c b/apps/plugins/puzzles/src/galaxies.c
index 5d7dbdf..11efd34 100644
--- a/apps/plugins/puzzles/src/galaxies.c
+++ b/apps/plugins/puzzles/src/galaxies.c
@@ -3652,6 +3652,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/guess.c b/apps/plugins/puzzles/src/guess.c
index a14f3bd..98019d2 100644
--- a/apps/plugins/puzzles/src/guess.c
+++ b/apps/plugins/puzzles/src/guess.c
@@ -1493,6 +1493,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/inertia.c b/apps/plugins/puzzles/src/inertia.c
index 22cf235..8206d00 100644
--- a/apps/plugins/puzzles/src/inertia.c
+++ b/apps/plugins/puzzles/src/inertia.c
@@ -2234,6 +2234,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/keen.c b/apps/plugins/puzzles/src/keen.c
index ec7af12..f3142a0 100644
--- a/apps/plugins/puzzles/src/keen.c
+++ b/apps/plugins/puzzles/src/keen.c
@@ -1251,6 +1251,20 @@ static const char *validate_desc(const game_params *params, const char *desc)
return NULL;
}
+static char *game_request_keys(const game_params *params)
+{
+ int i;
+ int w = params->w;
+ char *keys = smalloc(w+2);
+ for (i = 0; i < w; i++) {
+ if (i<9) keys[i] = '1' + i;
+ else keys[i] = 'a' + i - 9;
+ }
+ keys[w] = '\b';
+ keys[w+1] = '\0';
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
@@ -2354,6 +2368,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/latin.c b/apps/plugins/puzzles/src/latin.c
index 50fae3c..716064c 100644
--- a/apps/plugins/puzzles/src/latin.c
+++ b/apps/plugins/puzzles/src/latin.c
@@ -964,13 +964,30 @@ static int latin_solver_top(struct latin_solver *solver, int maxdiff,
got_result:
#ifdef STANDALONE_SOLVER
- if (solver_show_working)
- printf("%*s%s found\n",
- solver_recurse_depth*4, "",
- diff == diff_impossible ? "no solution (impossible)" :
- diff == diff_unfinished ? "no solution (unfinished)" :
- diff == diff_ambiguous ? "multiple solutions" :
- "one solution");
+ if (solver_show_working) {
+ if (diff != diff_impossible && diff != diff_unfinished &&
+ diff != diff_ambiguous) {
+ int x, y;
+
+ printf("%*sone solution found:\n", solver_recurse_depth*4, "");
+
+ for (y = 0; y < solver->o; y++) {
+ printf("%*s", solver_recurse_depth*4+1, "");
+ for (x = 0; x < solver->o; x++) {
+ int val = solver->grid[y*solver->o+x];
+ assert(val);
+ printf(" %s", solver->names[val-1]);
+ }
+ printf("\n");
+ }
+ } else {
+ printf("%*s%s found\n",
+ solver_recurse_depth*4, "",
+ diff == diff_impossible ? "no solution (impossible)" :
+ diff == diff_unfinished ? "no solution (unfinished)" :
+ "multiple solutions");
+ }
+ }
#endif
latin_solver_free_scratch(scratch);
diff --git a/apps/plugins/puzzles/src/lightup.c b/apps/plugins/puzzles/src/lightup.c
index fd363e0..acee853 100644
--- a/apps/plugins/puzzles/src/lightup.c
+++ b/apps/plugins/puzzles/src/lightup.c
@@ -2303,6 +2303,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/loopy.c b/apps/plugins/puzzles/src/loopy.c
index c14412d..f5f8917 100644
--- a/apps/plugins/puzzles/src/loopy.c
+++ b/apps/plugins/puzzles/src/loopy.c
@@ -3658,6 +3658,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/magnets.c b/apps/plugins/puzzles/src/magnets.c
index ebb5376..c9f97b6 100644
--- a/apps/plugins/puzzles/src/magnets.c
+++ b/apps/plugins/puzzles/src/magnets.c
@@ -2409,6 +2409,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/map.c b/apps/plugins/puzzles/src/map.c
index 3a16335..2037510 100644
--- a/apps/plugins/puzzles/src/map.c
+++ b/apps/plugins/puzzles/src/map.c
@@ -3236,6 +3236,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/midend.c b/apps/plugins/puzzles/src/midend.c
index 4ccb1f9..0c91388 100644
--- a/apps/plugins/puzzles/src/midend.c
+++ b/apps/plugins/puzzles/src/midend.c
@@ -25,6 +25,11 @@ struct midend_state_entry {
int movetype;
};
+struct midend_serialise_buf {
+ char *buf;
+ int len, size;
+};
+
struct midend {
frontend *frontend;
random_state *random;
@@ -63,8 +68,8 @@ struct midend {
int nstates, statesize, statepos;
struct midend_state_entry *states;
- char *newgame_undo_buf;
- int newgame_undo_len, newgame_undo_size;
+ struct midend_serialise_buf newgame_undo, newgame_redo;
+ int newgame_can_store_undo;
game_params *params, *curparams;
game_drawstate *drawstate;
@@ -158,8 +163,11 @@ midend *midend_new(frontend *fe, const game *ourgame,
me->random = random_new(randseed, randseedsize);
me->nstates = me->statesize = me->statepos = 0;
me->states = NULL;
- me->newgame_undo_buf = NULL;
- me->newgame_undo_size = me->newgame_undo_len = 0;
+ me->newgame_undo.buf = NULL;
+ me->newgame_undo.size = me->newgame_undo.len = 0;
+ me->newgame_redo.buf = NULL;
+ me->newgame_redo.size = me->newgame_redo.len = 0;
+ me->newgame_can_store_undo = FALSE;
me->params = ourgame->default_params();
me->game_id_change_notify_function = NULL;
me->game_id_change_notify_ctx = NULL;
@@ -221,6 +229,7 @@ static void midend_purge_states(midend *me)
if (me->states[me->nstates].movestr)
sfree(me->states[me->nstates].movestr);
}
+ me->newgame_redo.len = 0;
}
static void midend_free_game(midend *me)
@@ -257,7 +266,8 @@ void midend_free(midend *me)
if (me->drawing)
drawing_free(me->drawing);
random_free(me->random);
- sfree(me->newgame_undo_buf);
+ sfree(me->newgame_undo.buf);
+ sfree(me->newgame_redo.buf);
sfree(me->states);
sfree(me->desc);
sfree(me->privdesc);
@@ -386,36 +396,39 @@ void midend_force_redraw(midend *me)
static void newgame_serialise_write(void *ctx, const void *buf, int len)
{
- midend *const me = ctx;
+ struct midend_serialise_buf *ser = (struct midend_serialise_buf *)ctx;
int new_len;
- assert(len < INT_MAX - me->newgame_undo_len);
- new_len = me->newgame_undo_len + len;
- if (new_len > me->newgame_undo_size) {
- me->newgame_undo_size = new_len + new_len / 4 + 1024;
- me->newgame_undo_buf = sresize(me->newgame_undo_buf,
- me->newgame_undo_size, char);
+ assert(len < INT_MAX - ser->len);
+ new_len = ser->len + len;
+ if (new_len > ser->size) {
+ ser->size = new_len + new_len / 4 + 1024;
+ ser->buf = sresize(ser->buf, ser->size, char);
}
- memcpy(me->newgame_undo_buf + me->newgame_undo_len, buf, len);
- me->newgame_undo_len = new_len;
+ memcpy(ser->buf + ser->len, buf, len);
+ ser->len = new_len;
}
void midend_new_game(midend *me)
{
- me->newgame_undo_len = 0;
- if (me->nstates != 0) {
+ me->newgame_undo.len = 0;
+ if (me->newgame_can_store_undo) {
/*
* Serialise the whole of the game that we're about to
* supersede, so that the 'New Game' action can be undone
- * later. But if nstates == 0, that means there _isn't_ a
- * current game (not even a starting position), because this
- * is the initial call to midend_new_game when the midend is
- * first set up; in that situation, we want to avoid writing
- * out any serialisation, because it would be useless anyway
- * and just confuse us into thinking we had something to undo
- * to.
+ * later.
+ *
+ * We omit this in various situations, such as if there
+ * _isn't_ a current game (not even a starting position)
+ * because this is the initial call to midend_new_game when
+ * the midend is first set up, or if the midend state has
+ * already begun to be overwritten by midend_set_config. In
+ * those situations, we want to avoid writing out any
+ * serialisation, because they will be either invalid, or
+ * worse, valid but wrong.
*/
- midend_serialise(me, newgame_serialise_write, me);
+ midend_purge_states(me);
+ midend_serialise(me, newgame_serialise_write, &me->newgame_undo);
}
midend_stop_anim(me);
@@ -528,30 +541,31 @@ void midend_new_game(midend *me)
if (me->game_id_change_notify_function)
me->game_id_change_notify_function(me->game_id_change_notify_ctx);
+
+ me->newgame_can_store_undo = TRUE;
}
int midend_can_undo(midend *me)
{
- return (me->statepos > 1 || me->newgame_undo_len);
+ return (me->statepos > 1 || me->newgame_undo.len);
}
int midend_can_redo(midend *me)
{
- return (me->statepos < me->nstates);
+ return (me->statepos < me->nstates || me->newgame_redo.len);
}
struct newgame_undo_deserialise_read_ctx {
- midend *me;
+ struct midend_serialise_buf *ser;
int len, pos;
};
static int newgame_undo_deserialise_read(void *ctx, void *buf, int len)
{
struct newgame_undo_deserialise_read_ctx *const rctx = ctx;
- midend *const me = rctx->me;
int use = min(len, rctx->len - rctx->pos);
- memcpy(buf, me->newgame_undo_buf + rctx->pos, use);
+ memcpy(buf, rctx->ser->buf + rctx->pos, use);
rctx->pos += use;
return use;
}
@@ -624,12 +638,22 @@ static int midend_undo(midend *me)
me->statepos--;
me->dir = -1;
return 1;
- } else if (me->newgame_undo_len) {
- /* This undo cannot be undone with redo */
+ } else if (me->newgame_undo.len) {
struct newgame_undo_deserialise_read_ctx rctx;
struct newgame_undo_deserialise_check_ctx cctx;
- rctx.me = me;
- rctx.len = me->newgame_undo_len; /* copy for reentrancy safety */
+ struct midend_serialise_buf serbuf;
+
+ /*
+ * Serialise the current game so that you can later redo past
+ * this undo. Once we're committed to the undo actually
+ * happening, we'll copy this data into place.
+ */
+ serbuf.buf = NULL;
+ serbuf.len = serbuf.size = 0;
+ midend_serialise(me, newgame_serialise_write, &serbuf);
+
+ rctx.ser = &me->newgame_undo;
+ rctx.len = me->newgame_undo.len; /* copy for reentrancy safety */
rctx.pos = 0;
cctx.refused = FALSE;
deserialise_error = midend_deserialise_internal(
@@ -642,6 +666,7 @@ static int midend_undo(midend *me)
* contain the dummy error message generated by our check
* function, which we ignore.)
*/
+ sfree(serbuf.buf);
return 0;
} else {
/*
@@ -652,6 +677,22 @@ static int midend_undo(midend *me)
* replaced by the wrong file, etc., by user error.
*/
assert(!deserialise_error);
+
+ /*
+ * Clear the old newgame_undo serialisation, so that we
+ * don't try to undo past the beginning of the game we've
+ * just gone back to and end up at the front of it again.
+ */
+ me->newgame_undo.len = 0;
+
+ /*
+ * Copy the serialisation of the game we've just left into
+ * the midend so that we can redo back into it later.
+ */
+ me->newgame_redo.len = 0;
+ newgame_serialise_write(&me->newgame_redo, serbuf.buf, serbuf.len);
+
+ sfree(serbuf.buf);
return 1;
}
} else
@@ -660,6 +701,8 @@ static int midend_undo(midend *me)
static int midend_redo(midend *me)
{
+ const char *deserialise_error;
+
if (me->statepos < me->nstates) {
if (me->ui)
me->ourgame->changed_state(me->ui,
@@ -668,6 +711,63 @@ static int midend_redo(midend *me)
me->statepos++;
me->dir = +1;
return 1;
+ } else if (me->newgame_redo.len) {
+ struct newgame_undo_deserialise_read_ctx rctx;
+ struct newgame_undo_deserialise_check_ctx cctx;
+ struct midend_serialise_buf serbuf;
+
+ /*
+ * Serialise the current game so that you can later undo past
+ * this redo. Once we're committed to the undo actually
+ * happening, we'll copy this data into place.
+ */
+ serbuf.buf = NULL;
+ serbuf.len = serbuf.size = 0;
+ midend_serialise(me, newgame_serialise_write, &serbuf);
+
+ rctx.ser = &me->newgame_redo;
+ rctx.len = me->newgame_redo.len; /* copy for reentrancy safety */
+ rctx.pos = 0;
+ cctx.refused = FALSE;
+ deserialise_error = midend_deserialise_internal(
+ me, newgame_undo_deserialise_read, &rctx,
+ newgame_undo_deserialise_check, &cctx);
+ if (cctx.refused) {
+ /*
+ * Our post-deserialisation check shows that we can't use
+ * this saved game after all. (deserialise_error will
+ * contain the dummy error message generated by our check
+ * function, which we ignore.)
+ */
+ sfree(serbuf.buf);
+ return 0;
+ } else {
+ /*
+ * There should never be any _other_ deserialisation
+ * error, because this serialised data has been held in
+ * our memory since it was created, and hasn't had any
+ * opportunity to be corrupted on disk, accidentally
+ * replaced by the wrong file, etc., by user error.
+ */
+ assert(!deserialise_error);
+
+ /*
+ * Clear the old newgame_redo serialisation, so that we
+ * don't try to redo past the end of the game we've just
+ * come into and end up at the back of it again.
+ */
+ me->newgame_redo.len = 0;
+
+ /*
+ * Copy the serialisation of the game we've just left into
+ * the midend so that we can undo back into it later.
+ */
+ me->newgame_undo.len = 0;
+ newgame_serialise_write(&me->newgame_undo, serbuf.buf, serbuf.len);
+
+ sfree(serbuf.buf);
+ return 1;
+ }
} else
return 0;
}
@@ -1587,6 +1687,8 @@ static const char *midend_game_id_int(midend *me, const char *id, int defmode)
sfree(par);
+ me->newgame_can_store_undo = FALSE;
+
return NULL;
}
@@ -2224,13 +2326,14 @@ static const char *midend_deserialise_internal(
me->statepos = data.statepos;
/*
- * Don't save the "new game undo" state. So "new game" twice or
+ * Don't save the "new game undo/redo" state. So "new game" twice or
* (in some environments) switching away and back, will make a
* "new game" irreversible. Maybe in the future we will have a
* more sophisticated way to decide when to discard the previous
* game state.
*/
- me->newgame_undo_len = 0;
+ me->newgame_undo.len = 0;
+ me->newgame_redo.len = 0;
{
game_params *tmp;
diff --git a/apps/plugins/puzzles/src/mines.c b/apps/plugins/puzzles/src/mines.c
index 340bb4b..0a66a25 100644
--- a/apps/plugins/puzzles/src/mines.c
+++ b/apps/plugins/puzzles/src/mines.c
@@ -3193,6 +3193,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/net.c b/apps/plugins/puzzles/src/net.c
index ab2425e..2242633 100644
--- a/apps/plugins/puzzles/src/net.c
+++ b/apps/plugins/puzzles/src/net.c
@@ -3239,6 +3239,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/netslide.c b/apps/plugins/puzzles/src/netslide.c
index d4d9856..af4ef7d 100644
--- a/apps/plugins/puzzles/src/netslide.c
+++ b/apps/plugins/puzzles/src/netslide.c
@@ -1871,6 +1871,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/nullgame.c b/apps/plugins/puzzles/src/nullgame.c
index bc19c1e..13c464c 100644
--- a/apps/plugins/puzzles/src/nullgame.c
+++ b/apps/plugins/puzzles/src/nullgame.c
@@ -285,6 +285,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/palisade.c b/apps/plugins/puzzles/src/palisade.c
index e495bbe..0be8ac5 100644
--- a/apps/plugins/puzzles/src/palisade.c
+++ b/apps/plugins/puzzles/src/palisade.c
@@ -1390,6 +1390,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/pattern.c b/apps/plugins/puzzles/src/pattern.c
index e067f3a..f715df4 100644
--- a/apps/plugins/puzzles/src/pattern.c
+++ b/apps/plugins/puzzles/src/pattern.c
@@ -2000,6 +2000,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/pearl.c b/apps/plugins/puzzles/src/pearl.c
index 4f3be50..07949b5 100644
--- a/apps/plugins/puzzles/src/pearl.c
+++ b/apps/plugins/puzzles/src/pearl.c
@@ -2621,6 +2621,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/pegs.c b/apps/plugins/puzzles/src/pegs.c
index 565ba94..823d5c2 100644
--- a/apps/plugins/puzzles/src/pegs.c
+++ b/apps/plugins/puzzles/src/pegs.c
@@ -1316,6 +1316,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/puzzles.h b/apps/plugins/puzzles/src/puzzles.h
index 47f4b7e..031bdfc 100644
--- a/apps/plugins/puzzles/src/puzzles.h
+++ b/apps/plugins/puzzles/src/puzzles.h
@@ -613,6 +613,7 @@ struct game {
void (*free_ui)(game_ui *ui);
char *(*encode_ui)(const game_ui *ui);
void (*decode_ui)(game_ui *ui, const char *encoding);
+ char *(*request_keys)(const game_params *params);
void (*changed_state)(game_ui *ui, const game_state *oldstate,
const game_state *newstate);
char *(*interpret_move)(const game_state *state, game_ui *ui,
diff --git a/apps/plugins/puzzles/src/range.c b/apps/plugins/puzzles/src/range.c
index 41c6a35..03bdf6d 100644
--- a/apps/plugins/puzzles/src/range.c
+++ b/apps/plugins/puzzles/src/range.c
@@ -613,15 +613,16 @@ static move *solver_reasoning_recursion(game_state *state,
/* FIXME: add enum alias for smallest and largest (or N) */
for (colour = M_BLACK; colour <= M_WHITE; ++colour) {
newstate = dup_game(state);
- newstate->grid[cell] = colour;
+ newstate->grid[cell] = colour == M_BLACK ? BLACK : WHITE;
recursive_result = do_solve(newstate, nclues, clues, buf,
DIFF_RECURSION);
- free_game(newstate);
if (recursive_result == NULL) {
+ free_game(newstate);
solver_makemove(r, c, M_BLACK + M_WHITE - colour, state, &buf);
return buf;
}
for (i = 0; i < n && newstate->grid[i] != EMPTY; ++i);
+ free_game(newstate);
if (i == n) return buf;
}
}
@@ -1812,6 +1813,7 @@ struct game const thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/rect.c b/apps/plugins/puzzles/src/rect.c
index c9923ad..3d84f04 100644
--- a/apps/plugins/puzzles/src/rect.c
+++ b/apps/plugins/puzzles/src/rect.c
@@ -2977,6 +2977,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/samegame.c b/apps/plugins/puzzles/src/samegame.c
index d7d0676..4dfc302 100644
--- a/apps/plugins/puzzles/src/samegame.c
+++ b/apps/plugins/puzzles/src/samegame.c
@@ -1656,6 +1656,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/signpost.c b/apps/plugins/puzzles/src/signpost.c
index edcf63a..04a3152 100644
--- a/apps/plugins/puzzles/src/signpost.c
+++ b/apps/plugins/puzzles/src/signpost.c
@@ -2247,6 +2247,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/singles.c b/apps/plugins/puzzles/src/singles.c
index 5929d82..35c5fce 100644
--- a/apps/plugins/puzzles/src/singles.c
+++ b/apps/plugins/puzzles/src/singles.c
@@ -1828,6 +1828,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/sixteen.c b/apps/plugins/puzzles/src/sixteen.c
index 9fa7032..6bba4c8 100644
--- a/apps/plugins/puzzles/src/sixteen.c
+++ b/apps/plugins/puzzles/src/sixteen.c
@@ -1189,6 +1189,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/slant.c b/apps/plugins/puzzles/src/slant.c
index 3fd6611..5eeb668 100644
--- a/apps/plugins/puzzles/src/slant.c
+++ b/apps/plugins/puzzles/src/slant.c
@@ -2164,6 +2164,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/solo.c b/apps/plugins/puzzles/src/solo.c
index dd04cdf..ac0b807 100644
--- a/apps/plugins/puzzles/src/solo.c
+++ b/apps/plugins/puzzles/src/solo.c
@@ -510,7 +510,9 @@ static const char *validate_params(const game_params *params, int full)
if ((params->c * params->r) > 31)
return "Unable to support more than 31 distinct symbols in a puzzle";
if (params->killer && params->c * params->r > 9)
- return "Killer puzzle dimensions must be smaller than 10.";
+ return "Killer puzzle dimensions must be smaller than 10";
+ if (params->xtype && params->c * params->r < 4)
+ return "X-type puzzle dimensions must be larger than 3";
return NULL;
}
@@ -3103,6 +3105,8 @@ static int check_valid(int cr, struct block_structure *blocks,
sfree(used);
return FALSE;
}
+
+ memset(used, FALSE, cr);
for (i = 0; i < cr; i++)
if (grid[diag1(i)] > 0 && grid[diag1(i)] <= cr)
used[grid[diag1(i)]-1] = TRUE;
@@ -3604,6 +3608,20 @@ static struct block_structure *gen_killer_cages(int cr, random_state *rs,
return b;
}
+static char *game_request_keys(const game_params *params)
+{
+ int i;
+ int cr = params->c * params->r;
+ char *keys = smalloc(cr+2);
+ for (i = 0; i < cr; i++) {
+ if (i<9) keys[i] = '1' + i;
+ else keys[i] = 'a' + i - 9;
+ }
+ keys[cr] = '\b';
+ keys[cr+1] = '\0';
+ return keys;
+}
+
static char *new_game_desc(const game_params *params, random_state *rs,
char **aux, int interactive)
{
@@ -5575,6 +5593,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/tents.c b/apps/plugins/puzzles/src/tents.c
index 48d420e..48da7f2 100644
--- a/apps/plugins/puzzles/src/tents.c
+++ b/apps/plugins/puzzles/src/tents.c
@@ -2626,6 +2626,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/towers.c b/apps/plugins/puzzles/src/towers.c
index 9ccc6ae..14b12c6 100644
--- a/apps/plugins/puzzles/src/towers.c
+++ b/apps/plugins/puzzles/src/towers.c
@@ -864,6 +864,21 @@ static const char *validate_desc(const game_params *params, const char *desc)
return NULL;
}
+static char *game_request_keys(const game_params *params)
+{
+ int i;
+ int w = params->w;
+ char *keys = smalloc(w+2);
+ keys[0] = '\b';
+ for (i = 0; i < w; i++) {
+ if (i<9) keys[i] = '1' + i;
+ else keys[i] = 'a' + i - 9;
+ }
+ keys[w] = '\b';
+ keys[w+1] = '\0';
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
@@ -1993,6 +2008,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
@@ -2072,6 +2088,17 @@ int main(int argc, char **argv)
break;
}
+ if (really_show_working) {
+ /*
+ * Now run the solver again at the last difficulty level we
+ * tried, but this time with diagnostics enabled.
+ */
+ solver_show_working = really_show_working;
+ memcpy(s->grid, s->clues->immutable, p->w * p->w);
+ ret = solver(p->w, s->clues->clues, s->grid,
+ diff < DIFFCOUNT ? diff : DIFFCOUNT-1);
+ }
+
if (diff == DIFFCOUNT) {
if (grade)
printf("Difficulty rating: ambiguous\n");
@@ -2084,9 +2111,6 @@ int main(int argc, char **argv)
else
printf("Difficulty rating: %s\n", towers_diffnames[ret]);
} else {
- solver_show_working = really_show_working;
- memcpy(s->grid, s->clues->immutable, p->w * p->w);
- ret = solver(p->w, s->clues->clues, s->grid, diff);
if (ret != diff)
printf("Puzzle is inconsistent\n");
else
diff --git a/apps/plugins/puzzles/src/tracks.c b/apps/plugins/puzzles/src/tracks.c
index 3899007..5122f89 100644
--- a/apps/plugins/puzzles/src/tracks.c
+++ b/apps/plugins/puzzles/src/tracks.c
@@ -2646,6 +2646,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/twiddle.c b/apps/plugins/puzzles/src/twiddle.c
index cbf6f76..600f445 100644
--- a/apps/plugins/puzzles/src/twiddle.c
+++ b/apps/plugins/puzzles/src/twiddle.c
@@ -1293,6 +1293,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/undead.c b/apps/plugins/puzzles/src/undead.c
index df0735e..87e559a 100644
--- a/apps/plugins/puzzles/src/undead.c
+++ b/apps/plugins/puzzles/src/undead.c
@@ -1314,6 +1314,11 @@ void num2grid(int num, int width, int height, int *x, int *y) {
return;
}
+static char *game_request_keys(const game_params *params)
+{
+ return dupstr("GVZ\b");
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
@@ -2716,6 +2721,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/unequal.c b/apps/plugins/puzzles/src/unequal.c
index cb477c9..d8204a4 100644
--- a/apps/plugins/puzzles/src/unequal.c
+++ b/apps/plugins/puzzles/src/unequal.c
@@ -1280,6 +1280,21 @@ fail:
return NULL;
}
+static char *game_request_keys(const game_params *params)
+{
+ int order = params->order;
+ char off = (order > 9) ? '0' : '1';
+ char *keys = smalloc(order + 2);
+ int i;
+ for(i = 0; i < order; i++) {
+ if (i==10) off = 'a'-10;
+ keys[i] = i + off;
+ }
+ keys[order] = '\b';
+ keys[order+1] = '\0';
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
@@ -2011,6 +2026,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/unruly.c b/apps/plugins/puzzles/src/unruly.c
index 9d2d592..b3057a7 100644
--- a/apps/plugins/puzzles/src/unruly.c
+++ b/apps/plugins/puzzles/src/unruly.c
@@ -1926,6 +1926,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
diff --git a/apps/plugins/puzzles/src/untangle.c b/apps/plugins/puzzles/src/untangle.c
index 09c36fe..0bcd04d 100644
--- a/apps/plugins/puzzles/src/untangle.c
+++ b/apps/plugins/puzzles/src/untangle.c
@@ -1623,6 +1623,7 @@ const struct game thegame = {
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,