spectre-test.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. /*
  2. * Standalone test program for spectre.c.
  3. */
  4. #include <assert.h>
  5. #ifdef NO_TGMATH_H
  6. # include <math.h>
  7. #else
  8. # include <tgmath.h>
  9. #endif
  10. #include <stdarg.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include "puzzles.h"
  14. #include "spectre-internal.h"
  15. #include "spectre-tables-manual.h"
  16. #include "spectre-tables-auto.h"
  17. #include "spectre-help.h"
  18. static void step_tests(void)
  19. {
  20. SpectreContext ctx[1];
  21. random_state *rs;
  22. SpectreCoords *sc;
  23. unsigned outedge;
  24. rs = random_new("12345", 5);
  25. spectrectx_init_random(ctx, rs);
  26. /* Simplest possible transition: between the two Spectres making
  27. * up a G hex. */
  28. sc = spectre_coords_new();
  29. spectre_coords_make_space(sc, 1);
  30. sc->index = 0;
  31. sc->nc = 1;
  32. sc->c[0].type = HEX_G;
  33. sc->c[0].index = -1;
  34. spectrectx_step(ctx, sc, 12, &outedge);
  35. assert(outedge == 5);
  36. assert(sc->index == 1);
  37. assert(sc->nc == 1);
  38. assert(sc->c[0].type == HEX_G);
  39. assert(sc->c[0].index == -1);
  40. spectre_coords_free(sc);
  41. /* Test the double Spectre transition. Here, within a F superhex,
  42. * we attempt to step from the G subhex to the S one, in such a
  43. * way that the place where we enter the Spectre corresponding to
  44. * the S hex is on its spur of detached edge, causing us to
  45. * immediately transition back out of the other side of that spur
  46. * and end up in the D subhex instead. */
  47. sc = spectre_coords_new();
  48. spectre_coords_make_space(sc, 2);
  49. sc->index = 1;
  50. sc->nc = 2;
  51. sc->c[0].type = HEX_G;
  52. sc->c[0].index = 2;
  53. sc->c[1].type = HEX_F;
  54. sc->c[1].index = -1;
  55. spectrectx_step(ctx, sc, 1, &outedge);
  56. assert(outedge == 6);
  57. assert(sc->index == 0);
  58. assert(sc->nc == 2);
  59. assert(sc->c[0].type == HEX_D);
  60. assert(sc->c[0].index == 5);
  61. assert(sc->c[1].type == HEX_F);
  62. assert(sc->c[1].index == -1);
  63. spectre_coords_free(sc);
  64. /* However, _this_ transition leaves the same G subhex by the same
  65. * edge of the hexagon, but further along it, so that we land in
  66. * the S Spectre and stay there, without needing a double
  67. * transition. */
  68. sc = spectre_coords_new();
  69. spectre_coords_make_space(sc, 2);
  70. sc->index = 1;
  71. sc->nc = 2;
  72. sc->c[0].type = HEX_G;
  73. sc->c[0].index = 2;
  74. sc->c[1].type = HEX_F;
  75. sc->c[1].index = -1;
  76. spectrectx_step(ctx, sc, 13, &outedge);
  77. assert(outedge == 4);
  78. assert(sc->index == 0);
  79. assert(sc->nc == 2);
  80. assert(sc->c[0].type == HEX_S);
  81. assert(sc->c[0].index == 3);
  82. assert(sc->c[1].type == HEX_F);
  83. assert(sc->c[1].index == -1);
  84. spectre_coords_free(sc);
  85. /* A couple of randomly generated transition tests that go a long
  86. * way up the stack. */
  87. sc = spectre_coords_new();
  88. spectre_coords_make_space(sc, 7);
  89. sc->index = 0;
  90. sc->nc = 7;
  91. sc->c[0].type = HEX_S;
  92. sc->c[0].index = 3;
  93. sc->c[1].type = HEX_Y;
  94. sc->c[1].index = 7;
  95. sc->c[2].type = HEX_Y;
  96. sc->c[2].index = 4;
  97. sc->c[3].type = HEX_Y;
  98. sc->c[3].index = 4;
  99. sc->c[4].type = HEX_F;
  100. sc->c[4].index = 0;
  101. sc->c[5].type = HEX_X;
  102. sc->c[5].index = 1;
  103. sc->c[6].type = HEX_G;
  104. sc->c[6].index = -1;
  105. spectrectx_step(ctx, sc, 13, &outedge);
  106. assert(outedge == 12);
  107. assert(sc->index == 0);
  108. assert(sc->nc == 7);
  109. assert(sc->c[0].type == HEX_Y);
  110. assert(sc->c[0].index == 1);
  111. assert(sc->c[1].type == HEX_P);
  112. assert(sc->c[1].index == 1);
  113. assert(sc->c[2].type == HEX_D);
  114. assert(sc->c[2].index == 5);
  115. assert(sc->c[3].type == HEX_Y);
  116. assert(sc->c[3].index == 4);
  117. assert(sc->c[4].type == HEX_X);
  118. assert(sc->c[4].index == 7);
  119. assert(sc->c[5].type == HEX_S);
  120. assert(sc->c[5].index == 3);
  121. assert(sc->c[6].type == HEX_G);
  122. assert(sc->c[6].index == -1);
  123. spectre_coords_free(sc);
  124. sc = spectre_coords_new();
  125. spectre_coords_make_space(sc, 7);
  126. sc->index = 0;
  127. sc->nc = 7;
  128. sc->c[0].type = HEX_Y;
  129. sc->c[0].index = 7;
  130. sc->c[1].type = HEX_F;
  131. sc->c[1].index = 6;
  132. sc->c[2].type = HEX_Y;
  133. sc->c[2].index = 4;
  134. sc->c[3].type = HEX_X;
  135. sc->c[3].index = 7;
  136. sc->c[4].type = HEX_L;
  137. sc->c[4].index = 0;
  138. sc->c[5].type = HEX_S;
  139. sc->c[5].index = 3;
  140. sc->c[6].type = HEX_F;
  141. sc->c[6].index = -1;
  142. spectrectx_step(ctx, sc, 0, &outedge);
  143. assert(outedge == 1);
  144. assert(sc->index == 0);
  145. assert(sc->nc == 7);
  146. assert(sc->c[0].type == HEX_P);
  147. assert(sc->c[0].index == 1);
  148. assert(sc->c[1].type == HEX_F);
  149. assert(sc->c[1].index == 0);
  150. assert(sc->c[2].type == HEX_Y);
  151. assert(sc->c[2].index == 7);
  152. assert(sc->c[3].type == HEX_F);
  153. assert(sc->c[3].index == 0);
  154. assert(sc->c[4].type == HEX_G);
  155. assert(sc->c[4].index == 2);
  156. assert(sc->c[5].type == HEX_D);
  157. assert(sc->c[5].index == 5);
  158. assert(sc->c[6].type == HEX_F);
  159. assert(sc->c[6].index == -1);
  160. spectre_coords_free(sc);
  161. spectrectx_cleanup(ctx);
  162. random_free(rs);
  163. }
  164. struct genctx {
  165. Graphics *gr;
  166. FILE *fp; /* for non-graphical output modes */
  167. random_state *rs;
  168. Coord xmin, xmax, ymin, ymax;
  169. };
  170. static void gctx_set_size(
  171. struct genctx *gctx, int width, int height, double scale, bool centre,
  172. int *xmin, int *xmax, int *ymin, int *ymax)
  173. {
  174. if (centre) {
  175. *xmax = ceil(width/(2*scale));
  176. *xmin = -*xmax;
  177. *ymax = ceil(height/(2*scale));
  178. *ymin = -*ymax;
  179. } else {
  180. *xmin = *ymin = 0;
  181. *xmax = ceil(width/scale);
  182. *ymax = ceil(height/scale);
  183. }
  184. /* point_x() and point_y() double their output to avoid having
  185. * to use fractions, so double the bounds we'll compare their
  186. * results against */
  187. gctx->xmin.c1 = *xmin * 2; gctx->xmin.cr3 = 0;
  188. gctx->xmax.c1 = *xmax * 2; gctx->xmax.cr3 = 0;
  189. gctx->ymin.c1 = *ymin * 2; gctx->ymin.cr3 = 0;
  190. gctx->ymax.c1 = *ymax * 2; gctx->ymax.cr3 = 0;
  191. }
  192. static bool callback(void *vctx, const Spectre *spec)
  193. {
  194. struct genctx *gctx = (struct genctx *)vctx;
  195. size_t i;
  196. for (i = 0; i < 14; i++) {
  197. Point p = spec->vertices[i];
  198. Coord x = point_x(p), y = point_y(p);
  199. if (coord_cmp(x, gctx->xmin) >= 0 && coord_cmp(x, gctx->xmax) <= 0 &&
  200. coord_cmp(y, gctx->ymin) >= 0 && coord_cmp(y, gctx->ymax) <= 0)
  201. goto ok;
  202. }
  203. return false;
  204. ok:
  205. gr_draw_spectre_from_coords(gctx->gr, spec->sc, spec->vertices);
  206. if (gctx->fp) {
  207. /*
  208. * Emit calls to a made-up Python 'spectre()' function which
  209. * takes the following parameters:
  210. *
  211. * - lowest-level hexagon type (one-character string)
  212. * - index of Spectre within hexagon (0 or rarely 1)
  213. * - array of 14 point coordinates. Each is a 2-tuple
  214. * containing x and y. Each of those in turn is a 2-tuple
  215. * containing coordinates of 1 and sqrt(3).
  216. */
  217. fprintf(gctx->fp, "spectre('%s', %d, [",
  218. hex_names[spec->sc->c[0].type], spec->sc->index);
  219. for (i = 0; i < 14; i++) {
  220. Point p = spec->vertices[i];
  221. Coord x = point_x(p), y = point_y(p);
  222. fprintf(gctx->fp, "%s((%d,%d),(%d,%d))", i ? ", " : "",
  223. x.c1, x.cr3, y.c1, y.cr3);
  224. }
  225. fprintf(gctx->fp, "])\n");
  226. }
  227. return true;
  228. }
  229. static void spectrectx_init_random_with_four_colouring(
  230. SpectreContext *ctx, random_state *rs)
  231. {
  232. spectrectx_init_random(ctx, rs);
  233. ctx->prototype->hex_colour = random_upto(rs, 3);
  234. ctx->prototype->prev_hex_colour = (ctx->prototype->hex_colour + 1 +
  235. random_upto(rs, 2)) % 3;
  236. ctx->prototype->incoming_hex_edge = random_upto(rs, 2);
  237. }
  238. static void generate_bfs(struct genctx *gctx)
  239. {
  240. SpectreContext ctx[1];
  241. spectrectx_init_random_with_four_colouring(ctx, gctx->rs);
  242. spectrectx_generate(ctx, callback, gctx);
  243. spectrectx_cleanup(ctx);
  244. }
  245. static inline Point reflected(Point p)
  246. {
  247. /*
  248. * This reflection operation is used as a conjugation by
  249. * periodic_cheat(). For that purpose, it doesn't matter _what_
  250. * reflection it is, only that it reverses sense.
  251. *
  252. * generate_raster() also uses it to conjugate between the 'find
  253. * edges intersecting a horizontal line' and 'ditto vertical'
  254. * operations, so for that purpose, it wants to be the specific
  255. * reflection about the 45-degree line that swaps the positive x-
  256. * and y-axes.
  257. */
  258. Point r;
  259. size_t i;
  260. for (i = 0; i < 4; i++)
  261. r.coeffs[i] = p.coeffs[3-i];
  262. return r;
  263. }
  264. static void reflect_spectre(Spectre *spec)
  265. {
  266. size_t i;
  267. for (i = 0; i < 14; i++)
  268. spec->vertices[i] = reflected(spec->vertices[i]);
  269. }
  270. static void periodic_cheat(struct genctx *gctx)
  271. {
  272. Spectre start, sh, sv;
  273. size_t i;
  274. start.sc = NULL;
  275. {
  276. Point u = {{ 0, 0, 0, 0 }};
  277. Point v = {{ 1, 0, 0, 1 }};
  278. v = point_mul(v, point_rot(1));
  279. spectre_place(&start, u, v, 0);
  280. }
  281. sh = start;
  282. while (callback(gctx, &sh)) {
  283. sv = sh;
  284. i = 0;
  285. do {
  286. if (i) {
  287. spectre_place(&sv, sv.vertices[6], sv.vertices[7], 0);
  288. } else {
  289. spectre_place(&sv, reflected(sv.vertices[6]),
  290. reflected(sv.vertices[7]), 0);
  291. reflect_spectre(&sv);
  292. }
  293. i ^= 1;
  294. } while (callback(gctx, &sv));
  295. sv = sh;
  296. i = 0;
  297. do {
  298. if (i) {
  299. spectre_place(&sv, sv.vertices[0], sv.vertices[1], 6);
  300. } else {
  301. spectre_place(&sv, reflected(sv.vertices[0]),
  302. reflected(sv.vertices[1]), 6);
  303. reflect_spectre(&sv);
  304. }
  305. i ^= 1;
  306. } while (callback(gctx, &sv));
  307. spectre_place(&sh, sh.vertices[12], sh.vertices[11], 4);
  308. }
  309. sh = start;
  310. do {
  311. spectre_place(&sh, sh.vertices[5], sh.vertices[4], 11);
  312. sv = sh;
  313. i = 0;
  314. do {
  315. if (i) {
  316. spectre_place(&sv, sv.vertices[6], sv.vertices[7], 0);
  317. } else {
  318. spectre_place(&sv, reflected(sv.vertices[6]),
  319. reflected(sv.vertices[7]), 0);
  320. reflect_spectre(&sv);
  321. }
  322. i ^= 1;
  323. } while (callback(gctx, &sv));
  324. sv = sh;
  325. i = 0;
  326. do {
  327. if (i) {
  328. spectre_place(&sv, sv.vertices[0], sv.vertices[1], 6);
  329. } else {
  330. spectre_place(&sv, reflected(sv.vertices[0]),
  331. reflected(sv.vertices[1]), 6);
  332. reflect_spectre(&sv);
  333. }
  334. i ^= 1;
  335. } while (callback(gctx, &sv));
  336. } while (callback(gctx, &sh));
  337. }
  338. static Spectre *spectre_copy(const Spectre *orig)
  339. {
  340. Spectre *copy = snew(Spectre);
  341. memcpy(copy->vertices, orig->vertices, sizeof(copy->vertices));
  342. copy->sc = spectre_coords_copy(orig->sc);
  343. copy->next = NULL; /* not used in this tool */
  344. return copy;
  345. }
  346. static size_t find_crossings(struct genctx *gctx, const Spectre *spec, Coord y,
  347. size_t direction, unsigned *edges_out)
  348. {
  349. /*
  350. * Find edges of this Spectre which cross the horizontal line
  351. * specified by the coordinate y.
  352. *
  353. * For tie-breaking purposes, we're treating the line as actually
  354. * being at y + epsilon, so that a line with one endpoint _on_
  355. * that coordinate is counted as crossing it if it goes upwards,
  356. * and not downwards. Put another way, we seek edges one of whose
  357. * vertices is < y and the other >= y.
  358. *
  359. * Also, we're only interested in crossings in a particular
  360. * direction, specified by 'direction' being 0 or 1.
  361. */
  362. size_t i, j;
  363. struct Edge {
  364. unsigned edge;
  365. /* Location of the crossing point, as the ratio of two Coord */
  366. Coord n, d;
  367. } edges[14];
  368. size_t nedges = 0;
  369. for (i = 0; i < 14; i++) {
  370. Coord yc[2], d[2];
  371. yc[0] = point_y(spec->vertices[i]);
  372. yc[1] = point_y(spec->vertices[(i+1) % 14]);
  373. for (j = 0; j < 2; j++)
  374. d[j] = coord_sub(yc[j], y);
  375. if (coord_sign(d[1-direction]) >= 0 && coord_sign(d[direction]) < 0) {
  376. Coord a0 = coord_abs(d[0]), a1 = coord_abs(d[1]);
  377. Coord x0 = point_x(spec->vertices[i]);
  378. Coord x1 = point_x(spec->vertices[(i+1) % 14]);
  379. edges[nedges].d = coord_add(a0, a1);
  380. edges[nedges].n = coord_add(coord_mul(a1, x0), coord_mul(a0, x1));
  381. edges[nedges].edge = i;
  382. nedges++;
  383. /*
  384. * Insertion sort: swap this edge backwards in the array
  385. * until it's in the right order.
  386. */
  387. {
  388. size_t j = nedges - 1;
  389. while (j > 0 && coord_cmp(
  390. coord_mul(edges[j-1].n, edges[j].d),
  391. coord_mul(edges[j].n, edges[j-1].d)) > 0) {
  392. struct Edge tmp = edges[j-1];
  393. edges[j-1] = edges[j];
  394. edges[j] = tmp;
  395. j--;
  396. }
  397. }
  398. }
  399. }
  400. for (i = 0; i < nedges; i++)
  401. edges_out[i] = edges[i].edge;
  402. return nedges;
  403. }
  404. static void raster_emit(struct genctx *gctx, const Spectre *spec,
  405. Coord y, unsigned edge)
  406. {
  407. unsigned edges[14];
  408. size_t nedges;
  409. Coord yprev = coord_sub(y, coord_construct(2, 4));
  410. if (find_crossings(gctx, spec, yprev, true, edges))
  411. return; /* we've seen this on a previous raster_x pass */
  412. if (edge != (unsigned)-1) {
  413. nedges = find_crossings(gctx, spec, y, false, edges);
  414. assert(nedges > 0);
  415. if (edge != edges[0])
  416. return; /* we've seen this before within the same raster_x pass */
  417. }
  418. callback(gctx, spec);
  419. }
  420. static void raster_x(struct genctx *gctx, SpectreContext *ctx,
  421. const Spectre *start, Coord *yptr, Coord xlimit)
  422. {
  423. Spectre *curr, *new;
  424. Coord y;
  425. size_t i;
  426. unsigned incoming_edge;
  427. /*
  428. * Find out if this Spectre intersects our current
  429. * y-coordinate.
  430. */
  431. for (i = 0; i < 14; i++)
  432. if (coord_cmp(point_y(start->vertices[i]), *yptr) > 0)
  433. break;
  434. if (i == 14) {
  435. /*
  436. * No, this Spectre is still below the start line.
  437. */
  438. return;
  439. }
  440. /*
  441. * It does! Start an x iteration here, and increment y by 2 + 4
  442. * sqrt(3), which is the smallest possible y-extent of any
  443. * rotation of our starting Spectre.
  444. */
  445. y = *yptr;
  446. *yptr = coord_add(*yptr, coord_construct(2, 4));
  447. curr = spectre_copy(start);
  448. incoming_edge = -1;
  449. while (true) {
  450. unsigned edges[14];
  451. size_t nedges;
  452. raster_emit(gctx, curr, y, incoming_edge);
  453. nedges = find_crossings(gctx, curr, y, true, edges);
  454. assert(nedges > 0);
  455. for (i = 0; i+1 < nedges; i++) {
  456. new = spectre_adjacent(ctx, curr, edges[i], &incoming_edge);
  457. raster_emit(gctx, new, y, incoming_edge);
  458. spectre_free(new);
  459. }
  460. new = spectre_adjacent(ctx, curr, edges[nedges-1], &incoming_edge);
  461. spectre_free(curr);
  462. curr = new;
  463. /*
  464. * Find out whether this Spectre is entirely beyond the
  465. * x-limit.
  466. */
  467. for (i = 0; i < 14; i++)
  468. if (coord_cmp(point_x(curr->vertices[i]), xlimit) < 0)
  469. break;
  470. if (i == 14) /* no vertex broke that loop */
  471. break;
  472. }
  473. spectre_free(curr);
  474. }
  475. static void raster_y(struct genctx *gctx, SpectreContext *ctx,
  476. const Spectre *start, Coord x, Coord ylimit,
  477. Coord *yptr, Coord xlimit)
  478. {
  479. Spectre *curr, *new;
  480. curr = spectre_copy(start);
  481. while (true) {
  482. unsigned edges[14];
  483. size_t i, nedges;
  484. raster_x(gctx, ctx, curr, yptr, xlimit);
  485. reflect_spectre(curr);
  486. nedges = find_crossings(gctx, curr, x, false, edges);
  487. reflect_spectre(curr);
  488. assert(nedges > 0);
  489. for (i = 0; i+1 < nedges; i++) {
  490. new = spectre_adjacent(ctx, curr, edges[i], NULL);
  491. raster_x(gctx, ctx, new, yptr, xlimit);
  492. spectre_free(new);
  493. }
  494. new = spectre_adjacent(ctx, curr, edges[nedges-1], NULL);
  495. spectre_free(curr);
  496. curr = new;
  497. /*
  498. * Find out whether this Spectre is entirely beyond the
  499. * y-limit.
  500. */
  501. for (i = 0; i < 14; i++)
  502. if (coord_cmp(point_y(curr->vertices[i]), ylimit) < 0)
  503. break;
  504. if (i == 14) /* no vertex broke that loop */
  505. break;
  506. }
  507. spectre_free(curr);
  508. }
  509. static void generate_raster(struct genctx *gctx)
  510. {
  511. SpectreContext ctx[1];
  512. Spectre *start;
  513. Coord y = coord_integer(-10);
  514. spectrectx_init_random_with_four_colouring(ctx, gctx->rs);
  515. start = spectre_initial(ctx);
  516. /*
  517. * Move the starting Spectre down and left a bit, so that edge
  518. * effects causing a few Spectres to be missed on the initial
  519. * passes won't affect the overall result.
  520. */
  521. {
  522. Point offset = {{ -5, 0, 0, -5 }};
  523. size_t i;
  524. for (i = 0; i < 14; i++)
  525. start->vertices[i] = point_add(start->vertices[i], offset);
  526. }
  527. raster_y(gctx, ctx, start, coord_integer(-10), gctx->ymax, &y, gctx->xmax);
  528. spectre_free(start);
  529. spectrectx_cleanup(ctx);
  530. }
  531. static void generate_hexes(struct genctx *gctx)
  532. {
  533. SpectreContext ctx[1];
  534. spectrectx_init_random(ctx, gctx->rs);
  535. SpectreCoords *sc;
  536. unsigned orient, outedge, inedge;
  537. bool printed_any = false;
  538. size_t r = 1, ri = 0, rj = 0;
  539. Point centre = {{ 0, 0, 0, 0 }};
  540. const Point six = {{ 6, 0, 0, 0 }};
  541. sc = spectre_coords_copy(ctx->prototype);
  542. orient = random_upto(gctx->rs, 6);
  543. while (true) {
  544. Point top = {{ -2, 0, 4, 0 }};
  545. Point vertices[6];
  546. bool print_this = false;
  547. size_t i;
  548. for (i = 0; i < 6; i++) {
  549. vertices[i] = point_add(centre, point_mul(
  550. top, point_rot(2 * (orient + i))));
  551. Coord x = point_x(vertices[i]), y = point_y(vertices[i]);
  552. if (coord_cmp(x, gctx->xmin) >= 0 &&
  553. coord_cmp(x, gctx->xmax) <= 0 &&
  554. coord_cmp(y, gctx->ymin) >= 0 &&
  555. coord_cmp(y, gctx->ymax) <= 0)
  556. print_this = true;
  557. }
  558. if (print_this) {
  559. printed_any = true;
  560. gr_draw_hex(gctx->gr, -1, sc->c[0].type, vertices);
  561. }
  562. /*
  563. * Decide which way to step next. We spiral outwards from a
  564. * central hexagon.
  565. */
  566. outedge = (ri == 0 && rj == 0) ? 5 : ri;
  567. if (++rj >= r) {
  568. rj = 0;
  569. if (++ri >= 6) {
  570. ri = 0;
  571. if (!printed_any)
  572. break;
  573. printed_any = false;
  574. ++r;
  575. }
  576. }
  577. spectrectx_step_hex(ctx, sc, 0, (outedge + 6 - orient) % 6, &inedge);
  578. orient = (outedge + 9 - inedge) % 6;
  579. centre = point_add(centre, point_mul(six, point_rot(4 + 2 * outedge)));
  580. }
  581. spectre_coords_free(sc);
  582. spectrectx_cleanup(ctx);
  583. }
  584. int main(int argc, char **argv)
  585. {
  586. const char *random_seed = "12345";
  587. const char *outfile = "-";
  588. bool four_colour = false;
  589. enum {
  590. TESTS, TILING_BFS, TILING_RASTER, CHEAT, HEXES
  591. } mode = TILING_RASTER;
  592. enum { SVG, PYTHON } outmode = SVG;
  593. double scale = 10, linewidth = 1.5;
  594. int width = 1024, height = 768;
  595. bool arcs = false;
  596. while (--argc > 0) {
  597. const char *arg = *++argv;
  598. if (!strcmp(arg, "--help")) {
  599. printf(" usage: spectre-test [FIXME]\n"
  600. " also: spectre-test --test\n");
  601. return 0;
  602. } else if (!strcmp(arg, "--test")) {
  603. mode = TESTS;
  604. } else if (!strcmp(arg, "--hex")) {
  605. mode = HEXES;
  606. } else if (!strcmp(arg, "--bfs")) {
  607. mode = TILING_BFS;
  608. } else if (!strcmp(arg, "--cheat")) {
  609. mode = CHEAT;
  610. } else if (!strcmp(arg, "--python")) {
  611. outmode = PYTHON;
  612. } else if (!strcmp(arg, "--arcs")) {
  613. arcs = true;
  614. } else if (!strncmp(arg, "--seed=", 7)) {
  615. random_seed = arg+7;
  616. } else if (!strcmp(arg, "--fourcolour")) {
  617. four_colour = true;
  618. } else if (!strncmp(arg, "--scale=", 8)) {
  619. scale = atof(arg+8);
  620. } else if (!strncmp(arg, "--width=", 8)) {
  621. width = atof(arg+8);
  622. } else if (!strncmp(arg, "--height=", 9)) {
  623. height = atof(arg+9);
  624. } else if (!strncmp(arg, "--linewidth=", 12)) {
  625. linewidth = atof(arg+12);
  626. } else if (!strcmp(arg, "-o")) {
  627. if (--argc <= 0) {
  628. fprintf(stderr, "expected argument to '%s'\n", arg);
  629. return 1;
  630. }
  631. outfile = *++argv;
  632. } else {
  633. fprintf(stderr, "unexpected extra argument '%s'\n", arg);
  634. return 1;
  635. }
  636. }
  637. switch (mode) {
  638. case TESTS: {
  639. step_tests();
  640. break;
  641. }
  642. case TILING_BFS:
  643. case TILING_RASTER:
  644. case CHEAT: {
  645. struct genctx gctx[1];
  646. bool close_output = false;
  647. int xmin, xmax, ymin, ymax;
  648. gctx_set_size(gctx, width, height, scale, (mode != TILING_RASTER),
  649. &xmin, &xmax, &ymin, &ymax);
  650. switch (outmode) {
  651. case SVG:
  652. gctx->gr = gr_new(outfile, xmin, xmax, ymin, ymax, scale);
  653. gctx->gr->number_cells = false;
  654. gctx->gr->four_colour = four_colour;
  655. gctx->gr->linewidth = linewidth;
  656. gctx->gr->arcs = arcs;
  657. gctx->fp = NULL;
  658. break;
  659. case PYTHON:
  660. gctx->gr = NULL;
  661. if (!strcmp(outfile, "-")) {
  662. gctx->fp = stdout;
  663. } else {
  664. gctx->fp = fopen(outfile, "w");
  665. close_output = true;
  666. }
  667. break;
  668. }
  669. gctx->rs = random_new(random_seed, strlen(random_seed));
  670. switch (mode) {
  671. case TILING_RASTER:
  672. generate_raster(gctx);
  673. break;
  674. case TILING_BFS:
  675. generate_bfs(gctx);
  676. break;
  677. case CHEAT:
  678. periodic_cheat(gctx);
  679. break;
  680. default: /* shouldn't happen */
  681. break;
  682. }
  683. random_free(gctx->rs);
  684. gr_free(gctx->gr);
  685. if (close_output)
  686. fclose(gctx->fp);
  687. break;
  688. }
  689. case HEXES: {
  690. struct genctx gctx[1];
  691. int xmin, xmax, ymin, ymax;
  692. gctx_set_size(gctx, width, height, scale, true,
  693. &xmin, &xmax, &ymin, &ymax);
  694. gctx->gr = gr_new(outfile, xmin, xmax, ymin, ymax, scale);
  695. gctx->gr->jigsaw_mode = true;
  696. gctx->gr->number_edges = false;
  697. gctx->gr->linewidth = linewidth;
  698. gctx->rs = random_new(random_seed, strlen(random_seed));
  699. generate_hexes(gctx); /* FIXME: bounds */
  700. random_free(gctx->rs);
  701. gr_free(gctx->gr);
  702. break;
  703. }
  704. }
  705. }