Crosswords codebase overview
These documents describes the way the two applications are written. It doesn’t go into a lot of detail in each section, but gives a rough overview and hints to understand the codebase better.
This page covers the main game. In addition, it refers to code from the shared internals section. There are five major sections of code:
Downloaders and convertors
The primary window in the game is the
PlayWindow. This window has a
list that lets the user select puzzle sets. It contains the main
GtkStack to swap between views and manages the control flow between
them. It also has game controls are included in the top level, which
are forwarded to other parts of the application.
It also has a preferences dialog (
PlayPreferencesDialog) with the global
settings, as well as a help overlay.
The PlayWindow has three game phases that it can be in at any time that determines how it appears:
PUZZLE_PHASE_MAIN: Display the list to select puzzle sets
PUZZLE_PHASE_PICKER: Display the puzzle picker
PUZZLE_PHASE_GAME: Display the game board
Each phase as represented by a widget. The latter two are described in more detail below.
Puzzle sets represents collection of puzzles based around a
theme. They are shipped as a GResource with an internal config file
puzzle.config), defining that collection. They also optionally have
puzzles contained within the resource.
These are represented by a
PuzzleSet object which is used to control
and load aspects of the collection. All configuration settings are
loaded from puzzle.config and contained within the
object. It will also load all puzzles from disk — or resource — at
creation and store them in
PuzzleSetModel. This implements the
GListModel interface, which can be used to follow changes to the model.
Puzzle sets are referred to by a globally unique id field defined in the puzzle.config file. This identifier is used in many places. It’s used as a location on disk to store the puzzle set, it’s used as a path within the GResource, and it’s used to refer to the puzzle set through out the code.
The PuzzleSet also acts as a factory, creating both puzzle pickers and the game board (see below). Currently, due to limitations in the current code base, only one PuzzleSet can exist at a time. This will have to be changed before we can present multiple PlayWindows at the same time.
PuzzlePicker widget manages the user’s interaction with the
PuzzleSet. It is deceptively complex code. It has to present the
puzzles in a way for the user to handle them, and controls the
downloader (see below). The picker’s appearance is defined in
puzzle.config and is read from the PuzzleSetConfig object.
PuzzlePicker is a base widget with two variants so far:
PickerList. The grid presents a grid view of puzzles with
snapshots as can be seen in the cats-and-dogs puzzle set. The list is
less puzzle-like and doesn’t require solving prior puzzles before
advancing. This list uses a PuzzleSetModel as its base model.
The game board is a widget that displays the puzzle. Currently, the
only one supported is the
PlayXword which lets you play
crosswords. In the future, other puzzle types (such as acrostics or
word searches) could be supported through their own widget type. It
is created and configured by PuzzleSets and put in the main stack.
The PlayXword contains only one widget — the custom
PlayXwordColumn. There’s a somewhat complicated
(rationale)[play-xword-column.md] for this custom container, but the
end result is that changes to it take extra work to implement. The
main game widget within the PlayXword is a
PlayGrid. It also has
custom widgets for displaying clues and rows-of-clues (
The PlayXword controls the
XwordState and uses it to update other
widgets. It’s entirely stateless. Setting a new
IPuzPuzzle on the
PlayXword will result in a valid appearance, even if it’s entirely
different. To implement undo/redo it has a
PuzzleStack that manages
the IPuzPuzzle internally.
Downloaders and convertors
PuzzleDownloader is how puzzles are imported by the game. They
are initiated by the user pressing the download button in the puzzle
picker, or from the user passing a uri in on the command-line.
The downloader has a multi-stage process. First, it can optionally use a command provided by the puzzle set to download a file from a location, and store it in /tmp. This command is configured by the PuzzleSetConfig, and is managed by the PuzzleDownloader object.
Once the downloader has copied the file to /tmp, it is optionally
passed through the
ipuzconvertor utility to the game’s
directory. This convertor knows how to handle .ipuz and .jpz files. It
is written to be modular and should be able to be extended to other
formats relatively easily.