123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790 |
- /*
- * Standalone test program for spectre.c.
- */
- #include <assert.h>
- #ifdef NO_TGMATH_H
- # include <math.h>
- #else
- # include <tgmath.h>
- #endif
- #include <stdarg.h>
- #include <stdio.h>
- #include <string.h>
- #include "puzzles.h"
- #include "spectre-internal.h"
- #include "spectre-tables-manual.h"
- #include "spectre-tables-auto.h"
- #include "spectre-help.h"
- static void step_tests(void)
- {
- SpectreContext ctx[1];
- random_state *rs;
- SpectreCoords *sc;
- unsigned outedge;
- rs = random_new("12345", 5);
- spectrectx_init_random(ctx, rs);
- /* Simplest possible transition: between the two Spectres making
- * up a G hex. */
- sc = spectre_coords_new();
- spectre_coords_make_space(sc, 1);
- sc->index = 0;
- sc->nc = 1;
- sc->c[0].type = HEX_G;
- sc->c[0].index = -1;
- spectrectx_step(ctx, sc, 12, &outedge);
- assert(outedge == 5);
- assert(sc->index == 1);
- assert(sc->nc == 1);
- assert(sc->c[0].type == HEX_G);
- assert(sc->c[0].index == -1);
- spectre_coords_free(sc);
- /* Test the double Spectre transition. Here, within a F superhex,
- * we attempt to step from the G subhex to the S one, in such a
- * way that the place where we enter the Spectre corresponding to
- * the S hex is on its spur of detached edge, causing us to
- * immediately transition back out of the other side of that spur
- * and end up in the D subhex instead. */
- sc = spectre_coords_new();
- spectre_coords_make_space(sc, 2);
- sc->index = 1;
- sc->nc = 2;
- sc->c[0].type = HEX_G;
- sc->c[0].index = 2;
- sc->c[1].type = HEX_F;
- sc->c[1].index = -1;
- spectrectx_step(ctx, sc, 1, &outedge);
- assert(outedge == 6);
- assert(sc->index == 0);
- assert(sc->nc == 2);
- assert(sc->c[0].type == HEX_D);
- assert(sc->c[0].index == 5);
- assert(sc->c[1].type == HEX_F);
- assert(sc->c[1].index == -1);
- spectre_coords_free(sc);
- /* However, _this_ transition leaves the same G subhex by the same
- * edge of the hexagon, but further along it, so that we land in
- * the S Spectre and stay there, without needing a double
- * transition. */
- sc = spectre_coords_new();
- spectre_coords_make_space(sc, 2);
- sc->index = 1;
- sc->nc = 2;
- sc->c[0].type = HEX_G;
- sc->c[0].index = 2;
- sc->c[1].type = HEX_F;
- sc->c[1].index = -1;
- spectrectx_step(ctx, sc, 13, &outedge);
- assert(outedge == 4);
- assert(sc->index == 0);
- assert(sc->nc == 2);
- assert(sc->c[0].type == HEX_S);
- assert(sc->c[0].index == 3);
- assert(sc->c[1].type == HEX_F);
- assert(sc->c[1].index == -1);
- spectre_coords_free(sc);
- /* A couple of randomly generated transition tests that go a long
- * way up the stack. */
- sc = spectre_coords_new();
- spectre_coords_make_space(sc, 7);
- sc->index = 0;
- sc->nc = 7;
- sc->c[0].type = HEX_S;
- sc->c[0].index = 3;
- sc->c[1].type = HEX_Y;
- sc->c[1].index = 7;
- sc->c[2].type = HEX_Y;
- sc->c[2].index = 4;
- sc->c[3].type = HEX_Y;
- sc->c[3].index = 4;
- sc->c[4].type = HEX_F;
- sc->c[4].index = 0;
- sc->c[5].type = HEX_X;
- sc->c[5].index = 1;
- sc->c[6].type = HEX_G;
- sc->c[6].index = -1;
- spectrectx_step(ctx, sc, 13, &outedge);
- assert(outedge == 12);
- assert(sc->index == 0);
- assert(sc->nc == 7);
- assert(sc->c[0].type == HEX_Y);
- assert(sc->c[0].index == 1);
- assert(sc->c[1].type == HEX_P);
- assert(sc->c[1].index == 1);
- assert(sc->c[2].type == HEX_D);
- assert(sc->c[2].index == 5);
- assert(sc->c[3].type == HEX_Y);
- assert(sc->c[3].index == 4);
- assert(sc->c[4].type == HEX_X);
- assert(sc->c[4].index == 7);
- assert(sc->c[5].type == HEX_S);
- assert(sc->c[5].index == 3);
- assert(sc->c[6].type == HEX_G);
- assert(sc->c[6].index == -1);
- spectre_coords_free(sc);
- sc = spectre_coords_new();
- spectre_coords_make_space(sc, 7);
- sc->index = 0;
- sc->nc = 7;
- sc->c[0].type = HEX_Y;
- sc->c[0].index = 7;
- sc->c[1].type = HEX_F;
- sc->c[1].index = 6;
- sc->c[2].type = HEX_Y;
- sc->c[2].index = 4;
- sc->c[3].type = HEX_X;
- sc->c[3].index = 7;
- sc->c[4].type = HEX_L;
- sc->c[4].index = 0;
- sc->c[5].type = HEX_S;
- sc->c[5].index = 3;
- sc->c[6].type = HEX_F;
- sc->c[6].index = -1;
- spectrectx_step(ctx, sc, 0, &outedge);
- assert(outedge == 1);
- assert(sc->index == 0);
- assert(sc->nc == 7);
- assert(sc->c[0].type == HEX_P);
- assert(sc->c[0].index == 1);
- assert(sc->c[1].type == HEX_F);
- assert(sc->c[1].index == 0);
- assert(sc->c[2].type == HEX_Y);
- assert(sc->c[2].index == 7);
- assert(sc->c[3].type == HEX_F);
- assert(sc->c[3].index == 0);
- assert(sc->c[4].type == HEX_G);
- assert(sc->c[4].index == 2);
- assert(sc->c[5].type == HEX_D);
- assert(sc->c[5].index == 5);
- assert(sc->c[6].type == HEX_F);
- assert(sc->c[6].index == -1);
- spectre_coords_free(sc);
- spectrectx_cleanup(ctx);
- random_free(rs);
- }
- struct genctx {
- Graphics *gr;
- FILE *fp; /* for non-graphical output modes */
- random_state *rs;
- Coord xmin, xmax, ymin, ymax;
- };
- static void gctx_set_size(
- struct genctx *gctx, int width, int height, double scale, bool centre,
- int *xmin, int *xmax, int *ymin, int *ymax)
- {
- if (centre) {
- *xmax = ceil(width/(2*scale));
- *xmin = -*xmax;
- *ymax = ceil(height/(2*scale));
- *ymin = -*ymax;
- } else {
- *xmin = *ymin = 0;
- *xmax = ceil(width/scale);
- *ymax = ceil(height/scale);
- }
- /* point_x() and point_y() double their output to avoid having
- * to use fractions, so double the bounds we'll compare their
- * results against */
- gctx->xmin.c1 = *xmin * 2; gctx->xmin.cr3 = 0;
- gctx->xmax.c1 = *xmax * 2; gctx->xmax.cr3 = 0;
- gctx->ymin.c1 = *ymin * 2; gctx->ymin.cr3 = 0;
- gctx->ymax.c1 = *ymax * 2; gctx->ymax.cr3 = 0;
- }
- static bool callback(void *vctx, const Spectre *spec)
- {
- struct genctx *gctx = (struct genctx *)vctx;
- size_t i;
- for (i = 0; i < 14; i++) {
- Point p = spec->vertices[i];
- Coord x = point_x(p), y = point_y(p);
- if (coord_cmp(x, gctx->xmin) >= 0 && coord_cmp(x, gctx->xmax) <= 0 &&
- coord_cmp(y, gctx->ymin) >= 0 && coord_cmp(y, gctx->ymax) <= 0)
- goto ok;
- }
- return false;
- ok:
- gr_draw_spectre_from_coords(gctx->gr, spec->sc, spec->vertices);
- if (gctx->fp) {
- /*
- * Emit calls to a made-up Python 'spectre()' function which
- * takes the following parameters:
- *
- * - lowest-level hexagon type (one-character string)
- * - index of Spectre within hexagon (0 or rarely 1)
- * - array of 14 point coordinates. Each is a 2-tuple
- * containing x and y. Each of those in turn is a 2-tuple
- * containing coordinates of 1 and sqrt(3).
- */
- fprintf(gctx->fp, "spectre('%s', %d, [",
- hex_names[spec->sc->c[0].type], spec->sc->index);
- for (i = 0; i < 14; i++) {
- Point p = spec->vertices[i];
- Coord x = point_x(p), y = point_y(p);
- fprintf(gctx->fp, "%s((%d,%d),(%d,%d))", i ? ", " : "",
- x.c1, x.cr3, y.c1, y.cr3);
- }
- fprintf(gctx->fp, "])\n");
- }
- return true;
- }
- static void spectrectx_init_random_with_four_colouring(
- SpectreContext *ctx, random_state *rs)
- {
- spectrectx_init_random(ctx, rs);
- ctx->prototype->hex_colour = random_upto(rs, 3);
- ctx->prototype->prev_hex_colour = (ctx->prototype->hex_colour + 1 +
- random_upto(rs, 2)) % 3;
- ctx->prototype->incoming_hex_edge = random_upto(rs, 2);
- }
- static void generate_bfs(struct genctx *gctx)
- {
- SpectreContext ctx[1];
-
- spectrectx_init_random_with_four_colouring(ctx, gctx->rs);
- spectrectx_generate(ctx, callback, gctx);
- spectrectx_cleanup(ctx);
- }
- static inline Point reflected(Point p)
- {
- /*
- * This reflection operation is used as a conjugation by
- * periodic_cheat(). For that purpose, it doesn't matter _what_
- * reflection it is, only that it reverses sense.
- *
- * generate_raster() also uses it to conjugate between the 'find
- * edges intersecting a horizontal line' and 'ditto vertical'
- * operations, so for that purpose, it wants to be the specific
- * reflection about the 45-degree line that swaps the positive x-
- * and y-axes.
- */
- Point r;
- size_t i;
- for (i = 0; i < 4; i++)
- r.coeffs[i] = p.coeffs[3-i];
- return r;
- }
- static void reflect_spectre(Spectre *spec)
- {
- size_t i;
- for (i = 0; i < 14; i++)
- spec->vertices[i] = reflected(spec->vertices[i]);
- }
- static void periodic_cheat(struct genctx *gctx)
- {
- Spectre start, sh, sv;
- size_t i;
- start.sc = NULL;
- {
- Point u = {{ 0, 0, 0, 0 }};
- Point v = {{ 1, 0, 0, 1 }};
- v = point_mul(v, point_rot(1));
- spectre_place(&start, u, v, 0);
- }
- sh = start;
- while (callback(gctx, &sh)) {
- sv = sh;
- i = 0;
- do {
- if (i) {
- spectre_place(&sv, sv.vertices[6], sv.vertices[7], 0);
- } else {
- spectre_place(&sv, reflected(sv.vertices[6]),
- reflected(sv.vertices[7]), 0);
- reflect_spectre(&sv);
- }
- i ^= 1;
- } while (callback(gctx, &sv));
- sv = sh;
- i = 0;
- do {
- if (i) {
- spectre_place(&sv, sv.vertices[0], sv.vertices[1], 6);
- } else {
- spectre_place(&sv, reflected(sv.vertices[0]),
- reflected(sv.vertices[1]), 6);
- reflect_spectre(&sv);
- }
- i ^= 1;
- } while (callback(gctx, &sv));
- spectre_place(&sh, sh.vertices[12], sh.vertices[11], 4);
- }
- sh = start;
- do {
- spectre_place(&sh, sh.vertices[5], sh.vertices[4], 11);
- sv = sh;
- i = 0;
- do {
- if (i) {
- spectre_place(&sv, sv.vertices[6], sv.vertices[7], 0);
- } else {
- spectre_place(&sv, reflected(sv.vertices[6]),
- reflected(sv.vertices[7]), 0);
- reflect_spectre(&sv);
- }
- i ^= 1;
- } while (callback(gctx, &sv));
- sv = sh;
- i = 0;
- do {
- if (i) {
- spectre_place(&sv, sv.vertices[0], sv.vertices[1], 6);
- } else {
- spectre_place(&sv, reflected(sv.vertices[0]),
- reflected(sv.vertices[1]), 6);
- reflect_spectre(&sv);
- }
- i ^= 1;
- } while (callback(gctx, &sv));
- } while (callback(gctx, &sh));
- }
- static Spectre *spectre_copy(const Spectre *orig)
- {
- Spectre *copy = snew(Spectre);
- memcpy(copy->vertices, orig->vertices, sizeof(copy->vertices));
- copy->sc = spectre_coords_copy(orig->sc);
- copy->next = NULL; /* not used in this tool */
- return copy;
- }
- static size_t find_crossings(struct genctx *gctx, const Spectre *spec, Coord y,
- size_t direction, unsigned *edges_out)
- {
- /*
- * Find edges of this Spectre which cross the horizontal line
- * specified by the coordinate y.
- *
- * For tie-breaking purposes, we're treating the line as actually
- * being at y + epsilon, so that a line with one endpoint _on_
- * that coordinate is counted as crossing it if it goes upwards,
- * and not downwards. Put another way, we seek edges one of whose
- * vertices is < y and the other >= y.
- *
- * Also, we're only interested in crossings in a particular
- * direction, specified by 'direction' being 0 or 1.
- */
- size_t i, j;
- struct Edge {
- unsigned edge;
- /* Location of the crossing point, as the ratio of two Coord */
- Coord n, d;
- } edges[14];
- size_t nedges = 0;
- for (i = 0; i < 14; i++) {
- Coord yc[2], d[2];
- yc[0] = point_y(spec->vertices[i]);
- yc[1] = point_y(spec->vertices[(i+1) % 14]);
- for (j = 0; j < 2; j++)
- d[j] = coord_sub(yc[j], y);
- if (coord_sign(d[1-direction]) >= 0 && coord_sign(d[direction]) < 0) {
- Coord a0 = coord_abs(d[0]), a1 = coord_abs(d[1]);
- Coord x0 = point_x(spec->vertices[i]);
- Coord x1 = point_x(spec->vertices[(i+1) % 14]);
- edges[nedges].d = coord_add(a0, a1);
- edges[nedges].n = coord_add(coord_mul(a1, x0), coord_mul(a0, x1));
- edges[nedges].edge = i;
- nedges++;
- /*
- * Insertion sort: swap this edge backwards in the array
- * until it's in the right order.
- */
- {
- size_t j = nedges - 1;
- while (j > 0 && coord_cmp(
- coord_mul(edges[j-1].n, edges[j].d),
- coord_mul(edges[j].n, edges[j-1].d)) > 0) {
- struct Edge tmp = edges[j-1];
- edges[j-1] = edges[j];
- edges[j] = tmp;
- j--;
- }
- }
- }
- }
- for (i = 0; i < nedges; i++)
- edges_out[i] = edges[i].edge;
- return nedges;
- }
- static void raster_emit(struct genctx *gctx, const Spectre *spec,
- Coord y, unsigned edge)
- {
- unsigned edges[14];
- size_t nedges;
- Coord yprev = coord_sub(y, coord_construct(2, 4));
- if (find_crossings(gctx, spec, yprev, true, edges))
- return; /* we've seen this on a previous raster_x pass */
- if (edge != (unsigned)-1) {
- nedges = find_crossings(gctx, spec, y, false, edges);
- assert(nedges > 0);
- if (edge != edges[0])
- return; /* we've seen this before within the same raster_x pass */
- }
- callback(gctx, spec);
- }
- static void raster_x(struct genctx *gctx, SpectreContext *ctx,
- const Spectre *start, Coord *yptr, Coord xlimit)
- {
- Spectre *curr, *new;
- Coord y;
- size_t i;
- unsigned incoming_edge;
- /*
- * Find out if this Spectre intersects our current
- * y-coordinate.
- */
- for (i = 0; i < 14; i++)
- if (coord_cmp(point_y(start->vertices[i]), *yptr) > 0)
- break;
- if (i == 14) {
- /*
- * No, this Spectre is still below the start line.
- */
- return;
- }
- /*
- * It does! Start an x iteration here, and increment y by 2 + 4
- * sqrt(3), which is the smallest possible y-extent of any
- * rotation of our starting Spectre.
- */
- y = *yptr;
- *yptr = coord_add(*yptr, coord_construct(2, 4));
- curr = spectre_copy(start);
- incoming_edge = -1;
- while (true) {
- unsigned edges[14];
- size_t nedges;
- raster_emit(gctx, curr, y, incoming_edge);
- nedges = find_crossings(gctx, curr, y, true, edges);
- assert(nedges > 0);
- for (i = 0; i+1 < nedges; i++) {
- new = spectre_adjacent(ctx, curr, edges[i], &incoming_edge);
- raster_emit(gctx, new, y, incoming_edge);
- spectre_free(new);
- }
- new = spectre_adjacent(ctx, curr, edges[nedges-1], &incoming_edge);
- spectre_free(curr);
- curr = new;
- /*
- * Find out whether this Spectre is entirely beyond the
- * x-limit.
- */
- for (i = 0; i < 14; i++)
- if (coord_cmp(point_x(curr->vertices[i]), xlimit) < 0)
- break;
- if (i == 14) /* no vertex broke that loop */
- break;
- }
- spectre_free(curr);
- }
- static void raster_y(struct genctx *gctx, SpectreContext *ctx,
- const Spectre *start, Coord x, Coord ylimit,
- Coord *yptr, Coord xlimit)
- {
- Spectre *curr, *new;
- curr = spectre_copy(start);
- while (true) {
- unsigned edges[14];
- size_t i, nedges;
- raster_x(gctx, ctx, curr, yptr, xlimit);
- reflect_spectre(curr);
- nedges = find_crossings(gctx, curr, x, false, edges);
- reflect_spectre(curr);
- assert(nedges > 0);
- for (i = 0; i+1 < nedges; i++) {
- new = spectre_adjacent(ctx, curr, edges[i], NULL);
- raster_x(gctx, ctx, new, yptr, xlimit);
- spectre_free(new);
- }
- new = spectre_adjacent(ctx, curr, edges[nedges-1], NULL);
- spectre_free(curr);
- curr = new;
- /*
- * Find out whether this Spectre is entirely beyond the
- * y-limit.
- */
- for (i = 0; i < 14; i++)
- if (coord_cmp(point_y(curr->vertices[i]), ylimit) < 0)
- break;
- if (i == 14) /* no vertex broke that loop */
- break;
- }
- spectre_free(curr);
- }
- static void generate_raster(struct genctx *gctx)
- {
- SpectreContext ctx[1];
- Spectre *start;
- Coord y = coord_integer(-10);
- spectrectx_init_random_with_four_colouring(ctx, gctx->rs);
- start = spectre_initial(ctx);
- /*
- * Move the starting Spectre down and left a bit, so that edge
- * effects causing a few Spectres to be missed on the initial
- * passes won't affect the overall result.
- */
- {
- Point offset = {{ -5, 0, 0, -5 }};
- size_t i;
- for (i = 0; i < 14; i++)
- start->vertices[i] = point_add(start->vertices[i], offset);
- }
- raster_y(gctx, ctx, start, coord_integer(-10), gctx->ymax, &y, gctx->xmax);
- spectre_free(start);
- spectrectx_cleanup(ctx);
- }
- static void generate_hexes(struct genctx *gctx)
- {
- SpectreContext ctx[1];
- spectrectx_init_random(ctx, gctx->rs);
- SpectreCoords *sc;
- unsigned orient, outedge, inedge;
- bool printed_any = false;
- size_t r = 1, ri = 0, rj = 0;
- Point centre = {{ 0, 0, 0, 0 }};
- const Point six = {{ 6, 0, 0, 0 }};
- sc = spectre_coords_copy(ctx->prototype);
- orient = random_upto(gctx->rs, 6);
- while (true) {
- Point top = {{ -2, 0, 4, 0 }};
- Point vertices[6];
- bool print_this = false;
- size_t i;
- for (i = 0; i < 6; i++) {
- vertices[i] = point_add(centre, point_mul(
- top, point_rot(2 * (orient + i))));
- Coord x = point_x(vertices[i]), y = point_y(vertices[i]);
- if (coord_cmp(x, gctx->xmin) >= 0 &&
- coord_cmp(x, gctx->xmax) <= 0 &&
- coord_cmp(y, gctx->ymin) >= 0 &&
- coord_cmp(y, gctx->ymax) <= 0)
- print_this = true;
- }
- if (print_this) {
- printed_any = true;
- gr_draw_hex(gctx->gr, -1, sc->c[0].type, vertices);
- }
- /*
- * Decide which way to step next. We spiral outwards from a
- * central hexagon.
- */
- outedge = (ri == 0 && rj == 0) ? 5 : ri;
- if (++rj >= r) {
- rj = 0;
- if (++ri >= 6) {
- ri = 0;
- if (!printed_any)
- break;
- printed_any = false;
- ++r;
- }
- }
- spectrectx_step_hex(ctx, sc, 0, (outedge + 6 - orient) % 6, &inedge);
- orient = (outedge + 9 - inedge) % 6;
- centre = point_add(centre, point_mul(six, point_rot(4 + 2 * outedge)));
- }
- spectre_coords_free(sc);
- spectrectx_cleanup(ctx);
- }
- int main(int argc, char **argv)
- {
- const char *random_seed = "12345";
- const char *outfile = "-";
- bool four_colour = false;
- enum {
- TESTS, TILING_BFS, TILING_RASTER, CHEAT, HEXES
- } mode = TILING_RASTER;
- enum { SVG, PYTHON } outmode = SVG;
- double scale = 10, linewidth = 1.5;
- int width = 1024, height = 768;
- bool arcs = false;
- while (--argc > 0) {
- const char *arg = *++argv;
- if (!strcmp(arg, "--help")) {
- printf(" usage: spectre-test [FIXME]\n"
- " also: spectre-test --test\n");
- return 0;
- } else if (!strcmp(arg, "--test")) {
- mode = TESTS;
- } else if (!strcmp(arg, "--hex")) {
- mode = HEXES;
- } else if (!strcmp(arg, "--bfs")) {
- mode = TILING_BFS;
- } else if (!strcmp(arg, "--cheat")) {
- mode = CHEAT;
- } else if (!strcmp(arg, "--python")) {
- outmode = PYTHON;
- } else if (!strcmp(arg, "--arcs")) {
- arcs = true;
- } else if (!strncmp(arg, "--seed=", 7)) {
- random_seed = arg+7;
- } else if (!strcmp(arg, "--fourcolour")) {
- four_colour = true;
- } else if (!strncmp(arg, "--scale=", 8)) {
- scale = atof(arg+8);
- } else if (!strncmp(arg, "--width=", 8)) {
- width = atof(arg+8);
- } else if (!strncmp(arg, "--height=", 9)) {
- height = atof(arg+9);
- } else if (!strncmp(arg, "--linewidth=", 12)) {
- linewidth = atof(arg+12);
- } else if (!strcmp(arg, "-o")) {
- if (--argc <= 0) {
- fprintf(stderr, "expected argument to '%s'\n", arg);
- return 1;
- }
- outfile = *++argv;
- } else {
- fprintf(stderr, "unexpected extra argument '%s'\n", arg);
- return 1;
- }
- }
- switch (mode) {
- case TESTS: {
- step_tests();
- break;
- }
- case TILING_BFS:
- case TILING_RASTER:
- case CHEAT: {
- struct genctx gctx[1];
- bool close_output = false;
- int xmin, xmax, ymin, ymax;
- gctx_set_size(gctx, width, height, scale, (mode != TILING_RASTER),
- &xmin, &xmax, &ymin, &ymax);
- switch (outmode) {
- case SVG:
- gctx->gr = gr_new(outfile, xmin, xmax, ymin, ymax, scale);
- gctx->gr->number_cells = false;
- gctx->gr->four_colour = four_colour;
- gctx->gr->linewidth = linewidth;
- gctx->gr->arcs = arcs;
- gctx->fp = NULL;
- break;
- case PYTHON:
- gctx->gr = NULL;
- if (!strcmp(outfile, "-")) {
- gctx->fp = stdout;
- } else {
- gctx->fp = fopen(outfile, "w");
- close_output = true;
- }
- break;
- }
- gctx->rs = random_new(random_seed, strlen(random_seed));
- switch (mode) {
- case TILING_RASTER:
- generate_raster(gctx);
- break;
- case TILING_BFS:
- generate_bfs(gctx);
- break;
- case CHEAT:
- periodic_cheat(gctx);
- break;
- default: /* shouldn't happen */
- break;
- }
- random_free(gctx->rs);
- gr_free(gctx->gr);
- if (close_output)
- fclose(gctx->fp);
- break;
- }
- case HEXES: {
- struct genctx gctx[1];
- int xmin, xmax, ymin, ymax;
- gctx_set_size(gctx, width, height, scale, true,
- &xmin, &xmax, &ymin, &ymax);
- gctx->gr = gr_new(outfile, xmin, xmax, ymin, ymax, scale);
- gctx->gr->jigsaw_mode = true;
- gctx->gr->number_edges = false;
- gctx->gr->linewidth = linewidth;
- gctx->rs = random_new(random_seed, strlen(random_seed));
- generate_hexes(gctx); /* FIXME: bounds */
- random_free(gctx->rs);
- gr_free(gctx->gr);
- break;
- }
- }
- }
|