Crosswords Player 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 uses an
AdwOverlaySplitView to provide a sidebar 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
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 maintains its state in a PlayState struct. It has
four game phases that it can be in at any time that determines how it
appears:
GAME_PHASE_WELCOME: Display the welcome screen with tips and iconsGAME_PHASE_PICKERS: Display the puzzle picker for the current setGAME_PHASE_ADD: Display the “Add/Remove Puzzle Sources” screenGAME_PHASE_XWORD: Display the active game board
Each phase is represented by a widget. The pickers and game board 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: