puzzles: resync with upstream

This brings the puzzles source to upstream commit e2135d5. (I've made my own
changes on top of that.)

This brings in a couple bugfixes and a new solver for Dominosa.

Change-Id: I11d46b43171787832330a5e2e0d2f353f36f727d
This commit is contained in:
Franklin Wei 2019-05-15 18:16:27 -04:00
parent 4ed5727654
commit f940276fd9
11 changed files with 2533 additions and 504 deletions

View file

@ -19,6 +19,7 @@ src/misc.c
src/penrose.c src/penrose.c
src/printing.c src/printing.c
src/random.c src/random.c
src/sort.c
src/tdq.c src/tdq.c
src/tree234.c src/tree234.c
src/version.c src/version.c

View file

@ -9,7 +9,7 @@ my @presets = ();
my %presets = (); my %presets = ();
my $maxval = 0; my $maxval = 0;
while (<>) { while (<<>>) {
chomp; chomp;
if (/^(.*)(#.*): ([\d\.]+)$/) { if (/^(.*)(#.*): ([\d\.]+)$/) {
push @presets, $1 unless defined $presets{$1}; push @presets, $1 unless defined $presets{$1};

View file

@ -1,6 +1,6 @@
# -*- makefile -*- # -*- makefile -*-
DOMINOSA_EXTRA = laydomino DOMINOSA_EXTRA = laydomino dsf sort findloop
dominosa : [X] GTK COMMON dominosa DOMINOSA_EXTRA dominosa-icon|no-icon dominosa : [X] GTK COMMON dominosa DOMINOSA_EXTRA dominosa-icon|no-icon
@ -8,6 +8,9 @@ dominosa : [G] WINDOWS COMMON dominosa DOMINOSA_EXTRA dominosa.res|noicon.res
ALL += dominosa[COMBINED] DOMINOSA_EXTRA ALL += dominosa[COMBINED] DOMINOSA_EXTRA
dominosasolver : [U] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
dominosasolver : [C] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
!begin am gtk !begin am gtk
GAMES += dominosa GAMES += dominosa
!end !end

File diff suppressed because it is too large Load diff

View file

@ -212,28 +212,51 @@ function initPuzzle() {
// button down (our puzzles don't want those events). // button down (our puzzles don't want those events).
mousedown = Module.cwrap('mousedown', 'void', mousedown = Module.cwrap('mousedown', 'void',
['number', 'number', 'number']); ['number', 'number', 'number']);
buttons_down = 0;
button_phys2log = [null, null, null];
buttons_down = function() {
var i, toret = 0;
for (i = 0; i < 3; i++)
if (button_phys2log[i] !== null)
toret |= 1 << button_phys2log[i];
return toret;
};
onscreen_canvas.onmousedown = function(event) { onscreen_canvas.onmousedown = function(event) {
if (event.button >= 3)
return;
var xy = relative_mouse_coords(event, onscreen_canvas); var xy = relative_mouse_coords(event, onscreen_canvas);
mousedown(xy.x, xy.y, event.button); var logbutton = event.button;
buttons_down |= 1 << event.button; if (event.shiftKey)
logbutton = 1; // Shift-click overrides to middle button
else if (event.ctrlKey)
logbutton = 2; // Ctrl-click overrides to right button
mousedown(xy.x, xy.y, logbutton);
button_phys2log[event.button] = logbutton;
onscreen_canvas.setCapture(true); onscreen_canvas.setCapture(true);
}; };
mousemove = Module.cwrap('mousemove', 'void', mousemove = Module.cwrap('mousemove', 'void',
['number', 'number', 'number']); ['number', 'number', 'number']);
onscreen_canvas.onmousemove = function(event) { onscreen_canvas.onmousemove = function(event) {
if (buttons_down) { var down = buttons_down();
if (down) {
var xy = relative_mouse_coords(event, onscreen_canvas); var xy = relative_mouse_coords(event, onscreen_canvas);
mousemove(xy.x, xy.y, buttons_down); mousemove(xy.x, xy.y, down);
} }
}; };
mouseup = Module.cwrap('mouseup', 'void', mouseup = Module.cwrap('mouseup', 'void',
['number', 'number', 'number']); ['number', 'number', 'number']);
onscreen_canvas.onmouseup = function(event) { onscreen_canvas.onmouseup = function(event) {
if (buttons_down & (1 << event.button)) { if (event.button >= 3)
buttons_down ^= 1 << event.button; return;
if (button_phys2log[event.button] !== null) {
var xy = relative_mouse_coords(event, onscreen_canvas); var xy = relative_mouse_coords(event, onscreen_canvas);
mouseup(xy.x, xy.y, event.button); mouseup(xy.x, xy.y, button_phys2log[event.button]);
button_phys2log[event.button] = null;
} }
}; };

View file

@ -14,7 +14,7 @@
#include "puzzles.h" #include "puzzles.h"
struct findloopstate { struct findloopstate {
int parent, child, sibling; int parent, child, sibling, component_root;
bool visited; bool visited;
int index, minindex, maxindex; int index, minindex, maxindex;
int minreachable, maxreachable; int minreachable, maxreachable;
@ -57,6 +57,33 @@ bool findloop_is_loop_edge(struct findloopstate *pv, int u, int v)
return !(pv[u].bridge == v || pv[v].bridge == u); return !(pv[u].bridge == v || pv[v].bridge == u);
} }
static bool findloop_is_bridge_oneway(
struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices)
{
int r, total, below;
if (pv[u].bridge != v)
return false;
r = pv[u].component_root;
total = pv[r].maxindex - pv[r].minindex + 1;
below = pv[u].maxindex - pv[u].minindex + 1;
if (u_vertices)
*u_vertices = below;
if (v_vertices)
*v_vertices = total - below;
return true;
}
bool findloop_is_bridge(
struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices)
{
return (findloop_is_bridge_oneway(pv, u, v, u_vertices, v_vertices) ||
findloop_is_bridge_oneway(pv, v, u, v_vertices, u_vertices));
}
bool findloop_run(struct findloopstate *pv, int nvertices, bool findloop_run(struct findloopstate *pv, int nvertices,
neighbour_fn_t neighbour, void *ctx) neighbour_fn_t neighbour, void *ctx)
{ {
@ -94,6 +121,7 @@ bool findloop_run(struct findloopstate *pv, int nvertices,
*/ */
pv[v].sibling = pv[root].child; pv[v].sibling = pv[root].child;
pv[root].child = v; pv[root].child = v;
pv[v].component_root = v;
debug(("%d is new child of root\n", v)); debug(("%d is new child of root\n", v));
u = v; u = v;
@ -116,6 +144,7 @@ bool findloop_run(struct findloopstate *pv, int nvertices,
pv[w].child = -1; pv[w].child = -1;
pv[w].sibling = pv[u].child; pv[w].sibling = pv[u].child;
pv[w].parent = u; pv[w].parent = u;
pv[w].component_root = pv[u].component_root;
pv[u].child = w; pv[u].child = w;
} }

View file

@ -346,29 +346,44 @@ static void add_assoc(const game_state *state, space *tile, space *dot) {
tile->x, tile->y, dot->x, dot->y, dot->nassoc));*/ tile->x, tile->y, dot->x, dot->y, dot->nassoc));*/
} }
static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) { static bool ok_to_add_assoc_with_opposite_internal(
const game_state *state, space *tile, space *opposite)
{
int *colors; int *colors;
space *opposite = space_opposite_dot(state, tile, dot); bool toret;
if (opposite == NULL) { if (tile->flags & F_DOT)
return; return false;
} if (opposite == NULL)
if (opposite->flags & F_DOT) { return false;
return; if (opposite->flags & F_DOT)
} return false;
toret = true;
colors = snewn(state->w * state->h, int); colors = snewn(state->w * state->h, int);
check_complete(state, NULL, colors); check_complete(state, NULL, colors);
if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2]) {
sfree(colors); if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2])
return; toret = false;
} if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2])
if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2]) { toret = false;
sfree(colors);
return;
}
sfree(colors); sfree(colors);
return toret;
}
static bool ok_to_add_assoc_with_opposite(
const game_state *state, space *tile, space *dot)
{
space *opposite = space_opposite_dot(state, tile, dot);
return ok_to_add_assoc_with_opposite_internal(state, tile, opposite);
}
static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
space *opposite = space_opposite_dot(state, tile, dot);
assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
remove_assoc_with_opposite(state, tile); remove_assoc_with_opposite(state, tile);
add_assoc(state, tile, dot); add_assoc(state, tile, dot);
remove_assoc_with_opposite(state, opposite); remove_assoc_with_opposite(state, opposite);
@ -2596,8 +2611,15 @@ static char *interpret_move(const game_state *state, game_ui *ui,
*/ */
if (INUI(state, px, py)) { if (INUI(state, px, py)) {
sp = &SPACE(state, px, py); sp = &SPACE(state, px, py);
dot = &SPACE(state, ui->dotx, ui->doty);
if (!(sp->flags & F_DOT)) /*
* Exception: if it's not actually legal to add an arrow
* and its opposite at this position, we don't try,
* because otherwise we'd append an empty entry to the
* undo chain.
*/
if (ok_to_add_assoc_with_opposite(state, sp, dot))
sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d", sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
sep, px, py, ui->dotx, ui->doty); sep, px, py, ui->dotx, ui->doty);
} }

