Files
puzzles/auxiliary/spectre-tables-extra.h
Simon Tatham a33d9fad02 Loopy / grid.c: support the new Spectre monotiling.
This uses a tile shape very similar to the hat, but the tiling
_structure_ is totally changed so that there aren't any reflected
copies of the tile.

I'm not sure how much difference this makes to gameplay: the two
tilings are very similar for Loopy purposes. But the code was fun to
write, and I think the Spectre shape is noticeably prettier, so I'm
adding this to the collection anyway.

The test programs also generate a pile of SVG images used in the
companion article on my website.
2023-06-16 19:15:47 +01:00

335 lines
10 KiB
C

/*
* Further data tables used to generate the final transition maps.
*/
/*
* Locations in the plane of the centres of the 8 hexagons in the
* expansion of each hex.
*
* We take the centre-to-centre distance to be 6 units, so that other
* locations in the hex tiling (e.g. edge midpoints and vertices) will
* still have integer coefficients.
*
* These locations are represented using the same Point type used for
* the whole tiling, but all our angles are 60 degrees, so we don't
* ever need the coefficients of d or d^3, only of 1 and d^2.
*/
static const Point hex_centres[] = {
{{0, 0, 0, 0}}, {{6, 0, 0, 0}}, /* 0 1 */
{{0, 0, -6, 0}}, {{6, 0, -6, 0}}, /* 2 3 */
{{0, 0, -12, 0}}, {{6, 0, -12, 0}}, {{12, 0, -12, 0}}, /* 4 5 6 */
{{12, 0, -18, 0}}, /* 7 */
};
/*
* Orientations of all the sub-hexes in the expansion of each hex.
* Measured anticlockwise (that is, as a power of s) from 0, where 0
* means the hex is upright, with its own vertex #0 at the top.
*/
static const unsigned orientations_G[] = {
2, /* HEX_F */
1, /* HEX_X */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_P */
5, /* HEX_D */
0, /* HEX_J */
/* hex #7 is not present for this tile */
};
static const unsigned orientations_D[] = {
2, /* HEX_F */
1, /* HEX_P */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_X */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_X */
};
static const unsigned orientations_J[] = {
2, /* HEX_F */
1, /* HEX_P */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_Y */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_P */
};
static const unsigned orientations_L[] = {
2, /* HEX_F */
1, /* HEX_P */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_Y */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_X */
};
static const unsigned orientations_X[] = {
2, /* HEX_F */
1, /* HEX_Y */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_Y */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_P */
};
static const unsigned orientations_P[] = {
2, /* HEX_F */
1, /* HEX_Y */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_Y */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_X */
};
static const unsigned orientations_S[] = {
2, /* HEX_L */
1, /* HEX_P */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_X */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_X */
};
static const unsigned orientations_F[] = {
2, /* HEX_F */
1, /* HEX_P */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_Y */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_Y */
};
static const unsigned orientations_Y[] = {
2, /* HEX_F */
1, /* HEX_Y */
0, /* HEX_G */
1, /* HEX_S */
4, /* HEX_Y */
5, /* HEX_D */
0, /* HEX_F */
5, /* HEX_Y */
};
/*
* For each hex type, indicate the point on the boundary of the
* expansion that corresponds to vertex 0 of the superhex. Also,
* indicate the initial direction we head in to go round the edge.
*/
#define HEX_OUTLINE_START_COMMON {{ -4, 0, -10, 0 }}, {{ +2, 0, +2, 0 }}
#define HEX_OUTLINE_START_RARE {{ -2, 0, -14, 0 }}, {{ -2, 0, +4, 0 }}
#define HEX_OUTLINE_START_G HEX_OUTLINE_START_COMMON
#define HEX_OUTLINE_START_D HEX_OUTLINE_START_RARE
#define HEX_OUTLINE_START_J HEX_OUTLINE_START_COMMON
#define HEX_OUTLINE_START_L HEX_OUTLINE_START_COMMON
#define HEX_OUTLINE_START_X HEX_OUTLINE_START_COMMON
#define HEX_OUTLINE_START_P HEX_OUTLINE_START_COMMON
#define HEX_OUTLINE_START_S HEX_OUTLINE_START_RARE
#define HEX_OUTLINE_START_F HEX_OUTLINE_START_COMMON
#define HEX_OUTLINE_START_Y HEX_OUTLINE_START_COMMON
/*
* Similarly, for each hex type, indicate the point on the boundary of
* its Spectre expansion that corresponds to hex vertex 0.
*
* This time, it's easiest just to indicate which vertex of which
* sub-Spectre we take in each case, because the Spectre outlines
* don't take predictable turns between the edge expansions, so the
* routine consuming this data will have to look things up in its
* edgemap anyway.
*/
#define SPEC_OUTLINE_START_COMMON 0, 9
#define SPEC_OUTLINE_START_RARE 0, 8
#define SPEC_OUTLINE_START_G SPEC_OUTLINE_START_COMMON
#define SPEC_OUTLINE_START_D SPEC_OUTLINE_START_RARE
#define SPEC_OUTLINE_START_J SPEC_OUTLINE_START_COMMON
#define SPEC_OUTLINE_START_L SPEC_OUTLINE_START_COMMON
#define SPEC_OUTLINE_START_X SPEC_OUTLINE_START_COMMON
#define SPEC_OUTLINE_START_P SPEC_OUTLINE_START_COMMON
#define SPEC_OUTLINE_START_S SPEC_OUTLINE_START_RARE
#define SPEC_OUTLINE_START_F SPEC_OUTLINE_START_COMMON
#define SPEC_OUTLINE_START_Y SPEC_OUTLINE_START_COMMON
/*
* The paper also defines a set of 8 different classes of edges for
* the hexagons. (You can imagine these as different shapes of
* jigsaw-piece tab, constraining how the hexes can fit together). So
* for each hex, we need a list of its edge types.
*
* Most edge types come in two matching pairs, which the paper labels
* with the same lowercase Greek letter and a + or - superscript, e.g.
* alpha^+ and alpha^-. The usual rule is that when two edges meet,
* they have to be the + and - versions of the same letter. The
* exception to this rule is the 'eta' edge, which has no sign: it's
* symmetric, so any two eta edges can validly meet.
*
* We express this here by defining an enumeration in which eta = 0
* and all other edge types have positive values, so that integer
* negation can be used to indicate the other edge that fits with this
* one (and for eta, it doesn't change the value).
*/
enum Edge {
edge_eta = 0,
edge_alpha,
edge_beta,
edge_gamma,
edge_delta,
edge_epsilon,
edge_zeta,
edge_theta,
};
/*
* Edge types for each hex are specified anticlockwise, starting from
* the top vertex, so that edge #0 is the top-left diagonal edge, edge
* #1 the left-hand vertical edge, etc.
*/
static const int edges_G[6] = {
-edge_beta, -edge_alpha, +edge_alpha,
-edge_gamma, -edge_delta, +edge_beta,
};
static const int edges_D[6] = {
-edge_zeta, +edge_gamma, +edge_beta,
-edge_epsilon, +edge_alpha, -edge_gamma,
};
static const int edges_J[6] = {
-edge_beta, +edge_gamma, +edge_beta,
+edge_theta, +edge_beta, edge_eta,
};
static const int edges_L[6] = {
-edge_beta, +edge_gamma, +edge_beta,
-edge_epsilon, +edge_alpha, -edge_theta,
};
static const int edges_X[6] = {
-edge_beta, -edge_alpha, +edge_epsilon,
+edge_theta, +edge_beta, edge_eta,
};
static const int edges_P[6] = {
-edge_beta, -edge_alpha, +edge_epsilon,
-edge_epsilon, +edge_alpha, -edge_theta,
};
static const int edges_S[6] = {
+edge_delta, +edge_zeta, +edge_beta,
-edge_epsilon, +edge_alpha, -edge_gamma,
};
static const int edges_F[6] = {
-edge_beta, +edge_gamma, +edge_beta,
-edge_epsilon, +edge_epsilon, edge_eta,
};
static const int edges_Y[6] = {
-edge_beta, -edge_alpha, +edge_epsilon,
-edge_epsilon, +edge_epsilon, edge_eta,
};
/*
* Now specify the actual shape of each edge type, in terms of the
* angles of turns as you traverse the edge.
*
* Edges around the outline of a hex expansion are traversed
* _clockwise_, because each expansion step flips the handedness of
* the whole system.
*
* Each array has one fewer element than the number of sub-edges in
* the edge shape (for the usual reason - n edges in a path have only
* n-1 vertices separating them).
*
* These arrays show the positive version of each edge type. The
* negative version is obtained by reversing the order of the turns
* and also the sign of each turn.
*/
static const int hex_edge_shape_eta[] = { +2, +2, -2, -2 };
static const int hex_edge_shape_alpha[] = { +2, -2 };
static const int hex_edge_shape_beta[] = { -2 };
static const int hex_edge_shape_gamma[] = { +2, -2, -2, +2 };
static const int hex_edge_shape_delta[] = { -2, +2, -2, +2 };
static const int hex_edge_shape_epsilon[] = { +2, -2, -2 };
static const int hex_edge_shape_zeta[] = { -2, +2 };
static const int hex_edge_shape_theta[] = { +2, +2, -2, -2, +2 };
static const int *const hex_edge_shapes[] = {
hex_edge_shape_eta,
hex_edge_shape_alpha,
hex_edge_shape_beta,
hex_edge_shape_gamma,
hex_edge_shape_delta,
hex_edge_shape_epsilon,
hex_edge_shape_zeta,
hex_edge_shape_theta,
};
static const size_t hex_edge_lengths[] = {
lenof(hex_edge_shape_eta) + 1,
lenof(hex_edge_shape_alpha) + 1,
lenof(hex_edge_shape_beta) + 1,
lenof(hex_edge_shape_gamma) + 1,
lenof(hex_edge_shape_delta) + 1,
lenof(hex_edge_shape_epsilon) + 1,
lenof(hex_edge_shape_zeta) + 1,
lenof(hex_edge_shape_theta) + 1,
};
static const int spec_edge_shape_eta[] = { 0 };
static const int spec_edge_shape_alpha[] = { -2, +3 };
static const int spec_edge_shape_beta[] = { +3, -2 };
static const int spec_edge_shape_gamma[] = { +2 };
static const int spec_edge_shape_delta[] = { +2, +3, +2, -3, +2 };
static const int spec_edge_shape_epsilon[] = { +3 };
static const int spec_edge_shape_zeta[] = { -2 };
/* In expansion to Spectres, a theta edge corresponds to just one
* Spectre edge, so its turns array would be completely empty! */
static const int *const spec_edge_shapes[] = {
spec_edge_shape_eta,
spec_edge_shape_alpha,
spec_edge_shape_beta,
spec_edge_shape_gamma,
spec_edge_shape_delta,
spec_edge_shape_epsilon,
spec_edge_shape_zeta,
NULL, /* theta has no turns */
};
static const size_t spec_edge_lengths[] = {
lenof(spec_edge_shape_eta) + 1,
lenof(spec_edge_shape_alpha) + 1,
lenof(spec_edge_shape_beta) + 1,
lenof(spec_edge_shape_gamma) + 1,
lenof(spec_edge_shape_delta) + 1,
lenof(spec_edge_shape_epsilon) + 1,
lenof(spec_edge_shape_zeta) + 1,
1, /* theta is only one edge long */
};
/*
* Each edge type corresponds to a fixed number of edges of the
* hexagon layout in the expansion of each hex, and also to a fixed
* number of edges of the Spectre(s) that each hex expands to in the
* final step.
*/
static const int edgelen_hex[] = {
5, /* edge_eta */
3, /* edge_alpha */
2, /* edge_beta */
5, /* edge_gamma */
5, /* edge_delta */
4, /* edge_epsilon */
3, /* edge_zeta */
6, /* edge_theta */
};
static const int edgelen_spectre[] = {
2, /* edge_eta */
3, /* edge_alpha */
3, /* edge_beta */
2, /* edge_gamma */
6, /* edge_delta */
2, /* edge_epsilon */
2, /* edge_zeta */
1, /* edge_theta */
};