aboutsummaryrefslogtreecommitdiff
path: root/penrose.c (follow)
Commit message (Collapse)AuthorAge
* grid.c: new and improved Penrose tiling generator.Simon Tatham2023-07-07
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The new generator works on the same 'combinatorial coordinates' system as the more recently written Hats and Spectre generators. When I came up with that technique for the Hats tiling, I was already tempted to rewrite the Penrose generator on the same principle, because it has a lot of advantages over the previous technique of picking a randomly selected patch out of the subdivision of a huge starting tile. It generates the exact limiting distribution of possible tiling patches rather than an approximation based on how big a tile you subdivided; it doesn't use any overly large integers or overly precise floating point to suffer overflow or significance loss, because it never has to even _think_ about the coordinates of any point not in the actual output area. But I resisted the temptation to throw out our existing Penrose generator and move to the shiny new algorithm, for just one reason: backwards compatibility. There's no sensible way to translate existing Loopy game IDs into the new notation, so they would stop working, unless we kept the old generator around as well to interpret the previous system. And although _random seeds_ aren't guaranteed to generate the same result from one build of Puzzles to the next, I do try to keep existing descriptive game IDs working. So if we had to keep the old generator around anyway, it didn't seem worth writing a new one... ... at least, until we discovered that the old generator has a latent bug. The function that decides whether each tile is within the target area, and hence whether to make it part of the output grid, is based on floating-point calculation of the tile's vertices. So a change in FP rounding behaviour between two platforms could cause them to interpret the same grid description differently, invalidating a Loopy game on that grid. This is _unlikely_, as long as everyone uses IEEE 754 double, but the C standard doesn't actually guarantee that. We found this out when I started investigating a slight distortion in large instances of Penrose Loopy. For example, the Loopy random seed "40x40t11#12345", as of just before this commit, generates a game description beginning with the Penrose grid string "G-4944,5110,108", in which you can see a star shape of five darts a few tiles down the left edge, where two of the radii of the star don't properly line up with edges in the surrounding shell of kites that they should be collinear with. This turns out to be due to FP error: not because _double precision_ ran out, but because the definitions of COS54, SIN54, COS18 and SIN18 in penrose.c were specified to only 7 digits of precision. And when you expand a patch of tiling that large, you end up with integer combinations of those numbers with coefficients about 7 digits long, mostly cancelling out to leave a much smaller output value, and the inaccuracies in those constants start to be noticeable. But having noticed that, it then became clear that these badly computed values were also critical to _correctness_ of the grid. So they can't be fixed without invalidating old game IDs. _And_ then we realised that this also means existing platforms might disagree on a game ID's validity. So if we have to break compatibility anyway, we should go all the way, and instead of fixing the old system, introduce the shiny new system that gets all of this right. Therefore, the new penrose.c uses the more reliable combinatorial-coordinates system which doesn't deal in integers that large in the first place. Also, there's no longer any floating point at all in the calculation of tile vertex coordinates: the combinations of 1 and sqrt(5) are computed exactly by the n_times_root_k function. So now a large Penrose grid should never have obvious failures of alignment like that. The old system is kept for backwards compatibility. It's not fully reliable, because that bug hasn't been fixed - but any Penrose Loopy game ID that was working before on _some_ platform should still work on that one. However, new Penrose Loopy game IDs are based on combinatorial coordinates, computed in exact arithmetic, and should be properly stable. The new code looks suspiciously like the Spectre code (though considerably simpler - the Penrose coordinate maps are easy enough that I just hand-typed them rather than having an elaborate auxiliary data-generation tool). That's because they were similar enough in concept to make it possible to clone and hack. But there are enough divergences that it didn't seem like a good idea to try to merge them again afterwards - in particular, the fact that output Penrose tiles are generated by merging two triangular metatiles while Spectres are subdivisions of their metatiles.
* Reference my just-published article about aperiodic tilings.Simon Tatham2023-04-10
| | | | | | | | | | | | | | | | | | | In commit 8d6647548f7d005 I added the Hats grid type to Loopy, and mentioned in the commit message that I was very pleased with the algorithm I came up with. In fact, I was so pleased with it that I've decided it deserves a proper public writeup. So I've spent the Easter weekend producing one: https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-tilings/ In this commit I adjust the header comments in both penrose.c and hat.c to refer to the article (replacing a previous comment in penrose.c to a much less polished page containing a copy of my jotting-grade personal notes that I sent James Harvey once). Also, added some code to hatgen.c to output Python hat descriptions in a similar style to hat-test, which I used to generate a couple of the more difficult diagrams in the new article, and didn't want to lose.
* Fall back to <math.h> if <tgmath.h> doesn't work.Simon Tatham2023-04-06
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This fixes a build failure introduced by commit 2e48ce132e011e8 yesterday. When I saw that commit I expected the most likely problem would be in the NestedVM build, which is currently the thing with the most most out-of-date C implementation. And indeed the NestedVM toolchain doesn't have <tgmath.h> - but much more surprisingly, our _Windows_ builds failed too, with a compile error inside <tgmath.h> itself! I haven't looked closely into the problem yet. Our Windows builds are done with clang, which comes with its own <tgmath.h> superseding the standard Windows one. So you'd _hope_ that clang could make sense of its own header! But perhaps the problem is that this is an unusual compile mode and hasn't been tested. My fix is to simply add a cmake check for <tgmath.h> - which doesn't just check the file's existence, it actually tries compiling a file that #includes it, so it will detect 'file exists but is mysteriously broken' just as easily as 'not there at all'. So this makes the builds start working again, precisely on Ben's theory of opportunistically using <tgmath.h> where possible and falling back to <math.h> otherwise. It looks ugly, though! I'm half tempted to make a new header file whose job is to include a standard set of system headers, just so that that nasty #ifdef doesn't have to sit at the top of almost all the source files. But for the moment this at least gets the build working again.
* Replace <math.h> with <tgmath.h> throughoutBen Harris2023-04-04
| | | | | | | | | | | | | | | C89 provided only double-precision mathematical functions (sin() etc), and so despite using single-precision elsewhere, those are what Puzzles has traditionally used. C99 introduced single-precision equivalents (sinf() etc), and I hope it's been long enough that we can safely use them. Maybe they'll even be faster. Rather than directly use the single-precision functions, though, we use the magic macros from <tgmath.h> that automatically choose the precision of mathematical functions based on their arguments. This has the advantage that we only need to change which header we include, and thus that we can switch back again if some platform has trouble with the new header.
* Remove penrose_count_tiles().Simon Tatham2023-04-02
| | | | | Nothing uses it, and that's not surprising, because if anything had, we'd have noticed that it never actually got written!
* Move other test main()s out of library source files.Simon Tatham2023-04-02
| | | | | | | | | | | | | | | | | | | | | | | | | | Having stated the principle in the previous commit, I should apply it consistently. A source file linked into the Puzzles library of common support code should not also define a main() under ifdef. This commit only goes as far as the _library_ support modules. It would be a much bigger job to do the same for all the actual _puzzles_ that have test main()s or standalone-solver main()s. And it's not necessary, because modifying one of those source files only triggers a rebuild of _one_ puzzle, not absolutely everything. (Not to mention that it's quite likely the puzzle and the test main() will need to be modified in conjunction anyway.) As in the previous commit, this has required exposing a few internal API functions as global, and maybe editing them a bit. In particular, the one-shot internal function that divvy_rectangle() loops on until it succeeds is now exposed as divvy_rectangle_attempt(), which means the test program doesn't have to condition a failure counter into the real function. I've thrown away penrose-vector-test completely, because that didn't look like a test program with any ongoing use at all - it was surely vestigial, while James was getting the vector representation up and running in the first place.
* Fix missing statics and #includes on variables.Simon Tatham2023-02-18
| | | | | | | | | | | | | | | | | | | | | | | After Ben fixed all the unwanted global functions by using gcc's -Wmissing-declarations to spot any that were not predeclared, I remembered that clang has -Wmissing-variable-declarations, which does the same job for global objects. Enabled it in -DSTRICT=ON, and made the code clean under it. Mostly this was just a matter of sticking 'static' on the front of things. One variable was outright removed ('verbose' in signpost.c) because after I made it static clang was then able to spot that it was also unused. The more interesting cases were the ones where declarations had to be _added_ to header files. In particular, in COMBINED builds, puzzles.h now arranges to have predeclared each 'game' structure defined by a puzzle backend. Also there's a new tiny header file gtk.h, containing the declarations of xpm_icons and n_xpm_icons which are exported by each puzzle's autogenerated icon source file and by no-icon.c. Happily even the real XPM icon files were generated by our own Perl script rather than being raw xpm output from ImageMagick, so there was no difficulty adding the corresponding #include in there.
* Mark many more function (and some objects) staticBen Harris2023-02-18
| | | | | | | | | | | | | I noticed commit db3b531e2cab765a00475054d2e9046c9d0437d3 in the history where Simon added a bunch of "static" qualifiers. That suggested that consistently marking internal functions "static" is desirable, so I tried a build using GCC's -Wmissing-declarations, which requires prior declaration (presumed to be in a header file) of all global functions. This commit makes the GTK build clean under GCC's -Wmissing-declarations. I've also adding "static" to a few obviously internal objects, but GCC doesn't complain about those so I certainly haven't got them all.
* Build a lot of conditioned-out test and helper programs.Simon Tatham2021-05-25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Most of these aren't especially useful, but if we're going to have them in the code base at all, we should at least ensure they compile: bit-rotted conditioned-out code is of no value. One of the new programs is 'galaxieseditor', which borrows most of the Galaxies code but changes the UI so that you can create and remove _dots_ instead of edges, and then run the solver to see whether it can solve the puzzle you've designed. Unlike the rest, this is a GUI helper tool, using the 'guiprogram' cmake function introduced in the previous commit. The programs are: - 'combi', a test program for the utility module that generates all combinations of n things - 'divvy', a test program for the module that divides a rectangle at random into equally-sized polyominoes - 'penrose-test', a test program for the Penrose-tiling generator used in Loopy, which outputs an SVG of a piece of tiling - 'penrose-vector', a much smaller test program for the vector arithmetic subroutines in that code - 'sort-test', a test of this code base's local array sorting routine - 'tree234-test', the exhaustive test code that's been in tree234.c all along. Not all of them compiled first time. Most of the fixes were the usual kind of thing: fixing compiler warnings by removing unused variables/functions, bringing uses of internal APIs up to date. A notable one was that galaxieseditor's interpret_move() modified the input game state, which was an error all along and is now detected by me having made it a const pointer; I had to replace that with an extra wrinkle in the move-string format, so that now execute_move() makes the modification. The one I'm _least_ proud of is squelching a huge number of format-string warnings in tree234-test by interposing a variadic function without __attribute__((printf)).
* Apply the rotation in Penrose grid descriptions by rotating in theSimon Tatham2011-05-06
| | | | | | | | | | | | | | | | | | | | 4-vector representation, rather than mucking about with sines and cosines after grid generation. _Should_ make no difference in the generated grids (there's a theoretical risk of an unlucky rounding error just about managing to push some point in or out of bounds, but I think it's vanishingly small), but simplifies the coordinate- flattening procedure, and in particular increases its chance of getting vertical lines actually vertical. (Prior to this change, the game ID 10x10t12:G2554,-31,108_a3b12h0a212a3d102b2a23a2e3b01b0a2c2a0c0 was generating a not-quite-vertical edge at top left, in the Java port but not on Linux; I suspect differences in sin and cos as the cause of the discrepancy. With the rotation done like this, the points' x-coordinates are now computed without reference to their y-coordinates.) [originally from svn r9168]
* Portability fixes, mostly from James for Palm purposes. MostlySimon Tatham2011-05-04
| | | | | | | | additions of missing 'static' and explicit 'void' in parameter lists, plus one or two other things like explicitly casting chars in variadic argument lists to int and using DBL_MAX if HUGE_VAL isn't available. [originally from svn r9166]
* From James Harvey (via a period of collaborative polishing), a patchSimon Tatham2011-04-24
to add two kinds of Penrose tiling to the grid types supported by Loopy. This has involved a certain amount of infrastructure work, because of course the whole point of Penrose tilings is that they don't have to be the same every time: so now grid.c has grown the capacity to describe its grids as strings, and reconstitute them from those string descriptions. Hence a Penrose Loopy game description consists of a string identifying a particular piece of Penrose tiling, followed by the normal Loopy clue encoding. All the existing grid types decline to provide a grid description string, so their Loopy game descriptions have not changed encoding. [originally from svn r9159]