Files
deck_in_a_dash/docs/architecture.md
T

4.7 KiB

Architecture

See CLAUDE.md for build commands and the sharp edges to know before changing the engine. This document is the visual overview.

Components

Component architecture

  • res/ holds the source assets: the 22 Rider-Waite-Smith Major Arcana card images and A. E. Waite's Pictorial Key to the Tarot PDF (public domain), which tarot_data.c's card and position text is condensed from.
  • engine/third_party/astronomy/ is a vendored, unmodified copy of cosinekitty/astronomy (MIT), pinned to a specific commit — see its VENDORED.md.
  • engine/src/ is the core engine. Everything except main.c, reading.c's reading_print_text/reading_print_html functions, and i18n.c's file-loading half is free of stdio/CLI assumptions, specifically so it can be linked into the Pebble watchapp later without rework.
    • rng.c — deterministic string-seeded PRNG (FNV-1a + splitmix64).
    • tarot.c / tarot_data.c — the Celtic Cross draw and its content.
    • astro.c — natal chart + daily transits, built on the vendored Astronomy Engine.
    • i18n.c — loads a key=value translation file into a small fixed-size catalog; every display-text lookup elsewhere falls back to the built-in English string if no catalog is loaded or a key is missing from it.
    • reading.c — orchestrates the above into one DailyReading and (desktop-only) renders it as text or HTML.
    • main.c — CLI argument parsing, the only consumer of reading_print_*.
  • engine/i18n/ holds the translation source files (en.lang, de.lang, ...), one key=value file per language — see CLAUDE.md for the key-naming convention.
  • dist/ is generated by the single root Makefile: the deck-engine and interpreter-cli binaries, a copy of the card art under img/, a copy of the translation files under i18n/, run-engine.sh/run-interpreter.sh, and a user.properties template that's seeded once and never overwritten by later builds.
  • The Pebble watchapp does not exist yet. When it's built, it will call reading_generate() directly and walk the returned struct to lay out its own screens — it has no reason to touch reading_print_*.

Dataflow

Dataflow for one reading

A single reading is produced by one call: reading_generate(seed, birth, utc_moment, &out) in reading.c. It fans out to three independent computations and combines their results:

  1. astro_compute_natal_chart(birth)NatalChart — geocentric ecliptic sign/degree for the Sun through Pluto, the Ascendant, and whole-sign houses, all from the birth date/time/location.
  2. astro_compute_daily_transits(utc_moment, natal_chart)DailyTransits — today's planetary positions, the Moon phase, and every transiting-to-natal aspect within orb. Depends on the natal chart (read-only) to compute aspects against it.
  3. tarot_draw_celtic_cross(seed)CelticCrossSpread — ten cards with position and upright/reversed orientation, fully determined by the seed string alone.

These three results are combined into one DailyReading struct, which is either rendered by reading_print_text/reading_print_html (used by the CLI) or, in the future, walked directly by watchapp UI code. See input-output-format.md for the exact fields and output formats.

Every display string produced along the way (tarot_data.c's card and position text, astro.c's body/sign/moon-phase/aspect names, and reading.c's section headers) is looked up in i18n.c's translation catalog, with the built-in English text as the fallback. main.c loads the catalog from --lang/--i18n-dir (or run-engine.sh's lang= property) before calling reading_generate, so this is orthogonal to the three computations above — it only affects how their results are rendered as text, not the underlying TarotCard/ZodiacSign/etc. enum values.

Both entry points (direct CLI flags, or run-engine.sh pulling the seed/date from the OS clock and birth data from user.properties) converge on the same main.c argument parsing before calling reading_generate — there is exactly one code path from parsed input to a reading. run-interpreter.sh reuses those same OS-clock/user.properties inputs, but calls deck-engine --format json and pipes the result into interpreter-cli instead of printing a reading directly.

Regenerating the diagrams

The diagrams are Graphviz sources in docs/diagrams/, rendered to PNG:

cd docs
dot -Tpng -Gdpi=150 diagrams/architecture.dot -o images/architecture.png
dot -Tpng -Gdpi=150 diagrams/dataflow.dot -o images/dataflow.png