aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAge
...
* js: use the "load" event for loading save filesBen Harris2023-04-03
| | | | | | | | | | | | | | | This is in place of the "loadend" event. In Chromium, (and in the specification), "loadend" is triggered not only when the file is loaded but also when loading fails. Obviously when loading fails we don't want to try to parse the (nonexistent) resulting file. Using the "load" event works better, since it's only fired on success, and we can also have an "error" handler to report problems with loading files, albeit with no detail at all. This doesn't seem to make any difference in Firefox, which in my testing fires "load" and "loadend" on success and nothing at all on failure.
* js: Load save files into the C side incrementallyBen Harris2023-04-03
| | | | | | | | | | | | | | | | | | | | | | | | | Before this commit, JavaScript Puzzles loaded a save file by pushing the entire file onto the Emscripten stack and then reading it from there. This worked tolerably for typical save files, but Emscripten's stack defaults to only having 64 kiB of space. That meant that trying to load something that wasn't a real save file tended to cause a stack overflow. I expect that at least some real save files would suffer from the same problem. The stack overflow would generally cause a JavaScript exception and then leave the stack pointer outside the stack, so that any future attempt to call into C would fail as well. To fix this, arrange that the C function for reading data from the save file calls out to JavaScript. The JavaScript can then copy just the requested data into the caller's buffer. We can't pass a JavaScript function pointer to C, but since only one file can be loaded at a time, we can just have a global variable that's the current loading callback. There might still be a problem if you try to load a stupendously large file, since I think FileReader.readAsArrayBuffer() reads the whole file into the browser's RAM. It works on my laptop with files up to a few hundred megabytes, though.
* js: load games using FileReader.readAsArrayBuffer()Ben Harris2023-04-02
| | | | | | | | | | | | | | | | Using .readAsText() meant that trying to load a non-text file (for instance something that's not a save file at all) would generate an "RuntimeError: index out of bounds". This would then leave the Emscripten runtime in a broken state. It might even be possible for a real save file not to be valid UTF-8, for instance if it came from a platform that used a different character encoding for random seeds. There's still a problem with opening very large files, apparently because Emscripten tries to stuff the entire file onto the C stack. That will probably have to be fixed by properly exposing the incremental file-loading API to JavaScript.
* hat-test: more scaling and clipping options.Simon Tatham2023-04-02
| | | | | | | | | | | | | | | | This adds the ability to turn off hat-test's normal scaling of the bounding box to fit on an A4 page, which I intended for printing test patches (but never actually found a need to print one). The --unscaled mode seems more useful if you're planning to turn the output into an image, e.g. to use as a desktop background. Also added --clip, which generates a rectangle completely covered in hats (i.e. shows any hat that overlaps the output rectangle at all), as opposed to the normal mode which omits any hat that doesn't fit _entirely_ in the output rectangle (more similar to what Loopy wants). Actually generating a desktop background by this method is still a bit fiddly to get right, but it's better than before.
* hat-test: fix array underrun.Simon Tatham2023-04-02
| | | | | Having _checked_ whether a hat index in my four-colouring maps was -1, I then went ahead and used it as an array index anyway, oops!
* 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.
* Move hat-test into its own source file.Simon Tatham2023-04-02
| | | | | | | | | | | | | | | I noticed while hacking on hat-test recently that it's quite awkward to be compiling a test main() program that lives in a source file also built into the Puzzles support library, because every modification to main() also triggers a rebuild of the library, and thence of all the actual puzzles. So it's better if such a test main() has its own source file. In order to make hat-test work standalone, I've had to move a lot of hat.c's internal declarations out into a second header file. This also means making a bunch of internal functions global, which means they're also in the namespace of programs other than hat-test, which means in turn that they should have names with less implicit context.
* Magnets: add a check that magnets don't wrap between linesBen Harris2023-04-01
| | | | | | | | | | | | There was nothing in Magnet's description validation to prevent there being the left end of a magnet at the right end of a row and the right end of a magnet at the left end of the row below. Indeed as far as I can such a game (e.g. 3x3:..2,2..,...,1.1,TLRB*LRLR) plays entirely correctly except that one magnet is discontinuous. While this worked, it was entirely an artefact of the particular memory layout that Magnets uses and shouldn't have been allowed, so I've added an additional validation rule to stop it.
* Correct a range check in Magnets' layout verificationBen Harris2023-03-31
| | | | | | Squares in the grid are numbered from 0, so the upper limit check needs to use "<=" rather than "<". Without this, invalid descriptions can cause a read overrun off the end of the board.
* hat-test: option to generate four-coloured hat tilings.Simon Tatham2023-03-31
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This commit is purely frivolous even by Puzzles standards, in that it's totally unrelated to any actual puzzle. But I know at least one person has already used the 'hat-test' tool in this code base to generate a patch of hat tiling for decorative purposes, so it's useful in its own right. Also, now that I've worked out _how_ to do this, it's a shame not to keep the code. Of course, any tiling of the plane _can_ be four-coloured, just by the Four Colour Theorem. But for a tiling with structure it's nicer if the colouring is related to the structure in some way. And there's a reasonably nice explicit construction that does just that: the paper introducing the tiling observes that if each reflected hat is fused with a particular one of its neighbours, the resulting tiling is graph-theoretically equivalent to a tiling of the plane by hexagons. And _that_ tiling can be three-coloured, in a unique way up to colour choices. This induces a four-colouring of the hat tiling in which the reflected hats have a colour to themselves, and everything else is coloured the same as its corresponding hexagon in the three-colouring. Actually implementing this turns out not to be too difficult using my coordinate system. I hand-wrote tables giving a patch of colouring for each of the four kitemaps; then, whenever two kitemaps meet, you can determine how the colours map to each other by looking at the overlapping tiles. So I can have hat-test work out the colour of each tile as it goes. So hat-test now supports a '--fourcolour' option to apply this colouring to the output tiling.
* Require a grid description for hats gridBen Harris2023-03-31
| | | | | Without this, you can cause a null-pointer dereference by passing Loopy a game description with no grid description, like "10x10t16:".
* hat-test: allow choosing a random number seed.Simon Tatham2023-03-30
| | | | | | | The default one is always the same, because the main purpose of this tool is debugging. But one person has already wanted to use it for actually generating a tiling patch for another use, so let's make it easier to vary the randomness!
* Hats: choose the tiling's starting hat more uniformly.Simon Tatham2023-03-30
| | | | | | | This fills in the missing piece of commit 6f75879e9fe7cb5, which was trying to make the output patches of tiling as uniformly random as possible across the whole space of possible ones. I fixed every _intermediate_ step in the algorithm, but forgot the starting one!
* Hats: factor out the parent-choosing system.Simon Tatham2023-03-30
| | | | NFC, but I'm about to want to use it again elsewhere.
* Loopy: widen clip rectangle for redrawing clues.Simon Tatham2023-03-28
| | | | | | | | The new Hats tiling generates a lot of clues that are 2-digit numbers. At large puzzle sizes, the previous clip rectangle didn't quite include the ends of such a number, meaning that if the number had to be redrawn in red to highlight an error, the leftmost and rightmost parts of the text would remain black.
* hat-test: alternative data output mode to write Python.Simon Tatham2023-03-28
| | | | | | This mode emits a sequence of calls to an imaginary Python function. Should be useful to anyone wanting to post-process the tiling in any way.
* hat-test: allow specifying tiling size on the command line.Simon Tatham2023-03-28
| | | | | I'm tired of recompiling every time I want a different size of test patch.
* Hats tiling: make hat-test draw each hat in one go.Simon Tatham2023-03-28
| | | | | | | | | | | | | By introducing a second callback between the client of hat.c and the maybe_report_hat function, I enable the test main() to provide a different version of that callback, so that instead of enumerating each kite, it can directly generate a Postscript path per actual hat. This should make it more useful to people wanting to generate hat patterns for any other purpose. The internal callback gets more details than the external one; in particular, it receives a HatCoords, so that it can colour hats based on their position in the hierarchical structure.
* Hats tiling: more uniform parent selection.Simon Tatham2023-03-28
| | | | | | | | | | | | | This tweak improves the uniformity of the generated patches of hat tiling, by selecting from (the closest 32-bit approximation I can get to) the limiting probability distribution of finite patches in the whole plane. This shouldn't invalidate any grid description that contains enough coordinates to uniquely specify a piece of tiling - in particular, any generated by the game itself. But if anyone's been brave enough to hand-type a grid description in the last two days and left off some of the coordinates, then those might be invalidated.
* Fix references to the renamed 'auxiliary' directory.Simon Tatham2023-03-27
| | | | | | I renamed it in a hurry this morning after the first report of a git error message on Windows. Now I realise that several source files referred to the old name, and also need fixing.
* Rename the 'aux' subdirectory to avoid Windows restrictions.Simon Tatham2023-03-27
| | | | | James Harvey points out that Windows still forbids calling a file 'aux' in any context. Even a directory. Gaaah.
* Loopy / grid.c: new grid type, 'Hats'.Simon Tatham2023-03-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The big mathematical news this month is that a polygon has been discovered that will tile the plane but only aperiodically. Penrose tiles achieve this with two tile types; it's been an open question for decades whether you could do it with only one tile. Now someone has announced the discovery of such a thing, so _obviously_ this mathematically exciting tiling ought to be one of the Loopy grid options! The polygon, named a 'hat' by its discoverers, consists of the union of eight cells of the 'Kites' periodic tiling that Loopy already implements. So all the vertex coordinates of the whole tiling are vertices of the Kites grid, which makes handling the coordinates in an exact manner a lot easier than Penrose tilings. What's _harder_ than Penrose tilings is that, although this tiling can be generated by a vaguely similar system of recursive expansion, the expansion is geometrically distorting, which means you can't easily figure out which tiles can be discarded early to save CPU. Instead I've come up with a completely different system for generating a patch of tiling, by using a hierarchical coordinate system to track a location within many levels of the expansion process without ever simulating the process as a whole. I'm really quite pleased with that technique, and am tempted to try switching the Penrose generator over to it too - except that we'd have to keep the old generator around to stop old game ids being invalidated, and also, I think it would be slightly trickier without an underlying fixed grid and without overlaps in the tile expansion system. However, before coming up with that, I got most of the way through implementing the more obvious system of actually doing the expansions. The result worked, but was very slow (because I changed approach rather than try to implement tree-pruning under distortion). But the code was reusable for two other useful purposes: it generated the lookup tables needed for the production code, and it also generated a lot of useful diagrams. So I've committed it anyway as a supporting program, in a new 'aux' source subdirectory, and in aux/doc is a writeup of the coordinate system's concepts, with all those diagrams. (That's the kind of thing I'd normally put in a huge comment at the top of the file, but doing all those diagrams in ASCII art would be beyond miserable.) From a gameplay perspective: the hat polygon has 13 edges, but one of them has a vertex of the Kites tiling in the middle, and sometimes two other tile boundaries meet at that vertex. I've chosen to represent every hat as having degree 14 for Loopy purposes, because if you only included that extra vertex when it was needed, then people would be forever having to check whether this was a 13-hat or a 14-hat and it would be nightmarish to play. Even so, there's a lot of clicking involved to turn all those fiddly individual edges on or off. This grid is noticeably nicer to play in 'autofollow' mode, by setting LOOPY_AUTOFOLLOW in the environment to either 'fixed' or 'adaptive'. I'm tempted to make 'fixed' the default, except that I think it would confuse players of ordinary square Loopy!
* KaiOS: be more careful detecting the presence of KaiAdsBen Harris2023-03-22
| | | | | | | | | Now that we catch JavaScript errors and report them to the user, I could tell that the way we were detecting the presence of getKaiAd() didn't work because it caused an alert every time a build without KaiAds was started. Detecting the presence of a global variable is slightly tricky, but explicitly looking for it on "window" works and isn't very ugly.
* Turn on PUZZLES_SHOW_CURSOR on KaiOSBen Harris2023-03-22
| | | | | | | | Most KaiOS devices are primarily keyboard-based, so this seems like a reasonable approach. I've also switched to specifying boolean values as JSON booleans because that works now.
* Treat environment variable values beginning with "T" as trueBen Harris2023-03-22
| | | | | | So a value is true iff it begins with 'T', 't', 'Y', or 'y'. This is mostly so that naively converting JSON "true" to a string will work properly, but it should keep LISPers happy too.
* Add an environment variable to control initial cursor visibilityBen Harris2023-03-22
| | | | | | | | | | | | | If you define PUZZLES_INITIAL_CURSOR=y, puzzles that have a keyboard cursor will default to making it visible rather than invisible at the start of a new game. Behaviour is otherwise the same, so mouse actions will cause the cursor to vanish and keyboard actions will cause it to appear. It's just the default that has changed. The purpose of this is for use on devices and platforms where the primary or only means of interaction is keyboard-based. In those cases, starting with the keyboard cursor invisible is weird and a bit confusing.
* New shared function, getenv_bool()Ben Harris2023-03-22
| | | | | | | | | | | | | This provides a standard way to get a boolean from an environment variable. It treats the variable as true iff its value begins with 'y' or 'Y', like most of the current implementations. The function takes a default value which it returns if the environment variable is undefined. This replaces the various ad-hoc tests of environment variable scattered around and mostly doesn't change their behaviour. The exceptions are TOWERS_2D in Towers and DEBUG_PUZZLES in the Windows front end. Both of those were treated as true if they were defined at all, but now follow the same rules as other boolean environment variables.
* Galaxies: skew grid generation in favour of wiggliness.Simon Tatham2023-03-12
| | | | | | | | | | | | | | | | | | | Ben complained yesterday that Galaxies had a nasty habit of generating games whose solution was a boring set of rectangles. Now that even at Normal mode the solver is better at coping with wiggly tentacled regions, it seems like a good moment to fix that. This change arranges that when we initially generate a filled grid, we try ten times, and pick the wiggliest of the grids we found. This doesn't make any boring rectangle-filled grid _impossible_ - players will still have to stay on their toes, and can't rely 100% on at least a certain number of wiggles existing - but it makes the interesting grids a lot more likely to come up. This skew happens before checking solubility. So it doesn't increase grid generation time by a factor of ten (as it would if we generated ten _soluble_ grids and picked the wiggliest). It's still a speed drop, of course, but a more modest one than that.
* Galaxies: remove 'solver_recurse_depth' in live use.Simon Tatham2023-03-12
| | | | | | | | | | | | | | | | | | | It's horrible to have static mutable state in the live puzzle game. What if some downstream wanted to run the system in multiple threads? For purposes of limiting the recursion depth, we now pass an 'int depth' argument to each call to solver_state_inner(). So in the normal build of the actual puzzle, the static variable isn't needed at all. We only include it in binaries that are going to want to use it for printing indented diagnostics: the CLI solver program, and the live puzzle only if DEBUGGING is defined. The rest of the time, it's absent. A side effect of this change is that when the recursion code makes a guess at a particular tile, the message about that is now indented to the _outer_ level instead of the inner one, because the previous 'depth++' and 'depth--' statements wrapped the whole loop rather than just the recursive call to the solver inside. This makes recursive solving much easier to follow!
* Galaxies: add some higher Unreasonable presets.Simon Tatham2023-03-12
| | | | 10x10 and 15x15 Unreasonable are now feasible, so why not include them?
* Galaxies: remove the 'maxtries' system.Simon Tatham2023-03-12
| | | | | | | | | | | | | | | | | | Most games in this collection don't have one. If you ask them for a hard puzzle, they'll just keep looping round until they actually manage to deliver one. We try to arrange that the standard presets don't take too long to generate, but if the user turns up the game size _and_ uses an expensive difficulty level, our view is that that's up to them. After fixing the bug from the previous commit in which the Galaxies recursion depth limit was not actually doing anything, even 15x15 Unreasonable now generates happily in under a second. So I don't see any reason why Galaxies should be an exception. Hence, now if you ask Galaxies for an Unreasonable puzzle, you _will_ get a puzzle that it grades as Unreasonable, even if that takes a long time to generate.
* Galaxies: fix recursion depth limit in solver.Simon Tatham2023-03-12
| | | | | | | | | | | | | | | | | | | | | The static variable 'solver_recurse_depth' is _mostly_ used by the standalone solver, to appropriately indent the solver diagnostics for the current recursion level. So most uses of it are guarded by an '#ifdef STANDALONE_SOLVER' statement, or some equivalent (such as being inside the solvep() macro). One exception is the check that limits the recursion depth to 5, to avoid getting hung up forever on a too-hard game. Unfortunately, this check depends on the variable actually incrementing when we recurse another level - and it wasn't, because the increment itself was under ifdef! So the generator in live Galaxies could recurse arbitrarily deep, and generate puzzles that the standalone solver found too hard _even_ at Unreasonable mode. Removed the ifdefs, so that solver_recurse_depth is now incremented and decremented. Also, make sure to initialise the depth to 0 at the start of a solver run, just in case it had a bogus value left over from a previous run.
* galaxiessolver: fix soak-test mode.Simon Tatham2023-03-12
| | | | | It called new_game_desc with aux=NULL. But new_game_desc unconditionally writes through aux, expecting it to be valid always.
* Galaxies: new deduction by counting liberties of exclaves.Simon Tatham2023-03-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I've often noticed that Galaxies on 7x7 Unreasonable often generates puzzles that _I_ don't feel as if I had to use recursion and backtracking to solve, suggesting that there's an efficient mode of reasoning available that the puzzle could be using and isn't. One reason for this is that sometimes Galaxies gives up on trying to generate an Unreasonable puzzle (if its MAXTRIES counter runs out) and knowingly falls back to Normal. But that's not the only reason. The other day I got the puzzle 7x7:gsjgzhfedwgzhd, which Galaxies's own solver rates as Unreasonable, and I still think it should be Normal. The full solution to that puzzle is this: +-+-+-+-+-+-+-+ |y x| o | | | + +-+-+-+-+ +o+ | | | | o | | + + o + + +-+-+ | | | | | | | + +-+-+ +-+o+ + | o |o| |o| + +-+-+ +-+-+ + | | | | o | | + + o + +-+-+-+ | | | | | +-+-+-+ + +o+ + | o |x y| | +-+-+-+-+-+-+-+ and Galaxies's Normal-mode solver gets stuck on it at the point where it's managed to deduce that the tiles labelled 'x' can't possibly be associated with any dot other than leftmost dot on the centre row, but is then unable to figure out that the tiles labelled 'y' must also be associated with that dot. But clearly they must, because they're boxed in on all other sides (by the grid edge, and by tiles whose associations are totally obvious), so if either 'x' tile is to find _any_ path back to its home dot, it must go through the neighbouring 'y' tile. So, this commit adds the missing deduction: we use a dsf to identify 'exclaves' (connected sets of tiles all associated to the same dot, but which do not actually contain the dot), and for each exclave we count its 'liberties' (unassociated tiles bordering the exclave, i.e. which would extend the exclave if associated to the same dot). Any exclave with only one liberty must extend into that tile, or else it would be cut off completely from its home dot. In this case, each 'x' tile is an exclave by itself, with 'y' its only liberty. (And once that deduction is done, the pair {x,y} become a larger exclave, which can be deduced in the same way to connect to the next tile.) I think this is a deduction rule simple and obvious enough that it should go in at Normal mode. I've been using it all along in my own play, and was surprised to find the game wasn't already taking it into account. In addition, in a quick cross-test of the two versions, _most_ 7x7 Normal games generated by the modified Galaxies are still rated as Normal by the old less powerful solver. So it doesn't extend the difficulty of Normal mode by very much, if at all. Another benefit is that this should make Normal puzzles more likely to contain twisty regions of this type. Also, of course, the usual effect of adding extra deductions at levels below Unreasonable means that actually Unreasonable puzzles become that much more tricky! I have a couple of ideas for extending this technique to be more powerful still (filled in as comments at the top of the file). For the moment, I've just done the most obvious version. Perhaps the others might need to go in at a higher difficulty level.
* Galaxies: add a missing \n in a diagnostic.Simon Tatham2023-03-12
|
* Galaxies: fix edge coordinates in a diagnostic.Simon Tatham2023-03-10
| | | | | | The coordinates in this 'Setting edge' message from the solver don't match the coordinates of the edge that is actually set. No wonder the solver output was confusing!
* Tracks: missing \n in debug statement.Simon Tatham2023-03-10
|
* Add missing 'static' on dputs().Simon Tatham2023-03-10
| | | | | This fixes a build failure due to -Wmissing-prototypes if you build with -DDEBUGGING.
* Further restrict the keys that can have MOD_NUM_KEYPADBen Harris2023-03-05
| | | | | | | | | In all front ends other than JavaScript, and in JavaScript prior to my recent changes, MOD_NUM_KEYPAD can only be set on ASCII digits. For now, enforce this in midend_process_key() so as to avoid inconsistency between front ends. It might be useful to be able to distinguish keypad versions of more keys, but that should be co-ordinated across the code-base.
* Treat keypad-Enter as CURSOR_SELECT, same as Return.Simon Tatham2023-03-04
| | | | | | | | | | | | | | | | | | | | | The two Return/Enter keys have always been treated the same in the past, but a user complained today that Enter was no longer functioning as CURSOR_SELECT in the web puzzles. This happened in commit 9dbcfa765ba59a8, apparently because the web front end is now translating the Enter key as MOD_NUM_KEYPAD | '\r' instead of just '\r', and the new code in midend.c is only stripping off MOD_NUM_KEYPAD for values >= 0x80. Now it strips MOD_NUM_KEYPAD off C0 control characters as well, so that only _printable_ ASCII characters can still have that modifier when they get to the backend - i.e. you can tell numpad digits from normal digits, and ditto +-* etc. But keypad Enter is now turned into plain '\r' by the modifier removal code, and then into CURSOR_SELECT. Other front ends still aren't even bothering to set MOD_NUM_KEYPAD on the code sent by Enter. But that's fine, because now midend.c officially doesn't care whether they do or not.
* KaiOS: be more cautious about determining whether KaiAds is presentBen Harris2023-03-01
| | | | | | | Just checking for the getKaiAd() function by doing "if (!getKaiAd ..." throws a ReferenceError if it's not defined, and now my error box catches that. Using "if (getKaiAd === undefined ..." seems to be acceptable, though.
* js: Use the Pointer Events API, but only to capture the pointerBen Harris2023-03-01
| | | | | | | | | | | | Element.setPointerCapture() captures both pointer and mouse events, so we can use our existing mouse handlers and just have a minimal pointerdown handler that calls setPointerCapture(). This saves (or at least postpones) all the tedious rewriting referred to in ecd868ac. This means that now drags beyond the puzzle area work in WebKit-based browsers, and we don't get deprecation warnings in current Gecko-based ones. Older Gecko-based browsers continue to use Element.setCapture() and hence still work correctly.
* Inertia: insist that solutions must be non-emptyBen Harris2023-02-26
| | | | | | | | | | | | Any solution actually generated by the solver will contain at least one move, because it refuses to solve games that are already solved. However, a save file might contain an empty "solve" move. This causes an uninitialised read when execute_move() then tries to check if the next move is in accordance with the solution, because the check for running off the end of the solution happens after that. We now avoid this by treating a zero-length "solution" as an invalid move.
* Correctly handle some short save filesBen Harris2023-02-26
| | | | | | | | | | | | | A save file that ended in the middle of a value before the "SAVEFILE" field had been loaded would cause a read from uninitialised memory. While technically undefined behaviour this was practically pretty harmless. Fixed by handling unexpected EOF here the same an unexpected EOF anywhere else. This bug could be demonstrated by loading a truncated save file like this in a build with MemorySanitizer enabled: SAVEFILE:41:Simo
* Map: reduce maximum sizeBen Harris2023-02-26
| | | | | validate_desc relies on being able to calculate 2*wh in an int, so the maximum grid size is at most INT_MAX/2.
* Be more careful with type of left operand of <<Ben Harris2023-02-26
| | | | | | | On a 32-bit system, evaluating 1<<31 causes undefined behaviour because 1 is signed and so it produces signed overflow. UBSan has spotted a couple of occasions where this happens in Puzzles, so in each case I've converted the left operand to the unsigned result type we actually want.
* More cleverness in midend_process_key()Ben Harris2023-02-23
| | | | | | | | | | | It now strips off modifier flags from keys that shouldn't have them and maps printable characters with MOD_CTRL to the corresponding control characters. It also catches Ctrl+Shift+Z because that obviously belongs in the midend. I've updated the JavaScript front-end to take advantage of these changes. Other front ends are unchanged and should work just as they did before.
* Don't give the libFuzzer version of fuzzpuzz a special nameBen Harris2023-02-23
| | | | | | | | | | | | I've changed my mind already. The other versions of fuzzpuzz all have different command-line interfaces anyway, so I think the best approach is to just accept that and decide that precisely how fuzzpuzz works isn't a defined API. Fuzzing is inherently not an end-user activity, so I think it's acceptable to make it a bit inconsistent. This means that in Clang builds you get the non-libFuzzer version of fuzzpuzz by default (so you can use it with other fuzzers), but if you turn on WITH_LIBFUZZER then you'll get the libFuzzer version instead.
* Try to clean up fuzzpuzz a bitBen Harris2023-02-23
| | | | | | | I've separated out the various versions of main(), which has helped a little bit. I've also stopped using fmemopen() since libFuzzer might work on Windows. But I think I probably still have something fundamentally wrong in my approach.