I'm sick and tired of having unfinished puzzle code lying around on

several different systems in strange directories. So I'm creating an
`unfinished' directory within source control, and centralising all
my half-finished, half-baked or otherwise half-arsed puzzle
implementations into it. Herewith Sokoban (playable but rubbish
generation), Pearl (Masyu - rubbish generation and nothing else),
Path (Number Link - rubbish generation and nothing else) and NumGame
(the Countdown numbers game - currently just a solver and not even a
generator yet).

[originally from svn r6883]
This commit is contained in:
Simon Tatham
2006-10-29 09:41:02 +00:00
parent b7356cd209
commit b3364419da
7 changed files with 4593 additions and 0 deletions

9
unfinished/README Normal file
View File

@ -0,0 +1,9 @@
This subdirectory contains puzzle implementations which are
half-written, fundamentally flawed, or in other ways unready to be
shipped as part of the polished Puzzles collection.
Those puzzles which have .R files can be built as part of the
Puzzles collection by symlinking their source files into the parent
directory and re-running mkfiles.pl. Anything without a .R file
isn't even finished enough to do that, and you should read the
source file itself to find out the status.

914
unfinished/numgame.c Normal file
View File

@ -0,0 +1,914 @@
/*
* This program implements a breadth-first search which
* exhaustively solves the Countdown numbers game, and related
* games with slightly different rule sets such as `Flippo'.
*
* Currently it is simply a standalone command-line utility to
* which you provide a set of numbers and it tells you everything
* it can make together with how many different ways it can be
* made. I would like ultimately to turn it into the generator for
* a Puzzles puzzle, but I haven't even started on writing a
* Puzzles user interface yet.
*/
/*
* TODO:
*
* - start thinking about difficulty ratings
* + anything involving associative operations will be flagged
* as many-paths because of the associative options (e.g.
* 2*3*4 can be (2*3)*4 or 2*(3*4), or indeed (2*4)*3). This
* is probably a _good_ thing, since those are unusually
* easy.
* + tree-structured calculations ((a*b)/(c+d)) have multiple
* paths because the independent branches of the tree can be
* evaluated in either order, whereas straight-line
* calculations with no branches will be considered easier.
* Can we do anything about this? It's certainly not clear to
* me that tree-structure calculations are _easier_, although
* I'm also not convinced they're harder.
* + I think for a realistic difficulty assessment we must also
* consider the `obviousness' of the arithmetic operations in
* some heuristic sense, and also (in Countdown) how many
* numbers ended up being used.
* - actually try some generations
* - at this point we're probably ready to start on the Puzzles
* integration.
*/
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include "puzzles.h"
#include "tree234.h"
/*
* To search for numbers we can make, we employ a breadth-first
* search across the space of sets of input numbers. That is, for
* example, we start with the set (3,6,25,50,75,100); we apply
* moves which involve combining two numbers (e.g. adding the 50
* and the 75 takes us to the set (3,6,25,100,125); and then we see
* if we ever end up with a set containing (say) 952.
*
* If the rules are changed so that all the numbers must be used,
* this is easy to adjust to: we simply see if we end up with a set
* containing _only_ (say) 952.
*
* Obviously, we can vary the rules about permitted arithmetic
* operations simply by altering the set of valid moves in the bfs.
* However, there's one common rule in this sort of puzzle which
* takes a little more thought, and that's _concatenation_. For
* example, if you are given (say) four 4s and required to make 10,
* you are permitted to combine two of the 4s into a 44 to begin
* with, making (44-4)/4 = 10. However, you are generally not
* allowed to concatenate two numbers that _weren't_ both in the
* original input set (you couldn't multiply two 4s to get 16 and
* then concatenate a 4 on to it to make 164), so concatenation is
* not an operation which is valid in all situations.
*
* We could enforce this restriction by storing a flag alongside
* each number indicating whether or not it's an original number;
* the rules being that concatenation of two numbers is only valid
* if they both have the original flag, and that its output _also_
* has the original flag (so that you can concatenate three 4s into
* a 444), but that applying any other arithmetic operation clears
* the original flag on the output. However, we can get marginally
* simpler than that by observing that since concatenation has to
* happen to a number before any other operation, we can simply
* place all the concatenations at the start of the search. In
* other words, we have a global flag on an entire number _set_
* which indicates whether we are still permitted to perform
* concatenations; if so, we can concatenate any of the numbers in
* that set. Performing any other operation clears the flag.
*/
#define SETFLAG_CONCAT 1 /* we can do concatenation */
struct sets;
struct set {
int *numbers; /* rationals stored as n,d pairs */
short nnumbers; /* # of rationals, so half # of ints */
short flags; /* SETFLAG_CONCAT only, at present */
struct set *prev; /* index of ancestor set in set list */
unsigned char pa, pb, po, pr; /* operation that got here from prev */
int npaths; /* number of ways to reach this set */
};
struct output {
int number;
struct set *set;
int index; /* which number in the set is it? */
int npaths; /* number of ways to reach this */
};
#define SETLISTLEN 1024
#define NUMBERLISTLEN 32768
#define OUTPUTLISTLEN 1024
struct operation;
struct sets {
struct set **setlists;
int nsets, nsetlists, setlistsize;
tree234 *settree;
int **numberlists;
int nnumbers, nnumberlists, numberlistsize;
struct output **outputlists;
int noutputs, noutputlists, outputlistsize;
tree234 *outputtree;
const struct operation *const *ops;
};
#define OPFLAG_NEEDS_CONCAT 1
#define OPFLAG_KEEPS_CONCAT 2
struct operation {
/*
* Most operations should be shown in the output working, but
* concatenation should not; we just take the result of the
* concatenation and assume that it's obvious how it was
* derived.
*/
int display;
/*
* Text display of the operator.
*/
char *text;
/*
* Flags dictating when the operator can be applied.
*/
int flags;
/*
* Priority of the operator (for avoiding unnecessary
* parentheses when formatting it into a string).
*/
int priority;
/*
* Associativity of the operator. Bit 0 means we need parens
* when the left operand of one of these operators is another
* instance of it, e.g. (2^3)^4. Bit 1 means we need parens
* when the right operand is another instance of the same
* operator, e.g. 2-(3-4). Thus:
*
* - this field is 0 for a fully associative operator, since
* we never need parens.
* - it's 1 for a right-associative operator.
* - it's 2 for a left-associative operator.
* - it's 3 for a _non_-associative operator (which always
* uses parens just to be sure).
*/
int assoc;
/*
* Whether the operator is commutative. Saves time in the
* search if we don't have to try it both ways round.
*/
int commutes;
/*
* Function which implements the operator. Returns TRUE on
* success, FALSE on failure. Takes two rationals and writes
* out a third.
*/
int (*perform)(int *a, int *b, int *output);
};
struct rules {
const struct operation *const *ops;
int use_all;
};
#define MUL(r, a, b) do { \
(r) = (a) * (b); \
if ((b) && (a) && (r) / (b) != (a)) return FALSE; \
} while (0)
#define ADD(r, a, b) do { \
(r) = (a) + (b); \
if ((a) > 0 && (b) > 0 && (r) < 0) return FALSE; \
if ((a) < 0 && (b) < 0 && (r) > 0) return FALSE; \
} while (0)
#define OUT(output, n, d) do { \
int g = gcd((n),(d)); \
if ((d) < 0) g = -g; \
(output)[0] = (n)/g; \
(output)[1] = (d)/g; \
assert((output)[1] > 0); \
} while (0)
static int gcd(int x, int y)
{
while (x != 0 && y != 0) {
int t = x;
x = y;
y = t % y;
}
return abs(x + y); /* i.e. whichever one isn't zero */
}
static int perform_add(int *a, int *b, int *output)
{
int at, bt, tn, bn;
/*
* a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1)
*/
MUL(at, a[0], b[1]);
MUL(bt, b[0], a[1]);
ADD(tn, at, bt);
MUL(bn, a[1], b[1]);
OUT(output, tn, bn);
return TRUE;
}
static int perform_sub(int *a, int *b, int *output)
{
int at, bt, tn, bn;
/*
* a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1)
*/
MUL(at, a[0], b[1]);
MUL(bt, b[0], a[1]);
ADD(tn, at, -bt);
MUL(bn, a[1], b[1]);
OUT(output, tn, bn);
return TRUE;
}
static int perform_mul(int *a, int *b, int *output)
{
int tn, bn;
/*
* a0/a1 * b0/b1 = (a0*b0) / (a1*b1)
*/
MUL(tn, a[0], b[0]);
MUL(bn, a[1], b[1]);
OUT(output, tn, bn);
return TRUE;
}
static int perform_div(int *a, int *b, int *output)
{
int tn, bn;
/*
* Division by zero is outlawed.
*/
if (b[0] == 0)
return FALSE;
/*
* a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
*/
MUL(tn, a[0], b[1]);
MUL(bn, a[1], b[0]);
OUT(output, tn, bn);
return TRUE;
}
static int perform_exact_div(int *a, int *b, int *output)
{
int tn, bn;
/*
* Division by zero is outlawed.
*/
if (b[0] == 0)
return FALSE;
/*
* a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
*/
MUL(tn, a[0], b[1]);
MUL(bn, a[1], b[0]);
OUT(output, tn, bn);
/*
* Exact division means we require the result to be an integer.
*/
return (output[1] == 1);
}
static int perform_concat(int *a, int *b, int *output)
{
int t1, t2, p10;
/*
* We can't concatenate anything which isn't an integer.
*/
if (a[1] != 1 || b[1] != 1)
return FALSE;
/*
* For concatenation, we can safely assume leading zeroes
* aren't an issue. It isn't clear whether they `should' be
* allowed, but it turns out not to matter: concatenating a
* leading zero on to a number in order to harmlessly get rid
* of the zero is never necessary because unwanted zeroes can
* be disposed of by adding them to something instead. So we
* disallow them always.
*
* The only other possibility is that you might want to
* concatenate a leading zero on to something and then
* concatenate another non-zero digit on to _that_ (to make,
* for example, 106); but that's also unnecessary, because you
* can make 106 just as easily by concatenating the 0 on to the
* _end_ of the 1 first.
*/
if (a[0] == 0)
return FALSE;
/*
* Find the smallest power of ten strictly greater than b. This
* is the power of ten by which we'll multiply a.
*
* Special case: we must multiply a by at least 10, even if b
* is zero.
*/
p10 = 10;
while (p10 <= (INT_MAX/10) && p10 <= b[0])
p10 *= 10;
if (p10 > INT_MAX/10)
return FALSE; /* integer overflow */
MUL(t1, p10, a[0]);
ADD(t2, t1, b[0]);
OUT(output, t2, 1);
return TRUE;
}
const static struct operation op_add = {
TRUE, "+", 0, 10, 0, TRUE, perform_add
};
const static struct operation op_sub = {
TRUE, "-", 0, 10, 2, FALSE, perform_sub
};
const static struct operation op_mul = {
TRUE, "*", 0, 20, 0, TRUE, perform_mul
};
const static struct operation op_div = {
TRUE, "/", 0, 20, 2, FALSE, perform_div
};
const static struct operation op_xdiv = {
TRUE, "/", 0, 20, 2, FALSE, perform_exact_div
};
const static struct operation op_concat = {
FALSE, "", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT,
1000, 0, FALSE, perform_concat
};
/*
* In Countdown, divisions resulting in fractions are disallowed.
* http://www.askoxford.com/wordgames/countdown/rules/
*/
const static struct operation *const ops_countdown[] = {
&op_add, &op_mul, &op_sub, &op_xdiv, NULL
};
const static struct rules rules_countdown = {
ops_countdown, FALSE
};
/*
* A slightly different rule set which handles the reasonably well
* known puzzle of making 24 using two 3s and two 8s. For this we
* need rational rather than integer division.
*/
const static struct operation *const ops_3388[] = {
&op_add, &op_mul, &op_sub, &op_div, NULL
};
const static struct rules rules_3388 = {
ops_3388, TRUE
};
/*
* A still more permissive rule set usable for the four-4s problem
* and similar things. Permits concatenation.
*/
const static struct operation *const ops_four4s[] = {
&op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL
};
const static struct rules rules_four4s = {
ops_four4s, TRUE
};
#define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \
(long long)(b)[0] * (a)[1] )
static int addtoset(struct set *set, int newnumber[2])
{
int i, j;
/* Find where we want to insert the new number */
for (i = 0; i < set->nnumbers &&
ratcmp(set->numbers+2*i, <, newnumber); i++);
/* Move everything else up */
for (j = set->nnumbers; j > i; j--) {
set->numbers[2*j] = set->numbers[2*j-2];
set->numbers[2*j+1] = set->numbers[2*j-1];
}
/* Insert the new number */
set->numbers[2*i] = newnumber[0];
set->numbers[2*i+1] = newnumber[1];
set->nnumbers++;
return i;
}
#define ensure(array, size, newlen, type) do { \
if ((newlen) > (size)) { \
(size) = (newlen) + 512; \
(array) = sresize((array), (size), type); \
} \
} while (0)
static int setcmp(void *av, void *bv)
{
struct set *a = (struct set *)av;
struct set *b = (struct set *)bv;
int i;
if (a->nnumbers < b->nnumbers)
return -1;
else if (a->nnumbers > b->nnumbers)
return +1;
if (a->flags < b->flags)
return -1;
else if (a->flags > b->flags)
return +1;
for (i = 0; i < a->nnumbers; i++) {
if (ratcmp(a->numbers+2*i, <, b->numbers+2*i))
return -1;
else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i))
return +1;
}
return 0;
}
static int outputcmp(void *av, void *bv)
{
struct output *a = (struct output *)av;
struct output *b = (struct output *)bv;
if (a->number < b->number)
return -1;
else if (a->number > b->number)
return +1;
return 0;
}
static int outputfindcmp(void *av, void *bv)
{
int *a = (int *)av;
struct output *b = (struct output *)bv;
if (*a < b->number)
return -1;
else if (*a > b->number)
return +1;
return 0;
}
static void addset(struct sets *s, struct set *set, struct set *prev)
{
struct set *s2;
int npaths = (prev ? prev->npaths : 1);
assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN);
s2 = add234(s->settree, set);
if (s2 == set) {
/*
* New set added to the tree.
*/
set->prev = prev;
set->npaths = npaths;
s->nsets++;
s->nnumbers += 2 * set->nnumbers;
} else {
/*
* Rediscovered an existing set. Update its npaths only.
*/
s2->npaths += npaths;
}
}
static struct set *newset(struct sets *s, int nnumbers, int flags)
{
struct set *sn;
ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *);
while (s->nsetlists <= s->nsets / SETLISTLEN)
s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set);
sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN;
if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN)
s->nnumbers = s->nnumberlists * NUMBERLISTLEN;
ensure(s->numberlists, s->numberlistsize,
s->nnumbers/NUMBERLISTLEN+1, int *);
while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN)
s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int);
sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] +
s->nnumbers % NUMBERLISTLEN;
/*
* Start the set off empty.
*/
sn->nnumbers = 0;
sn->flags = flags;
return sn;
}
static int addoutput(struct sets *s, struct set *ss, int index, int *n)
{
struct output *o, *o2;
/*
* Target numbers are always integers.
*/
if (ss->numbers[2*index+1] != 1)
return FALSE;
ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1,
struct output *);
while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN)
s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN,
struct output);
o = s->outputlists[s->noutputs / OUTPUTLISTLEN] +
s->noutputs % OUTPUTLISTLEN;
o->number = ss->numbers[2*index];
o->set = ss;
o->index = index;
o->npaths = ss->npaths;
o2 = add234(s->outputtree, o);
if (o2 != o) {
o2->npaths += o->npaths;
} else {
s->noutputs++;
}
*n = o->number;
return TRUE;
}
static struct sets *do_search(int ninputs, int *inputs,
const struct rules *rules, int *target)
{
struct sets *s;
struct set *sn;
int qpos, i;
const struct operation *const *ops = rules->ops;
s = snew(struct sets);
s->setlists = NULL;
s->nsets = s->nsetlists = s->setlistsize = 0;
s->numberlists = NULL;
s->nnumbers = s->nnumberlists = s->numberlistsize = 0;
s->outputlists = NULL;
s->noutputs = s->noutputlists = s->outputlistsize = 0;
s->settree = newtree234(setcmp);
s->outputtree = newtree234(outputcmp);
s->ops = ops;
/*
* Start with the input set.
*/
sn = newset(s, ninputs, SETFLAG_CONCAT);
for (i = 0; i < ninputs; i++) {
int newnumber[2];
newnumber[0] = inputs[i];
newnumber[1] = 1;
addtoset(sn, newnumber);
}
addset(s, sn, NULL);
/*
* Now perform the breadth-first search: keep looping over sets
* until we run out of steam.
*/
qpos = 0;
while (qpos < s->nsets) {
struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN;
struct set *sn;
int i, j, k, m;
/*
* Record all the valid output numbers in this state. We
* can always do this if there's only one number in the
* state; otherwise, we can only do it if we aren't
* required to use all the numbers in coming to our answer.
*/
if (ss->nnumbers == 1 || !rules->use_all) {
for (i = 0; i < ss->nnumbers; i++) {
int n;
if (addoutput(s, ss, i, &n) && target && n == *target)
return s;
}
}
/*
* Try every possible operation from this state.
*/
for (k = 0; ops[k] && ops[k]->perform; k++) {
if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) &&
!(ss->flags & SETFLAG_CONCAT))
continue; /* can't use this operation here */
for (i = 0; i < ss->nnumbers; i++) {
for (j = 0; j < ss->nnumbers; j++) {
int n[2];
if (i == j)
continue; /* can't combine a number with itself */
if (i > j && ops[k]->commutes)
continue; /* no need to do this both ways round */
if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n))
continue; /* operation failed */
sn = newset(s, ss->nnumbers-1, ss->flags);
if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT))
sn->flags &= ~SETFLAG_CONCAT;
for (m = 0; m < ss->nnumbers; m++) {
if (m == i || m == j)
continue;
sn->numbers[2*sn->nnumbers] = ss->numbers[2*m];
sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1];
sn->nnumbers++;
}
sn->pa = i;
sn->pb = j;
sn->po = k;
sn->pr = addtoset(sn, n);
addset(s, sn, ss);
}
}
}
qpos++;
}
return s;
}
static void free_sets(struct sets *s)
{
int i;
freetree234(s->settree);
freetree234(s->outputtree);
for (i = 0; i < s->nsetlists; i++)
sfree(s->setlists[i]);
sfree(s->setlists);
for (i = 0; i < s->nnumberlists; i++)
sfree(s->numberlists[i]);
sfree(s->numberlists);
for (i = 0; i < s->noutputlists; i++)
sfree(s->outputlists[i]);
sfree(s->outputlists);
sfree(s);
}
/*
* Construct a text formula for producing a given output.
*/
void mkstring_recurse(char **str, int *len,
struct sets *s, struct set *ss, int index,
int priority, int assoc, int child)
{
if (ss->prev && index != ss->pr) {
int pi;
/*
* This number was passed straight down from this set's
* predecessor. Find its index in the previous set and
* recurse to there.
*/
pi = index;
assert(pi != ss->pr);
if (pi > ss->pr)
pi--;
if (pi >= min(ss->pa, ss->pb)) {
pi++;
if (pi >= max(ss->pa, ss->pb))
pi++;
}
mkstring_recurse(str, len, s, ss->prev, pi, priority, assoc, child);
} else if (ss->prev && index == ss->pr &&
s->ops[ss->po]->display) {
/*
* This number was created by a displayed operator in the
* transition from this set to its predecessor. Hence we
* write an open paren, then recurse into the first
* operand, then write the operator, then the second
* operand, and finally close the paren.
*/
char *op;
int parens, thispri, thisassoc;
/*
* Determine whether we need parentheses.
*/
thispri = s->ops[ss->po]->priority;
thisassoc = s->ops[ss->po]->assoc;
parens = (thispri < priority ||
(thispri == priority && (assoc & child)));
if (parens) {
if (str)
*(*str)++ = '(';
if (len)
(*len)++;
}
mkstring_recurse(str, len, s, ss->prev, ss->pa, thispri, thisassoc, 1);
for (op = s->ops[ss->po]->text; *op; op++) {
if (str)
*(*str)++ = *op;
if (len)
(*len)++;
}
mkstring_recurse(str, len, s, ss->prev, ss->pb, thispri, thisassoc, 2);
if (parens) {
if (str)
*(*str)++ = ')';
if (len)
(*len)++;
}
} else {
/*
* This number is either an original, or something formed
* by a non-displayed operator (concatenation). Either way,
* we display it as is.
*/
char buf[80], *p;
int blen;
blen = sprintf(buf, "%d", ss->numbers[2*index]);
if (ss->numbers[2*index+1] != 1)
blen += sprintf(buf+blen, "/%d", ss->numbers[2*index+1]);
assert(blen < lenof(buf));
for (p = buf; *p; p++) {
if (str)
*(*str)++ = *p;
if (len)
(*len)++;
}
}
}
char *mkstring(struct sets *s, struct output *o)
{
int len;
char *str, *p;
len = 0;
mkstring_recurse(NULL, &len, s, o->set, o->index, 0, 0, 0);
str = snewn(len+1, char);
p = str;
mkstring_recurse(&p, NULL, s, o->set, o->index, 0, 0, 0);
assert(p - str <= len);
*p = '\0';
return str;
}
int main(int argc, char **argv)
{
int doing_opts = TRUE;
const struct rules *rules = NULL;
char *pname = argv[0];
int got_target = FALSE, target = 0;
int numbers[10], nnumbers = 0;
int verbose = FALSE;
int pathcounts = FALSE;
struct output *o;
struct sets *s;
int i, start, limit;
while (--argc) {
char *p = *++argv;
int c;
if (doing_opts && *p == '-') {
p++;
if (!strcmp(p, "-")) {
doing_opts = FALSE;
continue;
} else while (*p) switch (c = *p++) {
case 'C':
rules = &rules_countdown;
break;
case 'B':
rules = &rules_3388;
break;
case 'D':
rules = &rules_four4s;
break;
case 'v':
verbose = TRUE;
break;
case 'p':
pathcounts = TRUE;
break;
case 't':
{
char *v;
if (*p) {
v = p;
p = NULL;
} else if (--argc) {
v = *++argv;
} else {
fprintf(stderr, "%s: option '-%c' expects an"
" argument\n", pname, c);
return 1;
}
switch (c) {
case 't':
got_target = TRUE;
target = atoi(v);
break;
}
}
break;
default:
fprintf(stderr, "%s: option '-%c' not"
" recognised\n", pname, c);
return 1;
}
} else {
if (nnumbers >= lenof(numbers)) {
fprintf(stderr, "%s: internal limit of %d numbers exceeded\n",
pname, lenof(numbers));
return 1;
} else {
numbers[nnumbers++] = atoi(p);
}
}
}
if (!rules) {
fprintf(stderr, "%s: no rule set specified; use -C,-B,-D\n", pname);
return 1;
}
if (!nnumbers) {
fprintf(stderr, "%s: no input numbers specified\n", pname);
return 1;
}
s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL));
if (got_target) {
o = findrelpos234(s->outputtree, &target, outputfindcmp,
REL234_LE, &start);
if (!o)
start = -1;
o = findrelpos234(s->outputtree, &target, outputfindcmp,
REL234_GE, &limit);
if (!o)
limit = -1;
assert(start != -1 || limit != -1);
if (start == -1)
start = limit;
else if (limit == -1)
limit = start;
limit++;
} else {
start = 0;
limit = count234(s->outputtree);
}
for (i = start; i < limit; i++) {
o = index234(s->outputtree, i);
printf("%d", o->number);
if (pathcounts)
printf(" [%d]", o->npaths);
if (got_target || verbose) {
char *p = mkstring(s, o);
printf(" = %s", p);
sfree(p);
}
printf("\n");
}
free_sets(s);
return 0;
}

