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:
parent
4ed5727654
commit
f940276fd9
11 changed files with 2533 additions and 504 deletions
|
@ -19,6 +19,7 @@ src/misc.c
|
|||
src/penrose.c
|
||||
src/printing.c
|
||||
src/random.c
|
||||
src/sort.c
|
||||
src/tdq.c
|
||||
src/tree234.c
|
||||
src/version.c
|
||||
|
|
|
@ -9,7 +9,7 @@ my @presets = ();
|
|||
my %presets = ();
|
||||
my $maxval = 0;
|
||||
|
||||
while (<>) {
|
||||
while (<<>>) {
|
||||
chomp;
|
||||
if (/^(.*)(#.*): ([\d\.]+)$/) {
|
||||
push @presets, $1 unless defined $presets{$1};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- makefile -*-
|
||||
|
||||
DOMINOSA_EXTRA = laydomino
|
||||
DOMINOSA_EXTRA = laydomino dsf sort findloop
|
||||
|
||||
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
|
||||
|
||||
dominosasolver : [U] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
|
||||
dominosasolver : [C] dominosa[STANDALONE_SOLVER] DOMINOSA_EXTRA STANDALONE
|
||||
|
||||
!begin am gtk
|
||||
GAMES += dominosa
|
||||
!end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -212,28 +212,51 @@ function initPuzzle() {
|
|||
// button down (our puzzles don't want those events).
|
||||
mousedown = Module.cwrap('mousedown', 'void',
|
||||
['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) {
|
||||
if (event.button >= 3)
|
||||
return;
|
||||
|
||||
var xy = relative_mouse_coords(event, onscreen_canvas);
|
||||
mousedown(xy.x, xy.y, event.button);
|
||||
buttons_down |= 1 << event.button;
|
||||
var logbutton = 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);
|
||||
};
|
||||
mousemove = Module.cwrap('mousemove', 'void',
|
||||
['number', 'number', 'number']);
|
||||
onscreen_canvas.onmousemove = function(event) {
|
||||
if (buttons_down) {
|
||||
var down = buttons_down();
|
||||
if (down) {
|
||||
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',
|
||||
['number', 'number', 'number']);
|
||||
onscreen_canvas.onmouseup = function(event) {
|
||||
if (buttons_down & (1 << event.button)) {
|
||||
buttons_down ^= 1 << event.button;
|
||||
if (event.button >= 3)
|
||||
return;
|
||||
|
||||
if (button_phys2log[event.button] !== null) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "puzzles.h"
|
||||
|
||||
struct findloopstate {
|
||||
int parent, child, sibling;
|
||||
int parent, child, sibling, component_root;
|
||||
bool visited;
|
||||
int index, minindex, maxindex;
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
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[root].child = v;
|
||||
pv[v].component_root = v;
|
||||
debug(("%d is new child of root\n", v));
|
||||
|
||||
u = v;
|
||||
|
@ -116,6 +144,7 @@ bool findloop_run(struct findloopstate *pv, int nvertices,
|
|||
pv[w].child = -1;
|
||||
pv[w].sibling = pv[u].child;
|
||||
pv[w].parent = u;
|
||||
pv[w].component_root = pv[u].component_root;
|
||||
pv[u].child = w;
|
||||
}
|
||||
|
||||
|
|
|
@ -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));*/
|
||||
}
|
||||
|
||||
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;
|
||||
space *opposite = space_opposite_dot(state, tile, dot);
|
||||
bool toret;
|
||||
|
||||
if (opposite == NULL) {
|
||||
return;
|
||||
}
|
||||
if (opposite->flags & F_DOT) {
|
||||
return;
|
||||
}
|
||||
if (tile->flags & F_DOT)
|
||||
return false;
|
||||
if (opposite == NULL)
|
||||
return false;
|
||||
if (opposite->flags & F_DOT)
|
||||
return false;
|
||||
|
||||
toret = true;
|
||||
colors = snewn(state->w * state->h, int);
|
||||
check_complete(state, NULL, colors);
|
||||
if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2]) {
|
||||
sfree(colors);
|
||||
return;
|
||||
}
|
||||
if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2]) {
|
||||
sfree(colors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2])
|
||||
toret = false;
|
||||
if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2])
|
||||
toret = false;
|
||||
|
||||
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);
|
||||
add_assoc(state, tile, dot);
|
||||
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)) {
|
||||
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",
|
||||
sep, px, py, ui->dotx, ui->doty);
|
||||
}
|
||||
|
|
|
@ -171,6 +171,8 @@ midend *midend_new(frontend *fe, const game *ourgame,
|
|||
me->params = ourgame->default_params();
|
||||
me->game_id_change_notify_function = 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
|
||||
|
@ -261,8 +263,13 @@ static void midend_free_preset_menu(midend *me, struct preset_menu *menu)
|
|||
|
||||
void midend_free(midend *me)
|
||||
{
|
||||
int i;
|
||||
|
||||
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)
|
||||
drawing_free(me->drawing);
|
||||
random_free(me->random);
|
||||
|
|
|
@ -792,6 +792,12 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
|
|||
* unoccupied.
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -594,6 +594,32 @@ bool findloop_run(struct findloopstate *state, int nvertices,
|
|||
*/
|
||||
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
|
||||
* to a particular game. This is enclosed in a data structure so
|
||||
|
|
160
apps/plugins/puzzles/src/sort.c
Normal file
160
apps/plugins/puzzles/src/sort.c
Normal 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 */
|
Loading…
Reference in a new issue