From 8af0c29615fdfe30035f2d4c3a7d2ccf06d6efa9 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 18 Nov 2017 15:16:40 +0000 Subject: [PATCH] New grid type: the trihexagonal tiling, or 'kagome lattice'. Regular hexagons and equilateral triangles in strict alternation, with two of each interleaved around each vertex. https://en.wikipedia.org/wiki/Trihexagonal_tiling Thanks to Michael Quevillon for the patch. --- LICENCE | 3 +- grid.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ grid.h | 1 + loopy.c | 3 ++ 4 files changed, 102 insertions(+), 1 deletion(-) diff --git a/LICENCE b/LICENCE index 4235005..dbcf1ac 100644 --- a/LICENCE +++ b/LICENCE @@ -2,7 +2,8 @@ This software is copyright (c) 2004-2014 Simon Tatham. Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas Kölker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd -Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens. +Schmidt, Steffen Bauer, Lennard Sprong, Rogier Goossens and Michael +Quevillon. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff --git a/grid.c b/grid.c index 52648e5..b5e6bb0 100644 --- a/grid.c +++ b/grid.c @@ -2060,6 +2060,102 @@ static grid *grid_new_greathexagonal(int width, int height, const char *desc) return g; } +#define KAGOME_TILESIZE 18 +/* Vector for side of triangle - ratio is close to sqrt(3) */ +#define KAGOME_A 15 +#define KAGOME_B 26 + +static void grid_size_kagome(int width, int height, + int *tilesize, int *xextent, int *yextent) +{ + int a = KAGOME_A; + int b = KAGOME_B; + + *tilesize = KAGOME_TILESIZE; + *xextent = (4*a) * (width-1) + 6*a; + *yextent = (2*b) * (height-1) + 2*b; +} + +static grid *grid_new_kagome(int width, int height, const char *desc) +{ + int x, y; + int a = KAGOME_A; + int b = KAGOME_B; + + /* Upper bounds - don't have to be exact */ + int max_faces = 6 * (width + 1) * (height + 1); + int max_dots = 6 * width * height; + + tree234 *points; + + grid *g = grid_empty(); + g->tilesize = KAGOME_TILESIZE; + g->faces = snewn(max_faces, grid_face); + g->dots = snewn(max_dots, grid_dot); + + points = newtree234(grid_point_cmp_fn); + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + grid_dot *d; + /* centre of hexagon */ + int px = (4*a) * x; + int py = (2*b) * y; + if (y % 2) + px += 2*a; + + /* hexagon */ + grid_face_add_new(g, 6); + d = grid_get_dot(g, points, px + a, py - b); grid_face_set_dot(g, d, 0); + d = grid_get_dot(g, points, px + 2*a, py ); grid_face_set_dot(g, d, 1); + d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 2); + d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 3); + d = grid_get_dot(g, points, px - 2*a, py ); grid_face_set_dot(g, d, 4); + d = grid_get_dot(g, points, px - a, py - b); grid_face_set_dot(g, d, 5); + + /* Triangle above right */ + if ((x < width - 1) || (!(y % 2) && y)) { + grid_face_add_new(g, 3); + d = grid_get_dot(g, points, px + 3*a, py - b); grid_face_set_dot(g, d, 0); + d = grid_get_dot(g, points, px + 2*a, py ); grid_face_set_dot(g, d, 1); + d = grid_get_dot(g, points, px + a, py - b); grid_face_set_dot(g, d, 2); + } + + /* Triangle below right */ + if ((x < width - 1) || (!(y % 2) && (y < height - 1))) { + grid_face_add_new(g, 3); + d = grid_get_dot(g, points, px + 3*a, py + b); grid_face_set_dot(g, d, 0); + d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 1); + d = grid_get_dot(g, points, px + 2*a, py ); grid_face_set_dot(g, d, 2); + } + + /* Left triangles */ + if (!x && (y % 2)) { + /* Triangle above left */ + grid_face_add_new(g, 3); + d = grid_get_dot(g, points, px - a, py - b); grid_face_set_dot(g, d, 0); + d = grid_get_dot(g, points, px - 2*a, py ); grid_face_set_dot(g, d, 1); + d = grid_get_dot(g, points, px - 3*a, py - b); grid_face_set_dot(g, d, 2); + + /* Triangle below left */ + if (y < height - 1) { + grid_face_add_new(g, 3); + d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 0); + d = grid_get_dot(g, points, px - 3*a, py + b); grid_face_set_dot(g, d, 1); + d = grid_get_dot(g, points, px - 2*a, py ); grid_face_set_dot(g, d, 2); + } + } + } + } + + freetree234(points); + assert(g->num_faces <= max_faces); + assert(g->num_dots <= max_dots); + + grid_make_consistent(g); + return g; +} + #define OCTAGONAL_TILESIZE 40 /* b/a approx sqrt(2) */ #define OCTAGONAL_A 29 diff --git a/grid.h b/grid.h index 19079a4..26d0b16 100644 --- a/grid.h +++ b/grid.h @@ -100,6 +100,7 @@ typedef struct grid { A(SNUBSQUARE,snubsquare) \ A(CAIRO,cairo) \ A(GREATHEXAGONAL,greathexagonal) \ + A(KAGOME,kagome) \ A(OCTAGONAL,octagonal) \ A(KITE,kites) \ A(FLORET,floret) \ diff --git a/loopy.c b/loopy.c index 5f1940e..c14412d 100644 --- a/loopy.c +++ b/loopy.c @@ -278,6 +278,7 @@ static void check_caches(const solver_state* sstate); A("Penrose (kite/dart)",PENROSE_P2,3,3) \ A("Penrose (rhombs)",PENROSE_P3,3,3) \ A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \ + A("Kagome",KAGOME,3,3) \ /* end of list */ #define GRID_NAME(title,type,amin,omin) title, @@ -544,6 +545,7 @@ static const game_params loopy_presets_more[] = { #ifdef SMALL_SCREEN { 7, 7, DIFF_HARD, LOOPY_GRID_HONEYCOMB }, { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL }, + { 5, 4, DIFF_HARD, LOOPY_GRID_KAGOME }, { 5, 5, DIFF_HARD, LOOPY_GRID_OCTAGONAL }, { 3, 3, DIFF_HARD, LOOPY_GRID_FLORET }, { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL }, @@ -552,6 +554,7 @@ static const game_params loopy_presets_more[] = { #else { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB }, { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL }, + { 5, 4, DIFF_HARD, LOOPY_GRID_KAGOME }, { 7, 7, DIFF_HARD, LOOPY_GRID_OCTAGONAL }, { 5, 5, DIFF_HARD, LOOPY_GRID_FLORET }, { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL },