Commit Graph

6 Commits

Author SHA1 Message Date
funman300 62b61cc786 feat(engine): switch card fronts to 4-colour deck
Hearts pink (`#fb9fb1`), Diamonds gold (`#ddb26f`), Clubs lime
(`#acc267`), Spades gray (`#d0d0d0`) — each suit picks up its
own base16-eighties accent so a player scanning the table can
distinguish the suit by hue alone (faster recognition than the
2-colour traditional red/black scheme; common in poker decks).
All four colours already exist in the palette as semantic
state-token accents, so this is a pure remapping at the suit-
glyph site, not a palette extension.

The outlined-glyph differentiation (♦ ♣ outlined, ♥ ♠ filled)
is preserved on top of the colour split — it stays the always-
on colour-blind fallback per `design-system.md` §Accessibility,
and matters more than ever now that CBM hearts (lime) and
default clubs (lime) share a hue.

### Changes

- `card_face_svg.rs`: split `SUIT_RED` / `SUIT_DARK` into four
  per-suit constants (`SUIT_HEART` / `SUIT_DIAMOND` / `SUIT_CLUB`
  / `SUIT_SPADE`). `suit_paint()` returns each suit's own
  colour. Card border picks up the suit colour automatically
  via the existing `(colour, paint)` destructure.
- `card_plugin.rs`: new `DIAMOND_SUIT_COLOUR` + `CLUB_SUIT_COLOUR`
  constants; `text_colour()` rewritten as a per-suit match (was
  red/black bifurcation). Both rendering paths (PNG production +
  constant fallback under MinimalPlugins) stay in lockstep.
- CBM behaviour clarified: only hearts swap to lime now;
  diamonds + clubs + spades are already hue-distinct from
  the heart pink and stay unchanged. Under CBM the heart
  (lime) and club (lime) share a hue but stay distinguishable
  via the always-on filled-vs-outlined glyph differentiation.
- HC behaviour: only hearts (→ HC red) and spades (→ HC white)
  have defined boosts. Diamonds (gold) and clubs (lime) are
  already mid-luminance accents and stay at their default.
  New test `text_colour_diamonds_and_clubs_are_immune_to_accessibility_flags`
  pins all four flag combinations as no-ops for the gold +
  lime suits.
- `design-system.md` §Suit Colors retitled "Four-color deck"
  with the 4-colour table; CBM section text updated to
  describe the hearts-only swap and the hearts/clubs hue
  collision under CBM.
- `card_face_svg_pin.rs` rebaselined: 26 hashes drift
  (13 clubs + 13 diamonds — the two suits whose colours
  changed). Hearts, spades, and the 5 backs all keep their
  prior hashes. Surgical scope, exactly what the pin test
  was designed to surface.

### Tests

1191 passing / 0 failing — net 0 from the prior baseline:
two old 2-colour tests removed
(`text_colour_is_red_for_hearts_and_diamonds`,
`text_colour_is_black_for_clubs_and_spades`), one consolidated
4-colour test added
(`text_colour_4_colour_deck_assigns_each_suit_its_own_hue`)
plus a pairwise-distinct invariant guard, and one new test
covering the gold/lime suits' immunity to CBM/HC flags. Six
existing CBM/HC tests rewritten to use only the suits each flag
actually affects under the new scheme (hearts for CBM, hearts +
spades for HC).

Workspace clippy clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:00:55 -07:00
funman300 dd101b3d54 fix(engine): render bottom-right card glyph upright (no 180° rotation)
The user noticed the bottom-right large suit glyphs were
rendering upside-down — point-up hearts, stem-up spades — because
the SVG transform pipeline applied a `rotate(180)` to match the
traditional playing-card inverted-corner convention.

That convention exists so a card reads correctly when flipped or
read from the opposite side of the table. Single-orientation
digital play doesn't benefit from it; most modern digital decks
have abandoned it. User preference is upright.

