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:
Main application
Puzzle sets
Puzzle pickers
Game board
Downloaders and convertors
Main application
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 setsPUZZLE_PHASE_PICKER
: Display the puzzle pickerPUZZLE_PHASE_GAME
: Display the game board
Each phase as represented by a widget. The latter two are described in more detail below.
Related code:
Puzzle sets
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 PuzzleSetConfig
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.
Related code:
Puzzle pickers
The 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: PickerGrid
and 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.
Related code:
Game board
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 (PlayClueRow
and PlayClueList
).
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.
Related code:
Downloaders and convertors
The 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 ipuz-convertor
utility to the game’s
directory. This convertor knows how to handle .ipuz, .puz, .jpz, and
.xd files. It is written to be modular and should be able to be extended
to other formats relatively easily.
Related code: