aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAge
* Untangle: replace manual int64 bodging with int64_t.Simon Tatham2023-05-01
| | | | | | Where possible, that is. If our compilation environment has provided int64_t, we can just make our int64 type be that, and not have to mess around with multi-word arithmetic at all.
* Replace check of __STDC_VERSION__ with HAVE_STDINT_H.Simon Tatham2023-05-01
| | | | | | Just spotted this in puzzles.h. We don't need to guess any more from the C standards version whether stdint.h is available: we've actually checked _precisely that_ in cmake, so it's better to use the answer.
* Loopy: slightly increase the size of dots.Simon Tatham2023-04-29
| | | | | | | | | | | In the Hats tiling, each tile has two consecutive edges collinear. When both edges are turned on, i.e. drawn in black just like the dot, it becomes _just slightly_ tricky to spot the dot in the middle of that straight line, which is important if you're counting edges around the face to check a clue. Increasing the radius from 2/32 to 3/32 of tile size is far too big. 2.5/32 seems reasonable, though.
* hat-test: fix memory leaks.Simon Tatham2023-04-29
| | | | I ran this again today with Leak Sanitiser on, which complained.
* Pattern: Reduce row clue spacing if there are lots of themBen Harris2023-04-24
| | | | | | | | Normally, we put two spaces between row clues, but if there are a lot then even with a smaller font size they might overflow the left edge of the window. To compensate, go down to one space between clues instead of two if there are a lot of them. On my laptop the GTK build demonstrates overflow with "13x1:1//1//1//1//1//1//1/1.1.1.1.1.1.1".
* Pattern: switch to small font when there are many row cluesBen Harris2023-04-24
| | | | | | | | | | | | | | | | | | | | If you have a particularly large number of clues in a row, they can end up falling off the left edge of the window. This used not to be a problem because the rendering code would squash them closer together if necessary. But then I switched to drawing them all as a single string (so that two-digit row clues would get enough space with a large font) and that broke the old mechanism. Now we detect if there are enough clues that our conservative guess at the string length looks like overflowing in the big font, and switch to the small one if necessary. If we had a drawing call to measure a string then we could be cleverer about this, but we don't. This problem can be demonstrated with "7x1:1//1//1//1/1.1.1.1" in the GTK port with the fonts my laptop has. I think overflow can still occur even with a small font, so once I've demonstrated that I'll try to fix it.
* Correct a comment in Pattern's clue-drawing codeBen Harris2023-04-24
|
* Emscripten: change the localStorage key used for preferences.Simon Tatham2023-04-24
| | | | | | | | | | | | I picked pathname + "#preferences" because Ben suggested either that or pathname + "/preferences", and I thought the former sounded neater. But Ben now suggests that it might be better to avoid using # in the key, just in case anyone should later want to use the whole URL _including_ the fragment ID as the key for some other localStorage usage - for example, autosaved progress per puzzle _instance_, keying off the puzzle URL with a fragment ID in the game description. So, per Ben's replacement suggestion, change the "#" to a space.
* Support user preferences in the Emscripten frontend.Simon Tatham2023-04-24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Here, user preferences are stored in localStorage, so that they can persist when you come back to the same puzzle page later. localStorage is global across a whole web server, which means we need to take care to put our uses of it in a namespace reasonably unlikely to collide with unrelated web pages on the same server. Ben suggested that a good way to do this would be to store things in localStorage under keys derived from location.pathname. In this case I've appended a fragment id "#preferences" to that, so that space alongside it remains for storing other things we might want in future (such as serialised saved-game files used as quick-save slots). When loading preferences, I've chosen to pass the whole serialised preferences buffer from Javascript to C as a single C string argument to a callback, rather than reusing the existing system for C to read the save file a chunk at a time. Partly that's because preferences data is bounded in size whereas saved games can keep growing; also it's because the way I'm storing preferences data means it will be a UTF-8 string, and I didn't fancy trying to figure out byte offsets in the data on the JS side. I think at this point I should stop keeping a list in the docs of which frontends support preferences. Most of the in-tree ones do now, and that means the remaining interesting frontends are ones I don't have a full list of. At this moment I guess no out-of-tree frontends support preferences (unless someone is _very_ quick off the mark), but as and when that changes, I won't necessarily know, and don't want to have to keep updating the docs when I find out.
* emcc.c: remove savefile_read_ctx.Simon Tatham2023-04-24
| | | | | | It wasn't ever used! Looks as if I pasted it in here from one of the other implementations, before realising that wasn't how I was going to read save files at all.
* emcc.c: missing (void) in a function definition.Simon Tatham2023-04-24
| | | | This isn't C++, ahem.
* Fix bounds check in buffer_append.Simon Tatham2023-04-24
| | | | | | | | | | We're about to append one character to the buffer _and_ put a \0 after it, so we need the buffer to be at least _two_ characters longer than where the current position is. I think this bug would have had a hard time showing up in normal use, but I managed to trigger it by completely messing up a prototype Emscripten preferences implementation, and a good thing too.
* Support user preferences in the Mac frontend.Simon Tatham2023-04-24
| | | | | | The low-level load and save routines are basically copy-pasted from gtk.c, with only minor changes to deal with the different locally appropriate config file location and the lack of savefile_write_ctx.
* GTK save_prefs: fix a wrongly sourced error report.Simon Tatham2023-04-24
| | | | | | After a failed rename(), we should find out what went wrong by looking in errno itself, not in wctx->error which reported a problem in the previous step.
* Slant: actually check ui->swap_buttons.Simon Tatham2023-04-23
| | | | | | Apparently I wrote the new-look code that sets up the prefs field in the UI, but didn't remember to rewrite the code in interpret_move that ought to read it.
* GTK: stop referring to &thegame in prefs I/O functions.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | I had to do this in the Windows front end to cope with compiling in both COMBINED and one-puzzle mode: you can't refer to &thegame because it might not exist in this build, so instead, if you want to load/save preferences for a given midend, you ask that midend for _its_ game structure, and use that to make the necessary file names. On Unix, we don't have COMBINED mode. But now I've thought of it, this seems like a good idiom anyway, for the sake of futureproofing against the day someone decides to implement combined mode on Unix. delete_prefs() doesn't get passed a frontend _or_ a midend, so that just has to take a bare 'const game *' parameter, and in main() we pass &thegame to it. So that will still need changing in a combined mode, if one is ever added.
* Support user preferences on Windows.Simon Tatham2023-04-23
| | | | | This is done using basically the same methods as on Unix, and just translating the system calls in save_prefs to a different API.
* make_prefs_path(): tolerate NULL inputs.Simon Tatham2023-04-23
| | | | | | | | Just noticed that if prefs_dir() returns NULL, we'll already have passed it to this function before the calling functions get round to checking. I think it's less wordy all round to make this helper function propagate NULL than to mess about with lots of extra if statements in between all the calls.
* Document the new Net preference.Simon Tatham2023-04-23
| | | | Naturally, I only remembered to do this after 'git push'.
* Net: preference for how loop highlighting interacts with locking.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | Net's loop highlighting detects any loop in the current state of the grid. I've occasionally found that to be a bit of a spoiler, since sometimes it can point out a deduction I should make before I've figured it out for myself - e.g. when I've just locked all but two of the squares involved in the loop, and the last two _just happen_ to be oriented so as to complete the loop. In that situation I'd prefer if the loop _didn't_ immediately light up and point out to me that I need to arrange that those squares aren't connected to each other. The simple answer is to only count edges connecting two _locked_ squares, for the purposes of loop detection. But this is obviously unacceptable to some players - in particular, those who play without the locking feature at all. So it should be a user preference.
* Fix failure to update me->ui when changing preferences.Simon Tatham2023-04-23
| | | | | | | | | This must have been introduced during a last-minute rebase, or similar - I'm sure it worked a couple of days ago! Because midend_set_config passed a NULL game_ui to midend_set_prefs, the latter would make up a temporary UI and apply the changes to that. As a result, the midend's main UI would keep the original backend preferences, and those would also be the ones saved to the config file.
* GTK: add a command-line --delete-prefs option.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | If you want to remove your saved preferences for a puzzle for any reason (perhaps because of one of those unsympathetic managers, or perhaps because it's become corrupted in some way), you can of course manually go and find the config file and delete it. But if you're not sure where it is, it's helpful to have a method of telling the puzzle itself to delete the config file. Perhaps it would be useful to expose this in the GUI as well, though I'm not quite sure where is the best place to put it. (_Perhaps_ it would also be useful to have a more thorough option that deleted _all_ puzzles' configurations - although that would involve telling every separate puzzle binary in this collection what all the other ones' names are, which would be an entirely new build headache.)
* Support preferences in the GTK frontend.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | Finally, some user-visible behaviour changes as a payoff for all that preparation work! In this commit, the GTK puzzles get a 'Preferences' option in the menu, which presents a dialog box to configure the preference settings. On closing that dialog box, the puzzle preferences are enacted immediately, and also saved to a configuration file where the next run of the same puzzle will reload them. The default file location is ~/.config/sgt-puzzles/<puzzlename>.conf, although you can override the .config dir via $XDG_CONFIG_HOME or override the Puzzles-specific subdir with $SGT_PUZZLES_DIR. This is the first commit that actually exposes all the new preferences work to the user, and therefore, I've also added documentation of all the current preference options.
* Universal preference option for one-key shortcuts.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | With this option turned off (it's on by default), the single-letter keyboard shortcuts like 'q' for quit and 'n' for new game don't function any more. You can still access the same functions via more complicated shortcuts like Ctrl-Q or Ctrl-N, and front ends can provide any other UI they like for the same operations, but this way, people aren't at risk of blowing away half an hour of puzzling with one misaimed key. This is a thing people have occasionally asked for, and I've generally resisted on the grounds that I have sympathy for people playing puzzles at work who need to be able to close the game quickly when an unsympathetic boss wanders by. But now we have a preferences system, we can cater to those people _and_ the ones who don't mind. More immediately useful: adding _at least one_ universal preference in the initial version of this system means that there will be no games with no preference options at all, and therefore, no need to put conditionals all through the participating frontends to deal with whether the Preferences menu option should even be provided. That would have been a waste of time because all those conditionals would just have to be removed again as soon as the first universal preference came along - so adding an easy one _now_ means we can save the effort of putting in the temporary conditionals in the first place.
* Keep a set of preferences in the midend.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This commit introduces a serialisation format for the user preferences stored in game_ui, using the keyword identifiers that get_prefs is required to write into its list of config_item. As a result, the serialisation format looks enough like an ordinary config file that a user could write one by hand. The preferences for the game backend are kept in serialised form in me->be_prefs. The typical use of this is to apply it to a just-created game_ui by calling midend_apply_prefs(), which deserialises the prefs buffer into a list of config_item and passes it to the backend's set_prefs function, overwriting the preference fields (but no others) of the game_ui. This is duly done when creating a new game, when loading a game from a save file, and also when printing a puzzle. To make the latter work, document_add_puzzle now takes a game_ui (and keeps ownership of it afterwards), and passes that to the backend's compute_size and print functions. The backend's own get_prefs and set_prefs functions are wrapped by midend_get_prefs and midend_set_prefs. This is partly as a convenience (it deals with optionally constructing a game_ui specially to call the backend with), but mostly so that there will be a convenient place in the midend to add standard preferences applying across all puzzles. No cross-puzzle preferences are provided yet. There are two external interfaces to all this, and in this commit, neither one is yet called by any frontend: A new pair of midend functions is exposed to the front end, called midend_load_prefs and midend_save_prefs. These have a similar API to midend_serialise and midend_deserialise, taking a read/write function pointer and a context. So front ends that can already load/save a game to a file on disk should find it easy to add a similar set of functions loading/saving user preferences. Secondly, a new value CFG_PREFS is added to the enumeration of configuration dialog types, alongside the ones for the Custom game type, entering a game description and entering a random seed. This should make it easy for frontends to offer a Preferences dialog, because it will operate almost exactly like three dialogs they already handle.
* Require games to accept new_ui(NULL) if they have preferences.Simon Tatham2023-04-23
| | | | | | | | | | | This will be necessary in the next commit, so that the midend can make a game_ui out of nothing in order to store preferences in it. The only game actually affected by this requirement is Pearl, which has a preference (GUI style) and also allocates space based on the game_state's grid size to store the coordinates of a path being dragged, so if there is no game_state, it can't do the latter - which is OK, because it also won't be expected to.
* Generalise the midend serialisation callbacks.Simon Tatham2023-04-23
| | | | | | | | | These are currently only used for internal serialisation of midend state in order to undo/redo across New Game, and are named accordingly. But I'm about to reuse them for another set of serialisation functions, so they'll want more general names, and also need to be moved higher up in the source file so as to have been defined where I'll want to use them.
* New backend functions: get_prefs and set_prefs.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | These are similar to the existing pair configure() and custom_params() in that get_prefs() returns an array of config_item describing a set of dialog-box controls to present to the user, and set_prefs() receives the same array with answers filled in and implements the answers. But where configure() and custom_params() operate on a game_params structure, the new pair operate on a game_ui, and are intended to permit GUI configuration of all the settings I just moved into that structure. However, nothing actually _calls_ these routines yet. All I've done in this commit is to add them to 'struct game' and implement them for the functions that need them. Also, config_item has new fields, permitting each config option to define a machine-readable identifying keyword as well as the user-facing description. For options of type C_CHOICES, each choice also has a keyword. These keyword fields are only defined at all by the new get_prefs() function - they're left uninitialised in existing uses of the dialog system. The idea is to use them when writing out the user's preferences into a configuration file on disk, although I haven't actually done any of that work in this commit.
* Move per-puzzle ad-hoc getenv preferences into game_ui.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Environment variables that set specific settings of particular puzzles, such as SLANT_SWAP_BUTTONS, LIGHTUP_LIT_BLOBS and LOOPY_AUTOFOLLOW, now all affect the game behaviour via fields in game_ui instead of being looked up by getenv in the individual functions that need to know them. The purpose of this refactoring is to put those config fields in a place where other more user-friendly configuration systems will also be able to access them, once I introduce one. However, for the moment, there's no functional change: I haven't _yet_ changed how the user sets those options. They're still set by environment variables alone. All I'm changing here is where the settings are stored inside the code, and exactly when they're read out of the environment to put into the game_ui. Specifically, the getenvs now happen during new_ui(). Or rather, in all the puzzles I've changed here, they happen in a subroutine legacy_prefs_override() called from within new_ui(), after it's set up the default values for the settings, and then gives the environment a chance to override them. Or rather, legacy_prefs_override() only actually calls getenv the first time, and after that, it's cached the answers it got. In order to make the override functions less wordy, I've altered the prototype of getenv_bool so that it returns an int rather than a bool, and takes its default return value in the same form. That way you can set the default to something other than 0 or 1, and find out whether a value was present at all. This commit only touches environment configuration specific to an individual puzzle. The midend also has some standard environment-based config options that apply to all puzzles, such as colour scheme and default presets and preset-menu extension. I haven't handled those yet.
* Pass a game_ui to compute_size, print_size and print.Simon Tatham2023-04-21
| | | | | | | I'm about to move some of the bodgy getenv-based options so that they become fields in game_ui. So these functions, which could previously access those options directly via getenv, will now need to be given a game_ui where they can look them up.
* Missing const in configure() documentation.Simon Tatham2023-04-21
| | | | | Commit de67801b0fd3dfa changed the 'name' field from char * to const char *, but didn't update the docs to match. Better late than never.
* Filling: switch to using dsf_minimal in minimize_clue_set.Simon Tatham2023-04-21
| | | | | | | | | Turns out that was another case where we were assuming the canonical dsf element was also the minimal one, because we process the clues in order and expect to start a linked list at the canonical element of each region and then add the rest of the cells to the existing one. Easily fixed by using the minimal element again.
* Loopy: draw even faint lines using draw_thick_line.Simon Tatham2023-04-20
| | | | | | | | | | | | | When playing on a high-DPI screen and running Loopy at a large tile size to compensate, the faint lines along grid edges in LINE_NO state are exceptionally hard to see, because they're still only one pixel wide even when everything else has been expanded. This has been true for ages, but it's more significant now, because the new Hats tiling mode needs a lot of counting of edges in all states (since the hats have 14 edges in total!), and it's awkward not to be able to see exactly where the LINE_NO edges are, or where an edge between two other tiles meets this hat's outline.
* Turn off Leak Sanitiser in the icons build.Simon Tatham2023-04-20
| | | | | | | | | | | | | | | | | | | | | If you configure a Linux build of Puzzles with -fsanitize=address, it will fail during the icons build, because the icon-maker programs leak memory when they're run, and by default, this causes ASan to report all the memory leaks at the end of the program *and then exit 1*. I don't think 'just fix the memory leaks' is a viable answer. _Some_ of the leaks come from the Puzzles code, and could be fixed by being more punctilious about freeing everything before exiting (although that is not necessary for any actually sensible purpose and would _only_ serve to stop Leak Sanitiser complaining). But some are outside the Puzzles code completely, apparently from fontconfig. So even if we fixed all the leaks we could find, we wouldn't prevent the failure status. When I want to build with ASan, I've been working around this by setting ASAN_OPTIONS=detect_leaks=0 in the environment before running the build command. Easier all round if we just do that _inside_ the cmake setup.
* Update the documentation for the dsf functions.Simon Tatham2023-04-20
| | | | Easier to do this all in one go after I've finished messing about.
* Actually rewrite the dsf implementation.Simon Tatham2023-04-20
| | | | | | | | | | | | | | | | | | | | | | This rewrite improves the core data structure implementation in two ways. Firstly, when merging two equivalence classes, we check their relative sizes, and choose the larger class's canonical element to be the overall root of the new class tree. This minimises the number of overlong paths to the root after the merge. Secondly, we defer path compression until _after_ the two classes are merged, rather than do it beforehand (via using edsf_canonify as a subroutine) and then have to do it wastefully again afterwards. The size-based root selection was what we _used_ to do, and delivers the better asymptotic performance. I reverted it so that Keen could track the min of each equivalence class. But since then I've realised you can have the asymptotic goodness _and_ min-tracking if you store the minima separately from the main data structure. So now Keen does that, and other clients don't have to pay the cost. Similarly, the flip tracking is now a cost that only users of flip dsfs have to pay, because a normal one doesn't store that information at all.
* Reorganise the dsf API into three kinds of dsf.Simon Tatham2023-04-20
| | | | | | | | | | | | | | | | | | This is preparing to separate out the auxiliary functionality, and perhaps leave space for making more of it in future. The previous name 'edsf' was too vague: the 'e' stood for 'extended', and didn't say anything about _how_ it was extended. It's now called a 'flip dsf', since it tracks whether elements in the same class are flipped relative to each other. More importantly, clients that are going to use the flip tracking must say so when they allocate the dsf. And Keen's need to track the minimal element of an equivalence class is going to become a non-default feature, so there needs to be a new kind of dsf that specially tracks those, and Keen will have to call it. While I'm here, I've renamed the three dsf creation functions so that they start with 'dsf_' like all the rest of the dsf API.
* Introduce a new dsf_equivalent() function.Simon Tatham2023-04-20
| | | | | Not very interesting, but the idiom for checking equivalence via two calls to dsf_canonify is cumbersome enough to be worth abbreviating.
* Remove conditioned-out dsf diagnostic code.Simon Tatham2023-04-20
| | | | | | | | print_dsf was declared in puzzles.h, but never called, and its definition was commented out. So it probably wouldn't still have worked anyway. The other commented-out printfs in that file don't look very useful either, and they just mean more stuff will need messing about with as I continue to refactor.
* Remove size parameter from dsf init and copy functions.Simon Tatham2023-04-20
| | | | | | | | | Now that the dsf knows its own size internally, there's no need to tell it again when one is copied or reinitialised. This makes dsf_init much more about *re*initialising a dsf, since now dsfs are always allocated using a function that will initialise them anyway. So I think it deserves a rename.
* Store a size field inside the DSF type.Simon Tatham2023-04-20
| | | | | | This permits bounds-checking of all inputs to dsf_canonify and dsf_merge, so that any out-of-range values will provoke assertion failure instead of undefined behaviour.
* Actually make DSF an opaque structure type.Simon Tatham2023-04-20
| | | | | | | | | | This makes good on all the previous preparatory commits, which I did separately so that each one individually has a reasonably readable diff, and all the mechanical changes are separated out from the rewrites that needed actual thought. Still no functional change, however: the DSF type wraps nothing but the same int pointer that 'DSF *' used to store directly.
* Declare all dsfs as a dedicated type name 'DSF'.Simon Tatham2023-04-20
| | | | | | | In this commit, 'DSF' is simply a typedef for 'int', so that the new declaration form 'DSF *' translates to the same type 'int *' that dsfs have always had. So all we're doing here is mechanically changing type declarations throughout the code.
* Remove a direct use of dsf internals in Filling.Simon Tatham2023-04-20
| | | | | The expression 'dsf[foo] >> 2' already has a sensible wrapper function, but Filling wasn't bothering to call it.
* Consistently use snew_dsf to allocate dsfs.Simon Tatham2023-04-20
| | | | | All remaining cases where a dsf was allocated via snewn(foo, int) are removed by this change.
* Use a dedicated copy function to copy dsfs.Simon Tatham2023-04-20
| | | | | | | | | Previously we were duplicating the contents of a dsf using straight-up memcpy. Now there's a dsf_copy function wrapping the same memcpy. For the moment, this still has to take a size parameter, because the size isn't stored inside the dsf itself. But once we make a proper data type, it will be.
* Use a dedicated free function to free dsfs.Simon Tatham2023-04-20
| | | | | No functional change: currently, this just wraps the previous sfree call.
* Stop putting dsfs in existing scratch int arrays.Simon Tatham2023-04-20
| | | | | | | | | | | | | | | | | | | | | | | I'm going to work towards turning 'dsf' into an opaque type, so that I can improve its implementation without breaking clients. The first step is to deal manually with puzzles that currently reuse a single array of ints for multiple purposes, some of which are dsf and some are not. In divvy_rectangle_attempt, 'tmp' was used as an int array and later a dsf, and I've made a new 'tmpdsf' to be the latter. In Dominosa, solver->pc_scratch2 was sometimes a dsf, and now there's a new solver->dsf_scratch. In Map, parse_edge_list() needed a dsf internally and then never exported it; it expected to be passed an array of 2*w*h ints and used the second half as a dsf. Now it expects only w*h ints, and allocates its own dsf internally, freeing it again before returning. And in Tents, find_errors() was allocating a single block of 2*w*h ints and using the second half of it as a dsf, apparently just to save one malloc. Now we malloc and free the dsf separately.
* Remove an unnecessary extern function declaration.Simon Tatham2023-04-20
| | | | | | | unfinished/separate.c had its own declaration of divvy_rectangle(), duplicating the one in puzzles.h. Probably that was where the declaration originally lived, before I moved it out into the main header.
* Move obfuscator tests into obfusc.c.Simon Tatham2023-04-16
| | | | | | | | I just found these self-tests lying around in mines.c under an #ifdef that nobody ever enables. Let's put them somewhere more sensible! We already have a separate tool for working with the obfuscation system in a puzzle-independent way, and it seems reasonable to put them in there.