mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 16:05:44 -07:00
Revamp the triangular grid generation in Loopy, which I've been
meaning to revisit for a while. The new version generates more nicely symmetric grids (best at even heights, but still improved even at odd heights), and avoids any 'ears' on the grid (triangles at the corners connected to only one other triangle, which tend to be boringly easy places to start solving). I've reused the grid-description-string mechanism invented for the Penrose tilings as a versioning mechanism for the triangular grids, so that old game descriptions should still be valid, and new triangular- grid game descriptions begin with an "0_" prefix to indicate that they are based on the new-style construction. [originally from svn r9824]
This commit is contained in:
121
grid.c
121
grid.c
@ -1525,15 +1525,34 @@ static void grid_size_triangular(int width, int height,
|
|||||||
int vec_y = TRIANGLE_VEC_Y;
|
int vec_y = TRIANGLE_VEC_Y;
|
||||||
|
|
||||||
*tilesize = TRIANGLE_TILESIZE;
|
*tilesize = TRIANGLE_TILESIZE;
|
||||||
*xextent = width * 2 * vec_x + vec_x;
|
*xextent = (width+1) * 2 * vec_x;
|
||||||
*yextent = height * vec_y;
|
*yextent = height * vec_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *grid_validate_desc_triangular(grid_type type, int width,
|
||||||
|
int height, char *desc)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Triangular grids: an absent description is valid (indicating
|
||||||
|
* the old-style approach which had 'ears', i.e. triangles
|
||||||
|
* connected to only one other face, at some grid corners), and so
|
||||||
|
* is a description reading just "0" (indicating the new-style
|
||||||
|
* approach in which those ears are trimmed off). Anything else is
|
||||||
|
* illegal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!desc || !strcmp(desc, "0"))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return "Unrecognised grid description.";
|
||||||
|
}
|
||||||
|
|
||||||
/* Doesn't use the previous method of generation, it pre-dates it!
|
/* Doesn't use the previous method of generation, it pre-dates it!
|
||||||
* A triangular grid is just about simple enough to do by "brute force" */
|
* A triangular grid is just about simple enough to do by "brute force" */
|
||||||
static grid *grid_new_triangular(int width, int height, char *desc)
|
static grid *grid_new_triangular(int width, int height, char *desc)
|
||||||
{
|
{
|
||||||
int x,y;
|
int x,y;
|
||||||
|
int version = (desc == NULL ? -1 : atoi(desc));
|
||||||
|
|
||||||
/* Vector for side of triangle - ratio is close to sqrt(3) */
|
/* Vector for side of triangle - ratio is close to sqrt(3) */
|
||||||
int vec_x = TRIANGLE_VEC_X;
|
int vec_x = TRIANGLE_VEC_X;
|
||||||
@ -1547,6 +1566,21 @@ static grid *grid_new_triangular(int width, int height, char *desc)
|
|||||||
grid *g = grid_empty();
|
grid *g = grid_empty();
|
||||||
g->tilesize = TRIANGLE_TILESIZE;
|
g->tilesize = TRIANGLE_TILESIZE;
|
||||||
|
|
||||||
|
if (version == -1) {
|
||||||
|
/*
|
||||||
|
* Old-style triangular grid generation, preserved as-is for
|
||||||
|
* backwards compatibility with old game ids, in which it's
|
||||||
|
* just a little asymmetric and there are 'ears' (faces linked
|
||||||
|
* to only one other face) at two grid corners.
|
||||||
|
*
|
||||||
|
* Example old-style game ids, which should still work, and in
|
||||||
|
* which you should see the ears in the TL/BR corners on the
|
||||||
|
* first one and in the TL/BL corners on the second:
|
||||||
|
*
|
||||||
|
* 5x5t1:2c2a1a2a201a1a1a1112a1a2b1211f0b21a2a2a0a
|
||||||
|
* 5x6t1:a022a212h1a1d1a12c2b11a012b1a20d1a0a12e
|
||||||
|
*/
|
||||||
|
|
||||||
g->num_faces = width * height * 2;
|
g->num_faces = width * height * 2;
|
||||||
g->num_dots = (width + 1) * (height + 1);
|
g->num_dots = (width + 1) * (height + 1);
|
||||||
g->faces = snewn(g->num_faces, grid_face);
|
g->faces = snewn(g->num_faces, grid_face);
|
||||||
@ -1603,6 +1637,74 @@ static grid *grid_new_triangular(int width, int height, char *desc)
|
|||||||
index += 2;
|
index += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* New-style approach, in which there are never any 'ears',
|
||||||
|
* and if height is even then the grid is nicely 4-way
|
||||||
|
* symmetric.
|
||||||
|
*
|
||||||
|
* Example new-style grids:
|
||||||
|
*
|
||||||
|
* 5x5t1:0_21120b11a1a01a1a00c1a0b211021c1h1a2a1a0a
|
||||||
|
* 5x6t1:0_a1212c22c2a02a2f22a0c12a110d0e1c0c0a101121a1
|
||||||
|
*/
|
||||||
|
tree234 *points = newtree234(grid_point_cmp_fn);
|
||||||
|
/* Upper bounds - don't have to be exact */
|
||||||
|
int max_faces = height * (2*width+1);
|
||||||
|
int max_dots = (height+1) * (width+1) * 4;
|
||||||
|
|
||||||
|
g->faces = snewn(max_faces, grid_face);
|
||||||
|
g->dots = snewn(max_dots, grid_dot);
|
||||||
|
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
/*
|
||||||
|
* Each row contains (width+1) triangles one way up, and
|
||||||
|
* (width) triangles the other way up. Which way up is
|
||||||
|
* which varies with parity of y. Also, the dots around
|
||||||
|
* each face will flip direction with parity of y, so we
|
||||||
|
* set up n1 and n2 to cope with that easily.
|
||||||
|
*/
|
||||||
|
int y0, y1, n1, n2;
|
||||||
|
y0 = y1 = y * vec_y;
|
||||||
|
if (y % 2) {
|
||||||
|
y1 += vec_y;
|
||||||
|
n1 = 2; n2 = 1;
|
||||||
|
} else {
|
||||||
|
y0 += vec_y;
|
||||||
|
n1 = 1; n2 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x <= width; x++) {
|
||||||
|
int x0 = 2*x * vec_x, x1 = x0 + vec_x, x2 = x1 + vec_x;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the grid has odd height, then we skip the first
|
||||||
|
* and last triangles on this row, otherwise they'll
|
||||||
|
* end up as ears.
|
||||||
|
*/
|
||||||
|
if (height % 2 == 1 && y == height-1 && (x == 0 || x == width))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
grid_face_add_new(g, 3);
|
||||||
|
grid_face_set_dot(g, grid_get_dot(g, points, x0, y0), 0);
|
||||||
|
grid_face_set_dot(g, grid_get_dot(g, points, x1, y1), n1);
|
||||||
|
grid_face_set_dot(g, grid_get_dot(g, points, x2, y0), n2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < width; x++) {
|
||||||
|
int x0 = (2*x+1) * vec_x, x1 = x0 + vec_x, x2 = x1 + vec_x;
|
||||||
|
|
||||||
|
grid_face_add_new(g, 3);
|
||||||
|
grid_face_set_dot(g, grid_get_dot(g, points, x0, y1), 0);
|
||||||
|
grid_face_set_dot(g, grid_get_dot(g, points, x1, y0), n2);
|
||||||
|
grid_face_set_dot(g, grid_get_dot(g, points, x2, y1), n1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freetree234(points);
|
||||||
|
assert(g->num_faces <= max_faces);
|
||||||
|
assert(g->num_dots <= max_dots);
|
||||||
|
}
|
||||||
|
|
||||||
grid_make_consistent(g);
|
grid_make_consistent(g);
|
||||||
return g;
|
return g;
|
||||||
@ -2708,21 +2810,26 @@ static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ)
|
|||||||
|
|
||||||
char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
|
char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
|
||||||
{
|
{
|
||||||
if (type != GRID_PENROSE_P2 && type != GRID_PENROSE_P3)
|
if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return grid_new_desc_penrose(type, width, height, rs);
|
return grid_new_desc_penrose(type, width, height, rs);
|
||||||
|
} else if (type == GRID_TRIANGULAR) {
|
||||||
|
return dupstr("0"); /* up-to-date version of triangular grid */
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *grid_validate_desc(grid_type type, int width, int height, char *desc)
|
char *grid_validate_desc(grid_type type, int width, int height, char *desc)
|
||||||
{
|
{
|
||||||
if (type != GRID_PENROSE_P2 && type != GRID_PENROSE_P3) {
|
if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
|
||||||
|
return grid_validate_desc_penrose(type, width, height, desc);
|
||||||
|
} else if (type == GRID_TRIANGULAR) {
|
||||||
|
return grid_validate_desc_triangular(type, width, height, desc);
|
||||||
|
} else {
|
||||||
if (desc != NULL)
|
if (desc != NULL)
|
||||||
return "Grid description strings not used with this grid type";
|
return "Grid description strings not used with this grid type";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return grid_validate_desc_penrose(type, width, height, desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
grid *grid_new(grid_type type, int width, int height, char *desc)
|
grid *grid_new(grid_type type, int width, int height, char *desc)
|
||||||
|
Reference in New Issue
Block a user