Drops the rotate from face_svg's bottom-right `<g transform>`
and adjusts the translate so the visible glyph still lands at
(178, 286)–(242, 350) — same screen footprint, same scale, just
no flip.

design-system.md § Game Cards updated in lockstep — line 220
no longer says "rotated 180°", instead documents the deliberate
deviation from the traditional convention.

Knock-on lockstep changes in this commit:
- EXPECTED in tests/card_face_svg_pin.rs rebaselined: 52 face
  hashes shift, 5 back hashes unchanged.
- assets/cards/faces/*.png regenerated (52 face PNGs).
- solitaire_engine/assets/themes/default/*_*.svg regenerated
  (52 theme face SVGs that production rasterises at startup).

Workspace clippy + cargo test --workspace clean. Pin test
passes against the new hashes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:09:55 -07:00
funman300 af414b6aed fix(engine): render card suit glyphs as SVG paths instead of text
The user's first post-migration screenshot showed near-invisible
suit glyphs on every card — the rank rendered at correct size but
the ♠ ♥ ♦ ♣ marks were tiny dots regardless of the requested
20px / 64px font-size.

Root cause: the bundled FiraMono in svg_loader::shared_fontdb
doesn't carry usable Unicode suit glyphs (U+2660-2666). usvg
silently fell back to a substitute rendering at default size,
producing the "tofu" effect.

Fixes by replacing the `<text>` glyph rendering with inline SVG
paths. `suit_path_d(suit)` returns a single closed-perimeter path
authored in a 32 × 32 logical box, then face_svg wraps it in two
`<g transform>` blocks (top-left small + bottom-right rotated
large). Path-based rendering bypasses the font system entirely
— same bytes on every machine, no fontdb dependency, no
substitution risk.

Same path data renders correctly whether filled (♥ ♠) or
outlined (♦ ♣ — the always-on color-blind glyph differentiation
from the design system).

Knock-on changes that must land in this commit per the migration
plan's lockstep rule:

- `EXPECTED` in tests/card_face_svg_pin.rs rebaselined: 52 face
  hashes change (text → path), 5 back hashes unchanged
  (back_svg untouched). The bootstrap pattern in the test
  handled the rebaseline cleanly — empty EXPECTED, re-run,
  paste, re-run.
- assets/cards/faces/*.png regenerated (the 52 face PNGs).
- solitaire_engine/assets/themes/default/*_*.svg regenerated
  (the 52 theme face SVGs that production rasterises at
  startup). Both rendering paths must agree.

Workspace clippy + cargo test --workspace clean. Pin test
passes against the new hashes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:02:04 -07:00
funman300 e8bf9d79da feat(engine): migrate cards to Terminal aesthetic — artwork + constants
Step 4+5 lockstep commit closing Option D from SESSION_HANDOFF.

The 52 face PNGs + 5 back PNGs in assets/cards/ are regenerated
to the Terminal-aesthetic artwork emitted by the
card_face_generator example (#1a1a1a face, #fb9fb1 / #d0d0d0
suit glyphs, scanline-pattern backs with palette-rotated badge
accents). Resolution drops from 512×768 to 256×384 — sufficient
for ~250 px-wide desktop sprites and ~⅓ the on-disk weight.

Constant fallback path migrated in lockstep so the
constant-fallback tests (under MinimalPlugins) and the PNG path
(production) agree at every commit boundary:

- CARD_FACE_COLOUR    → #1a1a1a (was off-white #fafaf2)
- RED_SUIT_COLOUR     → #fb9fb1 (was #c71f26)
- BLACK_SUIT_COLOUR   → #d0d0d0 (was #141414)
- CARD_FACE_COLOUR_RED_CBM → renamed to RED_SUIT_COLOUR_CBM,
  value #6fc2ef (was #d9ebff). Semantic shift: pre-Terminal
  this was a face-background tint, now it's a suit-glyph
  colour swap. The Terminal face is uniformly CARD_FACE_COLOUR
  regardless of CBM; CBM only swaps red suits to cyan in the
  glyph itself.
- card_back_colour() → returns the 5 base16-eighties accents
  matching card_face_svg::BACK_ACCENTS in lockstep, so the
  test-fallback back is the same hue family as the on-disk
  PNG art for that index.

Function signatures shift to follow the semantic move:

- text_colour gains a color_blind: bool parameter (returns
  RED_SUIT_COLOUR_CBM for red+CBM).
- face_colour deleted entirely. The face is uniform
  CARD_FACE_COLOUR; card_sprite inlines the constant. CBM
  parameter dropped from card_sprite as a knock-on.

Test updates land in this commit per the migration plan:

- text_colour_is_red_for_hearts_and_diamonds + sibling: pass
  `, false` to text_colour calls now that the signature has
  the CBM bool.
- 4 face_colour CBM tests replaced with 2 text_colour CBM
  tests asserting (a) red-suit cards swap to cyan in CBM and
  (b) black-suit cards do not change.

Engine test count: 747 → 745 (net -2 from the test
consolidation — 4 face_colour tests collapsed into 2
text_colour CBM tests).

Sign-off criteria: a human still needs to `cargo run -p
solitaire_app` and confirm Terminal cards render. clippy +
cargo test --workspace clean as of this commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 09:33:44 -07:00
funman300 b98cb8a99f feat(assets): swap card art to hayeah/playing-cards-assets (MIT)
Replaces the previous xCards-derived card faces (LGPL-3.0) with
hayeah/playing-cards-assets, which itself derives from the
public-domain vector-playing-cards Google Code project. The whole
package is MIT now — see CREDITS.md for the new attribution table
and the simpler license summary.

solitaire_engine/assets/themes/default/
  52 face SVGs (clubs/diamonds/hearts/spades × ace/2-10/jack/queen/
  king) — copied from hayeah, renamed to the canonical
  `{suit}_{rank}.svg` form `CardKey::manifest_name` produces. The
  bundled default theme manifest references each by the same name.
  back.svg — original midnight-purple-themed card back, hand-written
  to match the project's design tokens (BG_BASE / BG_ELEVATED /
  ACCENT_PRIMARY / ACCENT_SECONDARY). MIT, original work.

assets/cards/faces/{RANK}{SUIT}.png
  52 PNGs regenerated from the new SVGs at 750-tall via resvg 0.47.
  These remain the legacy backwards-compat path that
  `card_plugin::load_card_images` reads at startup; once the runtime
  theme system finishes loading the embedded default theme, the
  CardImageSet's face handles are overwritten with the SVG-rendered
  variants and these PNGs become moot. Keeping them in place avoids
  a brief blank-card flash before the async theme load completes.

solitaire_engine/src/assets/sources.rs
  embed_default_svg!() macro + DEFAULT_THEME_SVGS table that bundles
  every face + the back into the binary at compile time via
  include_bytes!. populate_embedded_default_theme now iterates the
  table so the EmbeddedAssetRegistry is populated under the same
  asset paths the manifest references.

CREDITS.md
  License summary collapses from MIT + LGPL-3.0 + OFL-1.1 to MIT +
  OFL-1.1 (the OFL still applies to FiraMono). The hayeah upstream
  URL replaces the previously-blank xCards entry.

cargo build / clippy --workspace --all-targets -- -D warnings / test
--workspace all green (960 passed, 0 failed, 9 ignored).
2026-05-01 16:06:58 +00:00
funman300 fbe984cf64 feat(engine): switch asset loading to AssetServer with xCards artwork
CI / Test & Lint (push) Failing after 19s
CI / Release Build (push) Has been skipped
Replace compile-time include_bytes!() embedding for card faces, backgrounds,
and font with runtime AssetServer::load() calls. Swap in 52 xCards @2x PNGs
(LGPL-3.0) as card face assets and xCards bicycle_blue as back_0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 20:06:02 +00:00