aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAge
* Dominosa: use new move_cursor() featuresBen Harris2023-08-09
|
* Use move_cursor() for cursor movement in Same GameBen Harris2023-08-09
| | | | No significant behavioural change.
* Appropriately generate MOVE_NO_EFFECT from '\b' in GuessBen Harris2023-08-09
| | | | This is the case that I care about for KaiOS.
* Use move_cursor() for cursor movement in GuessBen Harris2023-08-09
| | | | | This makes interpret_move() properly return MOVE_NO_EFFECT when the cursor can't move, and simplifies the code as well.
* move_cursor(): handle visible flag; return useful valueBen Harris2023-08-09
| | | | | | | | | | | | | | | | | This adds an extra parameter to move_cursor() that's an optional pointer to a bool indicating whether the cursor is visible. This allows for centralising the common idiom of having the keyboard cursor become visible when a cursor key is pressed. Consistently with the vast majority of existing puzzles, the cursor moves even if it was invisible before, and becomes visible even if it can't move. The function now also returns one of the special constants that can be returned by interpret_move(), so that the caller can correctly return MOVE_UI_UPDATE or MOVE_NO_EFFECT without needing to carefully check for changes itself. Callers are updated only to the extent that they all pass NULL as the new argument. Most of them could now be substantially simplified.
* Guess: define constants for flags OR'ed into peg coloursBen Harris2023-08-09
|
* Guess: move hold marker upward by two pixelsBen Harris2023-08-08
| | | | | | | | | | | | | | | | | | This means that it now potentially overlaps the peg above it (part of the current guess), rather than potentially overlapping the empty hole below. More importantly, it means that the hold marker is erased by the erasure of the rest of the peg area, so there's no need to explicitly draw absent hold markers in the background colour. That in turn means that absent hold markers don't nibble the tops off all the pegs at some tile sizes. Instead of this fix, I could have properly made the hold markers part of the first row of empty holes, but that would have been rather fiddly and I've long thought that the hold markers were too far from the peg that they're holding. I've also removed part of a comment about the drawing order of hold markers that seems to have been obsolete even before this commit.
* Pearl: re-use a single grid structure when generatingBen Harris2023-08-06
| | | | | | | | | | | | | | Pearl generally has to generate quite a lot of candidate loops before it can find one that makes a viable puzzle. Before this change it generated a new grid structure for each of those candidate loops. The result was that grid_new() accounted for over 5% of the puzzle-generation time. Pulling grid_new() out of the loop-generation loop makes "pearl --generate 100 8x8dt#0" about 6% faster on my laptop, while producing precisely the same output. Most of this change is just renaming the "grid" variable in new_clues() so it doesn't collide with the typedef of the same name.
* Same Game: level-triggered keyboard cursor hidingBen Harris2023-08-01
| | | | | | | | | | | | | | | | | Same Game doesn't want to show the keyboard cursor when the game is in a state where no move is possible. Previously, it did this by having game_changed_state() hide the cursor on entry to such a state. That meant that reaching a dead end and undoing out of it hid the cursor, which was confusing. Now the cursor is hidden in game_redraw() if the game is in a dead-end state without changing the displaysel flag in the game_ui. That way, if you undo out of a dead end, the cursor becomes visible again if it was visible before. This does mean that you can move the cursor in a dead-end state without being able to see where it's going. I think that's tolerable, but maybe the cursor keys should be disabled in that state as well.
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in Same GameBen Harris2023-07-31
|
* Same Game: don't hide keyboard cursor on unrecognised keysBen Harris2023-07-31
| | | | | Pressing "undo", for instance, should leave the keyboard cursor visible if it's visible already. Only mouse clicks should hide it.
* Same Game: scale TILE_GAP with tilesizeBen Harris2023-07-31
| | | | | | | | | | | | TILE_GAP represents the gap between the coloured parts of adjacent different-coloured tiles. Rather than a fixed 2 pixels, it's now 1/16 of the tilesize (rounded to nearest). This looks the same at the default tilesize of 32, but behaves better with larger or smaller tilesizes. At tilesizes below 8, the gap disappears altogether, but that seems appropriate: at 7 pixels there's not much space for the inner and outer squares and the cursor, and those are all more important than the gap..
* Flood: don't draw zero-width tile separatorsBen Harris2023-07-30
| | | | | | | | | | Flood's draw_tile() extravagantly uses up to eight rectangles to draw separators around each tile. This could be substantially improved, but a particularly low-hanging optimisation is not do draw them when the separator width is zero. This at least means that Flood completes its initial drawing on my test KaiOS device.
* Same Game: more efficient tile_redrawBen Harris2023-07-30
| | | | | | | | | | | | | | | | | | | | | I've rewritten tile_redraw to reduce the number of calls to draw_rect(). Before, it would generally make five calls to draw_rect() when drawing a tile. Now it makes at most three, and usually two. That's one draw_rect() for each colour that appears in the tile, which is as good as it can get. This reduces the time to draw a large puzzle by about 35% on Firefox 102. This is of significance to me because CanvasRenderingContext2D on my test KaiOS device seems to have a limit on the number of fill() and fillRect() calls that it will tolerate in a short time. This means that if you issue more than 1024 fillRect() calls in rapid succession, the later ones are simply ignored. Same Game's largest preset called draw_rect() so much that it hit this limit. That meant that the right-hand side of the grid didn't get properly drawn when starting a new game. Now that it is less profligate with draw_rect() it fits comfortably within the limit and I get to see the entire grid.
* js: keep colour strings in JavaScript rather than in CBen Harris2023-07-30
| | | | | | | | | | | | | | | The drawing routines in JavaScript used to take pointers to a C string containing a CSS colour name. That meant that JavaScript had to create a new JavaScript string on ever call to a drawing function, which seemed ugly. So now we instead pass colour numbers all the way down into JavaScript and keep an array of JavaScript strings there that can be re-used. The conversion from RGB triples to strings is still done in C, though. This doesn't seem to have fixed either of the bugs I hoped it would, but it does measurably improve drawing performance so I think it's worth doing.
* midend_get_prefs: Don't free memory that we just copied elsewhere.Asher Gordon2023-07-29
| | | | | | | | We were using free_cfg(be_prefs) after we copied be_prefs to all_prefs, but we actually want to use sfree(be_prefs) since we don't want to free each element that has been copied. None of the games so far use string preferences, which is why they haven't been affected by this bug.
* Use the standard game_mkhighlight in Same GameBen Harris2023-07-27
| | | | This should ensure that the cursor is visible on the background.
* Same Game: darken light colours to make keyboard cursor visibleBen Harris2023-07-27
| | | | | | | | | The keyboard cursor in Same Game is white. The default yellow, cyan, and light green were light enough to make the cursor hard to see. I've darkened them all (without changing their hues) so that the cursor is acceptably visible. This doesn't leave an ideal set of colours, but they are at least still adequately distinct from one another.
* Refactor the new icon installation code.Simon Tatham2023-07-14
| | | | | It's horribly repetitive, and we had a list of all the icons' pixel sizes anyway!
* Install the icons to the right location on LinuxEmmanuel Gil Peyrot2023-07-14
| | | | | | | | | | That way, any application displaying the .desktop with its icon will pick the icon size it deems the best one for the current rendering. See the Icon Theme Specification: https://specifications.freedesktop.org/icon-theme-spec/latest/ar01s07.html Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
* Generate more common icon sizesEmmanuel Gil Peyrot2023-07-14
| | | | | | | | | | | | This adds sizes 24×24 (common on Linux desktop, for instance in application bars), as well as 64×64 and 128×128 (common on Linux mobile). I kept the existing border sizes, but using the same one from 44×44 to 96×96 sounds a bit weird, it’d probably be best to revisit them at some point. Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
* grid_edge_bydots_cmpfn: remove dangerous pointer comparison.Simon Tatham2023-07-14
| | | | | | | | | | | Commit e6cdd70df867f06 made the grid_dot structures for a grid no longer be elements of the same array. But I didn't notice that grid_edge_bydots_cmpfn was doing pointer subtraction on them on the assumption that they were. Fixed by comparing the dots' new index fields, which should correspond exactly to their previous positions in the single array, so the behaviour should be just what it was before the change.
* osx.m: avoid division by zero in startConfigureSheet.Simon Tatham2023-07-13
| | | | | | | | | | | | | | | | | | | | | When we set up a configuration sheet, we track the minimum overall width that the controls will fit into (in a variable 'totalw'), and separately, the minimum width needed by each of the left and right columns containing control labels and actual controls ('leftw' and 'rightw'). If totalw > leftw+rightw at the end of the process, then we must expand the two columns so that they have the right sum. However, sometimes leftw+rightw can be zero, while totalw > 0. This occurs if _no_ control in the box was of a type that used the left and right columns for different things, so that the entire loop over the controls only incremented totalw, and not leftw or rightw. For example, in a puzzle such as Cube that defines no preferences of its own, the only control in the preferences pane is midend.c's standard "Keyboard shortcuts without Ctrl" preference, which is C_BOOLEAN and only uses totalw. In that situation, the code for proportionate distribution of the excess divides by zero. So it needs a special case.
* 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.
* grid.c: add dot coordinates to debugging dumps.Simon Tatham2023-07-07
| | | | | | | I wanted these in order to try to check whether all the faces of a grid were being traversed in the right orientation. That turned out not to be the cause of my problem, but it's still useful information to put in diagnostics.
* grid.c: allocate face/edge/dot arrays incrementally.Simon Tatham2023-07-07
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Previously, the 'faces', 'edges' and 'dots' arrays in a grid structure were arrays of actual grid_face, grid_edge and grid_dot structures, physically containing all the data about the grid. But they also referred to each other by pointers, which meant that they were hard to realloc larger (you'd have to go through and rewrite all the pointers whenever the base of an array moved). And _that_ meant that every grid type had to figure out a reasonably good upper bound on the number of all those things it was going to need, so that it could allocate those arrays the right size to begin with, and not have to grow them painfully later. For some grid types - particularly the new aperiodic ones - that was actually a painful part of the job. So I think enough is enough: counting up how much memory you're going to need before using it is a mug's game, and we should instead realloc on the fly. So now g->faces, g->edges and g->dots have an extra level of indirection. Instead of being arrays of actual structs, they're arrays of pointers, and each struct in them is individually allocated. Once a grid_face has been allocated, it never moves again, even if the array of pointers changes size. The old code had a common idiom of recovering the array index of a particular element by subtracting it from the array base, e.g. writing 'f - g->faces' or 'e - g->edges'. To make that lookup remain possible, I've added an 'index' field to each sub-structure, which is initialised at the point where we decide what array index it will live at. This should involve no functional change, but the code that previously had to figure out max_faces and max_dots up front for each grid type is now completely gone, and nobody has to solve those problems in advance any more.
* Move mul_root3 out into misc.c and generalise it.Simon Tatham2023-07-07
| | | | I'm going to want to reuse it for sqrt(5) as well as sqrt(3) soon.
* js: Copy-to-clipboard supportBen Harris2023-07-05
| | | | | | | | | | | | | | Now using the browser's "copy" operation while the focus is in the puzzle will copy the puzzle state to the clipboard. Browsers seem to have odd ideas about whate element to target with the "copy" event: Firefox targets the parent of the <canvas> while Chromium targets the <body>. To cope with these and possible future weirdness I attach the event handler to the document and then look to see if it's plausibly related to the canvas. Arguably we might want to handle a wider range of "copy" events, maybe any where the selection isn't empty. I'm not sure, though, so we'll start with the minimal change.
* Add user preference for Bridges' "G" key (show_hints)Ben Harris2023-06-26
| | | | | | | | | | I missed this in my previous addition of preferences for UI controls (4227ac1fd5dc25c247e7526526079b85e1890766) because it wasn't documented. Now it is documented and it has a preference. I've phrased it as showing possible bridge locations, which doesn't really make clear that "possible" relates only to the locations of islands and not to anything already placed. Improvements welcome!
* Bridges: remove a comment for a deleted line of codeBen Harris2023-06-26
| | | | The line was deleted in 5027095ce2a6dd844ce10489c91dc08bbc174a19.
* Fix control-character generation fixBen Harris2023-06-26
| | | | | | | The change I made in c224416c76e41f284b318adc51f08c3ed11de8e2 was incorrect: I accidentally removed a "return" statement and left in a debugging printf() when I meant to keep the return and drop the printf().
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in GalaxiesBen Harris2023-06-26
| | | | | | | | The boundary between them for mouse clicks probably wants to be revisited because at the moment it's slightly inside the edge of the grid. I tried using INUI() instead of INGRID() but that just gives a different wrong answer, so I may need to actually understand what's going on here.
* Pearl: slightly better handling of clicks outside the gridBen Harris2023-06-26
| | | | | | | | | | | | | | | In Pearl, a mouse-down outside the grid sets ui->ndragcoords to -1. The intended effect of this is to make sure that future drags are ignored, so you can't try to draw a line starting off the grid. However, this also has the effect of clearing any in-progress drag. This can happen if there's a keyboard "drag" in progress at the time. This is arguably wrong, but much more wrong was that interpret_move returned MOVE_UNUSED (and previously NULL) in this case. That meant that the display didn't get updated to show the abandonment of the drag, or the removal of the keyboard cursor that also happened. This commit changes MOVE_UNUSED to MOVE_UI_UPDATE so that at least the effect is correctly visible.
* Keen: fix another misuse of dsf_canonify.Simon Tatham2023-06-25
| | | | | | | | | | Chris Boyle points out that outline_block_structure has a comment saying that we're supposed to have picked a square with a boundary to its left. dsf_canonify no longer guarantees that, but dsf_minimal does. Switch to using that throughout the function. 'keen --generate 10#12345 --print 5x2' failed an assertion before this fix, and now doesn't.
* Reduce the set of keys from which we generate control charactersBen Harris2023-06-25
| | | | | | | | | | | | | | | | | | | | | midend_process_key() has some generic code for converting MOD_CTRL along with a printing character into a control character. This is derived from the Emscripten front-end because browsers don't do this themselves. Most other front ends seem to depend on the platform for this mapping. The mapping was applied to all printable ASCII characters, but this meant that Ctrl+-, which is commonly used by browsers to mean "zoom out" got converted into CR and then CURSOR_SELECT. That was confusing to say the least. So now, the CTRL mapping is only applied to characters in the roughly alphabetic range (0x40 to 0x7f), and MOD_CTRL applied to a character in the range 0x20 to 0x3f gets a return of PKR_UNUSED instead. That means that Ctrl+- in browsers now works properly. I don't think this will affect other front-ends because they're generally a lot less generous about passing MOD_CTRL to the mid-end. I've tested the GTK port nonetheless and not found any problems.
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in FlipBen Harris2023-06-24
|
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in FillingBen Harris2023-06-24
|
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in DominosaBen Harris2023-06-24
| | | | | | | | | The only tricky bit is whether clicking precisely on the diagonal of a square (which never has any effect on the game) is MOVE_UNUSED or MOVE_NO_EFFECT. I decided that having single-pixel lines in the middle of the grid causing events to be passed back to the environment would be very confusing, so they're MOVE_NO_EFFECT. Clicking entirely outside the grid, on the other hand, returns MOVE_UNUSED.
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in PegsBen Harris2023-06-23
| | | | | | | | Slightly more complicated than usual, because Pegs has irregularly shaped grids so detecting whether a click is inside requires more than just a range check. Also fixed a typo in a nearby comment.
* Blackbox: correct FROMDRAW() macro for C division semanticsBen Harris2023-06-22
| | | | | | | | | | | Integer division in C rounds towards zero, so if you want it to consistently round down you need to ensure that the arguments are positive. FROMDRAW() didn't do that, so clicks off the top and left corners of the grid got treated as being in the top row or left column (row and column 0) rather than ignored. This commit fixes the macro so that it offsets its argument upward before the division and compensates afterwards.
* spectre-test: support raster-mode tiling generation.Simon Tatham2023-06-18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is the most efficient way to apply the combinatorial coordinate system. As described in my original article (and mentioned again in the followup one), you can walk along a horizontal or vertical line of the tiling, identifying which edge of each tile the line will leave it by, and computing the location and coordinates of the next tile beyond that edge, so that you visit every tile intersected by the edge. By doing one iteration, say vertically up the left of your target area, and using the tiles you find as starting points for a series of perpendicular sub-iterations spaced closely enough not to miss any tiles, you can arrange to visit every tile intersecting your target region, without having ever had to store a large BFS queue of tiles left to visit. You only have to keep a small bounded number of coordinate variables for the whole run, so you can generate a very large patch of tiling with minimal memory and CPU time. You can even arrange not to emit any duplicates, without having to actually store the tiles you've already visited, by checking whether the y-coordinate of the previous horizontal iteration will have visited the same tile already. For Spectres, an extra wrinkle (almost literally) is that they're not convex, so a horizontal line can visit the same one twice, with another tile in between. So another part of de-duplication is noticing _that_: is the edge through which we've just entered this tile the first one we would have seen while traversing our line? If not, then trust that it's been emitted already. As a proof of concept (since I claimed it would work in my writeup article), and in case anyone wants larger tilings than actual Loopy will conveniently give you, I've implemented that algorithm in spectre-test. However, the actual grid generation for Loopy still uses the more memory-intensive breadth-first search: because that's what I implemented first (it's more likely to detect its own errors); because if I changed it now it would invalidate game descriptions (from all of two days' worth of live play, but even so); and because the linear space requirement isn't an important cost for Loopy, which is actually going to _store_ the whole grid after it's generated, so it needed linear space _anyway_.
* spectre_adjacent: optionally report dst_edge.Simon Tatham2023-06-18
| | | | | | | | Previously, you'd ask this function 'What lies on the other side of edge #i of this Spectre tile?' and it would tell you the identity of another Spectre. Now it will also tell you which _edge_ of that Spectre adjoins the specified edge of the input one. This will be used in the extra spectre-test mode I'm about to add.
* spectre.c: expose a couple more internal functions.Simon Tatham2023-06-18
| | | | spectre-test will want to use these for an additional generation mode.
* Spectre tiling: add a comment with some reference URLs.Simon Tatham2023-06-17
| | | | | I meant to fold this into yesterday's main commit, but there's always something that gets forgotten.
* Loopy / grid.c: support the new Spectre monotiling.Simon Tatham2023-06-16
| | | | | | | | | | | | | | This uses a tile shape very similar to the hat, but the tiling _structure_ is totally changed so that there aren't any reflected copies of the tile. I'm not sure how much difference this makes to gameplay: the two tilings are very similar for Loopy purposes. But the code was fun to write, and I think the Spectre shape is noticeably prettier, so I'm adding this to the collection anyway. The test programs also generate a pile of SVG images used in the companion article on my website.
* Fix some unused-variable warnings.Simon Tatham2023-06-16
| | | | | | | | | A test-build with a modern clang points out a number of 'set but not used' variables, which clang seems to have got better at recently. In cases where there's conditioned-out or commented-out code using the variable, I've left it in and added a warning-suppressing cast to void. Otherwise I've just deleted the variables.
* Add a 'core' library alongside 'common'.Simon Tatham2023-06-16
| | | | | | | | | | | | | | | | | The 'core' library contains almost all the same objects as 'common', but leaves out hat.c. And the auxiliary program 'hatgen' now links against that slightly reduced core library instead of 'common'. This avoids a dependency loop: one of hatgen's jobs is to generate hat-tables.h, but hat-tables.h is a dependency of it. Of course, the generated hat-tables.h is already committed, so this doesn't present a bootstrapping problem in a normal build. But if someone modifies hatgen.c in order to regenerate hat-tables.h, and does so in a way that makes it uncompilable, they can't rebuild hatgen and try again! Of course you can always revert changes with git, but it's annoying to have to. Better to keep the dependencies non-cyclic in the first place.
* hat-test: support SVG output.Simon Tatham2023-06-16
| | | | I want to generate an SVG diagram for an upcoming article.
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in PearlBen Harris2023-06-16
|
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in CubeBen Harris2023-06-15
|