diff options
Diffstat (limited to 'apps/plugins/puzzles/src/spectre-internal.h')
-rw-r--r-- | apps/plugins/puzzles/src/spectre-internal.h | 327 |
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 | |||
12 | typedef enum Hex { | ||
13 | #define HEX_ENUM_DECL(x) HEX_##x, | ||
14 | HEX_LETTERS(HEX_ENUM_DECL) | ||
15 | #undef HEX_ENUM_DECL | ||
16 | } Hex; | ||
17 | |||
18 | static inline unsigned num_subhexes(Hex h) | ||
19 | { | ||
20 | return h == HEX_G ? 7 : 8; | ||
21 | } | ||
22 | |||
23 | static 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 | */ | ||
31 | struct MapEntry { | ||
32 | bool internal; | ||
33 | unsigned char hi, lo; | ||
34 | }; | ||
35 | struct MapEdge { | ||
36 | unsigned char startindex, len; | ||
37 | }; | ||
38 | struct 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 | */ | ||
58 | typedef 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 | |||
63 | typedef 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 | |||
74 | SpectreCoords *spectre_coords_new(void); | ||
75 | void spectre_coords_free(SpectreCoords *hc); | ||
76 | void spectre_coords_make_space(SpectreCoords *hc, size_t size); | ||
77 | SpectreCoords *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 | */ | ||
93 | typedef struct Point { | ||
94 | int coeffs[4]; | ||
95 | } Point; | ||
96 | typedef struct Spectre Spectre; | ||
97 | struct 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 */ | ||
104 | void spectre_place(Spectre *spec, Point u, Point v, int index_of_u); | ||
105 | |||
106 | /* Free a Spectre and its contained coordinates */ | ||
107 | void spectre_free(Spectre *spec); | ||
108 | |||
109 | /* | ||
110 | * A Point is really a complex number, so we can add, subtract and | ||
111 | * multiply them. | ||
112 | */ | ||
113 | static 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 | } | ||
121 | static 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 | } | ||
129 | static 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 | } | ||
139 | static 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 | } | ||
157 | static 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 | */ | ||
170 | static 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 | */ | ||
217 | typedef 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 | |||
225 | void spectrectx_init_random(SpectreContext *ctx, random_state *rs); | ||
226 | void spectrectx_init_from_params( | ||
227 | SpectreContext *ctx, const struct SpectrePatchParams *ps); | ||
228 | void spectrectx_cleanup(SpectreContext *ctx); | ||
229 | SpectreCoords *spectrectx_initial_coords(SpectreContext *ctx); | ||
230 | void spectrectx_extend_coords(SpectreContext *ctx, SpectreCoords *hc, | ||
231 | size_t n); | ||
232 | void spectrectx_step(SpectreContext *ctx, SpectreCoords *sc, | ||
233 | unsigned edge, unsigned *outedge); | ||
234 | void 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 */ | ||
239 | void 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 */ | ||
244 | Spectre *spectre_initial(SpectreContext *ctx); | ||
245 | Spectre *spectre_adjacent(SpectreContext *ctx, const Spectre *src_spec, | ||
246 | unsigned src_edge, unsigned *dst_edge); | ||
247 | |||
248 | /* For extracting the point coordinates */ | ||
249 | typedef struct Coord { | ||
250 | int c1, cr3; /* coefficients of 1 and sqrt(3) respectively */ | ||
251 | } Coord; | ||
252 | |||
253 | static 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 | |||
259 | static 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 | |||
265 | static 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 | |||
280 | static inline Coord coord_construct(int c1, int cr3) | ||
281 | { | ||
282 | Coord c = { c1, cr3 }; | ||
283 | return c; | ||
284 | } | ||
285 | |||
286 | static inline Coord coord_integer(int c1) | ||
287 | { | ||
288 | return coord_construct(c1, 0); | ||
289 | } | ||
290 | |||
291 | static 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 | |||
299 | static 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 | |||
307 | static 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 | |||
315 | static 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 | |||
324 | static inline int coord_cmp(Coord a, Coord b) | ||
325 | { | ||
326 | return coord_sign(coord_sub(a, b)); | ||
327 | } | ||