Files
puzzles/auxiliary/hatgen.c
Simon Tatham 6f75879e9f Hats tiling: more uniform parent selection.
This tweak improves the uniformity of the generated patches of hat
tiling, by selecting from (the closest 32-bit approximation I can get
to) the limiting probability distribution of finite patches in the
whole plane.

This shouldn't invalidate any grid description that contains enough
coordinates to uniquely specify a piece of tiling - in particular, any
generated by the game itself. But if anyone's been brave enough to
hand-type a grid description in the last two days and left off some of
the coordinates, then those might be invalidated.
2023-03-28 20:51:02 +01:00

1688 lines
53 KiB
C

/*
* Generate patches of tiling by the 'hat' aperiodic monotile
* discovered in 2023.
*
* This implementation of hat-tiling generation was intended to be the
* basis for generating hat grids for Loopy. However, it turned out
* that I found a better strategy, so this source file isn't used by
* the main puzzle system. I've kept it anyway because I ended up
* adapting it to generate the file hat-tables.h containing the lookup
* tables for the algorithm I _did_ end up using. It also generates
* diagrams that are useful for understanding that algorithm, and for
* debugging it if anything still turns out to be wrong with it.
*
* Discoverers' website: https://cs.uwaterloo.ca/~csk/hat/
* Preprint of paper: https://arxiv.org/abs/2303.10798
*/
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "puzzles.h"
#include "tree234.h"
#include "hat.h"
/*
* General strategy:
*
* We construct the hat tiling by means of a substitution system of
* 'metatiles' which come in four types, called H,T,P,F. A (valid)
* tiling of these metatiles can be expanded to a larger one by a set
* of recursive subdivision rules. Once we have a large enough patch
* of metatiles, we apply a final transformation that converts each
* metatile into 1, 2 or 4 instances of the aperiodically tiling
* 'hat'.
*
* Unlike the similar substitution system for Penrose tilings, the
* expansion rules are not geometrically precise: the larger versions
* of each metatile fit together combinatorially in the same way, but
* their shapes are distorted slightly. So we must construct our
* expanded meta-tiling by breadth-first search out from a starting
* metatile, because we won't quite know the coordinates of the
* expanded version of each metatile until we know one of the ones
* next to it.
*/
/*
* Coordinate system:
*
* Everything in this code lives on the tiling known to grid.c as
* 'Kites', which can be viewed as a tiling of hexagons each of which
* is subdivided into six kites sharing their pointy vertex, or
* (equivalently) a tiling of equilateral triangles each subdivided
* into three kits sharing their blunt vertex.
*
* We express coordinates in this system relative to the basis (1, r)
* where r = (1 + sqrt(3)i) / 2 is a primitive 6th root of unity. This
* gives us a system in which two integer coordinates can address any
* grid point, provided we scale up so that the side length of the
* equilateral triangles in the tiling is 6.
*/
typedef struct Point {
int x, y; /* represents x + yr */
} Point;
static inline Point left6(Point p)
{
/* r satisfies the equation r^2 = r-1. Hence, multiplying by r
* (achieving a rotation anticlockwise by 1/6 turn) transforms
* x+yr into xr+yr^2 = xr+y(r-1) = (-y) + (x+y)r.
*
* It's easy to check that iterating this transformation six times
* gives you back the same coordinates you started with. */
Point q = { -p.y, p.x + p.y };
return q;
}
static inline Point right6(Point p)
{
/* Conversely, 1/r = 1 - r, so dividing by r turns x+yr into x/r+y
* = x(1-r) + y = (x+y) + (-x)r. */
Point q = { p.x + p.y, -p.x };
return q;
}
typedef enum MetatileType { MT_H, MT_T, MT_P, MT_F } MetatileType;
typedef struct Metatile Metatile;
typedef struct MetaCoord {
Metatile *parent;
int index;
} MetaCoord;
struct Metatile {
/* Data fields describing the metatile and its position. */
MetatileType type;
Point start, orientation;
MetaCoord coords[4];
size_t ncoords;
/* Auxiliary fields used to store the progress of the expansion
* algorithm. */
bool queued;
Metatile *qnext;
};
#define MT_MAXVERT 6 /* largest number of vertices of any metatile */
#define MT_MAXVDEGREE 3 /* largest degree of any vertex of a meta-tiling */
#define MT_MAXEXPAND 13 /* largest number of metatiles in any expansion */
#define MT_MAXHAT 4 /* largest number of hats in a metatile */
#define HAT_NVERT 14 /* vertices of a single hat (counting the straight one) */
#define HAT_NKITE 8 /* kites in a single hat */
static int metatile_cmp(void *av, void *bv)
{
Metatile *a = (Metatile *)av, *b = (Metatile *)bv;
if (a->type < b->type) return -1;
if (a->type > b->type) return +1;
if (a->start.x < b->start.x) return -1;
if (a->start.x > b->start.x) return +1;
if (a->start.y < b->start.y) return -1;
if (a->start.y > b->start.y) return +1;
if (a->orientation.x < b->orientation.x) return -1;
if (a->orientation.x > b->orientation.x) return +1;
if (a->orientation.y < b->orientation.y) return -1;
if (a->orientation.y > b->orientation.y) return +1;
return 0;
}
/*
* Return the coordinates of the vertices of a metatile, given the
* coordinates of the vertex we deem to be distinguished, and a vector
* of Euclidean length 1 showing its direction.
*
* If 'expanded' is true, we instead return the coordinates of the
* corresponding vertices of the expanded version of the same
* metatile.
*/
static size_t metatile_vertices(Metatile m, Point *out, bool expanded)
{
static const Point vertices_H[] = {
{0, 0}, {4, -2}, {12, 6}, {10, 10}, {-6, 18}, {-8, 16},
};
static const Point vertices_T[] = {
{0, 0}, {6, 6}, {-6, 12},
};
static const Point vertices_P[] = {
{0, 0}, {4, 4}, {-4, 20}, {-8, 16},
};
static const Point vertices_F[] = {
{0, 0}, {4, -2}, {6, 0}, {-2, 16}, {-6, 12},
};
static const Point expanded_H[] = {
{0, 0}, {12, -6}, {30, 12}, {24, 24}, {-12, 42}, {-18, 36},
};
static const Point expanded_T[] = {
{0, 0}, {12, 12}, {-12, 24},
};
static const Point expanded_P[] = {
{0, 0}, {14, 8}, {-4, 44}, {-18, 36},
};
static const Point expanded_F[] = {
{0, 0}, {14, -4}, {18, 6}, {0, 42}, {-14, 34},
};
const Point *vertices;
size_t nvertices;
size_t i;
switch (m.type) {
case MT_H:
vertices = expanded ? expanded_H : vertices_H;
nvertices = lenof(vertices_H);
break;
case MT_T:
vertices = expanded ? expanded_T : vertices_T;
nvertices = lenof(vertices_T);
break;
case MT_P:
vertices = expanded ? expanded_P : vertices_P;
nvertices = lenof(vertices_P);
break;
default /* case MT_F */:
vertices = expanded ? expanded_F : vertices_F;
nvertices = lenof(vertices_F);
break;
}
assert(nvertices <= MT_MAXVERT);
Point orientation_r = left6(m.orientation);
for (i = 0; i < nvertices; i++) {
Point v = vertices[i];
out[i].x = m.start.x + v.x * m.orientation.x + v.y * orientation_r.x;
out[i].y = m.start.y + v.x * m.orientation.y + v.y * orientation_r.y;
}
return nvertices;
}
/*
* Return a list of metatiles that arise from expanding a given tile.
*/
static size_t metatile_expand(Metatile m, Metatile *out)
{
static const Metatile tiles_H[] = {
{MT_H, {-4, 20}, {1, 0}},
{MT_H, {2, 2}, {1, 0}},
{MT_H, {8, 26}, {0, -1}},
{MT_T, {6, 24}, {-1, 0}},
{MT_P, {-8, 16}, {1, 0}},
{MT_P, {4, 34}, {0, -1}},
{MT_P, {6, 0}, {1, -1}},
{MT_F, {-10, 38}, {-1, 1}},
{MT_F, {-10, 44}, {0, -1}},
{MT_F, {-4, 2}, {1, 0}},
{MT_F, {2, 2}, {0, -1}},
{MT_F, {26, 14}, {1, 0}},
{MT_F, {32, 8}, {-1, 1}},
};
static const Metatile tiles_T[] = {
{MT_H, {10, 10}, {-1, 1}},
{MT_P, {-6, 0}, {1, 0}},
{MT_P, {8, 14}, {0, 1}},
{MT_P, {18, 6}, {-1, 1}},
{MT_F, {-14, 34}, {-1, 0}},
{MT_F, {-8, -2}, {1, -1}},
{MT_F, {22, 4}, {0, 1}},
};
static const Metatile tiles_P[] = {
{MT_H, {4, 22}, {0, 1}},
{MT_H, {10, 10}, {-1, 1}},
{MT_P, {-6, 0}, {1, 0}},
{MT_P, {6, 24}, {1, 0}},
{MT_P, {8, 14}, {0, 1}},
{MT_F, {-20, 40}, {1, -1}},
{MT_F, {-14, 34}, {-1, 0}},
{MT_F, {-8, -2}, {1, -1}},
{MT_F, {4, 46}, {-1, 1}},
{MT_F, {10, 10}, {1, 0}},
{MT_F, {16, 4}, {-1, 1}},
};
static const Metatile tiles_F[] = {
{MT_H, {8, 20}, {0, 1}},
{MT_H, {14, 8}, {-1, 1}},
{MT_P, {10, 22}, {1, 0}},
{MT_P, {12, 12}, {0, 1}},
{MT_F, {-16, 38}, {1, -1}},
{MT_F, {-10, 32}, {-1, 0}},
{MT_F, {-4, 2}, {1, 0}},
{MT_F, {2, 2}, {0, -1}},
{MT_F, {8, 44}, {-1, 1}},
{MT_F, {14, 8}, {1, 0}},
{MT_F, {20, 2}, {-1, 1}},
};
const Metatile *tiles;
size_t ntiles;
size_t i;
switch (m.type) {
case MT_H:
tiles = tiles_H;
ntiles = lenof(tiles_H);
break;
case MT_T:
tiles = tiles_T;
ntiles = lenof(tiles_T);
break;
case MT_P:
tiles = tiles_P;
ntiles = lenof(tiles_P);
break;
default /* case MT_F */:
tiles = tiles_F;
ntiles = lenof(tiles_F);
break;
}
assert(ntiles <= MT_MAXEXPAND);
Point orientation_r = left6(m.orientation);
for (i = 0; i < ntiles; i++) {
Metatile t = tiles[i];
out[i].type = t.type;
out[i].start.x = (m.start.x + t.start.x * m.orientation.x +
t.start.y * orientation_r.x);
out[i].start.y = (m.start.y + t.start.x * m.orientation.y +
t.start.y * orientation_r.y);
out[i].orientation.x = (t.orientation.x * m.orientation.x +
t.orientation.y * orientation_r.x);
out[i].orientation.y = (t.orientation.x * m.orientation.y +
t.orientation.y * orientation_r.y);
}
return ntiles;
}
/* Store data about each vertex during an expansion. */
typedef struct VertexMapping {
Point in;
/* Metatiles sharing this vertex */
Metatile *tiles[MT_MAXVDEGREE];
size_t ntiles;
/* The expanded coordinates of this vertex, if known */
bool mapped;
Point out;
} VertexMapping;
static int vertexmapping_cmp(void *av, void *bv)
{
VertexMapping *a = (VertexMapping *)av, *b = (VertexMapping *)bv;
if (a->in.x < b->in.x) return -1;
if (a->in.x > b->in.x) return +1;
if (a->in.y < b->in.y) return -1;
if (a->in.y > b->in.y) return +1;
return 0;
}
static int vertexmapping_find(void *av, void *bv)
{
Point *a = (Point *)av;
VertexMapping *b = (VertexMapping *)bv;
if (a->x < b->in.x) return -1;
if (a->x > b->in.x) return +1;
if (a->y < b->in.y) return -1;
if (a->y > b->in.y) return +1;
return 0;
}
typedef struct MetatileSet {
/* The tiles in the set */
tree234 *tiles;
/*
* Bounding box of a rectangular region within the original single
* tile this set was expanded from. We need this in order to pick
* a random chunk out of the tiling to return to our client: this
* box is the limit of where we might select our chunk from.
*
* The box is obtained by starting from the two obtuse vertices of
* the starting P metatile, and then mapping those two vertices
* through each expansion pass. This wouldn't work for the _other_
* two vertices of the P metatile, which end up in the middle of
* another metatile after the first expansion, so that the next
* expansion wouldn't find that point in its VertexMapping. But
* luckily the two inner P vertices do continue working: they
* alternate in subsequent expansions between vertex 1 and vertex
* 4 of an F metatile. And those are the ones we need to define a
* reliable bounding box - phew!
*/
Point vertices[2];
size_t nvertices;
} MetatileSet;
static MetatileSet *metatile_initial_set(MetatileType type)
{
MetatileSet *s;
Metatile *m;
Point vertices[MT_MAXVERT];
size_t nv;
s = snew(MetatileSet);
s->tiles = newtree234(metatile_cmp);
m = snew(Metatile);
m->type = type;
m->start.x = 0;
m->start.y = 0;
m->orientation.x = 1;
m->orientation.y = 0;
m->ncoords = 0;
add234(s->tiles, m);
if (type == MT_P) {
nv = metatile_vertices(*m, vertices, false);
assert(nv == 4);
s->vertices[0] = vertices[1];
s->vertices[1] = vertices[3];
s->nvertices = 2;
} else {
s->nvertices = 0;
}
return s;
}
static void metatile_free_set(MetatileSet *s)
{
Metatile *m;
while ((m = delpos234(s->tiles, 0)) != NULL)
sfree(m);
freetree234(s->tiles);
sfree(s);
}
typedef struct Queue {
Metatile *head, *tail;
} Queue;
static void map_vertex(VertexMapping *vm, Point out, Queue *queue)
{
size_t i;
debug(("map_vertex %d,%d -> %d,%d", vm->in.x, vm->in.y, out.x, out.y));
if (vm->mapped) {
debug((" (already done)\n"));
return;
}
debug(("\n"));
vm->mapped = true;
vm->out = out;
for (i = 0; i < vm->ntiles; i++) {
Metatile *t = vm->tiles[i];
if (!t->queued) {
t->queued = true;
t->qnext = NULL;
if (queue->tail)
queue->tail->qnext = t;
else
queue->head = t;
queue->tail = t;
debug(("queued %c @ %d,%d d=%d,%d\n", "HTPF"[t->type], t->start.x,
t->start.y, t->orientation.x, t->orientation.y));
}
}
}
/*
* Expand a set of metatiles into its next-generation set. Returns the
* new set. The old set is not freed, but the auxiliary fields of its
* Metatile structures will be used as intermediate storage.
*/
static MetatileSet *metatile_set_expand(MetatileSet *si)
{
tree234 *vmap;
VertexMapping *vm;
Metatile *m;
Queue queue = { NULL, NULL };
size_t i, j;
MetatileSet *so = snew(MetatileSet);
so->tiles = newtree234(metatile_cmp);
/*
* Enumerate all the vertices in our tiling, and store the set of
* tiles they belong to.
*/
vmap = newtree234(vertexmapping_cmp);
for (i = 0; (m = index234(si->tiles, i)) != NULL; i++) {
Point vertices[MT_MAXVERT];
size_t nv = metatile_vertices(*m, vertices, false);
for (j = 0; j < nv; j++) {
VertexMapping *newvm = snew(VertexMapping);
newvm->in = vertices[j];
newvm->ntiles = 0;
newvm->mapped = false;
vm = add234(vmap, newvm);
if (vm != newvm)
sfree(newvm);
assert(vm->ntiles < MT_MAXVDEGREE);
vm->tiles[vm->ntiles++] = m;
}
m->queued = false;
}
for (i = 0; (vm = index234(vmap, i)) != NULL; i++) {
debug(("vertex @ %d,%d {", vm->in.x, vm->in.y));
for (j = 0; j < vm->ntiles; j++) {
m = vm->tiles[j];
debug(("%s%c @ %d,%d d=%d,%d", j?", ":"", "HTPF"[m->type],
m->start.x, m->start.y, m->orientation.x,
m->orientation.y));
}
debug(("}\n"));
}
/*
* Initialise an arbitrary vertex to a known location.
*/
{
Point p = {0, 0};
m = index234(si->tiles, 0);
vm = find234(vmap, &m->start, vertexmapping_find);
map_vertex(vm, p, &queue);
}
/*
* Now process the queue of tiles to be expanded.
*/
debug(("-- start\n"));
while (queue.head) {
Metatile *m, m_moved;
Metatile t[MT_MAXEXPAND];
Point vi[MT_MAXVERT], vo[MT_MAXVERT];
size_t nv, nt;
m = queue.head;
queue.head = queue.head->qnext;
if (!queue.head)
queue.tail = NULL;
debug(("unqueued %c @ %d,%d d=%d,%d\n", "HTPF"[m->type],
m->start.x, m->start.y, m->orientation.x, m->orientation.y));
nv = metatile_vertices(*m, vi, false);
metatile_vertices(*m, vo, true);
/* Find a vertex of this tile that's already mapped, and use
* it to determine the placement. */
int dx, dy;
for (i = 0; i < nv; i++) {
vm = find234(vmap, &vi[i], vertexmapping_find);
assert(vm);
if (vm->mapped) {
dx = vm->out.x - vo[i].x;
dy = vm->out.y - vo[i].y;
debug(("found mapped vertex %d,%d -> %d,%d: "
"offset=%d,%d\n",
vm->in.x, vm->in.y, vm->out.x, vm->out.y, dx, dy));
break;
}
}
assert(i < nv && "Why was this tile queued without a mapped vertex?");
/* Now map all the rest of the vertices of the tile. */
for (i = 0; i < nv; i++) {
vm = find234(vmap, &vi[i], vertexmapping_find);
vo[i].x += dx;
vo[i].y += dy;
map_vertex(vm, vo[i], &queue);
}
/* And expand it, substituting in its new starting coordinate. */
m_moved = *m; /* structure copy */
m_moved.start = vo[0];
nt = metatile_expand(m_moved, t);
for (i = 0; i < nt; i++) {
Metatile *newmt = snew(Metatile);
*newmt = t[i]; /* structure copy */
newmt->ncoords = 0;
debug(("expanded %c @ %d,%d d=%d,%d\n", "HTPF"[newmt->type],
newmt->start.x, newmt->start.y, newmt->orientation.x,
newmt->orientation.y));
Metatile *added = add234(so->tiles, newmt);
if (added != newmt)
sfree(newmt);
assert(added->ncoords < lenof(added->coords));
added->coords[added->ncoords].parent = m;
added->coords[added->ncoords].index = i;
added->ncoords++;
}
}
for (i = 0; (m = index234(si->tiles, i)) != NULL; i++) {
if (!m->queued)
debug(("OMITTED %c @ %d,%d d=%d,%d\n", "HTPF"[m->type],
m->start.x, m->start.y, m->orientation.x,
m->orientation.y));
}
/*
* Write out the remapped versions of the tile set's bounding
* vertices.
*/
for (i = 0; i < si->nvertices; i++) {
vm = find234(vmap, &si->vertices[i], vertexmapping_find);
so->vertices[i] = vm->out;
}
so->nvertices = si->nvertices;
while ((vm = delpos234(vmap, 0)) != NULL)
sfree(vm);
freetree234(vmap);
return so;
}
typedef struct Hat {
Point start, orientation;
bool reversed;
const Metatile *parent;
int index;
} Hat;
static size_t metatile_hats(const Metatile *m, Hat *out)
{
static const Hat hats_H[] = {
{{6, 0}, {1, 0}, false},
{{6, 6}, {0, -1}, false},
{{0, 12}, {1, 0}, false},
{{0, 6}, {-1, 0}, true},
};
static const Hat hats_T[] = {
{{-2, 10}, {-1, 1}, false},
};
static const Hat hats_P[] = {
{{-2, 10}, {-1, 1}, false},
{{-2, 16}, {0, 1}, false},
};
static const Hat hats_F[] = {
{{0, 6}, {-1, 1}, false},
{{0, 12}, {0, 1}, false},
};
const Hat *hats;
size_t nhats;
size_t i;
switch (m->type) {
case MT_H:
hats = hats_H;
nhats = lenof(hats_H);
break;
case MT_T:
hats = hats_T;
nhats = lenof(hats_T);
break;
case MT_P:
hats = hats_P;
nhats = lenof(hats_P);
break;
default /* case MT_F */:
hats = hats_F;
nhats = lenof(hats_F);
break;
}
assert(nhats <= MT_MAXHAT);
Point orientation_r = left6(m->orientation);
for (i = 0; i < nhats; i++) {
Hat h = hats[i];
out[i].parent = m;
out[i].index = i;
out[i].start.x = (m->start.x + h.start.x * m->orientation.x +
h.start.y * orientation_r.x);
out[i].start.y = (m->start.y + h.start.x * m->orientation.y +
h.start.y * orientation_r.y);
out[i].orientation.x = (h.orientation.x * m->orientation.x +
h.orientation.y * orientation_r.x);
out[i].orientation.y = (h.orientation.x * m->orientation.y +
h.orientation.y * orientation_r.y);
out[i].reversed = h.reversed;
}
return nhats;
}
static size_t hat_vertices(Hat h, Point *out)
{
static const Point reference_hat[] = {
{0, 0}, {3, 0}, {2, 2}, {0, 3}, {-2, 4}, {-3, 3}, {-6, 6}, {-9, 6},
{-8, 4}, {-6, 3}, {-6, 0}, {-3, -3}, {-2, -2}, {0, -3},
};
size_t i;
Point orientation_r;
if (h.reversed)
orientation_r = right6(h.orientation);
else
orientation_r = left6(h.orientation);
assert(lenof(reference_hat) == HAT_NVERT);
for (i = 0; i < lenof(reference_hat); i++) {
Point v = reference_hat[h.reversed ? HAT_NVERT-1-i : i];
out[i].x = h.start.x + v.x * h.orientation.x + v.y * orientation_r.x;
out[i].y = h.start.y + v.x * h.orientation.y + v.y * orientation_r.y;
}
return lenof(reference_hat);
}
typedef struct BoundingBox {
Point bl, tr;
} BoundingBox;
static bool point_in_bbox(Point p, const BoundingBox *bbox)
{
int xl, xr, x;
if (!bbox)
return true;
/*
* Bounding boxes have vertical edges, not aligned with our basis
* vector r. So the 'true' x coordinate of (x,y) is proportional
* to 2x+y.
*/
if (p.y < bbox->bl.y || p.y > bbox->tr.y)
return false;
xl = 2*bbox->bl.x + bbox->bl.y;
xr = 2*bbox->tr.x + bbox->tr.y;
x = 2*p.x + p.y;
if (x < xl || x > xr)
return false;
return true;
}
static bool hat_in_bbox(Hat h, const BoundingBox *bbox)
{
Point p[HAT_NVERT];
size_t i, np;
if (!bbox)
return true;
np = hat_vertices(h, p);
for (i = 0; i < np; i++)
if (!point_in_bbox(p[i], bbox))
return false;
return true;
}
static Hat *metatile_set_to_hats(MetatileSet *s, size_t *nhats,
const BoundingBox *bbox)
{
Metatile *m;
size_t i, j, k, n;
Hat *h;
n = 0;
for (i = 0; (m = index234(s->tiles, i)) != NULL; i++) {
Hat htmp[MT_MAXHAT];
size_t nthis = metatile_hats(m, htmp);
for (k = 0; k < nthis; k++)
if (hat_in_bbox(htmp[k], bbox))
n++;
}
*nhats = n;
h = snewn(n, Hat);
j = 0;
for (i = 0; (m = index234(s->tiles, i)) != NULL; i++) {
Hat htmp[MT_MAXHAT];
size_t nthis = metatile_hats(m, htmp);
for (k = 0; k < nthis; k++) {
if (hat_in_bbox(htmp[k], bbox)) {
assert(j < n);
h[j++] = htmp[k]; /* structure copy */
}
}
}
assert(j == n);
return h;
}
#if 0
void hat_tiling_randomise(struct HatPatchParams *params, random_state *rs)
{
MetatileSet *s, *s2;
int x0, x1, y0, y1;
/*
* Iterate until we have a good-sized patch to select a rectangle
* from.
*/
s = metatile_initial_set(MT_P);
params->iterations = 0;
while (true) {
x0 = 2 * s->vertices[0].x + s->vertices[0].y;
x1 = 2 * s->vertices[1].x + s->vertices[1].y;
if (x1 < x0) {
int t = x1;
x1 = x0;
x0 = t;
}
y0 = s->vertices[0].y;
y1 = s->vertices[1].y;
if (y1 < y0) {
int t = y1;
y1 = y0;
y0 = t;
}
if (50*params->w <= x1-x0 && 50*params->h <= y1-y0)
break;
params->iterations++;
s2 = metatile_set_expand(s);
metatile_free_set(s);
s = s2;
}
/*
* Now select that rectangle.
*/
params->x = x0 + random_upto(rs, x1 - x0 - params->w + 1);
params->y = y0 + random_upto(rs, y1 - y0 - params->h + 1);
}
void hat_tiling_generate(struct HatPatchParams *params,
hat_tile_callback_fn cb, void *cbctx)
{
MetatileSet *s, *s2;
unsigned i;
size_t j, nh;
BoundingBox bbox;
Hat *hats;
s = metatile_initial_set(MT_P);
for (i = 0; i < params->iterations; i++) {
s2 = metatile_set_expand(s);
metatile_free_set(s);
s = s2;
}
bbox.bl.x = (params->x - params->y) / 2;
bbox.tr.x = ((params->x + params->w) - (params->y + params->h)) / 2;
bbox.bl.y = params->y;
bbox.tr.y = params->y + params->h;
hats = metatile_set_to_hats(s, &nh, &bbox);
for (j = 0; j < nh; j++) {
Point vertices[HAT_NVERT];
size_t nv = hat_vertices(hats[j], vertices);
int out[2 * HAT_NVERT];
size_t k;
for (k = 0; k < nv; k++) {
out[2*k] = 2 * vertices[k].x + vertices[k].y;
out[2*k+1] = vertices[k].y;
}
cb(cbctx, nv, out);
}
sfree(hats);
metatile_free_set(s);
}
#endif
#ifdef TEST_HAT
/*
* Assortment of test modes that output Postscript diagrams.
*/
static size_t hat_kite_centres(Hat h, Point *out)
{
static const Point reference_hat[] = {
{-7,5},{-5,4},{-5,1},{-4,-1},{-1,-1},{-2,1},{-1,2},{1,1},
};
size_t i;
Point orientation_r;
if (h.reversed)
orientation_r = right6(h.orientation);
else
orientation_r = left6(h.orientation);
assert(lenof(reference_hat) == HAT_NKITE);
for (i = 0; i < lenof(reference_hat); i++) {
Point v = reference_hat[i];
out[i].x = h.start.x + v.x * h.orientation.x + v.y * orientation_r.x;
out[i].y = h.start.y + v.x * h.orientation.y + v.y * orientation_r.y;
}
return lenof(reference_hat);
}
static inline int round6(int x)
{
int sign = x<0 ? -1 : +1;
x *= sign;
x += 3;
x /= 6;
x *= 6;
x *= sign;
return x;
}
static inline Point kite_left(Point k)
{
Point centre = { round6(k.x), round6(k.y) };
Point offset = { k.x - centre.x, k.y - centre.y };
offset = left6(offset);
Point r = { centre.x + offset.x, centre.y + offset.y };
return r;
}
static inline Point kite_right(Point k)
{
Point centre = { round6(k.x), round6(k.y) };
Point offset = { k.x - centre.x, k.y - centre.y };
offset = right6(offset);
Point r = { centre.x + offset.x, centre.y + offset.y };
return r;
}
static inline Point kite_forward_left(Point k)
{
Point centre = { round6(k.x), round6(k.y) };
Point offset = { k.x - centre.x, k.y - centre.y };
Point rotate = left6(offset);
Point r = { k.x + rotate.x + offset.x, k.y + rotate.y + offset.y };
return r;
}
static inline Point kite_forward_right(Point k)
{
Point centre = { round6(k.x), round6(k.y) };
Point offset = { k.x - centre.x, k.y - centre.y };
Point rotate = right6(offset);
Point r = { k.x + rotate.x + offset.x, k.y + rotate.y + offset.y };
return r;
}
typedef struct pspoint {
float x, y;
} pspoint;
static inline pspoint pscoords(Point p)
{
pspoint q = { p.x + p.y / 2.0F, p.y * sqrt(0.75) };
return q;
}
typedef struct psbbox {
bool started;
pspoint bl, tr;
} psbbox;
static inline void psbbox_add(psbbox *bbox, pspoint p)
{
if (!bbox->started || bbox->bl.x > p.x)
bbox->bl.x = p.x;
if (!bbox->started || bbox->tr.x < p.x)
bbox->tr.x = p.x;
if (!bbox->started || bbox->bl.y > p.y)
bbox->bl.y = p.y;
if (!bbox->started || bbox->tr.y < p.y)
bbox->tr.y = p.y;
bbox->started = true;
}
static void draw_metatiles_svg(const Metatile *tiles, size_t n,
const Point *bounds, bool coords)
{
size_t i, j;
psbbox bbox = { false };
for (i = 0; i < n; i++) {
Point vertices[MT_MAXVERT];
size_t nv = metatile_vertices(tiles[i], vertices, false);
for (j = 0; j < nv; j++)
psbbox_add(&bbox, pscoords(vertices[j]));
}
float ascale = 10, xscale = ascale, yscale = -ascale;
float border = 0.2 * ascale; /* leave room for strokes at the edges */
float ox = -xscale * bbox.bl.x + border;
float oy = -yscale * bbox.tr.y + border;
printf("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
printf("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" "
"width=\"%g\" height=\"%g\">\n",
ceil(ox + xscale * bbox.tr.x + 2*border),
ceil(oy + yscale * bbox.bl.y + 2*border));
for (i = 0; i < n; i++) {
Point vertices[MT_MAXVERT];
size_t nv = metatile_vertices(tiles[i], vertices, false);
pspoint pp[MT_MAXVERT];
for (j = 0; j < nv; j++) {
pp[j] = pscoords(vertices[j]);
pp[j].x = ox + xscale * pp[j].x;
pp[j].y = oy + yscale * pp[j].y;
}
printf("<path style=\""
"fill: none; "
"stroke: black; "
"stroke-width: %f; "
"stroke-linejoin: round; "
"stroke-linecap: round; "
"\" d=\"", 0.2 * ascale);
for (j = 0; j < nv; j++)
printf("%s %f %f \n", j ? "L" : "M", pp[j].x, pp[j].y);
printf("z\" />\n");
if (tiles[i].type != MT_F) {
/*
* Mark arrows on three of the metatile types (H, T, P),
* following the diagrams in the paper. (The metatile
* shapes other than F each have some symmetry, but their
* roles in the metatile substitution system are not
* similarly symmetric, so for diagnostic diagrams you
* want to mark their orientation.)
*/
pspoint lstart, lend;
pspoint astart, aend, aforward, aleft;
double d;
/*
* Determine endpoints of a line crossing the polygon in
* the appropriate direction, by case analysis on the
* individual tile types.
*/
switch (tiles[i].type) {
case MT_H:
lstart.x = (pp[4].x + pp[5].x) / 2;
lstart.y = (pp[4].y + pp[5].y) / 2;
lend.x = (pp[1].x + pp[2].x) / 2;
lend.y = (pp[1].y + pp[2].y) / 2;
break;
case MT_T:
lstart = pp[0];
lend.x = (pp[1].x + pp[2].x) / 2;
lend.y = (pp[1].y + pp[2].y) / 2;
break;
default /* case MT_P */:
lstart.x = (5*pp[3].x + 3*pp[0].x) / 8;
lstart.y = (5*pp[3].y + 3*pp[0].y) / 8;
lend.x = (5*pp[1].x + 3*pp[2].x) / 8;
lend.y = (5*pp[1].y + 3*pp[2].y) / 8;
break;
}
/*
* Now shorten that line a little and give it an arrowhead.
*/
astart.x = (4 * lstart.x + lend.x) / 5;
astart.y = (4 * lstart.y + lend.y) / 5;
aend.x = (lstart.x + 4 * lend.x) / 5;
aend.y = (lstart.y + 4 * lend.y) / 5;
aforward.x = aend.x - astart.x;
aforward.y = aend.y - astart.y;
d = sqrt(aforward.x*aforward.x + aforward.y*aforward.y);
aforward.x /= d;
aforward.y /= d;
aleft.x = -aforward.y;
aleft.y = +aforward.x;
printf("<path style=\""
"fill: none; "
"stroke: black; "
"stroke-width: %f; "
"stroke-opacity: 0.2; "
"stroke-linejoin: round; "
"stroke-linecap: round; "
"\" d=\"", 0.9 * ascale);
printf("M %f %f L %f %f ", astart.x, astart.y,
aend.x, aend.y);
printf("L %f %f ",
aend.x - 1.2 * ascale * (aforward.x + aleft.x),
aend.y - 1.2 * ascale * (aforward.y + aleft.y));
printf("M %f %f L %f %f ", aend.x, aend.y,
aend.x - 1.2 * ascale * (aforward.x - aleft.x),
aend.y - 1.2 * ascale * (aforward.y - aleft.y));
printf("\" />\n");
}
if (coords) {
/*
* Print each tile's coordinates.
*/
pspoint centre;
size_t j;
switch (tiles[i].type) {
case MT_H:
centre.x = (pp[0].x + pp[2].x + pp[4].x) / 3;
centre.y = (pp[0].y + pp[2].y + pp[4].y) / 3;
break;
case MT_T:
centre.x = (pp[0].x + pp[1].x + pp[2].x) / 3;
centre.y = (pp[0].y + pp[1].y + pp[2].y) / 3;
break;
case MT_P:
centre.x = (pp[0].x + pp[2].x) / 2;
centre.y = (pp[0].y + pp[2].y) / 2;
break;
default /* case MT_F */:
centre.x = (pp[2].x + pp[4].x) / 2;
centre.y = (pp[2].y + pp[4].y) / 2;
break;
}
double lineheight = ascale * 1.5;
double charheight = lineheight * 0.6; /* close enough */
double allheight = lineheight * (tiles[i].ncoords-1) + charheight;
for (j = 0; j < tiles[i].ncoords; j++) {
const Metatile *it;
unsigned cindex;
printf("<text style=\""
"fill: black; "
"font-family: Sans; "
"font-size: %g; "
"text-anchor: middle; "
"text-align: center; "
"\" x=\"%g\" y=\"%g\">", lineheight,
centre.x,
centre.y - allheight/2 + charheight + lineheight*j);
it = &tiles[i];
cindex = j;
while (cindex < it->ncoords) {
if (it != &tiles[i])
printf(".");
printf("%d", (int)it->coords[cindex].index);
it = it->coords[cindex].parent;
cindex = 0; /* BODGING AHOY */
}
printf("</text>\n");
}
}
}
printf("</svg>\n");
}
static void draw_metatile_set_svg(
MetatileSet *tiles, const Point *bounds, bool coords)
{
/*
* Slurp the tree234 of tiles into an array for display.
* Tedious, but this test code doesn't have to be particularly
* efficient.
*/
size_t nt = count234(tiles->tiles);
Metatile *t = snewn(nt, Metatile);
size_t i;
for (i = 0; i < nt; i++) {
Metatile *m = index234(tiles->tiles, i);
t[i] = *m; /* structure copy */
}
draw_metatiles_svg(t, nt, bounds, coords);
sfree(t);
}
static void draw_hats_svg(const Hat *hats, size_t n,
const Point *bounds, bool kites, char coordtype)
{
size_t i, j;
psbbox bbox = { false };
for (i = 0; i < n; i++) {
Point vertices[HAT_NVERT];
size_t nv = hat_vertices(hats[i], vertices);
for (j = 0; j < nv; j++)
psbbox_add(&bbox, pscoords(vertices[j]));
}
float ascale = (coordtype == 'k' || coordtype == 'K' ? 20 : 10);
float xscale = ascale, yscale = -ascale;
float border = 0.2 * ascale; /* leave room for strokes at the edges */
float ox = -xscale * bbox.bl.x + border;
float oy = -yscale * bbox.tr.y + border;
printf("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
printf("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" "
"width=\"%g\" height=\"%g\">\n",
ceil(ox + xscale * bbox.tr.x + 2*border),
ceil(oy + yscale * bbox.bl.y + 2*border));
for (i = 0; i < n; i++) {
Point vertices[HAT_NVERT];
pspoint psvs[HAT_NVERT];
size_t nv = hat_vertices(hats[i], vertices);
int is = hats[i].reversed ? -1 : +1;
int io = hats[i].reversed ? 13 : 0;
printf("<path style=\""
"fill: %s; "
"stroke: black; "
"stroke-width: %f; "
"stroke-linejoin: round; "
"stroke-linecap: round; "
"\" d=\"",
hats[i].reversed ? "rgba(0,0,0,0.2)" : "none",
0.2 * ascale);
for (j = 0; j < nv; j++) {
psvs[j] = pscoords(vertices[j]);
printf("%s %f %f\n", j ? "L" : "M",
ox + xscale * psvs[j].x, oy + yscale * psvs[j].y);
}
printf("z\" />\n");
if (kites) {
/*
* Draw internal lines within each hat dividing it into
* kites. This is done in a rather bodgy way, sorry.
*/
const char *fmt = "<path style=\""
"fill: none; "
"stroke: rgba(0,0,0,0.2); "
"stroke-width: %f; "
"stroke-linejoin: round; "
"stroke-linecap: round; "
"\" d=\"M %f %f L %f %f\" />\n";
float strokewidth = 0.1 * ascale;
printf(fmt, strokewidth,
ox + xscale * psvs[io+is*0].x,
oy + yscale * psvs[io+is*0].y,
ox + xscale * psvs[io+is*3].x,
oy + yscale * psvs[io+is*3].y);
printf(fmt, strokewidth,
ox + xscale * psvs[io+is*0].x,
oy + yscale * psvs[io+is*0].y,
ox + xscale * psvs[io+is*5].x,
oy + yscale * psvs[io+is*5].y);
printf(fmt, strokewidth,
ox + xscale * psvs[io+is*6].x,
oy + yscale * psvs[io+is*6].y,
ox + xscale * psvs[io+is*9].x,
oy + yscale * psvs[io+is*9].y);
printf(fmt, strokewidth,
ox + xscale * psvs[io+is*0].x,
oy + yscale * psvs[io+is*0].y,
ox + xscale * psvs[io+is*10].x,
oy + yscale * psvs[io+is*10].y);
printf(fmt, strokewidth,
ox + xscale * psvs[io+is*9].x,
oy + yscale * psvs[io+is*9].y,
ox + xscale * (psvs[io+is*6].x + psvs[io+is*12].x) / 2,
oy + yscale * (psvs[io+is*6].y + psvs[io+is*12].y) / 2);
printf(fmt, strokewidth,
ox + xscale * psvs[io+is*5].x,
oy + yscale * psvs[io+is*5].y,
ox + xscale * (psvs[io+is*6].x + psvs[io+is*12].x) / 2,
oy + yscale * (psvs[io+is*6].y + psvs[io+is*12].y) / 2);
printf(fmt, strokewidth,
ox + xscale * psvs[io+is*12].x,
oy + yscale * psvs[io+is*12].y,
ox + xscale * (psvs[io+is*6].x + psvs[io+is*12].x) / 2,
oy + yscale * (psvs[io+is*6].y + psvs[io+is*12].y) / 2);
}
if (coordtype == 'h') {
double lineheight = ascale * 2;
double charheight = lineheight * 0.6; /* close enough */
printf("<text style=\""
"fill: black; "
"font-family: Sans; "
"font-size: %gpx; "
"text-anchor: middle; "
"text-align: center; "
"\" x=\"%g\" y=\"%g\">", lineheight,
ox + xscale * (psvs[io+is*0].x + psvs[io+is*10].x) / 2,
oy + yscale * (psvs[io+is*0].y + psvs[io+is*10].y) / 2
+ charheight/2);
printf("%d", (int)i);
printf("</text>\n");
} else if (coordtype == 'k') {
Point kites[HAT_NKITE];
size_t nk = hat_kite_centres(hats[i], kites);
double lineheight = ascale * 0.5;
double charheight = lineheight * 0.6; /* close enough */
for (j = 0; j < nk; j++) {
pspoint p = pscoords(kites[j]);
printf("<text style=\""
"fill: black; "
"font-family: Sans; "
"font-size: %gpx; "
"text-anchor: middle; "
"text-align: center; "
"\" x=\"%g\" y=\"%g\">", lineheight,
ox + xscale * p.x,
oy + yscale * p.y + charheight/2);
printf("%d.%d.%d", (int)j, hats[i].index,
hats[i].parent->coords[0].index);
printf("</text>\n");
}
} else if (coordtype == 'K') {
Point kites[HAT_NKITE];
size_t nk = hat_kite_centres(hats[i], kites);
double lineheight = ascale * 1.1;
double charheight = lineheight * 0.6; /* close enough */
for (j = 0; j < nk; j++) {
pspoint p = pscoords(kites[j]);
printf("<text style=\""
"fill: black; "
"font-family: Sans; "
"font-size: %gpx; "
"text-anchor: middle; "
"text-align: center; "
"\" x=\"%g\" y=\"%g\">", lineheight,
ox + xscale * p.x,
oy + yscale * p.y + charheight/2);
printf("%d", (int)j);
printf("</text>\n");
}
}
}
printf("</svg>\n");
}
typedef enum KiteStep { KS_LEFT, KS_RIGHT, KS_F_LEFT, KS_F_RIGHT } KiteStep;
static inline Point kite_step(Point k, KiteStep step)
{
switch (step) {
case KS_LEFT: return kite_left(k);
case KS_RIGHT: return kite_right(k);
case KS_F_LEFT: return kite_forward_left(k);
default /* case KS_F_RIGHT */: return kite_forward_right(k);
}
}
int main(int argc, char **argv)
{
if (argc <= 1) {
printf("usage: hat-test <mode>\n");
printf("modes: H,T,P,F display a single unexpanded tile\n");
printf(" xH,xT,xP,xF display the expansion of one tile\n");
printf(" cH,cT,cP,cF display expansion with tile coords\n");
printf(" CH,CT,CP,CF display double expansion with coords\n");
printf(" hH,hT,hP,hF display the hats from one tile\n");
printf(" HH,HT,HP,HF hats from an expansion, with coords\n");
printf(" m1, m2, ... nth expansion of one H metatile\n");
printf(" M1, M2, ... nth expansion turned into real hats\n");
printf(" --hat show the kites in a single hat\n");
printf(" --tables generate hat-tables.h for hat.c\n");
return 0;
}
if (!strcmp(argv[1], "H") || !strcmp(argv[1], "T") ||
!strcmp(argv[1], "P") || !strcmp(argv[1], "F")) {
MetatileType type = (argv[1][0] == 'H' ? MT_H :
argv[1][0] == 'T' ? MT_T :
argv[1][0] == 'P' ? MT_P : MT_F);
Metatile m = {type, {0, 0}, {1, 0}};
draw_metatiles_svg(&m, 1, NULL, false);
return 0;
}
if (!strcmp(argv[1], "xH") || !strcmp(argv[1], "xT") ||
!strcmp(argv[1], "xP") || !strcmp(argv[1], "xF")) {
MetatileType type = (argv[1][1] == 'H' ? MT_H :
argv[1][1] == 'T' ? MT_T :
argv[1][1] == 'P' ? MT_P : MT_F);
Metatile m = {type, {0, 0}, {1, 0}};
Metatile t[MT_MAXEXPAND];
size_t nt = metatile_expand(m, t);
draw_metatiles_svg(t, nt, NULL, false);
return 0;
}
if (argv[1][0] && argv[1][1] &&
strchr("cC", argv[1][0]) && strchr("HTPF", argv[1][1])) {
MetatileType type = (argv[1][1] == 'H' ? MT_H :
argv[1][1] == 'T' ? MT_T :
argv[1][1] == 'P' ? MT_P : MT_F);
MetatileSet *t[3];
int nsets = (argv[1][0] == 'c' ? 2 : 3);
int i;
t[0] = metatile_initial_set(type);
for (i = 1; i < nsets; i++)
t[i] = metatile_set_expand(t[i-1]);
draw_metatile_set_svg(t[nsets-1], NULL, true);
for (i = 0; i < nsets; i++)
metatile_free_set(t[i]);
return 0;
}
if (!strcmp(argv[1], "hH") || !strcmp(argv[1], "hT") ||
!strcmp(argv[1], "hP") || !strcmp(argv[1], "hF")) {
MetatileType type = (
!strcmp(argv[1], "hH") ? MT_H :
!strcmp(argv[1], "hT") ? MT_T :
!strcmp(argv[1], "hP") ? MT_P : MT_F);
Metatile m = {type, {0, 0}, {1, 0}};
Hat h[MT_MAXHAT];
size_t nh = metatile_hats(&m, h);
draw_hats_svg(h, nh, NULL, false, 'h');
return 0;
}
if (!strcmp(argv[1], "--hat")) {
Hat h = { { 0, 0 }, { 1, 0 }, false, NULL, 0 };
draw_hats_svg(&h, 1, NULL, true, 'K');
return 0;
}
if (argv[1][0] == 'H' && argv[1][1] && strchr("HTPF", argv[1][1])) {
MetatileType type = (argv[1][1] == 'H' ? MT_H :
argv[1][1] == 'T' ? MT_T :
argv[1][1] == 'P' ? MT_P : MT_F);
MetatileSet *t[2];
size_t i, nh;
t[0] = metatile_initial_set(type);
t[1] = metatile_set_expand(t[0]);
Hat *h = metatile_set_to_hats(t[1], &nh, NULL);
draw_hats_svg(h, nh, NULL, true, 'k');
sfree(h);
for (i = 0; i < 2; i++)
metatile_free_set(t[i]);
return 0;
}
if (argv[1][0] == 'm' || argv[1][0] == 'M') {
int niter = atoi(argv[1] + 1);
MetatileSet *tiles = metatile_initial_set(MT_P);
while (niter-- > 0) {
MetatileSet *t2 = metatile_set_expand(tiles);
metatile_free_set(tiles);
tiles = t2;
}
if (argv[1][0] == 'M') {
size_t nh;
Hat *h = metatile_set_to_hats(tiles, &nh, NULL);
draw_hats_svg(h, nh, tiles->vertices, false, 0);
sfree(h);
} else {
draw_metatile_set_svg(tiles, tiles->vertices, false);
}
metatile_free_set(tiles);
return 0;
}
if (!strcmp(argv[1], "--tables")) {
size_t i, j, k;
printf("/*\n"
" * Header file autogenerated by auxiliary/hatgen.c\n"
" *\n"
" * To regenerate, run 'hatgen --tables > hat-tables.h'\n"
" */\n\n");
static const char HTPF[] = "HTPF";
printf("static const unsigned hats_in_metatile[] = {");
for (i = 0; i < 4; i++) {
Metatile m = {i, {0, 0}, {1, 0}};
Hat h[MT_MAXHAT];
size_t nh = metatile_hats(&m, h);
printf(" %zu,", nh);
}
printf(" };\n\n");
{
size_t psizes[4] = {0, 0, 0, 0};
size_t csizes[4] = {0, 0, 0, 0};
for (i = 0; i < 4; i++) {
Metatile m = {i, {0, 0}, {1, 0}};
Metatile t[MT_MAXEXPAND];
size_t nt = metatile_expand(m, t);
printf("static const TileType children_%c[] = {\n"
" ", HTPF[i]);
for (j = 0; j < nt; j++) {
MetatileType c = t[j].type;
psizes[c]++;
csizes[i]++;
printf(" TT_%c,", HTPF[c]);
}
printf("\n};\n");
}
printf("static const TileType *const children[] = {\n");
for (i = 0; i < 4; i++)
printf(" children_%c,\n", HTPF[i]);
printf("};\n");
printf("static const size_t nchildren[] = {\n");
for (i = 0; i < 4; i++)
printf(" %u,\n", (unsigned)csizes[i]);
printf("};\n\n");
}
{
for (i = 0; i < 4; i++) {
MetatileSet *t[2];
size_t j, k, nh, nmeta, ti;
struct list {
Point kite;
unsigned ik, ih, im;
} list[8*MT_MAXHAT*MT_MAXEXPAND];
size_t len = 0;
t[0] = metatile_initial_set(i);
t[1] = metatile_set_expand(t[0]);
Hat *h = metatile_set_to_hats(t[1], &nh, NULL);
printf("static const KitemapEntry kitemap_%c[] = {\n",
HTPF[i]);
Point origin = h[0].start;
for (j = 0; j < nh; j++) {
Point kites[HAT_NKITE];
size_t nk = hat_kite_centres(h[j], kites);
for (k = 0; k < nk; k++) {
struct list *le = &list[len++];
le->kite.x = kites[k].x - origin.x;
le->kite.y = kites[k].y - origin.y;
le->ik = k;
le->ih = h[j].index;
le->im = h[j].parent->coords[0].index;
#if 0
printf("// %d,%d = %u.%u.%u\n", le->kite.x, le->kite.y, le->ik, le->ih, le->im);
#endif
}
}
nmeta = count234(t[1]->tiles);
for (ti = 0; ti < 8 * 4 * nmeta; ti++) {
unsigned ik = ti % 8;
unsigned ih = ti / 8 % 4;
unsigned im = ti / (8*4);
struct list *src = NULL, *dst = NULL;
int istep;
for (j = 0; j < len; j++) {
struct list *tmp = &list[j];
if (tmp->ik == ik && tmp->ih == ih && tmp->im == im) {
src = tmp;
break;
}
}
if (ik == 0) {
printf(" /* hat #%u in metatile #%u (type %c)",
ih, im, HTPF[((Metatile *)index234(
t[1]->tiles, im))->type]);
if (!src)
printf(" does not exist");
printf(" */\n");
}
#if 0
if (src)
printf(" // src=%d,%d\n", src->kite.x, src->kite.y);
#endif
printf(" ");
for (istep = 0; istep < 4; istep++) {
KiteStep step = istep;
dst = NULL;
if (src) {
Point pdst = kite_step(src->kite, step);
#if 0
printf(" /* dst=%d,%d */", pdst.x, pdst.y);
#endif
for (k = 0; k < len; k++) {
struct list *tmp = &list[k];
if (tmp->kite.x == pdst.x &&
tmp->kite.y == pdst.y) {
dst = tmp;
break;
}
}
}
if (!dst) {
printf(" {-1,-1,-1},");
} else {
printf(" {%u,%u,%u},", dst->ik, dst->ih, dst->im);
}
}
printf("\n");
}
printf("};\n");
sfree(h);
for (j = 0; j < 2; j++)
metatile_free_set(t[j]);
}
printf("static const KitemapEntry *const "
"kitemap[] = {\n");
for (i = 0; i < 4; i++)
printf(" kitemap_%c,\n", HTPF[i]);
printf("};\n\n");
}
{
for (i = 0; i < 4; i++) {
MetatileSet *t[3];
Metatile *m;
int map[MT_MAXEXPAND * MT_MAXEXPAND];
size_t maplen;
for (j = 0; j < lenof(map); j++)
map[j] = -1;
t[0] = metatile_initial_set(i);
for (j = 1; j < 3; j++)
t[j] = metatile_set_expand(t[j-1]);
for (j = 0; (m = index234(t[2]->tiles, j)) != NULL; j++) {
unsigned coords[4];
size_t ncoords = 0;
int cindex;
#if 0
printf("// ***\n");
#endif
for (cindex = 0; cindex < m->ncoords; cindex++) {
#if 0
printf("// %d.%d\n", (int)m->coords[cindex].index,
(int)m->coords[cindex].parent->coords[0].index);
#endif
coords[ncoords++] = (
m->coords[cindex].index + MT_MAXEXPAND *
m->coords[cindex].parent->coords[0].index);
}
unsigned prev = ncoords-1;
for (k = 0; k < ncoords; k++) {
map[coords[prev]] = coords[k];
prev = k;
}
}
printf("static const MetamapEntry metamap_%c[] = {\n",
HTPF[i]);
maplen = MT_MAXEXPAND * count234(t[1]->tiles);
for (j = 0; j < maplen; j++) {
printf(" /* %u, %u -> */ ",
(unsigned)(j % MT_MAXEXPAND),
(unsigned)(j / MT_MAXEXPAND));
if (map[j] == -1) {
printf("{-1,-1}, /* does not exist */\n");
} else {
printf("{%u, %u},",
(unsigned)(map[j] % MT_MAXEXPAND),
(unsigned)(map[j] / MT_MAXEXPAND));
if (map[j] == j)
printf(" /* no alternatives */");
printf("\n");
}
}
printf("};\n");
for (j = 0; j < 3; j++)
metatile_free_set(t[j]);
}
printf("static const MetamapEntry *const "
"metamap[] = {\n");
for (i = 0; i < 4; i++)
printf(" metamap_%c,\n", HTPF[i]);
printf("};\n");
}
return 0;
}
fprintf(stderr, "unknown test mode '%s'\n", argv[1]);
return 1;
}
#endif