summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/spectre-internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/spectre-internal.h')
-rw-r--r--apps/plugins/puzzles/src/spectre-internal.h327
1 files changed, 327 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/spectre-internal.h b/apps/plugins/puzzles/src/spectre-internal.h
new file mode 100644
index 0000000000..e43d30bfef
--- /dev/null
+++ b/apps/plugins/puzzles/src/spectre-internal.h
@@ -0,0 +1,327 @@
1#include "spectre.h"
2
3/*
4 * List macro of the names for hexagon types, which will be reused all
5 * over the place.
6 *
7 * (I have to call the parameter to this list macro something other
8 * than X, because here, X is also one of the macro arguments!)
9 */
10#define HEX_LETTERS(Z) Z(G) Z(D) Z(J) Z(L) Z(X) Z(P) Z(S) Z(F) Z(Y)
11
12typedef enum Hex {
13 #define HEX_ENUM_DECL(x) HEX_##x,
14 HEX_LETTERS(HEX_ENUM_DECL)
15 #undef HEX_ENUM_DECL
16} Hex;
17
18static inline unsigned num_subhexes(Hex h)
19{
20 return h == HEX_G ? 7 : 8;
21}
22
23static inline unsigned num_spectres(Hex h)
24{
25 return h == HEX_G ? 2 : 1;
26}
27
28/*
29 * Data types used in the lookup tables.
30 */
31struct MapEntry {
32 bool internal;
33 unsigned char hi, lo;
34};
35struct MapEdge {
36 unsigned char startindex, len;
37};
38struct Possibility {
39 unsigned char hi, lo;
40 unsigned long prob;
41};
42
43/*
44 * Coordinate system for tracking Spectres and their hexagonal
45 * metatiles.
46 *
47 * SpectreCoords will store the index of a single Spectre within a
48 * smallest-size hexagon, plus an array of HexCoord each indexing a
49 * hexagon within the expansion of a larger hexagon.
50 *
51 * The last coordinate stored, sc->c[sc->nc-1], will have a hex type
52 * but no index (represented by index==-1). This means "we haven't
53 * decided yet what this level of metatile needs to be". If we need to
54 * refer to this level during the hatctx_step algorithm, we make it up
55 * at random, based on a table of what metatiles each type can
56 * possibly be part of, at what index.
57 */
58typedef struct HexCoord {
59 int index; /* index within that tile, or -1 if not yet known */
60 Hex type; /* type of this hexagon */
61} HexCoord;
62
63typedef struct SpectreCoords {
64 int index; /* index of Spectre within the order-0 hexagon */
65 HexCoord *c;
66 size_t nc, csize;
67
68 /* Used by spectre-test to four-colour output tilings, and
69 * maintained unconditionally because it's easier than making it
70 * conditional */
71 unsigned char hex_colour, prev_hex_colour, incoming_hex_edge;
72} SpectreCoords;
73
74SpectreCoords *spectre_coords_new(void);
75void spectre_coords_free(SpectreCoords *hc);
76void spectre_coords_make_space(SpectreCoords *hc, size_t size);
77SpectreCoords *spectre_coords_copy(SpectreCoords *hc_in);
78
79/*
80 * Coordinate system for locating Spectres in the plane.
81 *
82 * The 'Point' structure represents a single point by means of an
83 * integer linear combination of {1, d, d^2, d^3}, where d is the
84 * complex number exp(i pi/6) representing 1/12 of a turn about the
85 * origin.
86 *
87 * The 'Spectre' structure represents an entire Spectre in a tiling,
88 * giving both the locations of all of its vertices and its
89 * combinatorial coordinates. It also contains a linked-list pointer,
90 * used during breadth-first search to generate all the Spectres in an
91 * area.
92 */
93typedef struct Point {
94 int coeffs[4];
95} Point;
96typedef struct Spectre Spectre;
97struct Spectre {
98 Point vertices[14];
99 SpectreCoords *sc;
100 Spectre *next; /* used in breadth-first search */
101};
102
103/* Fill in all the coordinates of a Spectre starting from any single edge */
104void spectre_place(Spectre *spec, Point u, Point v, int index_of_u);
105
106/* Free a Spectre and its contained coordinates */
107void spectre_free(Spectre *spec);
108
109/*
110 * A Point is really a complex number, so we can add, subtract and
111 * multiply them.
112 */
113static inline Point point_add(Point a, Point b)
114{
115 Point r;
116 size_t i;
117 for (i = 0; i < 4; i++)
118 r.coeffs[i] = a.coeffs[i] + b.coeffs[i];
119 return r;
120}
121static inline Point point_sub(Point a, Point b)
122{
123 Point r;
124 size_t i;
125 for (i = 0; i < 4; i++)
126 r.coeffs[i] = a.coeffs[i] - b.coeffs[i];
127 return r;
128}
129static inline Point point_mul_by_d(Point x)
130{
131 Point r;
132 /* Multiply by d by using the identity d^4 - d^2 + 1 = 0, so d^4 = d^2+1 */
133 r.coeffs[0] = -x.coeffs[3];
134 r.coeffs[1] = x.coeffs[0];
135 r.coeffs[2] = x.coeffs[1] + x.coeffs[3];
136 r.coeffs[3] = x.coeffs[2];
137 return r;
138}
139static inline Point point_mul(Point a, Point b)
140{
141 size_t i, j;
142 Point r;
143
144 /* Initialise r to be a, scaled by b's d^3 term */
145 for (j = 0; j < 4; j++)
146 r.coeffs[j] = a.coeffs[j] * b.coeffs[3];
147
148 /* Now iterate r = d*r + (next coefficient down), by Horner's rule */
149 for (i = 3; i-- > 0 ;) {
150 r = point_mul_by_d(r);
151 for (j = 0; j < 4; j++)
152 r.coeffs[j] += a.coeffs[j] * b.coeffs[i];
153 }
154
155 return r;
156}
157static inline bool point_equal(Point a, Point b)
158{
159 size_t i;
160 for (i = 0; i < 4; i++)
161 if (a.coeffs[i] != b.coeffs[i])
162 return false;
163 return true;
164}
165
166/*
167 * Return the Point corresponding to a rotation of s steps around the
168 * origin, i.e. a rotation by 30*s degrees or s*pi/6 radians.
169 */
170static inline Point point_rot(int s)
171{
172 Point r = {{ 1, 0, 0, 0 }};
173 Point dpower = {{ 0, 1, 0, 0 }};
174
175 /* Reduce to a sensible range */
176 s = s % 12;
177 if (s < 0)
178 s += 12;
179
180 while (true) {
181 if (s & 1)
182 r = point_mul(r, dpower);
183 s >>= 1;
184 if (!s)
185 break;
186 dpower = point_mul(dpower, dpower);
187 }
188
189 return r;
190}
191
192/*
193 * SpectreContext is the shared context of a whole run of the
194 * algorithm. Its 'prototype' SpectreCoords object represents the
195 * coordinates of the starting Spectre, and is extended as necessary;
196 * any other SpectreCoord that needs extending will copy the
197 * higher-order values from ctx->prototype as needed, so that once
198 * each choice has been made, it remains consistent.
199 *
200 * When we're inventing a random piece of tiling in the first place,
201 * we append to ctx->prototype by choosing a random (but legal)
202 * higher-level metatile for the current topmost one to turn out to be
203 * part of. When we're replaying a generation whose parameters are
204 * already stored, we don't have a random_state, and we make fixed
205 * decisions if not enough coordinates were provided, as in the
206 * corresponding hat.c system.
207 *
208 * For a normal (non-testing) caller, spectrectx_generate() is the
209 * main useful function. It breadth-first searches a whole area to
210 * generate all the Spectres in it, starting from a (typically
211 * central) one with the coordinates of ctx->prototype. The callback
212 * function processes each Spectre as it's generated, and returns true
213 * or false to indicate whether that Spectre is within the bounds of
214 * the target area (and therefore the search should continue exploring
215 * its neighbours).
216 */
217typedef struct SpectreContext {
218 random_state *rs;
219 bool must_free_rs;
220 Point start_vertices[2]; /* vertices 0,1 of the starting Spectre */
221 int orientation; /* orientation to put in SpectrePatchParams */
222 SpectreCoords *prototype;
223} SpectreContext;
224
225void spectrectx_init_random(SpectreContext *ctx, random_state *rs);
226void spectrectx_init_from_params(
227 SpectreContext *ctx, const struct SpectrePatchParams *ps);
228void spectrectx_cleanup(SpectreContext *ctx);
229SpectreCoords *spectrectx_initial_coords(SpectreContext *ctx);
230void spectrectx_extend_coords(SpectreContext *ctx, SpectreCoords *hc,
231 size_t n);
232void spectrectx_step(SpectreContext *ctx, SpectreCoords *sc,
233 unsigned edge, unsigned *outedge);
234void spectrectx_generate(SpectreContext *ctx,
235 bool (*callback)(void *cbctx, const Spectre *spec),
236 void *cbctx);
237
238/* For spectre-test to directly generate a tiling of hexes */
239void spectrectx_step_hex(SpectreContext *ctx, SpectreCoords *sc,
240 size_t depth, unsigned edge, unsigned *outedge);
241
242/* Subroutines that step around the tiling specified by a SpectreCtx,
243 * delivering both plane and combinatorial coordinates as they go */
244Spectre *spectre_initial(SpectreContext *ctx);
245Spectre *spectre_adjacent(SpectreContext *ctx, const Spectre *src_spec,
246 unsigned src_edge, unsigned *dst_edge);
247
248/* For extracting the point coordinates */
249typedef struct Coord {
250 int c1, cr3; /* coefficients of 1 and sqrt(3) respectively */
251} Coord;
252
253static inline Coord point_x(Point p)
254{
255 Coord x = { 2 * p.coeffs[0] + p.coeffs[2], p.coeffs[1] };
256 return x;
257}
258
259static inline Coord point_y(Point p)
260{
261 Coord y = { 2 * p.coeffs[3] + p.coeffs[1], p.coeffs[2] };
262 return y;
263}
264
265static inline int coord_sign(Coord x)
266{
267 if (x.c1 == 0 && x.cr3 == 0)
268 return 0;
269 if (x.c1 >= 0 && x.cr3 >= 0)
270 return +1;
271 if (x.c1 <= 0 && x.cr3 <= 0)
272 return -1;
273
274 if (x.c1 * x.c1 > 3 * x.cr3 * x.cr3)
275 return x.c1 < 0 ? -1 : +1;
276 else
277 return x.cr3 < 0 ? -1 : +1;
278}
279
280static inline Coord coord_construct(int c1, int cr3)
281{
282 Coord c = { c1, cr3 };
283 return c;
284}
285
286static inline Coord coord_integer(int c1)
287{
288 return coord_construct(c1, 0);
289}
290
291static inline Coord coord_add(Coord a, Coord b)
292{
293 Coord sum;
294 sum.c1 = a.c1 + b.c1;
295 sum.cr3 = a.cr3 + b.cr3;
296 return sum;
297}
298
299static inline Coord coord_sub(Coord a, Coord b)
300{
301 Coord diff;
302 diff.c1 = a.c1 - b.c1;
303 diff.cr3 = a.cr3 - b.cr3;
304 return diff;
305}
306
307static inline Coord coord_mul(Coord a, Coord b)
308{
309 Coord prod;
310 prod.c1 = a.c1 * b.c1 + 3 * a.cr3 * b.cr3;
311 prod.cr3 = a.c1 * b.cr3 + a.cr3 * b.c1;
312 return prod;
313}
314
315static inline Coord coord_abs(Coord a)
316{
317 int sign = coord_sign(a);
318 Coord abs;
319 abs.c1 = a.c1 * sign;
320 abs.cr3 = a.cr3 * sign;
321 return abs;
322}
323
324static inline int coord_cmp(Coord a, Coord b)
325{
326 return coord_sign(coord_sub(a, b));
327}