mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Files

This is the main bulk of this boolification work, but although it's making the largest actual change, it should also be the least disruptive to anyone interacting with this code base downstream of me, because it doesn't modify any interface between modules: all the inter-module APIs were updated one by one in the previous commits. This just cleans up the code within each individual source file to use bool in place of int where I think that makes things clearer.
862 lines
22 KiB
C
862 lines
22 KiB
C
/*
|
|
* separate.c: Implementation of `Block Puzzle', a Japanese-only
|
|
* Nikoli puzzle seen at
|
|
* http://www.nikoli.co.jp/ja/puzzles/block_puzzle/
|
|
*
|
|
* It's difficult to be absolutely sure of the rules since online
|
|
* Japanese translators are so bad, but looking at the sample
|
|
* puzzle it seems fairly clear that the rules of this one are
|
|
* very simple. You have an mxn grid in which every square
|
|
* contains a letter, there are k distinct letters with k dividing
|
|
* mn, and every letter occurs the same number of times; your aim
|
|
* is to find a partition of the grid into disjoint k-ominoes such
|
|
* that each k-omino contains exactly one of each letter.
|
|
*
|
|
* (It may be that Nikoli always have m,n,k equal to one another.
|
|
* However, I don't see that that's critical to the puzzle; k|mn
|
|
* is the only really important constraint, and even that could
|
|
* probably be dispensed with if some squares were marked as
|
|
* unused.)
|
|
*/
|
|
|
|
/*
|
|
* Current status: only the solver/generator is yet written, and
|
|
* although working in principle it's _very_ slow. It generates
|
|
* 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and
|
|
* 7x7n7 only with a serious strain. I haven't dared try it higher
|
|
* than that yet.
|
|
*
|
|
* One idea to speed it up is to implement more of the solver.
|
|
* Ideas I've so far had include:
|
|
*
|
|
* - Generalise the deduction currently expressed as `an
|
|
* undersized chain with only one direction to extend must take
|
|
* it'. More generally, the deduction should say `if all the
|
|
* possible k-ominoes containing a given chain also contain
|
|
* square x, then mark square x as part of that k-omino'.
|
|
* + For example, consider this case:
|
|
*
|
|
* a ? b This represents the top left of a board; the letters
|
|
* ? ? ? a,b,c do not represent the letters used in the puzzle,
|
|
* c ? ? but indicate that those three squares are known to be
|
|
* of different ominoes. Now if k >= 4, we can immediately
|
|
* deduce that the square midway between b and c belongs to the
|
|
* same omino as a, because there is no way we can make a 4-or-
|
|
* more-omino containing a which does not also contain that square.
|
|
* (Most easily seen by imagining cutting that square out of the
|
|
* grid; then, clearly, the omino containing a has only two
|
|
* squares to expand into, and needs at least three.)
|
|
*
|
|
* The key difficulty with this mode of reasoning is
|
|
* identifying such squares. I can't immediately think of a
|
|
* simple algorithm for finding them on a wholesale basis.
|
|
*
|
|
* - Bfs out from a chain looking for the letters it lacks. For
|
|
* example, in this situation (top three rows of a 7x7n7 grid):
|
|
*
|
|
* +-----------+-+
|
|
* |E-A-F-B-C D|D|
|
|
* +------- ||
|
|
* |E-C-G-D G|G E|
|
|
* +-+--- |
|
|
* |E|E G A B F A|
|
|
*
|
|
* In this situation we can be sure that the top left chain
|
|
* E-A-F-B-C does extend rightwards to the D, because there is
|
|
* no other D within reach of that chain. Note also that the
|
|
* bfs can skip squares which are known to belong to other
|
|
* ominoes than this one.
|
|
*
|
|
* (This deduction, I fear, should only be used in an
|
|
* emergency, because it relies on _all_ squares within range
|
|
* of the bfs having particular values and so using it during
|
|
* incremental generation rather nails down a lot of the grid.)
|
|
*
|
|
* It's conceivable that another thing we could do would be to
|
|
* increase the flexibility in the grid generator: instead of
|
|
* nailing down the _value_ of any square depended on, merely nail
|
|
* down its equivalence to other squares. Unfortunately this turns
|
|
* the letter-selection phase of generation into a general graph
|
|
* colouring problem (we must draw a graph with equivalence
|
|
* classes of squares as the vertices, and an edge between any two
|
|
* vertices representing equivalence classes which contain squares
|
|
* that share an omino, and then k-colour the result) and hence
|
|
* requires recursion, which bodes ill for something we're doing
|
|
* that many times per generation.
|
|
*
|
|
* I suppose a simple thing I could try would be tuning the retry
|
|
* count, just in case it's set too high or too low for efficient
|
|
* generation.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "puzzles.h"
|
|
|
|
enum {
|
|
COL_BACKGROUND,
|
|
NCOLOURS
|
|
};
|
|
|
|
struct game_params {
|
|
int w, h, k;
|
|
};
|
|
|
|
struct game_state {
|
|
int FIXME;
|
|
};
|
|
|
|
static game_params *default_params(void)
|
|
{
|
|
game_params *ret = snew(game_params);
|
|
|
|
ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool game_fetch_preset(int i, char **name, game_params **params)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void free_params(game_params *params)
|
|
{
|
|
sfree(params);
|
|
}
|
|
|
|
static game_params *dup_params(const game_params *params)
|
|
{
|
|
game_params *ret = snew(game_params);
|
|
*ret = *params; /* structure copy */
|
|
return ret;
|
|
}
|
|
|
|
static void decode_params(game_params *params, char const *string)
|
|
{
|
|
params->w = params->h = params->k = atoi(string);
|
|
while (*string && isdigit((unsigned char)*string)) string++;
|
|
if (*string == 'x') {
|
|
string++;
|
|
params->h = atoi(string);
|
|
while (*string && isdigit((unsigned char)*string)) string++;
|
|
}
|
|
if (*string == 'n') {
|
|
string++;
|
|
params->k = atoi(string);
|
|
while (*string && isdigit((unsigned char)*string)) string++;
|
|
}
|
|
}
|
|
|
|
static char *encode_params(const game_params *params, bool full)
|
|
{
|
|
char buf[256];
|
|
sprintf(buf, "%dx%dn%d", params->w, params->h, params->k);
|
|
return dupstr(buf);
|
|
}
|
|
|
|
static config_item *game_configure(const game_params *params)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static game_params *custom_params(const config_item *cfg)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static const char *validate_params(const game_params *params, bool full)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Solver and generator.
|
|
*/
|
|
|
|
struct solver_scratch {
|
|
int w, h, k;
|
|
|
|
/*
|
|
* Tracks connectedness between squares.
|
|
*/
|
|
int *dsf;
|
|
|
|
/*
|
|
* size[dsf_canonify(dsf, yx)] tracks the size of the
|
|
* connected component containing yx.
|
|
*/
|
|
int *size;
|
|
|
|
/*
|
|
* contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not
|
|
* the connected component containing yx includes letter i. If
|
|
* the value is -1, it doesn't; otherwise its value is the
|
|
* index in the main grid of the square which contributes that
|
|
* letter to the component.
|
|
*/
|
|
int *contents;
|
|
|
|
/*
|
|
* disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)]
|
|
* tracks whether or not the connected components containing
|
|
* yx1 and yx2 are known to be distinct.
|
|
*/
|
|
bool *disconnect;
|
|
|
|
/*
|
|
* Temporary space used only inside particular solver loops.
|
|
*/
|
|
int *tmp;
|
|
};
|
|
|
|
struct solver_scratch *solver_scratch_new(int w, int h, int k)
|
|
{
|
|
int wh = w*h;
|
|
struct solver_scratch *sc = snew(struct solver_scratch);
|
|
|
|
sc->w = w;
|
|
sc->h = h;
|
|
sc->k = k;
|
|
|
|
sc->dsf = snew_dsf(wh);
|
|
sc->size = snewn(wh, int);
|
|
sc->contents = snewn(wh * k, int);
|
|
sc->disconnect = snewn(wh*wh, bool);
|
|
sc->tmp = snewn(wh, int);
|
|
|
|
return sc;
|
|
}
|
|
|
|
void solver_scratch_free(struct solver_scratch *sc)
|
|
{
|
|
sfree(sc->dsf);
|
|
sfree(sc->size);
|
|
sfree(sc->contents);
|
|
sfree(sc->disconnect);
|
|
sfree(sc->tmp);
|
|
sfree(sc);
|
|
}
|
|
|
|
void solver_connect(struct solver_scratch *sc, int yx1, int yx2)
|
|
{
|
|
int w = sc->w, h = sc->h, k = sc->k;
|
|
int wh = w*h;
|
|
int i, yxnew;
|
|
|
|
yx1 = dsf_canonify(sc->dsf, yx1);
|
|
yx2 = dsf_canonify(sc->dsf, yx2);
|
|
assert(yx1 != yx2);
|
|
|
|
/*
|
|
* To connect two components together into a bigger one, we
|
|
* start by merging them in the dsf itself.
|
|
*/
|
|
dsf_merge(sc->dsf, yx1, yx2);
|
|
yxnew = dsf_canonify(sc->dsf, yx2);
|
|
|
|
/*
|
|
* The size of the new component is the sum of the sizes of the
|
|
* old ones.
|
|
*/
|
|
sc->size[yxnew] = sc->size[yx1] + sc->size[yx2];
|
|
|
|
/*
|
|
* The contents bitmap of the new component is the union of the
|
|
* contents of the old ones.
|
|
*
|
|
* Given two numbers at most one of which is not -1, we can
|
|
* find the other one by adding the two and adding 1; this
|
|
* will yield -1 if both were -1 to begin with, otherwise the
|
|
* other.
|
|
*
|
|
* (A neater approach would be to take their bitwise AND, but
|
|
* this is unfortunately not well-defined standard C when done
|
|
* to signed integers.)
|
|
*/
|
|
for (i = 0; i < k; i++) {
|
|
assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0);
|
|
sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] +
|
|
sc->contents[yx2*k+i] + 1);
|
|
}
|
|
|
|
/*
|
|
* We must combine the rows _and_ the columns in the disconnect
|
|
* matrix.
|
|
*/
|
|
for (i = 0; i < wh; i++)
|
|
sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] ||
|
|
sc->disconnect[yx2*wh+i]);
|
|
for (i = 0; i < wh; i++)
|
|
sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] ||
|
|
sc->disconnect[i*wh+yx2]);
|
|
}
|
|
|
|
void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2)
|
|
{
|
|
int w = sc->w, h = sc->h;
|
|
int wh = w*h;
|
|
|
|
yx1 = dsf_canonify(sc->dsf, yx1);
|
|
yx2 = dsf_canonify(sc->dsf, yx2);
|
|
assert(yx1 != yx2);
|
|
assert(!sc->disconnect[yx1*wh+yx2]);
|
|
assert(!sc->disconnect[yx2*wh+yx1]);
|
|
|
|
/*
|
|
* Mark the components as disconnected from each other in the
|
|
* disconnect matrix.
|
|
*/
|
|
sc->disconnect[yx1*wh+yx2] = true;
|
|
sc->disconnect[yx2*wh+yx1] = true;
|
|
}
|
|
|
|
void solver_init(struct solver_scratch *sc)
|
|
{
|
|
int w = sc->w, h = sc->h;
|
|
int wh = w*h;
|
|
int i;
|
|
|
|
/*
|
|
* Set up most of the scratch space. We don't set up the
|
|
* contents array, however, because this will change if we
|
|
* adjust the letter arrangement and re-run the solver.
|
|
*/
|
|
dsf_init(sc->dsf, wh);
|
|
for (i = 0; i < wh; i++) sc->size[i] = 1;
|
|
memset(sc->disconnect, 0, wh*wh * sizeof(bool));
|
|
}
|
|
|
|
int solver_attempt(struct solver_scratch *sc, const unsigned char *grid,
|
|
bool *gen_lock)
|
|
{
|
|
int w = sc->w, h = sc->h, k = sc->k;
|
|
int wh = w*h;
|
|
int i, x, y;
|
|
bool done_something_overall = false;
|
|
|
|
/*
|
|
* Set up the contents array from the grid.
|
|
*/
|
|
for (i = 0; i < wh*k; i++)
|
|
sc->contents[i] = -1;
|
|
for (i = 0; i < wh; i++)
|
|
sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i;
|
|
|
|
while (1) {
|
|
bool done_something = false;
|
|
|
|
/*
|
|
* Go over the grid looking for reasons to add to the
|
|
* disconnect matrix. We're after pairs of squares which:
|
|
*
|
|
* - are adjacent in the grid
|
|
* - belong to distinct dsf components
|
|
* - their components are not already marked as
|
|
* disconnected
|
|
* - their components share a letter in common.
|
|
*/
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++) {
|
|
int dir;
|
|
for (dir = 0; dir < 2; dir++) {
|
|
int x2 = x + dir, y2 = y + 1 - dir;
|
|
int yx = y*w+x, yx2 = y2*w+x2;
|
|
|
|
if (x2 >= w || y2 >= h)
|
|
continue; /* one square is outside the grid */
|
|
|
|
yx = dsf_canonify(sc->dsf, yx);
|
|
yx2 = dsf_canonify(sc->dsf, yx2);
|
|
if (yx == yx2)
|
|
continue; /* same dsf component */
|
|
|
|
if (sc->disconnect[yx*wh+yx2])
|
|
continue; /* already known disconnected */
|
|
|
|
for (i = 0; i < k; i++)
|
|
if (sc->contents[yx*k+i] >= 0 &&
|
|
sc->contents[yx2*k+i] >= 0)
|
|
break;
|
|
if (i == k)
|
|
continue; /* no letter in common */
|
|
|
|
/*
|
|
* We've found one. Mark yx and yx2 as
|
|
* disconnected from each other.
|
|
*/
|
|
#ifdef SOLVER_DIAGNOSTICS
|
|
printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i);
|
|
#endif
|
|
solver_disconnect(sc, yx, yx2);
|
|
done_something = done_something_overall = true;
|
|
|
|
/*
|
|
* We have just made a deduction which hinges
|
|
* on two particular grid squares being the
|
|
* same. If we are feeding back to a generator
|
|
* loop, we must therefore mark those squares
|
|
* as fixed in the generator, so that future
|
|
* rearrangement of the grid will not break
|
|
* the information on which we have already
|
|
* based deductions.
|
|
*/
|
|
if (gen_lock) {
|
|
gen_lock[sc->contents[yx*k+i]] = true;
|
|
gen_lock[sc->contents[yx2*k+i]] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now go over the grid looking for dsf components which
|
|
* are below maximum size and only have one way to extend,
|
|
* and extending them.
|
|
*/
|
|
for (i = 0; i < wh; i++)
|
|
sc->tmp[i] = -1;
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++) {
|
|
int yx = dsf_canonify(sc->dsf, y*w+x);
|
|
int dir;
|
|
|
|
if (sc->size[yx] == k)
|
|
continue;
|
|
|
|
for (dir = 0; dir < 4; dir++) {
|
|
int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0);
|
|
int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0);
|
|
int yx2, yx2c;
|
|
|
|
if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w)
|
|
continue;
|
|
yx2 = y2*w+x2;
|
|
yx2c = dsf_canonify(sc->dsf, yx2);
|
|
|
|
if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) {
|
|
/*
|
|
* Component yx can be extended into square
|
|
* yx2.
|
|
*/
|
|
if (sc->tmp[yx] == -1)
|
|
sc->tmp[yx] = yx2;
|
|
else if (sc->tmp[yx] != yx2)
|
|
sc->tmp[yx] = -2; /* multiple choices found */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < wh; i++) {
|
|
if (sc->tmp[i] >= 0) {
|
|
/*
|
|
* Make sure we haven't connected the two already
|
|
* during this loop (which could happen if for
|
|
* _both_ components this was the only way to
|
|
* extend them).
|
|
*/
|
|
if (dsf_canonify(sc->dsf, i) ==
|
|
dsf_canonify(sc->dsf, sc->tmp[i]))
|
|
continue;
|
|
|
|
#ifdef SOLVER_DIAGNOSTICS
|
|
printf("Connecting %d and %d\n", i, sc->tmp[i]);
|
|
#endif
|
|
solver_connect(sc, i, sc->tmp[i]);
|
|
done_something = done_something_overall = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!done_something)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Return 0 if we haven't made any progress; 1 if we've done
|
|
* something but not solved it completely; 2 if we've solved
|
|
* it completely.
|
|
*/
|
|
for (i = 0; i < wh; i++)
|
|
if (sc->size[dsf_canonify(sc->dsf, i)] != k)
|
|
break;
|
|
if (i == wh)
|
|
return 2;
|
|
if (done_something_overall)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *generate(int w, int h, int k, random_state *rs)
|
|
{
|
|
int wh = w*h;
|
|
int n = wh/k;
|
|
struct solver_scratch *sc;
|
|
unsigned char *grid;
|
|
unsigned char *shuffled;
|
|
int i, j, m, retries;
|
|
int *permutation;
|
|
bool *gen_lock;
|
|
extern int *divvy_rectangle(int w, int h, int k, random_state *rs);
|
|
|
|
sc = solver_scratch_new(w, h, k);
|
|
grid = snewn(wh, unsigned char);
|
|
shuffled = snewn(k, unsigned char);
|
|
permutation = snewn(wh, int);
|
|
gen_lock = snewn(wh, bool);
|
|
|
|
do {
|
|
int *dsf = divvy_rectangle(w, h, k, rs);
|
|
|
|
/*
|
|
* Go through the dsf and find the indices of all the
|
|
* squares involved in each omino, in a manner conducive
|
|
* to per-omino indexing. We set permutation[i*k+j] to be
|
|
* the index of the jth square (ordered arbitrarily) in
|
|
* omino i.
|
|
*/
|
|
for (i = j = 0; i < wh; i++)
|
|
if (dsf_canonify(dsf, i) == i) {
|
|
sc->tmp[i] = j;
|
|
/*
|
|
* During this loop and the following one, we use
|
|
* the last element of each row of permutation[]
|
|
* as a counter of the number of indices so far
|
|
* placed in it. When we place the final index of
|
|
* an omino, that counter is overwritten, but that
|
|
* doesn't matter because we'll never use it
|
|
* again. Of course this depends critically on
|
|
* divvy_rectangle() having returned correct
|
|
* results, or else chaos would ensue.
|
|
*/
|
|
permutation[j*k+k-1] = 0;
|
|
j++;
|
|
}
|
|
for (i = 0; i < wh; i++) {
|
|
j = sc->tmp[dsf_canonify(dsf, i)];
|
|
m = permutation[j*k+k-1]++;
|
|
permutation[j*k+m] = i;
|
|
}
|
|
|
|
/*
|
|
* Track which squares' letters we have already depended
|
|
* on for deductions. This is gradually updated by
|
|
* solver_attempt().
|
|
*/
|
|
memset(gen_lock, 0, wh * sizeof(bool));
|
|
|
|
/*
|
|
* Now repeatedly fill the grid with letters, and attempt
|
|
* to solve it. If the solver makes progress but does not
|
|
* fail completely, then gen_lock will have been updated
|
|
* and we try again. On a complete failure, though, we
|
|
* have no option but to give up and abandon this set of
|
|
* ominoes.
|
|
*/
|
|
solver_init(sc);
|
|
retries = k*k;
|
|
while (1) {
|
|
/*
|
|
* Fill the grid with letters. We can safely use
|
|
* sc->tmp to hold the set of letters required at each
|
|
* stage, since it's at least size k and is currently
|
|
* unused.
|
|
*/
|
|
for (i = 0; i < n; i++) {
|
|
/*
|
|
* First, determine the set of letters already
|
|
* placed in this omino by gen_lock.
|
|
*/
|
|
for (j = 0; j < k; j++)
|
|
sc->tmp[j] = j;
|
|
for (j = 0; j < k; j++) {
|
|
int index = permutation[i*k+j];
|
|
int letter = grid[index];
|
|
if (gen_lock[index])
|
|
sc->tmp[letter] = -1;
|
|
}
|
|
/*
|
|
* Now collect together all the remaining letters
|
|
* and randomly shuffle them.
|
|
*/
|
|
for (j = m = 0; j < k; j++)
|
|
if (sc->tmp[j] >= 0)
|
|
sc->tmp[m++] = sc->tmp[j];
|
|
shuffle(sc->tmp, m, sizeof(*sc->tmp), rs);
|
|
/*
|
|
* Finally, write the shuffled letters into the
|
|
* grid.
|
|
*/
|
|
for (j = 0; j < k; j++) {
|
|
int index = permutation[i*k+j];
|
|
if (!gen_lock[index])
|
|
grid[index] = sc->tmp[--m];
|
|
}
|
|
assert(m == 0);
|
|
}
|
|
|
|
/*
|
|
* Now we have a candidate grid. Attempt to progress
|
|
* the solution.
|
|
*/
|
|
m = solver_attempt(sc, grid, gen_lock);
|
|
if (m == 2 || /* success */
|
|
(m == 0 && retries-- <= 0)) /* failure */
|
|
break;
|
|
if (m == 1)
|
|
retries = k*k; /* reset this counter, and continue */
|
|
}
|
|
|
|
sfree(dsf);
|
|
} while (m == 0);
|
|
|
|
sfree(gen_lock);
|
|
sfree(permutation);
|
|
sfree(shuffled);
|
|
solver_scratch_free(sc);
|
|
|
|
return grid;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* End of solver/generator code.
|
|
*/
|
|
|
|
static char *new_game_desc(const game_params *params, random_state *rs,
|
|
char **aux, bool interactive)
|
|
{
|
|
int w = params->w, h = params->h, wh = w*h, k = params->k;
|
|
unsigned char *grid;
|
|
char *desc;
|
|
int i;
|
|
|
|
grid = generate(w, h, k, rs);
|
|
|
|
desc = snewn(wh+1, char);
|
|
for (i = 0; i < wh; i++)
|
|
desc[i] = 'A' + grid[i];
|
|
desc[wh] = '\0';
|
|
|
|
sfree(grid);
|
|
|
|
return desc;
|
|
}
|
|
|
|
static const char *validate_desc(const game_params *params, const char *desc)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static game_state *new_game(midend *me, const game_params *params,
|
|
const char *desc)
|
|
{
|
|
game_state *state = snew(game_state);
|
|
|
|
state->FIXME = 0;
|
|
|
|
return state;
|
|
}
|
|
|
|
static game_state *dup_game(const game_state *state)
|
|
{
|
|
game_state *ret = snew(game_state);
|
|
|
|
ret->FIXME = state->FIXME;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void free_game(game_state *state)
|
|
{
|
|
sfree(state);
|
|
}
|
|
|
|
static char *solve_game(const game_state *state, const game_state *currstate,
|
|
const char *aux, const char **error)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static bool game_can_format_as_text_now(const game_params *params)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static char *game_text_format(const game_state *state)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static game_ui *new_ui(const game_state *state)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void free_ui(game_ui *ui)
|
|
{
|
|
}
|
|
|
|
static char *encode_ui(const game_ui *ui)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void decode_ui(game_ui *ui, const char *encoding)
|
|
{
|
|
}
|
|
|
|
static void game_changed_state(game_ui *ui, const game_state *oldstate,
|
|
const game_state *newstate)
|
|
{
|
|
}
|
|
|
|
struct game_drawstate {
|
|
int tilesize;
|
|
int FIXME;
|
|
};
|
|
|
|
static char *interpret_move(const game_state *state, game_ui *ui,
|
|
const game_drawstate *ds,
|
|
int x, int y, int button)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static game_state *execute_move(const game_state *state, const char *move)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Drawing routines.
|
|
*/
|
|
|
|
static void game_compute_size(const game_params *params, int tilesize,
|
|
int *x, int *y)
|
|
{
|
|
*x = *y = 10 * tilesize; /* FIXME */
|
|
}
|
|
|
|
static void game_set_size(drawing *dr, game_drawstate *ds,
|
|
const game_params *params, int tilesize)
|
|
{
|
|
ds->tilesize = tilesize;
|
|
}
|
|
|
|
static float *game_colours(frontend *fe, int *ncolours)
|
|
{
|
|
float *ret = snewn(3 * NCOLOURS, float);
|
|
|
|
frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
|
|
|
|
*ncolours = NCOLOURS;
|
|
return ret;
|
|
}
|
|
|
|
static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
|
|
{
|
|
struct game_drawstate *ds = snew(struct game_drawstate);
|
|
|
|
ds->tilesize = 0;
|
|
ds->FIXME = 0;
|
|
|
|
return ds;
|
|
}
|
|
|
|
static void game_free_drawstate(drawing *dr, game_drawstate *ds)
|
|
{
|
|
sfree(ds);
|
|
}
|
|
|
|
static void game_redraw(drawing *dr, game_drawstate *ds,
|
|
const game_state *oldstate, const game_state *state,
|
|
int dir, const game_ui *ui,
|
|
float animtime, float flashtime)
|
|
{
|
|
/*
|
|
* The initial contents of the window are not guaranteed and
|
|
* can vary with front ends. To be on the safe side, all games
|
|
* should start by drawing a big background-colour rectangle
|
|
* covering the whole window.
|
|
*/
|
|
draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
|
|
}
|
|
|
|
static float game_anim_length(const game_state *oldstate,
|
|
const game_state *newstate, int dir, game_ui *ui)
|
|
{
|
|
return 0.0F;
|
|
}
|
|
|
|
static float game_flash_length(const game_state *oldstate,
|
|
const game_state *newstate, int dir, game_ui *ui)
|
|
{
|
|
return 0.0F;
|
|
}
|
|
|
|
static int game_status(const game_state *state)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static bool game_timing_state(const game_state *state, game_ui *ui)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void game_print_size(const game_params *params, float *x, float *y)
|
|
{
|
|
}
|
|
|
|
static void game_print(drawing *dr, const game_state *state, int tilesize)
|
|
{
|
|
}
|
|
|
|
#ifdef COMBINED
|
|
#define thegame separate
|
|
#endif
|
|
|
|
const struct game thegame = {
|
|
"Separate", NULL, NULL,
|
|
default_params,
|
|
game_fetch_preset, NULL,
|
|
decode_params,
|
|
encode_params,
|
|
free_params,
|
|
dup_params,
|
|
false, game_configure, custom_params,
|
|
validate_params,
|
|
new_game_desc,
|
|
validate_desc,
|
|
new_game,
|
|
dup_game,
|
|
free_game,
|
|
false, solve_game,
|
|
false, game_can_format_as_text_now, game_text_format,
|
|
new_ui,
|
|
free_ui,
|
|
encode_ui,
|
|
decode_ui,
|
|
NULL, /* game_request_keys */
|
|
game_changed_state,
|
|
interpret_move,
|
|
execute_move,
|
|
20 /* FIXME */, game_compute_size, game_set_size,
|
|
game_colours,
|
|
game_new_drawstate,
|
|
game_free_drawstate,
|
|
game_redraw,
|
|
game_anim_length,
|
|
game_flash_length,
|
|
game_status,
|
|
false, false, game_print_size, game_print,
|
|
false, /* wants_statusbar */
|
|
false, game_timing_state,
|
|
0, /* flags */
|
|
};
|