mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-20 23:51:29 -07:00
Files

The big mathematical news this month is that a polygon has been discovered that will tile the plane but only aperiodically. Penrose tiles achieve this with two tile types; it's been an open question for decades whether you could do it with only one tile. Now someone has announced the discovery of such a thing, so _obviously_ this mathematically exciting tiling ought to be one of the Loopy grid options! The polygon, named a 'hat' by its discoverers, consists of the union of eight cells of the 'Kites' periodic tiling that Loopy already implements. So all the vertex coordinates of the whole tiling are vertices of the Kites grid, which makes handling the coordinates in an exact manner a lot easier than Penrose tilings. What's _harder_ than Penrose tilings is that, although this tiling can be generated by a vaguely similar system of recursive expansion, the expansion is geometrically distorting, which means you can't easily figure out which tiles can be discarded early to save CPU. Instead I've come up with a completely different system for generating a patch of tiling, by using a hierarchical coordinate system to track a location within many levels of the expansion process without ever simulating the process as a whole. I'm really quite pleased with that technique, and am tempted to try switching the Penrose generator over to it too - except that we'd have to keep the old generator around to stop old game ids being invalidated, and also, I think it would be slightly trickier without an underlying fixed grid and without overlaps in the tile expansion system. However, before coming up with that, I got most of the way through implementing the more obvious system of actually doing the expansions. The result worked, but was very slow (because I changed approach rather than try to implement tree-pruning under distortion). But the code was reusable for two other useful purposes: it generated the lookup tables needed for the production code, and it also generated a lot of useful diagrams. So I've committed it anyway as a supporting program, in a new 'aux' source subdirectory, and in aux/doc is a writeup of the coordinate system's concepts, with all those diagrams. (That's the kind of thing I'd normally put in a huge comment at the top of the file, but doing all those diagrams in ASCII art would be beyond miserable.) From a gameplay perspective: the hat polygon has 13 edges, but one of them has a vertex of the Kites tiling in the middle, and sometimes two other tile boundaries meet at that vertex. I've chosen to represent every hat as having degree 14 for Loopy purposes, because if you only included that extra vertex when it was needed, then people would be forever having to check whether this was a 13-hat or a 14-hat and it would be nightmarish to play. Even so, there's a lot of clicking involved to turn all those fiddly individual edges on or off. This grid is noticeably nicer to play in 'autofollow' mode, by setting LOOPY_AUTOFOLLOW in the environment to either 'fixed' or 'adaptive'. I'm tempted to make 'fixed' the default, except that I think it would confuse players of ordinary square Loopy!
140 lines
4.8 KiB
C
140 lines
4.8 KiB
C
/*
|
|
* (c) Lambros Lambrou 2008
|
|
*
|
|
* Code for working with general grids, which can be any planar graph
|
|
* with faces, edges and vertices (dots). Includes generators for a few
|
|
* types of grid, including square, hexagonal, triangular and others.
|
|
*/
|
|
|
|
#ifndef PUZZLES_GRID_H
|
|
#define PUZZLES_GRID_H
|
|
|
|
#include "puzzles.h" /* for random_state */
|
|
|
|
/* Useful macros */
|
|
#define SQ(x) ( (x) * (x) )
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Grid structures:
|
|
* A grid is made up of faces, edges and dots. These structures hold
|
|
* the incidence relationships between these types. For example, an
|
|
* edge always joins two dots, and is adjacent to two faces.
|
|
* The "grid_xxx **" members are lists of pointers which are dynamically
|
|
* allocated during grid generation.
|
|
* A pointer to a face/edge/dot will always point somewhere inside one of the
|
|
* three lists of the main "grid" structure: faces, edges, dots.
|
|
* Could have used integer offsets into these lists, but using actual
|
|
* pointers instead gives us type-safety.
|
|
*/
|
|
|
|
/* Need forward declarations */
|
|
typedef struct grid_face grid_face;
|
|
typedef struct grid_edge grid_edge;
|
|
typedef struct grid_dot grid_dot;
|
|
|
|
struct grid_face {
|
|
int order; /* Number of edges, also the number of dots */
|
|
grid_edge **edges; /* edges around this face */
|
|
grid_dot **dots; /* corners of this face */
|
|
/*
|
|
* For each face, we optionally compute and store its 'incentre'.
|
|
* The incentre of a triangle is the centre of a circle tangent to
|
|
* all three edges; I generalise the concept to arbitrary polygons
|
|
* by defining it to be the centre of the largest circle you can fit
|
|
* anywhere in the polygon. It's a useful thing to know because if
|
|
* you want to draw any symbol or text in the face (e.g. clue
|
|
* numbers in Loopy), that's the place it will most easily fit.
|
|
*
|
|
* When a grid is first generated, no face has this information
|
|
* computed, because it's fiddly to do. You can call
|
|
* grid_find_incentre() on a face, and it will fill in ix,iy below
|
|
* and set has_incentre to indicate that it's done so.
|
|
*/
|
|
bool has_incentre;
|
|
int ix, iy; /* incentre (centre of largest inscribed circle) */
|
|
};
|
|
struct grid_edge {
|
|
grid_dot *dot1, *dot2;
|
|
grid_face *face1, *face2; /* Use NULL for the infinite outside face */
|
|
};
|
|
struct grid_dot {
|
|
int order;
|
|
grid_edge **edges;
|
|
grid_face **faces; /* A NULL grid_face* means infinite outside face */
|
|
|
|
/* Position in some fairly arbitrary (Cartesian) coordinate system.
|
|
* Use large enough values such that we can get away with
|
|
* integer arithmetic, but small enough such that arithmetic
|
|
* won't overflow. */
|
|
int x, y;
|
|
};
|
|
typedef struct grid {
|
|
/* These are (dynamically allocated) arrays of all the
|
|
* faces, edges, dots that are in the grid. */
|
|
int num_faces; grid_face *faces;
|
|
int num_edges; grid_edge *edges;
|
|
int num_dots; grid_dot *dots;
|
|
|
|
/* Cache the bounding-box of the grid, so the drawing-code can quickly
|
|
* figure out the proper scaling to draw onto a given area. */
|
|
int lowest_x, lowest_y, highest_x, highest_y;
|
|
|
|
/* A measure of tile size for this grid (in grid coordinates), to help
|
|
* the renderer decide how large to draw the grid.
|
|
* Roughly the size of a single tile - for example the side-length
|
|
* of a square cell. */
|
|
int tilesize;
|
|
|
|
/* We really don't want to copy this monstrosity!
|
|
* A grid is immutable once generated.
|
|
*/
|
|
int refcount;
|
|
} grid;
|
|
|
|
/* Grids are specified by type: GRID_SQUARE, GRID_KITE, etc. */
|
|
|
|
#define GRIDGEN_LIST(A) \
|
|
A(SQUARE,square) \
|
|
A(HONEYCOMB,honeycomb) \
|
|
A(TRIANGULAR,triangular) \
|
|
A(SNUBSQUARE,snubsquare) \
|
|
A(CAIRO,cairo) \
|
|
A(GREATHEXAGONAL,greathexagonal) \
|
|
A(KAGOME,kagome) \
|
|
A(OCTAGONAL,octagonal) \
|
|
A(KITE,kites) \
|
|
A(FLORET,floret) \
|
|
A(DODECAGONAL,dodecagonal) \
|
|
A(GREATDODECAGONAL,greatdodecagonal) \
|
|
A(GREATGREATDODECAGONAL,greatgreatdodecagonal) \
|
|
A(COMPASSDODECAGONAL,compassdodecagonal) \
|
|
A(PENROSE_P2,penrose_p2_kite) \
|
|
A(PENROSE_P3,penrose_p3_thick) \
|
|
A(HATS,hats) \
|
|
/* end of list */
|
|
|
|
#define ENUM(upper,lower) GRID_ ## upper,
|
|
typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type;
|
|
#undef ENUM
|
|
|
|
const char *grid_validate_params(grid_type type, int width, int height);
|
|
|
|
/* Free directly after use if non-NULL. Will never contain an underscore
|
|
* (so clients can safely use that as a separator). */
|
|
char *grid_new_desc(grid_type type, int width, int height, random_state *rs);
|
|
const char *grid_validate_desc(grid_type type, int width, int height,
|
|
const char *desc);
|
|
|
|
grid *grid_new(grid_type type, int width, int height, const char *desc);
|
|
|
|
void grid_free(grid *g);
|
|
|
|
grid_edge *grid_nearest_edge(grid *g, int x, int y);
|
|
|
|
void grid_compute_size(grid_type type, int width, int height,
|
|
int *tilesize, int *xextent, int *yextent);
|
|
|
|
void grid_find_incentre(grid_face *f);
|
|
|
|
#endif /* PUZZLES_GRID_H */
|