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

Previously, you'd ask this function 'What lies on the other side of edge #i of this Spectre tile?' and it would tell you the identity of another Spectre. Now it will also tell you which _edge_ of that Spectre adjoins the specified edge of the input one. This will be used in the extra spectre-test mode I'm about to add.
328 lines
9.3 KiB
C
328 lines
9.3 KiB
C
#include "spectre.h"
|
|
|
|
/*
|
|
* List macro of the names for hexagon types, which will be reused all
|
|
* over the place.
|
|
*
|
|
* (I have to call the parameter to this list macro something other
|
|
* than X, because here, X is also one of the macro arguments!)
|
|
*/
|
|
#define HEX_LETTERS(Z) Z(G) Z(D) Z(J) Z(L) Z(X) Z(P) Z(S) Z(F) Z(Y)
|
|
|
|
typedef enum Hex {
|
|
#define HEX_ENUM_DECL(x) HEX_##x,
|
|
HEX_LETTERS(HEX_ENUM_DECL)
|
|
#undef HEX_ENUM_DECL
|
|
} Hex;
|
|
|
|
static inline unsigned num_subhexes(Hex h)
|
|
{
|
|
return h == HEX_G ? 7 : 8;
|
|
}
|
|
|
|
static inline unsigned num_spectres(Hex h)
|
|
{
|
|
return h == HEX_G ? 2 : 1;
|
|
}
|
|
|
|
/*
|
|
* Data types used in the lookup tables.
|
|
*/
|
|
struct MapEntry {
|
|
bool internal;
|
|
unsigned char hi, lo;
|
|
};
|
|
struct MapEdge {
|
|
unsigned char startindex, len;
|
|
};
|
|
struct Possibility {
|
|
unsigned char hi, lo;
|
|
unsigned long prob;
|
|
};
|
|
|
|
/*
|
|
* Coordinate system for tracking Spectres and their hexagonal
|
|
* metatiles.
|
|
*
|
|
* SpectreCoords will store the index of a single Spectre within a
|
|
* smallest-size hexagon, plus an array of HexCoord each indexing a
|
|
* hexagon within the expansion of a larger hexagon.
|
|
*
|
|
* The last coordinate stored, sc->c[sc->nc-1], will have a hex type
|
|
* but no index (represented by index==-1). This means "we haven't
|
|
* decided yet what this level of metatile needs to be". If we need to
|
|
* refer to this level during the hatctx_step algorithm, we make it up
|
|
* at random, based on a table of what metatiles each type can
|
|
* possibly be part of, at what index.
|
|
*/
|
|
typedef struct HexCoord {
|
|
int index; /* index within that tile, or -1 if not yet known */
|
|
Hex type; /* type of this hexagon */
|
|
} HexCoord;
|
|
|
|
typedef struct SpectreCoords {
|
|
int index; /* index of Spectre within the order-0 hexagon */
|
|
HexCoord *c;
|
|
size_t nc, csize;
|
|
|
|
/* Used by spectre-test to four-colour output tilings, and
|
|
* maintained unconditionally because it's easier than making it
|
|
* conditional */
|
|
unsigned char hex_colour, prev_hex_colour, incoming_hex_edge;
|
|
} SpectreCoords;
|
|
|
|
SpectreCoords *spectre_coords_new(void);
|
|
void spectre_coords_free(SpectreCoords *hc);
|
|
void spectre_coords_make_space(SpectreCoords *hc, size_t size);
|
|
SpectreCoords *spectre_coords_copy(SpectreCoords *hc_in);
|
|
|
|
/*
|
|
* Coordinate system for locating Spectres in the plane.
|
|
*
|
|
* The 'Point' structure represents a single point by means of an
|
|
* integer linear combination of {1, d, d^2, d^3}, where d is the
|
|
* complex number exp(i pi/6) representing 1/12 of a turn about the
|
|
* origin.
|
|
*
|
|
* The 'Spectre' structure represents an entire Spectre in a tiling,
|
|
* giving both the locations of all of its vertices and its
|
|
* combinatorial coordinates. It also contains a linked-list pointer,
|
|
* used during breadth-first search to generate all the Spectres in an
|
|
* area.
|
|
*/
|
|
typedef struct Point {
|
|
int coeffs[4];
|
|
} Point;
|
|
typedef struct Spectre Spectre;
|
|
struct Spectre {
|
|
Point vertices[14];
|
|
SpectreCoords *sc;
|
|
Spectre *next; /* used in breadth-first search */
|
|
};
|
|
|
|
/* Fill in all the coordinates of a Spectre starting from any single edge */
|
|
void spectre_place(Spectre *spec, Point u, Point v, int index_of_u);
|
|
|
|
/* Free a Spectre and its contained coordinates */
|
|
void spectre_free(Spectre *spec);
|
|
|
|
/*
|
|
* A Point is really a complex number, so we can add, subtract and
|
|
* multiply them.
|
|
*/
|
|
static inline Point point_add(Point a, Point b)
|
|
{
|
|
Point r;
|
|
size_t i;
|
|
for (i = 0; i < 4; i++)
|
|
r.coeffs[i] = a.coeffs[i] + b.coeffs[i];
|
|
return r;
|
|
}
|
|
static inline Point point_sub(Point a, Point b)
|
|
{
|
|
Point r;
|
|
size_t i;
|
|
for (i = 0; i < 4; i++)
|
|
r.coeffs[i] = a.coeffs[i] - b.coeffs[i];
|
|
return r;
|
|
}
|
|
static inline Point point_mul_by_d(Point x)
|
|
{
|
|
Point r;
|
|
/* Multiply by d by using the identity d^4 - d^2 + 1 = 0, so d^4 = d^2+1 */
|
|
r.coeffs[0] = -x.coeffs[3];
|
|
r.coeffs[1] = x.coeffs[0];
|
|
r.coeffs[2] = x.coeffs[1] + x.coeffs[3];
|
|
r.coeffs[3] = x.coeffs[2];
|
|
return r;
|
|
}
|
|
static inline Point point_mul(Point a, Point b)
|
|
{
|
|
size_t i, j;
|
|
Point r;
|
|
|
|
/* Initialise r to be a, scaled by b's d^3 term */
|
|
for (j = 0; j < 4; j++)
|
|
r.coeffs[j] = a.coeffs[j] * b.coeffs[3];
|
|
|
|
/* Now iterate r = d*r + (next coefficient down), by Horner's rule */
|
|
for (i = 3; i-- > 0 ;) {
|
|
r = point_mul_by_d(r);
|
|
for (j = 0; j < 4; j++)
|
|
r.coeffs[j] += a.coeffs[j] * b.coeffs[i];
|
|
}
|
|
|
|
return r;
|
|
}
|
|
static inline bool point_equal(Point a, Point b)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < 4; i++)
|
|
if (a.coeffs[i] != b.coeffs[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Return the Point corresponding to a rotation of s steps around the
|
|
* origin, i.e. a rotation by 30*s degrees or s*pi/6 radians.
|
|
*/
|
|
static inline Point point_rot(int s)
|
|
{
|
|
Point r = {{ 1, 0, 0, 0 }};
|
|
Point dpower = {{ 0, 1, 0, 0 }};
|
|
|
|
/* Reduce to a sensible range */
|
|
s = s % 12;
|
|
if (s < 0)
|
|
s += 12;
|
|
|
|
while (true) {
|
|
if (s & 1)
|
|
r = point_mul(r, dpower);
|
|
s >>= 1;
|
|
if (!s)
|
|
break;
|
|
dpower = point_mul(dpower, dpower);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* SpectreContext is the shared context of a whole run of the
|
|
* algorithm. Its 'prototype' SpectreCoords object represents the
|
|
* coordinates of the starting Spectre, and is extended as necessary;
|
|
* any other SpectreCoord that needs extending will copy the
|
|
* higher-order values from ctx->prototype as needed, so that once
|
|
* each choice has been made, it remains consistent.
|
|
*
|
|
* When we're inventing a random piece of tiling in the first place,
|
|
* we append to ctx->prototype by choosing a random (but legal)
|
|
* higher-level metatile for the current topmost one to turn out to be
|
|
* part of. When we're replaying a generation whose parameters are
|
|
* already stored, we don't have a random_state, and we make fixed
|
|
* decisions if not enough coordinates were provided, as in the
|
|
* corresponding hat.c system.
|
|
*
|
|
* For a normal (non-testing) caller, spectrectx_generate() is the
|
|
* main useful function. It breadth-first searches a whole area to
|
|
* generate all the Spectres in it, starting from a (typically
|
|
* central) one with the coordinates of ctx->prototype. The callback
|
|
* function processes each Spectre as it's generated, and returns true
|
|
* or false to indicate whether that Spectre is within the bounds of
|
|
* the target area (and therefore the search should continue exploring
|
|
* its neighbours).
|
|
*/
|
|
typedef struct SpectreContext {
|
|
random_state *rs;
|
|
bool must_free_rs;
|
|
Point start_vertices[2]; /* vertices 0,1 of the starting Spectre */
|
|
int orientation; /* orientation to put in SpectrePatchParams */
|
|
SpectreCoords *prototype;
|
|
} SpectreContext;
|
|
|
|
void spectrectx_init_random(SpectreContext *ctx, random_state *rs);
|
|
void spectrectx_init_from_params(
|
|
SpectreContext *ctx, const struct SpectrePatchParams *ps);
|
|
void spectrectx_cleanup(SpectreContext *ctx);
|
|
SpectreCoords *spectrectx_initial_coords(SpectreContext *ctx);
|
|
void spectrectx_extend_coords(SpectreContext *ctx, SpectreCoords *hc,
|
|
size_t n);
|
|
void spectrectx_step(SpectreContext *ctx, SpectreCoords *sc,
|
|
unsigned edge, unsigned *outedge);
|
|
void spectrectx_generate(SpectreContext *ctx,
|
|
bool (*callback)(void *cbctx, const Spectre *spec),
|
|
void *cbctx);
|
|
|
|
/* For spectre-test to directly generate a tiling of hexes */
|
|
void spectrectx_step_hex(SpectreContext *ctx, SpectreCoords *sc,
|
|
size_t depth, unsigned edge, unsigned *outedge);
|
|
|
|
/* Subroutines that step around the tiling specified by a SpectreCtx,
|
|
* delivering both plane and combinatorial coordinates as they go */
|
|
Spectre *spectre_initial(SpectreContext *ctx);
|
|
Spectre *spectre_adjacent(SpectreContext *ctx, const Spectre *src_spec,
|
|
unsigned src_edge, unsigned *dst_edge);
|
|
|
|
/* For extracting the point coordinates */
|
|
typedef struct Coord {
|
|
int c1, cr3; /* coefficients of 1 and sqrt(3) respectively */
|
|
} Coord;
|
|
|
|
static inline Coord point_x(Point p)
|
|
{
|
|
Coord x = { 2 * p.coeffs[0] + p.coeffs[2], p.coeffs[1] };
|
|
return x;
|
|
}
|
|
|
|
static inline Coord point_y(Point p)
|
|
{
|
|
Coord y = { 2 * p.coeffs[3] + p.coeffs[1], p.coeffs[2] };
|
|
return y;
|
|
}
|
|
|
|
static inline int coord_sign(Coord x)
|
|
{
|
|
if (x.c1 == 0 && x.cr3 == 0)
|
|
return 0;
|
|
if (x.c1 >= 0 && x.cr3 >= 0)
|
|
return +1;
|
|
if (x.c1 <= 0 && x.cr3 <= 0)
|
|
return -1;
|
|
|
|
if (x.c1 * x.c1 > 3 * x.cr3 * x.cr3)
|
|
return x.c1 < 0 ? -1 : +1;
|
|
else
|
|
return x.cr3 < 0 ? -1 : +1;
|
|
}
|
|
|
|
static inline Coord coord_construct(int c1, int cr3)
|
|
{
|
|
Coord c = { c1, cr3 };
|
|
return c;
|
|
}
|
|
|
|
static inline Coord coord_integer(int c1)
|
|
{
|
|
return coord_construct(c1, 0);
|
|
}
|
|
|
|
static inline Coord coord_add(Coord a, Coord b)
|
|
{
|
|
Coord sum;
|
|
sum.c1 = a.c1 + b.c1;
|
|
sum.cr3 = a.cr3 + b.cr3;
|
|
return sum;
|
|
}
|
|
|
|
static inline Coord coord_sub(Coord a, Coord b)
|
|
{
|
|
Coord diff;
|
|
diff.c1 = a.c1 - b.c1;
|
|
diff.cr3 = a.cr3 - b.cr3;
|
|
return diff;
|
|
}
|
|
|
|
static inline Coord coord_mul(Coord a, Coord b)
|
|
{
|
|
Coord prod;
|
|
prod.c1 = a.c1 * b.c1 + 3 * a.cr3 * b.cr3;
|
|
prod.cr3 = a.c1 * b.cr3 + a.cr3 * b.c1;
|
|
return prod;
|
|
}
|
|
|
|
static inline Coord coord_abs(Coord a)
|
|
{
|
|
int sign = coord_sign(a);
|
|
Coord abs;
|
|
abs.c1 = a.c1 * sign;
|
|
abs.cr3 = a.cr3 * sign;
|
|
return abs;
|
|
}
|
|
|
|
static inline int coord_cmp(Coord a, Coord b)
|
|
{
|
|
return coord_sign(coord_sub(a, b));
|
|
}
|