aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAge
* grid.c: fix size miscalculation in Floret tiling.Simon Tatham2020-04-12
| | | | | | | | | | | | | A Floret grid of height h usually alternates columns of h hexagonal florets with columns of h-1. An exception is when h=1, in which case alternating columns of 1 and 0 florets would leave the grid disconnected. So in that situation all columns have 1 floret in them, and the starting y-coordinate oscillates to make the grid tile sensibly. However, that special case wasn't taken account of when calculating the grid height. As a result the anomalous extra florets in the height-1 tiling fell off the bottom of the puzzle window.
* GTK 3: handle nontrivial window scale factors.Simon Tatham2020-04-07
| | | | | | | | | | | | | | | A user pointed out that if you run a GTK 3 puzzles with "GDK_SCALE=2" in the environment, the main game drawing area is blurred. That's because we're choosing the size of our backing Cairo surface based on the number of _logical_ pixels in the window size, not taking into account the fact that the non-unit scale factor means the number of physical pixels is larger. Everything 'works' in the basis - Cairo happily expands the smaller backing surface into the larger window - but resolution is lost in the process. Now we detect the window's scale factor, construct the backing surface appropriately, and compensate for that scaling when drawing to the surface and when blitting the surface to the window.
* Mines: add validation for negative mine count.Simon Tatham2020-03-17
| | | | | If this gets through validation, it causes an infinite loop after gameplay begins.
* Tracks: fix a small memory leak.Simon Tatham2020-02-26
| | | | Spotted by Leak Sanitiser while I was testing the standalone solver.
* Tracks: add reverse neighbour deduction in hard mode.Simon Tatham2020-02-26
| | | | | | | | | | | | | | | | | This is the contrapositive of the deduction introduced in the previous commit. Previously I said: a square A can have some edges blocked in such a way that you know it can't be filled without a particular one of its neighbours B also being filled. And then, if you know the row containing A and B only has one filled square left to find, then it can't be A. This commit adds the obvious followup: if you know the row only has one _empty_ square left, then for the same reason, it can't be B! I'm putting this in at the new Hard difficulty, mostly out of guesswork rather than rigorous play-testing, because I don't remember ever having _observed_ myself making this deduction in the past. I'm open to changing the settings if someone has a good argument for it.
* Tracks: new parity-based deduction.Simon Tatham2020-02-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is another deduction I've known about in principle for ages but the game didn't implement. In the simplest case, it's obvious: if you can draw a line across the grid that separates the track endpoints from each other, and the track doesn't yet cross that line at all, then it's going to have to cross it at _some_ point. So when you've narrowed down to only one possible crossing place, you can fill it in as definite. IF the track already crosses your line and goes back again, the same rule still applies: _some_ part of your track is on one side of the line, and needs to get to the other. A more sensible way of expressing this is to say that the track must cross the boundary an _odd_ number of times if the two endpoints are on opposite sides of it. And conversely, if you've drawn a line across the grid that both endpoints are on the _same_ side of, then the track must cross it an _even_ number of times - every time it goes to the 'wrong' side (where the endpoints aren't), it will have to come back again eventually. But this doesn't just apply to a _line_ across the grid. You can pick any subset you like of the squares of the grid, and imagine the closed loop bounding that subset as your 'line'. (Or the _set_ of closed loops, in the most general case, because your subset doesn't _have_ to be simply connected - or even connected at all - to make this argument valid.) If your boundary is a closed loop, then both endpoints are always on the same side of that boundary - namely, the outside - and so the track has to cross the boundary an even number of times. So any time you can identify such a subset in which all but one boundary edge is already filled in, you can fill in the last one by parity. (This most general boundary-based system takes in all the previous cases as special cases. In the original case where it looks as if you need odd parity for a line across the grid separating the endpoints, what you're really doing is drawing a closed loop around one half of the grid, and considering the actual endpoint itself to be the place where the track leaves that region again - so, looked at that way, the parity is back to even.) The tricky part of implementing this is to avoid having to iterate over all subsets of the grid looking for one whose boundary has the right property. Luckily, we don't have to: a nice way to look at it is to define a graph whose vertices are grid squares, with neighbouring squares joined by a _graph_ edge if the _grid_ edge between those squares is not yet in a known state. Then we're looking for an edge of that graph which if removed would break it up into more separate components than it's already in. That is, we want a _bridge_ in the graph - which we can find all of in linear time using Tarjan's bridge-finding algorithm, conveniently implemented already in this collection in findloop.c. Having found a bridge edge of that graph, you imagine removing it, and find one of the two connected components it's just broken its previous component up into. That's your subset of grid squares, and now you can count track crossings around the boundary and fill in the bridge edge by parity. When I actually came to implement this, it turned out that the very first puzzle it generated was actually hard for me to solve, because as it turns out, this general analysis is much better at identifying opportunities to use this deduction than I am. A straight line right across the grid is often obvious: a few squares tucked into a complicated half-solved piece of the worldl, not so much. So I'm introducing a new Hard difficulty level, and putting this solution technique in there.
* Tracks: new neighbour-based deduction.Simon Tatham2020-02-26
| | | | | | | | | | | | | This is a deduction I've been using in my own head for years: if you only have one remaining filled square to put in a row, then it can't be any square that has two adjacent edges blocked, because if that square contains anything at all then it would have to be a corner piece, and a corner piece forces the square next to it to be filled as well. I ran across a puzzle today that this implementation couldn't solve, but I solved it fine by hand and found the deduction I was using that wasn't implemented here. Now it is.
* Tracks: add standalone solver program.Simon Tatham2020-02-26
| | | | | | | Having one of these makes it much easier to debug what's going on when the solver can't solve something. Also, now the solver can grade the difficulty of a puzzle, it's useful to expose that feature in a command-line tool.
* Tracks: make solver return max difficulty used.Simon Tatham2020-02-26
| | | | | | | This should speed up game generation, because now we don't have to run the solver _twice_ whenever we want to check that the grid has exactly the intended difficulty. Instead, we can just run it once and check the max_diff output.
* Improve const-correctness in printing API.Asher Gordon2019-12-30
| | | | | Most of the functions in printing.c do not modify their 'document *' argument, and therefore can declare them as 'const'.
* Add printing support for GTK.Asher Gordon2019-12-30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Printing is only available in GTK versions >= 2.10. We can only embed the page setup dialog on GTK >= 2.18, so on a GTK version less than that, we must use a separate page setup dialog. In GTK, printing is usually done one page at a time, so also modify printing.c to allow printing of a single page at a time. Create a separate drawing API for drawing to the screen and for printing. Create a vtable for functions which need to be different depending on whether they were called from the printing or drawing API. When a function is called from the printing API, it is passed a separate instance of the frontend than if it were called from the drawing API. In that instance of the frontend, an appropriate vtable is available depending on whether it was called from the printing or drawing API. The low-level functions used for printing are enabled even if printing is not enabled. This is in case we ever need to use them for something other than printing with GTK. For example, using Cairo as a printing backend when printing from the command line. Enabling the low-level functions even when GTK printing is not available also allows them to be compiled under as many build settings as possible, and thus lowers the chance of undetected breakage. Move the definition of ROOT2 from ps.c to puzzles.h so other files can use it (gtk.c needs it for hatching). Also add myself to the copyright list. [Committer's note: by 'printing', this log message refers to the GTK GUI printing system, which handles selecting a printer, printing to a file, previewing and so on. The existing facility to generate printable puzzles in Postscript form by running the GTK binaries in command-line mode with the --print option is unaffected. -SGT]
* Update the copyright holders list in puzzles.but.Simon Tatham2019-12-28
| | | | | | Asher Gordon points out that when Michael Quevillon was added to the LICENCE file in commit 8af0c2961, he didn't also make it in to the copy of the same list in puzzles.but.
* Don't segfault when no icons are available.Asher Gordon2019-12-25
| | | | | | | | | | When no icons are available, n_xpm_icons will be 0, and menu_about_event() will try to access xpm_icons[n_xpm_icons-1]. Since n_xpm_icons is 0, this becomes xpm_icons[-1] which is an invalid value, causing a segfault. Instead, check if n_xpm_icons is 0, and if so, don't pass any icon to gtk_show_about_dialog().
* Make --screenshot work even in (Cairo) GTK2 builds.Simon Tatham2019-11-13
| | | | | | | | | | | | I had occasion recently to want to take a puzzle screenshot on a machine that didn't have the GTK3 libraries installed, but is advanced enough to build the GTK2+Cairo version of the puzzles. That _ought_ to be good enough to take screenshots using bare Cairo without GTK; I think the only reason why I didn't bother to support it before is because on GTK2, frontend_default_colours() queries the widget style to choose a shade of grey. But we have a fixed fallback shade of grey to use on GTK3, so it's easy to just use that same fallback in headless GTK2.
* .gitignore: add more autotools detritus.Simon Tatham2019-11-13
| | | | | | | One of these days I should sit down and work out exactly when a run of autoconf generates an autom4te.cache directory, and why it can suddenly turn up unexpectedly one day after years of never needing to put it in .gitignore!
* Fix build failure reported in gcc 9.Simon Tatham2019-09-01
| | | | | | | | | | | Apparently gcc 9 is clever enough to say 'Hey, runtime field width in an sprintf targeting a fixed-size buffer!', but not clever enough to notice that the width was computed earlier as the max of lots of default-width sprintfs into the same buffer (so _either_ it's safe, or else - on a hypothetical platform with a 263-bit int - the damage was already done). Added a bounds check or two to keep it happy.
* Fix build failure in C90 mode.Simon Tatham2019-04-14
| | | | Thanks to Anders Höglund for pointing it out.
* Dominosa: move set analysis with doubles into Extreme.Simon Tatham2019-04-13
| | | | | | | | | | | | | | | | | This change doesn't alter the overall power of the Dominosa solver; it just moves the boundary between Hard and Extreme so that fewer puzzles count as Hard, and more as Extreme. Specifically, either of the two cases of set analysis potentially involving a double domino with both squares in the set is now classed as Extreme. The effects on difficulty are that Hard is now easier, and Extreme is at least easier _on average_. But the main purpose is the effect on generation time: before this change, Dominosa Extreme was the slowest puzzle present to generate in the whole collection, by a factor of more than three. I think the forcing-chain deductions just didn't make very many additional grids soluble, so that the generator had to try a lot of candidates before finding one that could be solved using forcing chains but not with all the other techniques put together.
* Dominosa: add area-parity deductions, at Basic level.Simon Tatham2019-04-13
| | | | | | | | | | | | This is a technique I've had on the todo list (and been using by hand) for years: a domino can't be placed if it would divide the remaining area of the grid into pieces containing an odd number of squares. The findloop subsystem is already well set up for finding domino placements that would divide the grid, and the new is_bridge query function can now tell me the sizes of the area on each side of the bridge, which makes it trivial to implement this deduction by simply running findloop and iterating over the output array.
* findloop: alternative query function.Simon Tatham2019-04-13
| | | | | | | | | | | | | | | | | | | | | | | | This one tells you if a graph edge _is_ a bridge (i.e. it has inverted sense to the existing is_loop_edge query). But it also returns auxiliary data, telling you: _if_ this edge were removed, and thereby disconnected some connected component of the graph, what would be the sizes of the two new components? The data structure built up by the algorithm very nearly contained enough information to answer that already: because we assign sequential indices to all the vertices in a traversal of our spanning forest, and any bridge must be an edge of that forest, it follows that we already know the size of _one_ of the two new components, just by looking up the (minindex,maxindex) values for the vertex at the child end of the edge. To determine the other subcomponent's size, we subtract that from the size of the full component. That's not quite so easy because we don't already store that - but it's trivial to annotate every vertex's data with a pointer back to the topmost node of the spanning forest in its component, and then we can look at the (minindex,maxindex) pair of that. So now we know the size of the original component and the size of one of the pieces it would be split into, so we can just subtract.
* Dominosa: another forcing-chain based deduction.Simon Tatham2019-04-13
| | | | | | | | We've already spotted when a domino occurs twice in the _same_ forcing chain. But now we also spot when it occurs twice in the same _pair_ of complementary forcing chains, one in each of the two options. Then it must appear in one of those two places, and hence, can't appear anywhere else.
* Dominosa: another local deduction in Basic level.Simon Tatham2019-04-13
| | | | | | | | | | | | | | This is necessary to solve the following test puzzle that someone sent me in 2006 and I just recovered from my email archive: 6:65111036543150325534405211110064266620632365204324442053 Without this new deduction, the previous solver can't solve that puzzle even at full power, but the half-solved state it leaves the grid in has an obvious move in the top right corner (placing the 6-2 domino vertically in that corner forces two 3-0s to its left). Now that kind of move can be made too, and the solver can handle this puzzle (grading it as Hard).
* Javascript frontend: make Shift- and Ctrl-click work.Simon Tatham2019-04-12
| | | | | | In other front ends, Shift-click is an alternative to the middle button, and Ctrl-click the right button. Apparently I completely forgot to implement this in the JS front end. Better late than never.
* Dominosa: further forms of set analysis.Simon Tatham2019-04-11
| | | | | | | | | | | | | | | | | | I realised that even with the extra case for a double domino potentially using two squares in a set, I'd missed two tricks. Firstly, if the double domino is _required_ to use two of the squares, you can rule out any placement in which it only uses one. But I was only ruling out those in which it used _none_. Secondly, if you have the same number of squares as dominoes, so that the double domino _can_ but _need not_ use two of the squares, then I previously thought there was no deduction you could make at all. But there is! In that situation, the double does have to use _one_ of the squares, or else there would be only the n-1 heterogeneous dominoes to go round the n squares. So you can still rule out placements for the double which fail to overlap any square in the set, even if you can't (yet) do anything about the other dominoes involved.
* Dominosa: be more careful about >= Hard layout.Simon Tatham2019-04-11
| | | | | | Now we don't just ensure that alloc_try_hard arranged a confounder for every domino; we also make sure that the full Basic-mode solver can't place even a single domino with certainty.
* Dominosa: max-difficulty option in the solver.Simon Tatham2019-04-11
| | | | | | Now, as well as grading a puzzle by the highest difficulty it needed during its solution, I can check _how much_ of a given puzzle is soluble if you remove the higher difficulty levels.
* Dominosa: more sophisticated grid layout in >= Hard mode.Simon Tatham2019-04-10
| | | | | | | | | | | | | | | | | | | | The new Hard and Extreme difficulty levels allow you to make a start on a grid even if there is no individual domino that can be easily placed. So it's more elegant to _enforce_ that, in the same way that Hard-mode Slant tries to avoid the initial toeholds that Easy mode depends on. Hence, I've refactored the domino layout code into several alternative versions. The new one, enabled at Hard mode and above, arranges that every domino has more than one possible position, so that you have to use some kind of hard deduction to even get off the ground. While I'm at it, the old layout system has had a makeover: in the course of its refactoring, I've arranged to iterate over the domino values _and_ locations in random order, instead of going over the locations in grid order. The idea is that that might eliminate a directional bias. But more importantly, it changes the previous meaning of random number seeds.
* Dominosa: add presets for Hard and Extreme difficulty.Simon Tatham2019-04-05
| | | | | | I decided not to go all the way up to order-9 Extreme, because that takes a lot of CPU to generate. People can select it by hand if they don't mind that.
* Dominosa: prevent hangs generating tiny hard puzzles.Simon Tatham2019-04-05
| | | | | | As with several other puzzles, the harder difficulty levels turn out to be impossible to generate at very small sizes, which I fudge by replacing them with the hardest level actually feasible.
* Dominosa: add an Extreme difficulty, with forcing chains.Simon Tatham2019-04-05
| | | | | | | | | | | | | | | | | | | | | This technique borrows its name from the Solo / Map deduction in which you find a path of vertices in your graph each of which has two possible values, such that a choice for one end vertex of the chain forces everything along it. In Dominosa, an approximate analogue is a path of squares each of which has only two possible domino placements remaining, and it has the extra-useful property that it's bidirectional - once you've identified such a path, either all the odd domino placements along it must be right, or all the even ones. So if you can find an inconsistency in either set, you can rule out the whole lot and settle on the other set. Having done that basic analysis (which turns out to be surprisingly easy with an edsf to help), there are multiple ways you can actually rule out one of the same-parity chains. One is if the same domino would have to appear twice in it; another is if the set of dominoes that the chain would place would rule out all the choices for some completely different square. There are probably others too, but those are the ones I've implemented.
* Fix a handful of memory leaks in the midend.Simon Tatham2019-04-05
| | | | | I happened to notice these when running dominosa --generate under Leak Sanitiser.
* New utility routine: sort with a context parameter.Simon Tatham2019-04-05
| | | | | | | | | | I'm about to have a need to sort an array based on auxiliary data held in a variable that's not globally accessible, so I need a sort routine that accepts an extra parameter and passes it through to the compare function. Sorting algorithm is heapsort, because it's the N log N algorithm I can implement most reliably.
* Dominosa: update the to-do list.Simon Tatham2019-04-04
| | | | | | | | In particular, reorganise the priorities. I think forcing chains are the most important thing that still wants adding; the parity search would be easy enough but I don't know how often it would actually be _useful_; the extended set analysis would be nice but I don't know how to make it computationally feasible.
* Dominosa: allow set analysis even with adjacency.Simon Tatham2019-04-04
| | | | | | | | | | | | | | | | | I've always had the vague idea that the usual set analysis deduction goes wrong when there are two adjacent squares, because they might be opposite ends of the same domino and mess up the count. But I just realised that actually you can correct for that by adjusting the required count by one: if you have four 0 squares which between them can only be parts of 0-0, 0-1 and 0-2, then the only way this can work is if two of them are able to be the 0-0 - but in that case, you can still eliminate those dominoes from all placements elsewhere. So set analysis _can_ work in that situation; you just have to compensate for the possible double. (This enhanced form _might_ turn out to be something that needs promoting into a separate difficulty level, but for the moment, I'll try leaving it in Hard and seeing if that's OK.)
* Dominosa: add a Hard difficulty which can do set analysis.Simon Tatham2019-04-04
| | | | | | | | This is another thing I've been doing with my own brain for ages as a more interesting alternative to scouring the grid for the simpler deduction that must be there somewhere - but now the solver can understand it too, so it can generate puzzles that _need_ it (or at least need something beyond the simpler strategies it understands).
* Dominosa: new deduction deduce_local_duplicate().Simon Tatham2019-04-04
| | | | | | This is a reasonably simple local deduction I've been using myself for ages, and feel comfortable adding to the existing Basic difficulty level.
* Dominosa: introduce a difficulty system.Simon Tatham2019-04-04
| | | | | | | | | | | Currently, there are just two difficulty levels. 'Basic' is the same as the old solver; 'Trivial' is the subset that guarantees the puzzle can be solved using only the two simplest deductions of all: 'this domino can only go in one place' and 'only one domino orientation can fit in this square'. The solver has also acquired a -g option, to grade the difficulty of an input puzzle using this system.
* Dominosa: rewrite the solver.Simon Tatham2019-04-04
| | | | | | | | | | | The new solver should be equivalent to the previous solver's intelligence level, but it's more usefully split up into basic data-structure maintenance and separate deduction routines that you can omit some of. So it's a better basis to build on when adding further deductions or dividing the existing ones into tiers. The new solver also produces much more legible diagnostics, when the command-line solver is run in -v mode.
* Dominosa: add a command-line solver.Simon Tatham2019-04-04
| | | | | | I've made the existing optional solver diagnostics appear as the verbose output of the solver program. They're not particularly legible at the moment, but they're better than nothing.
* Galaxies: prevent creation of empty undo-chain items.Simon Tatham2019-02-18
| | | | | | | | | | | | | | If you drag an arrow on to a square which is already filled in as part of a completed region, or whose counterpart is filled in, or whose counterpart is actually a dot, then the game can't actually place a double arrow. Previously, it didn't find that out until execute_move time, at which point it was too late to prevent a no-op action from being placed on the undo chain. Now we do those checks in interpret_move, before generating the move string that tries to place the double arrow in the first place. So execute_move can now enforce by assertion that arrow-placement moves it gets are valid.
* Pegs: clear ui->cur_jumping on undo or redo.Simon Tatham2019-02-10
| | | | | | | | Fixes an assertion failure in which you move the keyboard cursor to a peg, press Enter to indicate that you're about to jump it to somewhere, and then instead execute an undo or redo action which replaces the peg with a hole. Thanks to Vitaly Ostrosablin for the report.
* benchmark.pl: replace use of Perl <> with <<>>.Simon Tatham2019-01-25
| | | | | | | I've only just found out that it has the effect of treating the argv words not as plain filenames, but as arguments to Perl default 'open', i.e. if they end in | then the text before that is treated as a command. That's not what was intended!
* Replace fe->preset_menu when we change midend.Simon Tatham2018-12-12
| | | | | | | | | | | | | | | Thanks to Rocco Matano for reporting that in the -DCOMBINED version of the Windows front end, switching games causes a crash because the presets menu from the old midend is still left over in fe, and its presence inhibits the setup code from making a new one. Now we throw it out at the same time as we throw out the old midend itself. Also, the condition 'if (!fe->preset_menu)' was misguided. I think the point of that was to avoid pointlessly tearing down and rebuilding the preset menu when we're _not_ changing game - but that's a cost too small to worry about if it causes the slightest trouble. Now fe->preset_menu should always be NULL at that point in the function, so I've replaced the if with an assert.
* Fix GTK 2 crash introduced by previous commit.Simon Tatham2018-11-25
| | | | | | | | | Moving the snaffle_colours() call earlier is fine in GTK 3, where the potential call to frontend_default_colour doesn't depend on the window already having been created. But it falls over in GTK 2 where it does. Moved the non-headless-mode version of that call back to where it was before the --screenshot change.
* Don't initialise GTK in --screenshot mode.Simon Tatham2018-11-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | I had this idea today and immediately wondered why I'd never had it before! To generate the puzzle screenshots used on the website and as program icons, we run the GTK front end with the --screenshot option, which sets up GTK, insists on connecting to an X server (or other display), draws the state of a puzzle on a Cairo surface, writes that surface out to a .png file, and exits. But there's no reason we actually need the GTK setup during that process, especially because the surface we do the drawing on is our _own_ surface, not even one provided to us by GTK. We could just set up a Cairo surface by itself, draw on it, and save it to a file. Calling gtk_init is not only pointless, but actively inconvenient, because it means the build script depends on having an X server available for the sole purpose of making gtk_init not complain. So now I've simplified things, by adding a 'headless' flag in new_window and the frontend structure, which suppresses all uses of actual GTK, leaving only the Cairo surface setup and enough supporting stuff (like colours) to generate the puzzle image. One awkward build dependency removed. This means that --screenshot no longer works in GTK 2, which I don't care about, because it only needs to run on _one_ platform.
* Add missing 'static' to game-internal declarations.Simon Tatham2018-11-13
| | | | | | | | | Another thing I spotted while trawling the whole source base was that a couple of games had omitted 'static' on a lot of their internal functions. Checking with nm, there turned out to be quite a few more than I'd spotted by eye, so this should fix them all. Also added one missing 'const', on the lookup table nbits[] in Tracks.
* Unruly, Group: reference-count the 'immutable' array.Simon Tatham2018-11-13
| | | | | | | | | I noticed this during the bool trawl: both of these games have an array of flags indicating which grid squares are immutable starting clues, and copy it in every call to dup_game, which is completely unnecessary because it doesn't change during play. So now each one lives in a reference-counted structure, as per my usual practice in similar cases elsewhere.
* Add missing binary 'matching' to .gitignore.Simon Tatham2018-11-13
|
* Add a missing const in unfinished/sokoban.c.Simon Tatham2018-11-13
| | | | | I noticed this when I temporarily enabled compilation of all the unfinished puzzles while doing the bool trawl.
* Use C99 bool within source modules.Simon Tatham2018-11-13
| | | | | | | | | | This is the main bulk of this boolification work, but although it's making the largest actual change, it should also be the least disruptive to anyone interacting with this code base downstream of me, because it doesn't modify any interface between modules: all the inter-module APIs were updated one by one in the previous commits. This just cleans up the code within each individual source file to use bool in place of int where I think that makes things clearer.