--- /dev/null
+# set ARCH in your environment if you want to do a multible architecture
+# build. This means you Mac users.
+# ARCH= -arch x386 -arch x86_64
+
+LDFLAGS= $(ARCH) -lm
+CFLAGS= -Wall -Wno-parentheses $(ARCH) -I. -I..
+OBJS= hexmap.o
+SRC=$(OBJS:.o=.c)
+TESTS= t/cantor.t t/distance.t t/adjacency.t t/range.t t/hexbin.t \
+ t/gridsize.t t/center.t
+
+all: test libhexmap.a
+
+clean:
+ rm -f $(OBJS) libhexmap.a $(TESTS)
+
+t/%.t: t/%.o t/tap.o $(OBJS)
+ $(CC) -I.. -I. -o $@ $+ $(LDFLAGS)
+
+test: $(TESTS)
+ @prove --exec '' 2>/dev/null
+
+hextest: hextest.c libhexmap.a
+ $(CC) $(CFLAGS) -o $@ hextest.c -L. -lhexmap
+
+libhexmap.a: $(OBJS)
+ ar r $@ $+
+ ranlib $@
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+.SUFFIXES: .c .h
--- /dev/null
+#include "hexmap.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+static int inversecantor(int cantor, int *x, int *y);
+
+/*
+ * This file is written by Nathan Wagner and dedicated to the public
+ * domain
+ */
+
+double HL_vertexv[] = {
+ .577350269189625764509148780502, 0.0,
+ .288675134594812882254574390251, 0.5,
+ -.288675134594812882254574390251, 0.5,
+ -.577350269189625764509148780502, 0.0,
+ -.288675134594812882254574390251, -0.5,
+ .288675134594812882254574390251, -0.5};
+
+/* these all are for a hex one unit across */
+double hexptvd[6][2] = {
+ {.577350269189625764509148780502, 0.0}, /* 1.0/sqrt3 */
+ {.288675134594812882254574390251, 0.5}, /* 0.5/sqrt3 */
+ {-.288675134594812882254574390251, 0.5},
+ {-.577350269189625764509148780502, 0.0},
+ {-.288675134594812882254574390251, -0.5},
+ {.288675134594812882254574390251, -0.5}
+};
+
+/* TODO how is this related? to the above? */
+double texptvd[6][2] = {
+ {1.154700538379251529018297561004, 0.5}, /* 2.0/sqrt3 */
+ {.866025403784438646763723170753, 1.0}, /* 1.5/sqrt3 */
+ {.288675134594812882254574390251, 1.0},
+ {0.0, 0.5},
+ {.288675134594812882254574390251, 0.0},
+ {.866025403784438646763723170753, 0.0}
+};
+
+double hexpthd[6][2] = {
+ {0.0, .577350269189625764509148780502},
+ {0.5, .288675134594812882254574390251},
+ {0.5, -.288675134594812882254574390251},
+ {0.0, -.577350269189625764509148780502},
+ {-0.5, -.288675134594812882254574390251},
+ {-0.5, .288675134594812882254574390251}
+};
+
+void HL_vertices(int cantor, double *vc) {
+ int i;
+ double xc, yc;
+
+ HL_hexcenter(cantor, &xc, &yc);
+
+ for (i=0; i<6; i++) {
+ *vc++ = hexptvd[i][0] + xc;
+ *vc++ = hexptvd[i][1] + yc;
+ }
+ *vc++ = hexptvd[0][0] + xc;
+ *vc++ = hexptvd[0][1] + yc;
+}
+
+void HL_trianglefan(int cantor, double *vc) {
+ HL_hexcenter(cantor, vc, vc+1);
+ HL_vertices(cantor, vc+2);
+}
+
+double HL_center_x(int cantor) {
+ double x;
+
+ HL_hexcenter(cantor, &x, 0);
+ return x;
+}
+
+double HL_center_y(int cantor) {
+ double y;
+
+ HL_hexcenter(cantor, 0, &y);
+ return y;
+}
+
+int HL_hexcenter(int cantor, double *xc, double *yc) {
+ int x, y;
+ double stride = 1.5/sqrt(3.0);
+
+ inversecantor(cantor, &x, &y);
+
+ if (xc) *xc = x * stride;
+ if (yc && x >= 0) *yc = y + ((x + 1) % 2) / 2.0 - 0.5;
+ if (yc && x < 0) *yc = y + ((-x + 1) % 2) / 2.0 - 0.5;
+
+ return cantor;
+}
+
+/*
+ * This function assumes that the hexes are one unit across, and vertically
+ * oriented. If that is not the case, you will need to transform
+ * your input coordinates first.
+ */
+int HL_cantor_bin(double x, double y) {
+ int i, j;
+
+ HL_hexbin(1.0, x, y, &i, &j);
+ return HL_cantor_xy(i, j);
+}
+
+static int xy2ijk(int x, int y, int *i, int *j, int *k) {
+ int pi, pj, pk;
+
+ pi = x;
+ pj = -y;
+ if (x < 0) {
+ pj = pj + (-x + 1) / 2;
+ } else {
+ pj = pj - x/2;
+ }
+ pk = -pi - pj;
+
+ if (i) *i = pi;
+ if (j) *j = pj;
+ if (k) *k = pk;
+
+ return HL_cantor_xy(x,y);
+}
+
+static int ijk2xy(int i, int j, int k, int *x, int *y) {
+ int px, py;
+
+ px = i;
+
+ /* py = -j - i/2; */
+ py = -j;
+
+ if (i < 0) {
+ py += (-i + 1)/2;
+ } else {
+ py -= i/2;
+ }
+
+ if (x) *x = px;
+ if (y) *y = py;
+
+ return HL_cantor_xy(px,py);
+}
+
+int HL_cantor_ijk(int i, int j, int k) {
+ return ijk2xy(i,j,k,0,0);
+}
+
+int HL_distance(int from, int to) {
+ int dist = 0, i;;
+ int fc[3], tc[3];
+
+ HL_cantor_arrays(from, 0, fc);
+ HL_cantor_arrays(to, 0, tc);
+
+ for (i=0;i<=2;i++) {
+ dist += abs(fc[i] - tc[i]);
+ }
+
+ return dist / 2;
+}
+
+int HL_hexes_within_range(int hex, int range, int *list, int size) {
+ int count = 0;
+ int i;
+
+ if (range == 0) {
+ return HL_hexes_at_range(hex, 0, list, size);
+ }
+
+ for (i=1;i<=range;i++) {
+ count += HL_hexes_at_range(hex, i, count > size ? 0 : list+count, size-count);
+ }
+ return count;
+}
+
+int HL_hexes_at_range(int hex, int range, int *list, int size) {
+ int q; /* p and q are count/loop vars */
+ int c[3]; /* ijk coord array */
+
+ if (range == 0) {
+ if (list) {
+ list[0] = hex;
+ }
+ return 1;
+ } else if (range < 0) {
+ return 0;
+ }
+
+ /* TODO don't bother to collect if the list isn't big enough? */
+ /* i.e. if (!list || size < range * 6) */
+ if (!list || size < 1) return range * 6;
+
+ HL_cantor_arrays(hex, 0, c);
+ c[0] += range;
+ c[2] = -c[0] - c[1];
+ hex = HL_cantor_ijkp(c);
+
+ for(q=0; q<size && q < range * 6; q++) {
+ list[q] = hex;
+ hex = HL_adjhex(hex, q/range+2);
+ }
+
+ return range * 6;
+}
+
+/* direction 0 is positive X , counter clockwise from there */
+int HL_adjhex(int start, int dir) {
+ int c[3];
+
+ HL_cantor_arrays(start, 0, c);
+
+ switch (dir%6) {
+ case 2:
+ c[0]--; c[1]++; break;
+ case 1:
+ c[1]++; c[2]--; break;
+ case 0:
+ c[0]++; c[2]--; break;
+ case 5:
+ c[0]++; c[1]--; break;
+ case 4:
+ c[1]--; c[2]++; break;
+ case 3:
+ c[0]--; ; c[2]++; break;
+ }
+
+ return HL_cantor_ijkp(c);
+}
+
+/* returns last hex in found path */
+int HL_findpath(int start, int end, int *path, int size) {
+ return 0;
+}
+
+int HL_cantor_xyp(int *xy) {
+ return HL_cantor_xy(xy[0], xy[1]);
+}
+
+int HL_cantor_ijkp(int *ijk) {
+ return HL_cantor_ijk(ijk[0], ijk[1], ijk[2]);
+}
+
+int HL_cantor_arrays(int can, int *xy, int *ijk) {
+ return HL_cantor_decode(can, xy, xy ? xy+1 : 0,
+ ijk, ijk ? ijk+1 : 0, ijk ? ijk+2 : 0);
+}
+
+int HL_cantor_decode(int can, int *x, int *y, int *i, int *j, int *k) {
+ int px, py;
+
+ inversecantor(can, &px, &py);
+ if (x) *x = px;
+ if (y) *y = py;
+
+ xy2ijk(px, py, i, j, k);
+
+ return can;
+}
+
+int HL_cantor_i(int cantor) {
+ int i;
+
+ HL_cantor_decode(cantor, 0,0, &i,0,0);
+ return i;
+}
+
+int HL_cantor_j(int cantor) {
+ int j;
+
+ HL_cantor_decode(cantor, 0,0, 0,&j,0);
+ return j;
+}
+
+int HL_cantor_k(int cantor) {
+ int k;
+
+ HL_cantor_decode(cantor, 0,0, 0,0,&k);
+ return k;
+}
+
+int HL_cantor_x(int cantor) {
+ int x;
+ inversecantor(cantor, &x, 0);
+ return x;
+}
+
+int HL_cantor_y(int cantor) {
+ int y;
+ inversecantor(cantor, 0, &y);
+ return y;
+}
+
+/* Determine if a map with these dimensions will overflow */
+int HL_map_bounds_ok(int xdim, int ydim) {
+
+ /* return (x+y) * (x + y + 1) / 2 + y+1; */
+
+ if (INT_MAX - xdim - 1 < ydim) return 0;
+ if (INT_MAX / (xdim+ydim) < (xdim+ydim+1)) return 0;
+ if ( (xdim+ydim) * (xdim+ydim+1) / 2 > INT_MAX - ydim - 1)
+ return 0;
+
+ return 1;
+}
+
+int HL_map_max_dimension(void) {
+ int low, high, try;
+
+ low = 1; high = INT_MAX/2;
+
+ while (low != high - 1) {
+ try = (low + high) / 2;
+ if (HL_map_bounds_ok(try,try)) {
+ low = try;
+ } else {
+ high = try;
+ }
+ }
+
+ return low;
+}
+
+#ifdef ASTAR
+
+struct astar_hex {
+ int hex;
+ int f, g, h;
+ int parent;
+};
+
+struct astar {
+ int open, closed; /* how many in open or closed */
+ int closedroom, openroom; /* how much is malloced */
+ struct astar_hex *openlist;
+ struct astar_hex *closedlist;
+ int known, index;
+ int error;
+};
+
+static void astar_init(struct astar *state) {
+ state->open = 0;
+ state->closed = 0;
+ state->closedroom = 0;
+ state->openroom = 0;
+ state->openlist = 0;
+ state->closedlist = 0;
+ state->error = 0;
+ state->known = 0;
+ state->index = 0;
+
+ return;
+}
+
+static void removeopen(struct astar *s, int hex) {
+ int index;
+
+ if (s->error) return;
+ if (s->open == 0) return;
+
+ if (s->known != hex) {
+ for (index = 0; index < s->open; index++) {
+ if (s->openlist[index].hex == hex) break;
+ }
+ } else {
+ index = s->index;
+ s->known = 0; s->index = 0;
+ }
+ s->openlist[index] = s->openlist[open];
+ s->open--;
+}
+
+static void addopen(struct astar *s, int hex) {
+ struct astar_hex *mem;
+ if (s->error) return;
+
+ if (s->open + 1 > s->openroom) {
+ mem = realloc(s->openlist, s->openroom * 2 * sizeof *mem);
+ if (mem == NULL) {
+ s->error = errno;
+ return;
+ }
+ state->openlist = mem;
+ s->openroom = s->openroom * 2;
+ }
+
+ s->openlist[s->open].hex = hex;
+ s->openlist[s->open].f = 0;
+ s->openlist[s->open].g = 0;
+ s->openlist[s->open].h = 0;
+ s->known = hex;
+ s->index = s->open;
+ s->open = s->open + 1;
+}
+
+int lowrank(struct astar *s) {
+ int f;
+ int hex;
+ int i;
+
+ if (!s || s->open = 0) return 0;
+
+ hex = s->openlist[0].hex;
+ f = s->openlist[0].f;
+
+ for(i=1; i < s->open; i++) {
+ if (s->openlist[i].f < f) {
+ hex = s->openlist[i].hex;
+ f = s->openlist[i].f;
+ }
+ /* TODO check s->openlist[i].tiebreak */
+ }
+
+ return hex;
+}
+
+static int default_metric(int a, int b) {
+ return 1;
+}
+
+/*
+ * find a path from start to end. we use A*
+ */
+int HL_findpath(int start, int end, int psize, int *path,
+ double (*metric)(int,int)) {
+ int neighbors[6];
+ int count;
+ int cur = 0;
+ struct astar state;
+ int neighbor;
+
+ if (start == end) return 0;
+ if (!metric) metric = default_metric;
+
+ astar_init(&state);
+ addopen(&state, start);
+
+ while (1) {
+ cur = lowrank(&state);
+ if (cur == end) {
+ break;
+ }
+ addclosed(&state, cur);
+ count = HL_hexes_at_range(cur->hex, 1, neighbors, 6);
+ for (i=0;i<count;i++) {
+ neighbor = neighbors[i];
+ cost = g(&state, current) + metric(current, neighbor);
+ if (inopen(&state, neighbor) && cost < g(&state, neighbor)) {
+ removeopen(&state, neighbor);
+ } else
+ if (inclosed(&state, neighbor) && cost < g(&state, neighbor)) {
+ /* should never happen with
+ * admissible heuristic
+ */
+ removeclosed(&state, neighbor);
+ } else
+ if (!inopen(&state, neighbor) && !inclosed(&state, neighbor)) {
+ addopen(&state, neighbor);
+ setg(&state, neighbor, cost);
+ setrank(&state, neighbor, cost + h(neighbor,end));
+ setparent(&state, neighbor, current);
+ } else {
+ abort();
+ }
+ }
+ }
+
+ count = 0;
+ while (parent(&state, cur) != start) {
+ count++;
+ cur = parent(&state, cur);
+ }
+ cur = end;
+ while (parent(&state, cur) != start) {
+ path[count--] = cur;
+ cur = parent(&state, cur);
+ }
+
+}
+
+#endif
+
+static int inversenatcantor(int cantor, int *x, int *y) {
+ int w, t, py;
+
+ cantor -= 1;
+
+ w = (int)floor((sqrt(8.0 * cantor + 1.0) - 1.0)/2.0);
+ t = (w * w + w)/2;
+
+ py = cantor - t;
+
+ if (y) *y = py;
+ if (x) *x = w - py;
+
+ return cantor;
+}
+
+/*
+ * map non negative integer pairs to their cantor pairing function
+ * number, plus one. We add one so that the result is never zero,
+ * leaving zero to be "invalid" or "none" or what have you.
+ */
+
+static int natcantor(int x, int y) {
+ return (x+y) * (x + y + 1) / 2 + y+1;
+}
+
+/* See http://en.wikipedia.org/wiki/Cantor_pairing_function */
+/* see also http://szudzik.com/ElegantPairing.pdf */
+/*
+ * if either coordinate is negative, map the integers onto the
+ * whole numbers, and then return the negative of the adjusted
+ * cantor number. As for most grids negative coordinates will
+ * be invalid, this will allow for a <= 0 test for invalid
+ * or out of bounds (on the negative side anyway, you'll
+ * still have to test for out of range on the positive side).
+ *
+ * TODO figure out what the maximum supported coordinates are
+ * for given integer sizes.
+ */
+int HL_cantor_xy(int x, int y) {
+ if (x < 0 || y < 0) {
+ x = abs(2 * x) - (x < 0);
+ y = abs(2 * y) - (y < 0);
+ return -natcantor(x, y);
+ }
+ return natcantor(x,y);
+}
+
+static int inversecantor(int cantor, int *x, int *y) {
+ if (cantor < 0) {
+ inversenatcantor(-cantor, x, y);
+ if (x) {
+ if (*x % 2) {
+ *x = -(*x+1)/2;
+ } else {
+ *x = *x / 2;
+ }
+ }
+ if (y) {
+ if (*y % 2) {
+ *y = -(*y+1)/2;
+ } else {
+ *y = *y/2;
+ }
+ }
+ } else {
+ inversenatcantor(cantor, x, y);
+ }
+
+ return cantor;
+}
+
+struct hex {
+ int iso;
+ int x, y, z;
+};
+
+/* y *must* be positive down as the xy /iso conversion assumes this */
+static int hex_xy(struct hex *h) {
+ if (!h->iso) return 1;
+ if (h->x >= 0) {
+ h->y = -h->y - (h->x+1)/2;
+ } else {
+ /* need to round toward -inf, not toward zero, so x-1 */
+ h->y = -h->y - h->x/2;
+ }
+ h->iso = 0;
+
+ return 1;
+}
+
+static int hex_iso(struct hex *h) {
+ if (h->iso) return 1;
+
+ if (h->x >= 0) {
+ h->y = (-h->y - (h->x+1)/2);
+ } else {
+ /* need to round toward -inf, not toward zero, so x-1 */
+ h->y = (-h->y - (h->x)/2);
+ }
+
+ h->z = -h->x - h->y;
+ h->iso = 1;
+ return 1;
+}
+
+int HL_hexbin(double width, double x, double y, int *i, int *j) {
+ double z, rx, ry, rz;
+ double abs_dx, abs_dy, abs_dz;
+ int ix, iy, iz, s;
+ struct hex h;
+
+ /* TODO just hard-code this cosine */
+ x = x / cos(30 * M_PI / 180.0); /* rotated X coord */
+ y = y - x / 2.0; /* adjustment for rotated X */
+
+ /* adjust for actual hexwidth */
+ x /= width;
+ y /= width;
+
+ z = -x - y;
+
+ ix = rx = floor(x + 0.5);
+ iy = ry = floor(y + 0.5);
+ iz = rz = floor(z + 0.5);
+
+ s = ix + iy + iz;
+
+ if (s) {
+ abs_dx = fabs(rx - x);
+ abs_dy = fabs(ry - y);
+ abs_dz = fabs(rz - z);
+
+ if (abs_dx >= abs_dy && abs_dx >= abs_dz) {
+ ix -= s;
+ } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) {
+ iy -= s;
+ } else {
+ iz -= s;
+ }
+ }
+ h.x = ix;
+ h.y = iy;
+ h.z = iz;
+ h.iso = 1;
+
+ hex_xy(&h);
+ if (i) *i = h.x;
+ if (j) *j = h.y;
+ return HL_cantor_xy(h.x, h.y);
+}
--- /dev/null
+#ifndef HEXLIB_H_
+#define HEXLIB_H_ 1
+
+/*
+ * this library implements a generic library for hexgrids for games.
+ * it avoids display issues, other than some general provisions
+ * for data
+ *
+ * This file is written by Nathan Wagner and dedicated to the
+ * public domain.
+ */
+
+/* max size for cantor numbers is 16383, or 32767 if non-negative,
+ * assuming a 32 bit int
+ */
+
+/* map from cartesian coordinates to hex coords */
+/* cartesian coordinates are positive Y up, hex coords
+ * are positive Y down. You may need to pass in the
+ * negative of your Y coordinate
+ *
+ * width is the distance from one hexside to the opposite
+ * hexside. i and j are the cartesian coordinates of the hex
+ * the x,y point falls in. If a point happens to fall exactly
+ * on a hex boundary, the result will depend on how the machine
+ * does the floating point math, but should be consistent.
+ */
+int HL_hexbin(double width, double x, double y, int *i, int *j);
+
+/* generate the cantor mapping of a hexes x,y coordinate.
+ * See http://en.wikipedia.org/wiki/Cantor_pairing_function.
+ * We modify the basic formula to support negative coordinates
+ *
+ * The cantor mapping is *not* a contiguous mapping, as it maps
+ * onto a triangle, so you can't use the cantor mapping as
+ * an index into an array. Or, you can, but the array
+ * will be (I think) twice as large as it needs to be
+ *
+ * See also http://mathworld.wolfram.com/PairingFunction.html
+ * for a reference to a square mapping by Stein that doesn't
+ * have this problem, at least for square maps.
+ *
+ * We use the cantor mapping so that the generated integer
+ * is independent of the size of the grid.
+ */
+
+/* Cartesian functions */
+int HL_cantor_xy(int x, int y);
+int HL_cantor_xyp(int *xy);
+int HL_cantor_x(int cantor);
+int HL_cantor_y(int cantor);
+
+/* Isometric functions */
+int HL_cantor_ijk(int i, int j, int k);
+int HL_cantor_ijkp(int *ijk);
+int HL_cantor_i(int cantor);
+int HL_cantor_k(int cantor);
+int HL_cantor_j(int cantor);
+
+int HL_cantor_bin(double x, double y);
+
+/* A general decode function */
+int HL_cantor_decode(int can, int *x, int *y, int *i, int *j, int *k);
+int HL_cantor_arrays(int can, int *xy, int *ijk);
+
+/*
+ * layout assistance functions
+ *
+ * HL_center_x(int cantor) returns the cartesian X coordinate
+ * of a given hex, assuming a width of 1.0. Scaling can
+ * be done by the caller.
+ *
+ * Similarly for HL_center_y() and HL_hexcenter()
+ */
+double HL_center_x(int cantor);
+double HL_center_y(int cantor);
+int HL_hexcenter(int cantor, double *x, double *y);
+
+/* how far is it on the hex grid */
+int HL_distance(int from_cantor, int to_cantor);
+
+int HL_adjhex(int start, int dir);
+int HL_hexes_at_range(int hex, int range, int *list, int size);
+int HL_hexes_within_range(int hex, int range, int *list, int size);
+
+typedef double (*HL_costfunc)(int start, int finish);
+
+/* writes up to len cantors to path. returns last
+ * value written
+ * perhaps it should return the last value not written, if the
+ * path is incomplete?
+ * either way, it can then be used in a loop
+ */
+int HL_path(int start, int end, int *path, int len);
+
+int HL_map_bounds_ok(int xdim, int ydim);
+int HL_map_max_dimension(void);
+
+/*
+ * Plotting support
+ */
+void HL_trianglefan(int cantor, double *);
+
+struct HL_hex {
+ int x,y; /* hmm. would be nice to be able to just point to a generic */
+};
+
+#endif
--- /dev/null
+#include "hexmap.h"
+
+#include "tap.h"
+
+int main(void) {
+ int hex, start, dist;
+ int x, y, i;
+
+ plan_tests(294);
+
+ for (x = -3 ; x <= 3; x++) {
+ for (y = -3 ; y <= 3; y++) {
+ start = HL_cantor_xy(x, y);
+ for (i = 0; i < 6; i++) {
+ hex = HL_adjhex(start, i);
+ dist = HL_distance(hex, start);
+ ok(dist == 1,
+ "%d, %d direction %d distance one",
+ x, y, i);
+ }
+ }
+ }
+
+ return exit_status();
+}
--- /dev/null
+#include <stdio.h>
+
+#include "hexmap.h"
+
+#include "tap.h"
+
+int main(void) {
+ int xy[2];
+ int ijk[3];
+ int hex;
+ int x, y;
+
+ plan_tests(81*2);
+
+ for (x=-4;x<=4;x++) {
+ for (y=-4;y<=4;y++) {
+ hex = HL_cantor_xy(x,y);
+ HL_cantor_arrays(hex, xy, ijk);
+ ok(x == xy[0], "x check %d %d", x, y);
+ ok(y == xy[1], "y check %d %d", x, y);
+ }
+ }
+
+ return exit_status();
+}
--- /dev/null
+#include <math.h>
+
+#include "hexmap.h"
+
+#include "tap.h"
+
+int main(void) {
+ int start, x, y;
+
+ plan_tests(6);
+
+ x = 0; y = 0;
+ start = HL_cantor_xy(x,y);
+ ok(HL_center_x(start) == x * 1.5 / sqrt(3.0), "%d %d center X", x, y);
+ ok(HL_center_y(start) == 0.0, "%d %d center Y", x, y);
+
+ x = 1; y = 1;
+ start = HL_cantor_xy(x,y);
+ ok(HL_center_x(start) == x * 1.5 / sqrt(3.0), "%d %d center X", x, y);
+ ok(HL_center_y(start) == 0.5, "%d %d center Y = %f", x, y,
+ HL_center_y(start));
+
+ x = 2; y = 2;
+ start = HL_cantor_xy(x,y);
+ ok(HL_center_x(start) == x * 1.5 / sqrt(3.0), "%d %d center X", x, y);
+ ok(HL_center_y(start) == 2.0, "%d %d center Y = %f", x, y,
+ HL_center_y(start));
+
+ return exit_status();
+}
--- /dev/null
+#include "hexmap.h"
+
+#include "tap.h"
+
+void dcheck(int x1, int y1, int x2, int y2, int expect) {
+ int from, to, dist;
+ from = HL_cantor_xy(x1,y1);
+ to = HL_cantor_xy(x2,y2);
+ dist = HL_distance(from,to);
+ ok(dist == expect,
+ "distance from (%02d, %02d) to (%02d, %02d) = %d (expect %d)\n",
+ x1, y1, x2, y2, dist, expect);
+}
+
+int main(void) {
+ plan_tests(5);
+
+ dcheck(1,1,2,1,1);
+ dcheck(1,1,2,2,2);
+ dcheck(2,2,2,1,1);
+ dcheck(1,1,2,3,3);
+ dcheck(3,3,3,3,0);
+
+ return exit_status();
+}
--- /dev/null
+#include "hexmap.h"
+
+#include "tap.h"
+
+int searchbound(int low, int high) {
+ int try;
+
+ while (low != high - 1) {
+ try = (low + high) / 2;
+ if (HL_map_bounds_ok(try,try)) {
+ low = try;
+ } else {
+ high = try;
+ }
+ }
+ return low;
+}
+
+int main(void) {
+ int start;
+ int x;
+
+ plan_tests(2);
+
+ x = 1;
+ do {
+ x = x * 2;
+ } while (HL_map_bounds_ok(x,x));
+ x = searchbound(x/2, x);
+ ok(x == HL_map_max_dimension(), "checking HL_map_max_dimension()");
+
+ start = HL_cantor_xy(x,x);
+ ok(start > 0, "maximum cantor size is greater than zero");
+
+ return 0;
+}
--- /dev/null
+#include "hexmap.h"
+
+#include "tap.h"
+
+int main(void) {
+ int x, y;
+
+ plan_tests(6);
+
+ HL_hexbin(1.0, 0.444194, -4.639363, &x, &y);
+ ok(x == 1 && y == 4, "hexbin 0.444194, 4.639363 = 1, 4, %d %d", x, y);
+
+ HL_hexbin(1.0, 0.0, 0.0, &x, &y);
+ ok(x == 0 && y == 0, "0.0 0.0 -> 0 0");
+
+ HL_hexbin(0.1111111, 0.288675, 0.500000, &x, &y);
+ ok(x == 3, "center bin X %d == 3", x);
+ ok(y == -5, "center bin Y %d == -5", y);
+
+ HL_hexbin(0.1111111, 0.866025, 0.500000, &x, &y);
+ ok(x == 9, "vertex bin X %d == 9", x);
+ ok(y == -5, "vertex bin Y %d == -5", y);
+
+ return exit_status();
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+
+#include "hexmap.h"
+
+#include "tap.h"
+
+int main(void) {
+ int xy[2];
+ int ijk[3];
+ int hex, start, dist;
+ int x, y, i, count;
+ int range[32];
+
+ printf("checking within function\n");
+ start = HL_cantor_xy(3,3);
+ count = HL_hexes_within_range(start, 2, range, 0);
+ printf("count = %d\n", count);
+ assert(count == 18);
+ count = HL_hexes_within_range(start, 2, range, count);
+ for (i = 0; i < count; i++) {
+ dumphex(range[i]);
+ }
+
+ printf("checking hex center\n");
+ start = HL_cantor_xy(0,0);
+ dumphex(start);
+ printf("center = %f, %f\n", HL_center_x(start), HL_center_y(start));
+ start = HL_cantor_xy(1,1);
+ dumphex(start);
+ printf("center = %f, %f\n", HL_center_x(start), HL_center_y(start));
+ start = HL_cantor_xy(-1,1);
+ dumphex(start);
+ printf("center = %f, %f\n", HL_center_x(start), HL_center_y(start));
+ start = HL_cantor_xy(2,1);
+ dumphex(start);
+ printf("center = %f, %f\n", HL_center_x(start), HL_center_y(start));
+ start = HL_cantor_xy(2,5);
+ dumphex(start);
+ printf("center = %f, %f\n", HL_center_x(start), HL_center_y(start));
+ start = HL_cantor_xy(-2,5);
+ dumphex(start);
+ printf("center = %f, %f\n", HL_center_x(start), HL_center_y(start));
+
+ printf("checking hexbin -0.444194, 4.639363\n");
+ start = HL_hexbin(0, -0.444194, 4.639363);
+ dumphex(start);
+
+ printf("checking maximum grid size\n");
+ x = 1;
+ do {
+ x = x * 2;
+ printf("checking %d...\n", x);
+ } while (HL_map_bounds_ok(x,x));
+ x = searchbound(x/2, x);
+ printf("maximum size = %d\n", x);
+ assert(x = HL_map_max_dimension());
+
+ start = HL_cantor_xy(x,x);
+ assert(start>0);
+ dumphex(start);
+
+ printf("trying max+1 = HL_cantor_xy(%d,%d)\n", x+1, x+1);
+ printf("the dump should be bogus\n");
+ start = HL_cantor_xy(x+1,x+1);
+ assert(start<0);
+ dumphex(start);
+
+ return 0;
+}
+
+int searchbound(int low, int high) {
+ int try;
+
+ while (low != high - 1) {
+ try = (low + high) / 2;
+ printf("searching, low = %d, high = %d, trying %d\n", low, high, try);
+ if (HL_map_bounds_ok(try,try)) {
+ low = try;
+ } else {
+ high = try;
+ }
+ }
+ return low;
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+
+#include "hexmap.h"
+
+#include "tap.h"
+
+int icmp(const void *ap, const void *bp) {
+ int a;
+ int b;
+
+ a = *((int *)(ap));
+ b = *((int *)(bp));
+
+ return a-b;
+}
+
+void sort(int *r, int ct) {
+ qsort(r, ct, sizeof *r, icmp);
+}
+
+int main(void) {
+ int start;
+ int i, count;
+ int range[32];
+ int testrange[32];
+
+ plan_tests(7+4);
+
+ printf("range = 1 from 3,3\n");
+ start = HL_cantor_xy(3,3);
+ count = HL_hexes_at_range(start, 1, range, 0);
+ ok(count == 6, "6 hexes at range 1");
+ count = HL_hexes_at_range(start, 1, range, count);
+ sort(range, 6);
+ testrange[0] = HL_cantor_xy(3,2);
+ testrange[1] = HL_cantor_xy(3,4);
+ testrange[2] = HL_cantor_xy(4,3);
+ testrange[3] = HL_cantor_xy(4,2);
+ testrange[4] = HL_cantor_xy(2,3);
+ testrange[5] = HL_cantor_xy(2,2);
+ sort(testrange, 6);
+ for (i = 0; i < count; i++) {
+ ok(range[i] == testrange[i], "3, 3 range 1 hex %d", testrange[i]);
+ }
+
+ start = HL_cantor_xy(0,3);
+ count = HL_hexes_at_range(start, 1, range, 0);
+ ok(count == 6, "6 hexes at range1 from 0,3");
+
+ start = HL_cantor_xy(-1,5);
+ count = HL_hexes_at_range(start, 1, range, 0);
+ ok(count == 6, "6 hexes at range1 from -1,5");
+
+ start = HL_cantor_xy(-2,3);
+ count = HL_hexes_at_range(start, 1, range, 0);
+ ok(count == 6, "6 hexes at range1 from -2,3");
+
+ start = HL_cantor_xy(3,3);
+ count = HL_hexes_at_range(start, 2, range, 0);
+ ok(count == 12, "6 hexes at range1 from 3,3");
+
+ return exit_status();
+}
--- /dev/null
+.Dd December 20, 2004
+.Os
+.Dt TAP 3
+.Sh NAME
+.Nm tap
+.Nd write tests that implement the Test Anything Protocol
+.Sh SYNOPSIS
+.In tap.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides functions for writing test scripts that produce output
+consistent with the Test Anything Protocol. A test harness that parses
+this protocol can run these tests and produce useful reports indicating
+their success or failure.
+.Ss PRINTF STRINGS
+In the descriptions that follow, for any function that takes as the
+last two parameters
+.Dq Fa char * , Fa ...
+it can be assumed that the
+.Fa char *
+is a
+.Fn printf
+-like format string, and the optional arguments are values to be placed
+in that string.
+.Ss TEST PLANS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn plan_tests "unsigned int"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_no_plan "void"
+.Xc
+.It Xo
+.Ft int
+.Fn plan_skip_all "char *" "..."
+.Xc
+.El
+.Pp
+You must first specify a test plan. This indicates how many tests you
+intend to run, and allows the test harness to notice if any tests were
+missed, or if the test program exited prematurely.
+.Pp
+To do this, use
+.Fn plan_tests ,
+which always returns 0. The function will cause your program to exit
+prematurely if you specify 0 tests.
+.Pp
+In some situations you may not know how many tests you will be running, or
+you are developing your test program, and do not want to update the
+.Fn plan_tests
+parameter every time you make a change. For those situations use
+.Fn plan_no_plan .
+It returns 0, and indicates to the test harness that an indeterminate number
+of tests will be run.
+.Pp
+Both
+.Fn plan_tests
+and
+.Fn plan_no_plan
+will cause your test program to exit prematurely with a diagnostic
+message if they are called more than once.
+.Pp
+If your test program detects at run time that some required functionality
+is missing (for example, it relies on a database connection which is not
+present, or a particular configuration option that has not been included
+in the running kernel) use
+.Fn plan_skip_all ,
+passing as parameters a string to display indicating the reason for skipping
+the tests.
+.Ss SIMPLE TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft unsigned int
+.Fn ok "expression" "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn ok1 "expression"
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn pass "char *" "..."
+.Xc
+.It Xo
+.Ft unsigned int
+.Fn fail "char *" "..."
+.Xc
+.El
+.Pp
+Tests are implemented as expressions checked by calls to the
+.Fn ok
+and
+.Fn ok1
+macros. In both cases
+.Fa expression
+should evaluate to true if the test succeeded.
+.Pp
+.Fn ok
+allows you to specify a name, or comment, describing the test which will
+be included in the output.
+.Fn ok1
+is for those times when the expression to be tested is self
+explanatory and does not need an associated comment. In those cases
+the test expression becomes the comment.
+.Pp
+These four calls are equivalent:
+.Bd -literal -offset indent
+int i = 5;
+
+ok(i == 5, "i equals 5"); /* Overly verbose */
+ok(i == 5, "i equals %d", i); /* Just to demonstrate printf-like
+ behaviour of the test name */
+ok(i == 5, "i == 5"); /* Needless repetition */
+ok1(i == 5); /* Just right */
+.Ed
+.Pp
+It is good practice to ensure that the test name describes the meaning
+behind the test rather than what you are testing. Viz
+.Bd -literal -offset indent
+ok(db != NULL, "db is not NULL"); /* Not bad, but */
+ok(db != NULL, "Database conn. succeeded"); /* this is better */
+.Ed
+.Pp
+.Fn ok
+and
+.Fn ok1
+return 1 if the expression evaluated to true, and 0 if it evaluated to
+false. This lets you chain calls from
+.Fn ok
+to
+.Fn diag
+to only produce diagnostic output if the test failed. For example, this
+code will include diagnostic information about why the database connection
+failed, but only if the test failed.
+.Bd -literal -offset indent
+ok(db != NULL, "Database conn. succeeded") ||
+ diag("Database error code: %d", dberrno);
+.Ed
+.Pp
+You also have
+.Fn pass
+and
+.Fn fail .
+From the Test::More documentation:
+.Bd -literal -offset indent
+Sometimes you just want to say that the tests have passed.
+Usually the case is you've got some complicated condition
+that is difficult to wedge into an ok(). In this case,
+you can simply use pass() (to declare the test ok) or fail
+(for not ok).
+
+Use these very, very, very sparingly.
+.Ed
+.Pp
+These are synonyms for ok(1, ...) and ok(0, ...).
+.Ss SKIPPING TESTS
+.Bl -tag -width indent
+.It Xo
+.Ft int
+.Fn skip "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Fn skip_start "expression" "unsigned int" "char *" "..."
+.Xc
+.It Xo
+.Sy skip_end
+.Xc
+.El
+.Pp
+Sets of tests can be skipped. Ordinarily you would do this because
+the test can't be run in this particular testing environment.
+.Pp
+For example, suppose some tests should be run as root. If the test is
+not being run as root then the tests should be skipped. In this
+implementation, skipped tests are flagged as being ok, with a special
+message indicating that they were skipped. It is your responsibility
+to ensure that the number of tests skipped (the first parameter to
+.Fn skip )
+is correct for the number of tests to skip.
+.Pp
+One way of implementing this is with a
+.Dq do { } while(0);
+loop, or an
+.Dq if( ) { } else { }
+construct, to ensure that there are no additional side effects from the
+skipped tests.
+.Bd -literal -offset indent
+if(getuid() != 0) {
+ skip(1, "because test only works as root");
+} else {
+ ok(do_something_as_root() == 0, "Did something as root");
+}
+.Ed
+.Pp
+Two macros are provided to assist with this. The previous example could
+be re-written as follows.
+.Bd -literal -offset indent
+skip_start(getuid() != 0, 1, "because test only works as root");
+
+ok(do_something_as_root() == 0, "Did something as root");
+
+skip_end; /* It's a macro, no parentheses */
+.Ed
+.Ss MARKING TESTS AS Dq TODO
+.Bl -tag -width indent
+.It Xo
+.Ft void
+.Fn todo_start "char *" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn todo_end "void"
+.Xc
+.El
+.Pp
+Sets of tests can be flagged as being
+.Dq TODO .
+These are tests that you expect to fail, probably because you haven't
+fixed a bug, or finished a new feature yet. These tests will still be
+run, but with additional output that indicates that they are expected
+to fail. Should a test start to succeed unexpectedly, tools like
+.Xr prove 1
+will indicate this, and you can move the test out of the todo
+block. This is much more useful than simply commenting out (or
+.Dq #ifdef 0 ... #endif )
+the tests.
+.Bd -literal -offset indent
+todo_start("dwim() not returning true yet");
+
+ok(dwim(), "Did what the user wanted");
+
+todo_end();
+.Ed
+.Pp
+Should
+.Fn dwim
+ever start succeeding you will know about it as soon as you run the
+tests. Note that
+.Em unlike
+the
+.Fn skip_*
+family, additional code between
+.Fn todo_start
+and
+.Fn todo_end
+.Em is
+executed.
+.Ss SKIP vs. TODO
+From the Test::More documentation;
+.Bd -literal -offset indent
+If it's something the user might not be able to do, use SKIP.
+This includes optional modules that aren't installed, running
+under an OS that doesn't have some feature (like fork() or
+symlinks), or maybe you need an Internet connection and one
+isn't available.
+
+If it's something the programmer hasn't done yet, use TODO.
+This is for any code you haven't written yet, or bugs you have
+yet to fix, but want to put tests in your testing script
+(always a good idea).
+.Ed
+.Ss DIAGNOSTIC OUTPUT
+.Bl -tag -width indent
+.It Xo
+.Fr unsigned int
+.Fn diag "char *" "..."
+.Xc
+.El
+.Pp
+If your tests need to produce diagnostic output, use
+.Fn diag .
+It ensures that the output will not be considered by the TAP test harness.
+.Fn diag
+adds the necessary trailing
+.Dq \en
+for you.
+.Bd -literal -offset indent
+diag("Expected return code 0, got return code %d", rcode);
+.Ed
+.Pp
+.Fn diag
+always returns 0.
+.Ss EXIT STATUS
+.Bl -tag -width indent
+.It Xo
+.Fr int
+.Fn exit_status void
+.Xc
+.El
+.Pp
+For maximum compatability your test program should return a particular
+exit code. This is calculated by
+.Fn exit_status
+so it is sufficient to always return from
+.Fn main
+with either
+.Dq return exit_status();
+or
+.Dq exit(exit_status());
+as appropriate.
+.Sh EXAMPLES
+The
+.Pa tests
+directory in the source distribution contains numerous tests of
+.Nm
+functionality, written using
+.Nm .
+Examine them for examples of how to construct test suites.
+.Sh COMPATABILITY
+.Nm
+strives to be compatible with the Perl Test::More and Test::Harness
+modules. The test suite verifies that
+.Nm
+is bug-for-bug compatible with their behaviour. This is why some
+functions which would more naturally return nothing return constant
+values.
+.Pp
+If the
+.Lb libpthread
+is found at compile time,
+.Nm
+.Em should
+be thread safe. Indications to the contrary (and test cases that expose
+incorrect behaviour) are very welcome.
+.Sh SEE ALSO
+.Xr Test::More 1 ,
+.Xr Test::Harness 1 ,
+.Xr prove 1
+.Sh STANDARDS
+.Nm
+requires a
+.St -isoC-99
+compiler. Some of the
+.Nm
+functionality is implemented as variadic macros, and that functionality
+was not formally codified until C99. Patches to use
+.Nm
+with earlier compilers that have their own implementation of variadic
+macros will be gratefully received.
+.Sh HISTORY
+.Nm
+was written to help improve the quality and coverage of the FreeBSD
+regression test suite, and released in the hope that others find it
+a useful tool to help improve the quality of their code.
+.Sh AUTHORS
+.An "Nik Clayton" Aq nik@ngo.org.uk ,
+.Aq nik@FreeBSD.org
+.Pp
+.Nm
+would not exist without the efforts of
+.An "Michael G Schwern" Aq schqern@pobox.com ,
+.An "Andy Lester" Aq andy@petdance.com ,
+and the countless others who have worked on the Perl QA programme.
+.Sh BUGS
+Ideally, running the tests would have no side effects on the behaviour
+of the application you are testing. However, it is not always possible
+to avoid them. The following side effects of using
+.Nm
+are known.
+.Bl -bullet -offset indent
+.It
+stdout is set to unbuffered mode after calling any of the
+.Fn plan_*
+functions.
+.El
--- /dev/null
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+
+/* Encapsulate the pthread code in a conditional. In the absence of
+ libpthread the code does nothing */
+#ifdef HAVE_LIBPTHREAD
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M);
+# define UNLOCK pthread_mutex_unlock(&M);
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void _expected_tests(unsigned int);
+static void _tap_init(void);
+static void _cleanup(void);
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, char *file, unsigned int line,
+ char *test_name, ...)
+{
+ va_list ap;
+ char *local_test_name = NULL;
+ char *c;
+ int name_is_digits;
+
+ LOCK;
+
+ test_count++;
+
+ /* Start by taking the test name and performing any printf()
+ expansions on it */
+ if(test_name != NULL) {
+ va_start(ap, test_name);
+ vasprintf(&local_test_name, test_name, ap);
+ va_end(ap);
+
+ /* Make sure the test name contains more than digits
+ and spaces. Emit an error message and exit if it
+ does */
+ if(local_test_name) {
+ name_is_digits = 1;
+ for(c = local_test_name; *c != '\0'; c++) {
+ if(!isdigit(*c) && !isspace(*c)) {
+ name_is_digits = 0;
+ break;
+ }
+ }
+
+ if(name_is_digits) {
+ diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name);
+ diag(" Very confusing.");
+ }
+ }
+ }
+
+ if(!ok) {
+ printf("not ");
+ failures++;
+ }
+
+ printf("ok %d", test_count);
+
+ if(test_name != NULL) {
+ printf(" - ");
+
+ /* Print the test name, escaping any '#' characters it
+ might contain */
+ if(local_test_name != NULL) {
+ flockfile(stdout);
+ for(c = local_test_name; *c != '\0'; c++) {
+ if(*c == '#')
+ fputc('\\', stdout);
+ fputc((int)*c, stdout);
+ }
+ funlockfile(stdout);
+ } else { /* vasprintf() failed, use a fixed message */
+ printf("%s", todo_msg_fixed);
+ }
+ }
+
+ /* If we're in a todo_start() block then flag the test as being
+ TODO. todo_msg should contain the message to print at this
+ point. If it's NULL then asprintf() failed, and we should
+ use the fixed message.
+
+ This is not counted as a failure, so decrement the counter if
+ the test failed. */
+ if(todo) {
+ printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+ if(!ok)
+ failures--;
+ }
+
+ printf("\n");
+
+ if(!ok)
+ diag(" Failed %stest (%s:%s() at line %d)",
+ todo ? "(TODO) " : "", file, func, line);
+
+ free(local_test_name);
+
+ UNLOCK;
+
+ /* We only care (when testing) that ok is positive, but here we
+ specifically only want to return 1 or 0 */
+ return ok ? 1 : 0;
+}
+
+/*
+ * Initialise the TAP library. Will only do so once, however many times it's
+ * called.
+ */
+void
+_tap_init(void)
+{
+ static int run_once = 0;
+
+ LOCK;
+
+ if(!run_once) {
+ atexit(_cleanup);
+
+ /* stdout needs to be unbuffered so that the output appears
+ in the same place relative to stderr output as it does
+ with Test::Harness */
+ setbuf(stdout, 0);
+ run_once = 1;
+ }
+
+ UNLOCK;
+}
+
+/*
+ * Note that there's no plan.
+ */
+int
+plan_no_plan(void)
+{
+
+ LOCK;
+
+ _tap_init();
+
+ if(have_plan != 0) {
+ fprintf(stderr, "You tried to plan twice!\n");
+ test_died = 1;
+ UNLOCK;
+ exit(255);
+ }
+
+ have_plan = 1;
+ no_plan = 1;
+
+ UNLOCK;
+
+ return 0;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+int
+plan_skip_all(char *reason)
+{
+
+ LOCK;
+
+ _tap_init();
+
+ skip_all = 1;
+
+ printf("1..0");
+
+ if(reason != NULL)
+ printf(" # Skip %s", reason);
+
+ printf("\n");
+
+ UNLOCK;
+
+ exit(0);
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+int
+plan_tests(unsigned int tests)
+{
+
+ LOCK;
+
+ _tap_init();
+
+ if(have_plan != 0) {
+ fprintf(stderr, "You tried to plan twice!\n");
+ test_died = 1;
+ UNLOCK;
+ exit(255);
+ }
+
+ if(tests == 0) {
+ fprintf(stderr, "You said to run 0 tests! You've got to run something.\n");
+ test_died = 1;
+ UNLOCK;
+ exit(255);
+ }
+
+ have_plan = 1;
+
+ _expected_tests(tests);
+
+ UNLOCK;
+
+ return 0;
+}
+
+unsigned int
+diag(char *fmt, ...)
+{
+ va_list ap;
+
+ LOCK;
+
+ fputs("# ", stderr);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fputs("\n", stderr);
+
+ UNLOCK;
+
+ return 0;
+}
+
+void
+_expected_tests(unsigned int tests)
+{
+
+ LOCK;
+
+ printf("1..%d\n", tests);
+ e_tests = tests;
+
+ UNLOCK;
+}
+
+int
+skip(unsigned int n, char *fmt, ...)
+{
+ va_list ap;
+ char *skip_msg;
+
+ LOCK;
+
+ va_start(ap, fmt);
+ asprintf(&skip_msg, fmt, ap);
+ va_end(ap);
+
+ while(n-- > 0) {
+ test_count++;
+ printf("ok %d # skip %s\n", test_count,
+ skip_msg != NULL ?
+ skip_msg : "libtap():malloc() failed");
+ }
+
+ free(skip_msg);
+
+ UNLOCK;
+
+ return 1;
+}
+
+void
+todo_start(char *fmt, ...)
+{
+ va_list ap;
+
+ LOCK;
+
+ va_start(ap, fmt);
+ vasprintf(&todo_msg, fmt, ap);
+ va_end(ap);
+
+ todo = 1;
+
+ UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+ LOCK;
+
+ todo = 0;
+ free(todo_msg);
+
+ UNLOCK;
+}
+
+int
+exit_status(void)
+{
+ int r;
+
+ LOCK;
+
+ /* If there's no plan, just return the number of failures */
+ if(no_plan || !have_plan) {
+ UNLOCK;
+ return failures;
+ }
+
+ /* Ran too many tests? Return the number of tests that were run
+ that shouldn't have been */
+ if(e_tests < test_count) {
+ r = test_count - e_tests;
+ UNLOCK;
+ return r;
+ }
+
+ /* Return the number of tests that failed + the number of tests
+ that weren't run */
+ r = failures + e_tests - test_count;
+ UNLOCK;
+
+ return r;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+void
+_cleanup(void)
+{
+
+ LOCK;
+
+ /* If plan_no_plan() wasn't called, and we don't have a plan,
+ and we're not skipping everything, then something happened
+ before we could produce any output */
+ if(!no_plan && !have_plan && !skip_all) {
+ diag("Looks like your test died before it could output anything.");
+ UNLOCK;
+ return;
+ }
+
+ if(test_died) {
+ diag("Looks like your test died just after %d.", test_count);
+ UNLOCK;
+ return;
+ }
+
+
+ /* No plan provided, but now we know how many tests were run, and can
+ print the header at the end */
+ if(!skip_all && (no_plan || !have_plan)) {
+ printf("1..%d\n", test_count);
+ }
+
+ if((have_plan && !no_plan) && e_tests < test_count) {
+ diag("Looks like you planned %d tests but ran %d extra.",
+ e_tests, test_count - e_tests);
+ UNLOCK;
+ return;
+ }
+
+ if((have_plan || !no_plan) && e_tests > test_count) {
+ diag("Looks like you planned %d tests but only ran %d.",
+ e_tests, test_count);
+ UNLOCK;
+ return;
+ }
+
+ if(failures)
+ diag("Looks like you failed %d tests of %d.",
+ failures, test_count);
+
+ UNLOCK;
+}
--- /dev/null
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
+ and requires the caller to add the final comma if they've ommitted
+ the optional arguments */
+#ifdef __GNUC__
+# define ok(e, test, ...) ((e) ? \
+ _gen_result(1, __func__, __FILE__, __LINE__, \
+ test, ## __VA_ARGS__) : \
+ _gen_result(0, __func__, __FILE__, __LINE__, \
+ test, ## __VA_ARGS__))
+
+# define ok1(e) ((e) ? \
+ _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+ _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
+# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
+
+# define skip_start(test, n, fmt, ...) \
+ do { \
+ if((test)) { \
+ skip(n, fmt, ## __VA_ARGS__); \
+ continue; \
+ }
+#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
+# define ok(e, ...) ((e) ? \
+ _gen_result(1, __func__, __FILE__, __LINE__, \
+ __VA_ARGS__) : \
+ _gen_result(0, __func__, __FILE__, __LINE__, \
+ __VA_ARGS__))
+
+# define ok1(e) ((e) ? \
+ _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+ _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+# define pass(...) ok(1, __VA_ARGS__);
+# define fail(...) ok(0, __VA_ARGS__);
+
+# define skip_start(test, n, ...) \
+ do { \
+ if((test)) { \
+ skip(n, __VA_ARGS__); \
+ continue; \
+ }
+#else /* __STDC_VERSION__ */
+# error "Needs gcc or C99 compiler for variadic macros."
+#endif /* __STDC_VERSION__ */
+
+# define skip_end } while(0);
+
+unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
+
+int plan_no_plan(void);
+int plan_skip_all(char *);
+int plan_tests(unsigned int);
+
+unsigned int diag(char *, ...);
+
+int skip(unsigned int, char *, ...);
+
+void todo_start(char *, ...);
+void todo_end(void);
+
+int exit_status(void);
--- /dev/null
+#include "hexmap.h"
+
+#include "tap.h"
+
+int main(void) {
+ int xy[2];
+ int ijk[3];
+ int hex, start, dist;
+ int x, y, i, count;
+ int range[32];
+
+ plan_tests(1);
+
+ start = HL_cantor_xy(3,3);
+ count = HL_hexes_within_range(start, 2, range, 0);
+ ok(count == 18, "18 hexes within 2 hexes of 3,3");
+
+ return exit_status();
+}