View file

@ -171,6 +171,8 @@ midend *midend_new(frontend *fe, const game *ourgame,
me->params = ourgame->default_params(); me->params = ourgame->default_params();
me->game_id_change_notify_function = NULL; me->game_id_change_notify_function = NULL;
me->game_id_change_notify_ctx = NULL; me->game_id_change_notify_ctx = NULL;
me->encoded_presets = NULL;
me->n_encoded_presets = 0;
/* /*
* Allow environment-based changing of the default settings by * Allow environment-based changing of the default settings by
@ -261,8 +263,13 @@ static void midend_free_preset_menu(midend *me, struct preset_menu *menu)
void midend_free(midend *me) void midend_free(midend *me)
{ {
int i;
midend_free_game(me); midend_free_game(me);
for (i = 0; i < me->n_encoded_presets; i++)
sfree(me->encoded_presets[i]);
sfree(me->encoded_presets);
if (me->drawing) if (me->drawing)
drawing_free(me->drawing); drawing_free(me->drawing);
random_free(me->random); random_free(me->random);

View file

@ -792,6 +792,12 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
* unoccupied. * unoccupied.
*/ */
ui->dragging = false; ui->dragging = false;
/*
* Also, cancel a keyboard-driven jump if one is half way to being
* input.
*/
ui->cur_jumping = false;
} }
#define PREFERRED_TILE_SIZE 33 #define PREFERRED_TILE_SIZE 33

