A command-line toolkit for reading, validating, and extracting data from .puz
crossword files. It parses puzzles to JSON, validates directories in bulk,
inspects raw file structure, and exports clue/answer data. It's built on the
puz-parse library.
- Installation
- Commands
- Parsing to JSON
- Validating a directory
- Inspecting a file
- Output format
- License
cargo install puzOr build from source:
git clone https://github.com/mwln/puz.rs.git
cd puz.rs
cargo install --path cliPrebuilt binaries for common platforms are also attached to each release.
puz [FILES]... parse puzzles to JSON (default)
puz parse [FILES]... parse puzzles to JSON (explicit form)
puz validate <DIR> bulk-validate every .puz file under a directory
puz export <DIR> export clue/answer pairs as JSON Lines
puz dump header <FILE> declared dimensions, clue count, bitmask, version
puz dump grid <FILE> the solution and blank grids, with any mismatches
puz dump strings <FILE> title, author, copyright, the clue list, and notes
puz dump clues <FILE> clue numbering vs. the file's declared/provided clues
puz dump answers <FILE> clues paired with their answers, as a JSON array
puz inspect sections <FILE> extension sections (GRBS, RTBL, GEXT, ...)
The dump and inspect commands read the file bytes directly rather than fully
parsing, so they still produce useful output for files that fail to parse.
Colored, Unicode-styled output is used when writing to a terminal. It is
disabled automatically when output is redirected (e.g. piped to a file), when
the NO_COLOR environment variable is set, or with the global --no-color
flag.
Running puz with file arguments (no subcommand) parses them to JSON, the same
as puz parse. Parse warnings are printed to stderr.
Parse a file and print JSON to stdout:
puz puzzle.puzPretty-print the output:
puz puzzle.puz --prettyParse several files at once (returned as a JSON array):
puz puzzle1.puz puzzle2.puz --prettyFor a single file, drop the surrounding array and print just the object:
puz puzzle.puz --single --prettyWrite to a file instead of stdout:
puz puzzle.puz --output output.json| Option | Description |
|---|---|
<FILES>... |
One or more .puz files to parse. Supports shell globs. |
-o, --output <FILE> |
Write output to a file instead of stdout. |
-p, --pretty |
Indent the JSON for readability. |
-s, --single |
For a single file, output the puzzle object directly instead of wrapping it in an array. |
Recursively parse every .puz file under a directory and print a summary of
parse errors and warnings:
puz validate ./puzzles| Option | Description |
|---|---|
<DIR> |
Directory to scan recursively for .puz files. |
--verbose |
Print a line for every file, including clean ones. |
--errors-only |
Print only hard parse failures, not warnings. |
Extract every clue paired with its answer across a directory, as JSON Lines (one JSON object per line):
puz export ./puzzles > clues.jsonlEach line has the fields reliably found in the file, plus the source path:
{
"file": "<path>",
"title": "<string>",
"author": "<string>",
"direction": "across|down",
"number": <int>,
"clue": "<string>",
"answer": "<string>"
}Outlet and date are not emitted: they are inconsistent inside .puz files and
usually live in the directory layout. Derive them from file downstream. A
progress summary is written to stderr, so redirecting stdout gives a clean
data file. Files that fail to parse are skipped with a note on stderr.
The output streams, so it composes with standard tools:
puz export ./puzzles | jq -r 'select(.answer == "OREO") | .clue' | sort | uniq -cThe dump and inspect commands show a file's raw structure. They are useful
for understanding an unusual puzzle or debugging one that does not parse.
puz dump header puzzle.puz # dimensions, clue count, bitmask, version
puz dump grid puzzle.puz # solution + blank grids, black-square mismatches
puz dump strings puzzle.puz # title/author/copyright, numbered clues, notes
puz dump clues puzzle.puz # computed clue numbering vs. the file's clue list
puz dump answers puzzle.puz # clues paired with answers, as JSON
puz inspect sections puzzle.puz # GRBS / RTBL / GEXT extension sectionsdump clues is handy for puzzles whose declared clue count does not match the
grid geometry: it shows the across/down slot counts, the declared num_clues,
the number of clue strings in the file, and any extras.
dump answers prints a JSON array of every clue with the answer read from the
solution grid, one object per entry:
[
{ "direction": "across", "number": 1, "clue": "Cry of disgust", "answer": "BAH" },
{ "direction": "down", "number": 1, "clue": "...", "answer": "BRAVE" }
]The answer is the solution-grid characters for that slot, taken as-is, so a
rebus or theme cell shows whatever character the grid stores. Pass --pretty
to indent the output. This is a quick way to check that a puzzle's clues and
answers line up.
The parse command (and the bare puz FILES... default) prints a JSON array of
parsed puzzles, one entry per input file. With --single and exactly one file,
it prints that puzzle object on its own.
Each puzzle object mirrors the puz-parse data model:
info: metadata (title, author, copyright, notes, width, height, version, scrambled flag, diagramless flag)grid: the blank and solution grids, each an array of row stringsclues: across and down clues keyed by clue number, plus the raw clue listextensions: rebus, circled, and given squares, when the puzzle has them
See the puz-parse README for what each field contains.
Licensed under the MIT License.