786
unfinished/path.c Normal file
View File

@ -0,0 +1,786 @@
/*
* Experimental grid generator for Nikoli's `Number Link' puzzle.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "puzzles.h"
/*
* 2005-07-08: This is currently a Path grid generator which will
* construct valid grids at a plausible speed. However, the grids
* are not of suitable quality to be used directly as puzzles.
*
* The basic strategy is to start with an empty grid, and
* repeatedly either (a) add a new path to it, or (b) extend one
* end of a path by one square in some direction and push other
* paths into new shapes in the process. The effect of this is that
* we are able to construct a set of paths which between them fill
* the entire grid.
*
* Quality issues: if we set the main loop to do (a) where possible
* and (b) only where necessary, we end up with a grid containing a
* few too many small paths, which therefore doesn't make for an
* interesting puzzle. If we reverse the priority so that we do (b)
* where possible and (a) only where necessary, we end up with some
* staggeringly interwoven grids with very very few separate paths,
* but the result of this is that there's invariably a solution
* other than the intended one which leaves many grid squares
* unfilled. There's also a separate problem which is that many
* grids have really boring and obvious paths in them, such as the
* entire bottom row of the grid being taken up by a single path.
*
* It's not impossible that a few tweaks might eliminate or reduce
* the incidence of boring paths, and might also find a happy
* medium between too many and too few. There remains the question
* of unique solutions, however. I fear there is no alternative but
* to write - somehow! - a solver.
*
* While I'm here, some notes on UI strategy for the parts of the
* puzzle implementation that _aren't_ the generator:
*
* - data model is to track connections between adjacent squares,
* so that you aren't limited to extending a path out from each
* number but can also mark sections of path which you know
* _will_ come in handy later.
*
* - user interface is to click in one square and drag to an
* adjacent one, thus creating a link between them. We can
* probably tolerate rapid mouse motion causing a drag directly
* to a square which is a rook move away, but any other rapid
* motion is ambiguous and probably the best option is to wait
* until the mouse returns to a square we know how to reach.
*
* - a drag causing the current path to backtrack has the effect
* of removing bits of it.
*
* - the UI should enforce at all times the constraint that at
* most two links can come into any square.
*
* - my Cunning Plan for actually implementing this: the game_ui
* contains a grid-sized array, which is copied from the current
* game_state on starting a drag. While a drag is active, the
* contents of the game_ui is adjusted with every mouse motion,
* and is displayed _in place_ of the game_state itself. On
* termination of a drag, the game_ui array is copied back into
* the new game_state (or rather, a string move is encoded which
* has precisely the set of link changes to cause that effect).
*/
/*
* Standard notation for directions.
*/
#define L 0
#define U 1
#define R 2
#define D 3
#define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0)
#define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0)
/*
* Perform a breadth-first search over a grid of squares with the
* colour of square (X,Y) given by grid[Y*w+X]. The search begins
* at (x,y), and finds all squares which are the same colour as
* (x,y) and reachable from it by orthogonal moves. On return:
* - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if
* unreachable or a different colour
* - the returned value is the number of reachable squares,
* including (x,y) itself
* - list[0] up to list[returned value - 1] list those squares, in
* increasing order of distance from (x,y) (and in arbitrary
* order within that).
*/
static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list)
{
int i, j, c, listsize, listdone;
/*
* Start by clearing the output arrays.
*/
for (i = 0; i < w*h; i++)
dist[i] = list[i] = -1;
/*
* Set up the initial list.
*/
listsize = 1;
listdone = 0;
list[0] = y*w+x;
dist[y*w+x] = 0;
c = grid[y*w+x];
/*
* Repeatedly process a square and add any extra squares to the
* end of list.
*/
while (listdone < listsize) {
i = list[listdone++];
y = i / w;
x = i % w;
for (j = 0; j < 4; j++) {
int xx, yy, ii;
xx = x + DX(j);
yy = y + DY(j);
ii = yy*w+xx;
if (xx >= 0 && xx < w && yy >= 0 && yy < h &&
grid[ii] == c && dist[ii] == -1) {
dist[ii] = dist[i] + 1;
assert(listsize < w*h);
list[listsize++] = ii;
}
}
}
return listsize;
}
struct genctx {
int w, h;
int *grid, *sparegrid, *sparegrid2, *sparegrid3;
int *dist, *list;
int npaths, pathsize;
int *pathends, *sparepathends; /* 2*npaths entries */
int *pathspare; /* npaths entries */
int *extends; /* 8*npaths entries */
};
static struct genctx *new_genctx(int w, int h)
{
struct genctx *ctx = snew(struct genctx);
ctx->w = w;
ctx->h = h;
ctx->grid = snewn(w * h, int);
ctx->sparegrid = snewn(w * h, int);
ctx->sparegrid2 = snewn(w * h, int);
ctx->sparegrid3 = snewn(w * h, int);
ctx->dist = snewn(w * h, int);
ctx->list = snewn(w * h, int);
ctx->npaths = ctx->pathsize = 0;
ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL;
return ctx;
}
static void free_genctx(struct genctx *ctx)
{
sfree(ctx->grid);
sfree(ctx->sparegrid);
sfree(ctx->sparegrid2);
sfree(ctx->sparegrid3);
sfree(ctx->dist);
sfree(ctx->list);
sfree(ctx->pathends);
sfree(ctx->sparepathends);
sfree(ctx->pathspare);
sfree(ctx->extends);
}
static int newpath(struct genctx *ctx)
{
int n;
n = ctx->npaths++;
if (ctx->npaths > ctx->pathsize) {
ctx->pathsize += 16;
ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int);
ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int);
ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int);
ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int);
}
return n;
}
static int is_endpoint(struct genctx *ctx, int x, int y)
{
int w = ctx->w, h = ctx->h, c;
assert(x >= 0 && x < w && y >= 0 && y < h);
c = ctx->grid[y*w+x];
if (c < 0)
return FALSE; /* empty square is not an endpoint! */
assert(c >= 0 && c < ctx->npaths);
if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x)
return TRUE;
return FALSE;
}
/*
* Tries to extend a path by one square in the given direction,
* pushing other paths around if necessary. Returns TRUE on success
* or FALSE on failure.
*/
static int extend_path(struct genctx *ctx, int path, int end, int direction)
{
int w = ctx->w, h = ctx->h;
int x, y, xe, ye, cut;
int i, j, jp, n, first, last;
assert(path >= 0 && path < ctx->npaths);
assert(end == 0 || end == 1);
/*
* Find the endpoint of the path and the point we plan to
* extend it into.
*/
y = ctx->pathends[path * 2 + end] / w;
x = ctx->pathends[path * 2 + end] % w;
assert(x >= 0 && x < w && y >= 0 && y < h);
xe = x + DX(direction);
ye = y + DY(direction);
if (xe < 0 || xe >= w || ye < 0 || ye >= h)
return FALSE; /* could not extend in this direction */
/*
* We don't extend paths _directly_ into endpoints of other
* paths, although we don't mind too much if a knock-on effect
* of an extension is to push part of another path into a third
* path's endpoint.
*/
if (is_endpoint(ctx, xe, ye))
return FALSE;
/*
* We can't extend a path back the way it came.
*/
if (ctx->grid[ye*w+xe] == path)
return FALSE;
/*
* Paths may not double back on themselves. Check if the new
* point is adjacent to any point of this path other than (x,y).
*/
for (j = 0; j < 4; j++) {
int xf, yf;
xf = xe + DX(j);
yf = ye + DY(j);
if (xf >= 0 && xf < w && yf >= 0 && yf < h &&
(xf != x || yf != y) && ctx->grid[yf*w+xf] == path)
return FALSE;
}
/*
* Now we're convinced it's valid to _attempt_ the extension.
* It may still fail if we run out of space to push other paths
* into.
*
* So now we can set up our temporary data structures. We will
* need:
*
* - a spare copy of the grid on which to gradually move paths
* around (sparegrid)
*
* - a second spare copy with which to remember how paths
* looked just before being cut (sparegrid2). FIXME: is
* sparegrid2 necessary? right now it's never different from
* grid itself
*
* - a third spare copy with which to do the internal
* calculations involved in reconstituting a cut path
* (sparegrid3)
*
* - something to track which paths currently need
* reconstituting after being cut, and which have already
* been cut (pathspare)
*
* - a spare copy of pathends to store the altered states in
* (sparepathends)
*/
memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int));
memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int));
memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int));
for (i = 0; i < ctx->npaths; i++)
ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */
/*
* Working in sparegrid, actually extend the path. If it cuts
* another, begin a loop in which we restore any cut path by
* moving it out of the way.
*/
cut = ctx->sparegrid[ye*w+xe];
ctx->sparegrid[ye*w+xe] = path;
ctx->sparepathends[path*2+end] = ye*w+xe;
ctx->pathspare[path] = 2; /* this one is sacrosanct */
if (cut >= 0) {
assert(cut >= 0 && cut < ctx->npaths);
ctx->pathspare[cut] = 1; /* broken */
while (1) {
for (i = 0; i < ctx->npaths; i++)
if (ctx->pathspare[i] == 1)
break;
if (i == ctx->npaths)
break; /* we're done */
/*
* Path i needs restoring. So walk along its original
* track (as given in sparegrid2) and see where it's
* been cut. Where it has, surround the cut points in
* the same colour, without overwriting already-fixed
* paths.
*/
memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int));
n = bfs(w, h, ctx->sparegrid2,
ctx->pathends[i*2] % w, ctx->pathends[i*2] / w,
ctx->dist, ctx->list);
first = last = -1;
if (ctx->sparegrid3[ctx->pathends[i*2]] != i ||
ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return FALSE;/* FIXME */
for (j = 0; j < n; j++) {
jp = ctx->list[j];
assert(ctx->dist[jp] == j);
assert(ctx->sparegrid2[jp] == i);
/*
* Wipe out the original path in sparegrid.
*/
if (ctx->sparegrid[jp] == i)
ctx->sparegrid[jp] = -1;
/*
* Be prepared to shorten the path at either end if
* the endpoints have been stomped on.
*/
if (ctx->sparegrid3[jp] == i) {
if (first < 0)
first = jp;
last = jp;
}
if (ctx->sparegrid3[jp] != i) {
int jx = jp % w, jy = jp / w;
int dx, dy;
for (dy = -1; dy <= +1; dy++)
for (dx = -1; dx <= +1; dx++) {
int newp, newv;
if (!dy && !dx)
continue; /* central square */
if (jx+dx < 0 || jx+dx >= w ||
jy+dy < 0 || jy+dy >= h)
continue; /* out of range */
newp = (jy+dy)*w+(jx+dx);
newv = ctx->sparegrid3[newp];
if (newv >= 0 && (newv == i ||
ctx->pathspare[newv] == 2))
continue; /* can't use this square */
ctx->sparegrid3[newp] = i;
}
}
}
if (first < 0 || last < 0)
return FALSE; /* path is completely wiped out! */
/*
* Now we've covered sparegrid3 in possible squares for
* the new layout of path i. Find the actual layout
* we're going to use by bfs: we want the shortest path
* from one endpoint to the other.
*/
n = bfs(w, h, ctx->sparegrid3, first % w, first / w,
ctx->dist, ctx->list);
if (ctx->dist[last] < 2) {
/*
* Either there is no way to get between the path's
* endpoints, or the remaining endpoints simply
* aren't far enough apart to make the path viable
* any more. This means the entire push operation
* has failed.
*/
return FALSE;
}
/*
* Write the new path into sparegrid. Also save the new
* endpoint locations, in case they've changed.
*/
jp = last;
j = ctx->dist[jp];
while (1) {
int d;
if (ctx->sparegrid[jp] >= 0) {
if (ctx->pathspare[ctx->sparegrid[jp]] == 2)
return FALSE; /* somehow we've hit a fixed path */
ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */
}
ctx->sparegrid[jp] = i;
if (j == 0)
break;
/*
* Now look at the neighbours of jp to find one
* which has dist[] one less.
*/
for (d = 0; d < 4; d++) {
int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d);
if (jx >= 0 && jx < w && jy >= 0 && jy < w &&
ctx->dist[jy*w+jx] == j-1) {
jp = jy*w+jx;
j--;
break;
}
}
assert(d < 4);
}
ctx->sparepathends[i*2] = first;
ctx->sparepathends[i*2+1] = last;
//printf("new ends of path %d: %d,%d\n", i, first, last);
ctx->pathspare[i] = 2; /* fixed */
}
}
/*
* If we got here, the extension was successful!
*/
memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int));
memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int));
return TRUE;
}
/*
* Tries to add a new path to the grid.
*/
static int add_path(struct genctx *ctx, random_state *rs)
{
int w = ctx->w, h = ctx->h;
int i, ii, n;
/*
* Our strategy is:
* - randomly choose an empty square in the grid
* - do a BFS from that point to find a long path starting
* from it
* - if we run out of viable empty squares, return failure.
*/
/*
* Use `sparegrid' to collect a list of empty squares.
*/
n = 0;
for (i = 0; i < w*h; i++)
if (ctx->grid[i] == -1)
ctx->sparegrid[n++] = i;
/*
* Shuffle the grid.
*/
for (i = n; i-- > 1 ;) {
int k = random_upto(rs, i+1);
if (k != i) {
int t = ctx->sparegrid[i];
ctx->sparegrid[i] = ctx->sparegrid[k];
ctx->sparegrid[k] = t;
}
}
/*
* Loop over it trying to add paths. This looks like a
* horrifying N^4 algorithm (that is, (w*h)^2), but I predict
* that in fact the worst case will very rarely arise because
* when there's lots of grid space an attempt will succeed very
* quickly.
*/
for (ii = 0; ii < n; ii++) {
int i = ctx->sparegrid[ii];
int y = i / w, x = i % w, nsq;
int r, c, j;
/*
* BFS from here to find long paths.
*/
nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list);
/*
* If there aren't any long enough, give up immediately.
*/
assert(nsq > 0); /* must be the start square at least! */
if (ctx->dist[ctx->list[nsq-1]] < 3)
continue;
/*
* Find the first viable endpoint in ctx->list (i.e. the
* first point with distance at least three). I could
* binary-search for this, but that would be O(log N)
* whereas in fact I can get a constant time bound by just
* searching up from the start - after all, there can be at
* most 13 points at _less_ than distance 3 from the
* starting one!
*/
for (j = 0; j < nsq; j++)
if (ctx->dist[ctx->list[j]] >= 3)
break;
assert(j < nsq); /* we tested above that there was one */
/*
* Now we know that any element of `list' between j and nsq
* would be valid in principle. However, we want a few long
* paths rather than many small ones, so select only those
* elements which are either the maximum length or one
* below it.
*/
while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]])
j++;
r = j + random_upto(rs, nsq - j);
j = ctx->list[r];
/*
* And that's our endpoint. Mark the new path on the grid.
*/
c = newpath(ctx);
ctx->pathends[c*2] = i;
ctx->pathends[c*2+1] = j;
ctx->grid[j] = c;
while (j != i) {
int d, np, index, pts[4];
np = 0;
for (d = 0; d < 4; d++) {
int xn = (j % w) + DX(d), yn = (j / w) + DY(d);
if (xn >= 0 && xn < w && yn >= 0 && yn < w &&
ctx->dist[yn*w+xn] == ctx->dist[j] - 1)
pts[np++] = yn*w+xn;
}
if (np > 1)
index = random_upto(rs, np);
else
index = 0;
j = pts[index];
ctx->grid[j] = c;
}
return TRUE;
}
return FALSE;
}
/*
* The main grid generation loop.
*/
static void gridgen_mainloop(struct genctx *ctx, random_state *rs)
{
int w = ctx->w, h = ctx->h;
int i, n;
/*
* The generation algorithm doesn't always converge. Loop round
* until it does.
*/
while (1) {
for (i = 0; i < w*h; i++)
ctx->grid[i] = -1;
ctx->npaths = 0;
while (1) {
/*
* See if the grid is full.
*/
for (i = 0; i < w*h; i++)
if (ctx->grid[i] < 0)
break;
if (i == w*h)
return;
#ifdef GENERATION_DIAGNOSTICS
{
int x, y;
for (y = 0; y < h; y++) {
printf("|");
for (x = 0; x < w; x++) {
if (ctx->grid[y*w+x] >= 0)
printf("%2d", ctx->grid[y*w+x]);
else
printf(" .");
}
printf(" |\n");
}
}
#endif
/*
* Try adding a path.
*/
if (add_path(ctx, rs)) {
#ifdef GENERATION_DIAGNOSTICS
printf("added path\n");
#endif
continue;
}
/*
* Try extending a path. First list all the possible
* extensions.
*/
for (i = 0; i < ctx->npaths * 8; i++)
ctx->extends[i] = i;
n = i;
/*
* Then shuffle the list.
*/
for (i = n; i-- > 1 ;) {
int k = random_upto(rs, i+1);
if (k != i) {
int t = ctx->extends[i];
ctx->extends[i] = ctx->extends[k];
ctx->extends[k] = t;
}
}
/*
* Now try each one in turn until one works.
*/
for (i = 0; i < n; i++) {
int p, d, e;
p = ctx->extends[i];
d = p % 4;
p /= 4;
e = p % 2;
p /= 2;
#ifdef GENERATION_DIAGNOSTICS
printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e,
ctx->pathends[p*2+e] % w,
ctx->pathends[p*2+e] / w, d);
#endif
if (extend_path(ctx, p, e, d)) {
#ifdef GENERATION_DIAGNOSTICS
printf("extended path %d end %d (%d,%d) in dir %d\n", p, e,
ctx->pathends[p*2+e] % w,
ctx->pathends[p*2+e] / w, d);
#endif
break;
}
}
if (i < n)
continue;
break;
}
}
}
/*
* Wrapper function which deals with the boring bits such as
* removing the solution from the generated grid, shuffling the
* numeric labels and creating/disposing of the context structure.
*/
static int *gridgen(int w, int h, random_state *rs)
{
struct genctx *ctx;
int *ret;
int i;
ctx = new_genctx(w, h);
gridgen_mainloop(ctx, rs);
/*
* There is likely to be an ordering bias in the numbers
* (longer paths on lower numbers due to there having been more
* grid space when laying them down). So we must shuffle the
* numbers. We use ctx->pathspare for this.
*
* This is also as good a time as any to shift to numbering
* from 1, for display to the user.
*/
for (i = 0; i < ctx->npaths; i++)
ctx->pathspare[i] = i+1;
for (i = ctx->npaths; i-- > 1 ;) {
int k = random_upto(rs, i+1);
if (k != i) {
int t = ctx->pathspare[i];
ctx->pathspare[i] = ctx->pathspare[k];
ctx->pathspare[k] = t;
}
}
/* FIXME: remove this at some point! */
{
int y, x;
for (y = 0; y < h; y++) {
printf("|");
for (x = 0; x < w; x++) {
assert(ctx->grid[y*w+x] >= 0);
printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]);
}
printf(" |\n");
}
printf("\n");
}
/*
* Clear the grid, and write in just the endpoints.
*/
for (i = 0; i < w*h; i++)
ctx->grid[i] = 0;
for (i = 0; i < ctx->npaths; i++) {
ctx->grid[ctx->pathends[i*2]] =
ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i];
}
ret = ctx->grid;
ctx->grid = NULL;
free_genctx(ctx);
return ret;
}
#ifdef TEST_GEN
#define TEST_GENERAL
int main(void)
{
int w = 10, h = 8;
random_state *rs = random_init("12345", 5);
int x, y, i, *grid;
for (i = 0; i < 10; i++) {
grid = gridgen(w, h, rs);
for (y = 0; y < h; y++) {
printf("|");
for (x = 0; x < w; x++) {
if (grid[y*w+x] > 0)
printf("%2d", grid[y*w+x]);
else
printf(" .");
}
printf(" |\n");
}
printf("\n");
sfree(grid);
}
return 0;
}
#endif
#ifdef TEST_GENERAL
#include <stdarg.h>
void fatal(char *fmt, ...)
{
va_list ap;
fprintf(stderr, "fatal error: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
#endif

17
unfinished/pearl.R Normal file
View File

@ -0,0 +1,17 @@
# -*- makefile -*-
PEARL = pearl dsf
pearl : [X] GTK COMMON PEARL
pearl : [G] WINDOWS COMMON PEARL
ALL += PEARL
!begin gtk
GAMES += pearl
!end
!begin >list.c
A(pearl) \
!end

1401
unfinished/pearl.c Normal file

File diff suppressed because it is too large Load Diff

15
unfinished/sokoban.R Normal file
View File

@ -0,0 +1,15 @@
# -*- makefile -*-
sokoban : [X] GTK COMMON sokoban
sokoban : [G] WINDOWS COMMON sokoban
ALL += sokoban
!begin gtk
GAMES += sokoban
!end
!begin >list.c
A(sokoban) \
!end

1451
unfinished/sokoban.c Normal file

File diff suppressed because it is too large Load Diff