View file

@ -594,6 +594,32 @@ bool findloop_run(struct findloopstate *state, int nvertices,
*/ */
bool findloop_is_loop_edge(struct findloopstate *state, int u, int v); bool findloop_is_loop_edge(struct findloopstate *state, int u, int v);
/*
* Alternative query function, which returns true if the u-v edge is a
* _bridge_, i.e. a non-loop edge, i.e. an edge whose removal would
* disconnect a currently connected component of the graph.
*
* If the return value is true, then the numbers of vertices that
* would be in the new components containing u and v are written into
* u_vertices and v_vertices respectively.
*/
bool findloop_is_bridge(
struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices);
/*
* Helper function to sort an array. Differs from standard qsort in
* that it takes a context parameter that is passed to the compare
* function.
*
* I wrap it in a macro so that you only need to give the element
* count of the array. The element size is determined by sizeof.
*/
typedef int (*arraysort_cmpfn_t)(const void *av, const void *bv, void *ctx);
void arraysort_fn(void *array, size_t nmemb, size_t size,
arraysort_cmpfn_t cmp, void *ctx);
#define arraysort(array, nmemb, cmp, ctx) \
arraysort_fn(array, nmemb, sizeof(*(array)), cmp, ctx)
/* /*
* Data structure containing the function calls and data specific * Data structure containing the function calls and data specific
* to a particular game. This is enclosed in a data structure so * to a particular game. This is enclosed in a data structure so

View file

@ -0,0 +1,160 @@
/*
* Implement arraysort() defined in puzzles.h.
*
* Strategy: heapsort.
*/
#include <stddef.h>
#include <string.h>
#include "puzzles.h"
static void memswap(void *av, void *bv, size_t size)
{
char t[4096];
char *a = (char *)av, *b = (char *)bv;
while (size > 0) {
size_t thissize = size < sizeof(t) ? size : sizeof(t);
memcpy(t, a, thissize);
memcpy(a, b, thissize);
memcpy(b, t, thissize);
size -= thissize;
a += thissize;
b += thissize;
}
}
#define PTR(i) ((char *)array + size * (i))
#define SWAP(i,j) memswap(PTR(i), PTR(j), size)
#define CMP(i,j) cmp(PTR(i), PTR(j), ctx)
#define LCHILD(i) (2*(i)+1)
#define RCHILD(i) (2*(i)+2)
#define PARENT(i) (((i)-1)/2)
static void downheap(void *array, size_t nmemb, size_t size,
arraysort_cmpfn_t cmp, void *ctx, size_t i)
{
while (LCHILD(i) < nmemb) {
/* Identify the smallest element out of i and its children. */
size_t j = i;
if (CMP(j, LCHILD(i)) < 0)
j = LCHILD(i);
if (RCHILD(i) < nmemb &&
CMP(j, RCHILD(i)) < 0)
j = RCHILD(i);
if (j == i)
return; /* smallest element is already where it should be */
SWAP(j, i);
i = j;
}
}
void arraysort_fn(void *array, size_t nmemb, size_t size,
arraysort_cmpfn_t cmp, void *ctx)
{
size_t i;
if (nmemb < 2)
return; /* trivial */
/*
* Stage 1: build the heap.
*
* Linear-time if we do it by downheaping the elements in
* decreasing order of index, instead of the more obvious approach
* of upheaping in increasing order. (Also, it means we don't need
* the upheap function at all.)
*
* We don't need to downheap anything in the second half of the
* array, because it can't have any children to swap with anyway.
*/
for (i = PARENT(nmemb-1) + 1; i-- > 0 ;)
downheap(array, nmemb, size, cmp, ctx, i);
/*
* Stage 2: dismantle the heap by repeatedly swapping the root
* element (at index 0) into the last position and then
* downheaping the new root.
*/
for (i = nmemb-1; i > 0; i--) {
SWAP(0, i);
downheap(array, i, size, cmp, ctx, 0);
}
}
#ifdef SORT_TEST
#include <stdlib.h>
#include <time.h>
int testcmp(const void *av, const void *bv, void *ctx)
{
int a = *(const int *)av, b = *(const int *)bv;
const int *keys = (const int *)ctx;
return keys[a] < keys[b] ? -1 : keys[a] > keys[b] ? +1 : 0;
}
int resetcmp(const void *av, const void *bv)
{
int a = *(const int *)av, b = *(const int *)bv;
return a < b ? -1 : a > b ? +1 : 0;
}
int main(int argc, char **argv)
{
typedef int Array[3723];
Array data, keys;
int iteration;
unsigned seed;
seed = (argc > 1 ? strtoul(argv[1], NULL, 0) : time(NULL));
printf("Random seed = %u\n", seed);
srand(seed);
for (iteration = 0; iteration < 10000; iteration++) {
int j;
const char *fail = NULL;
for (j = 0; j < lenof(data); j++) {
data[j] = j;
keys[j] = rand();
}
arraysort(data, lenof(data), testcmp, keys);
for (j = 1; j < lenof(data); j++) {
if (keys[data[j]] < keys[data[j-1]])
fail = "output misordered";
}
if (!fail) {
Array reset;
memcpy(reset, data, sizeof(data));
qsort(reset, lenof(reset), sizeof(*reset), resetcmp);
for (j = 0; j < lenof(reset); j++)
if (reset[j] != j)
fail = "output not permuted";
}
if (fail) {
printf("Failed at iteration %d: %s\n", iteration, fail);
printf("Key values:\n");
for (j = 0; j < lenof(keys); j++)
printf(" [%2d] %10d\n", j, keys[j]);
printf("Output sorted order:\n");
for (j = 0; j < lenof(data); j++)
printf(" [%2d] %10d\n", data[j], keys[data[j]]);
return 1;
}
}
printf("OK\n");
return 0;
}
#endif /* SORT_TEST */