Finding 3 of the 2026-07-29 security review: bids, passes and plays
were validated properly, but a kitty discard only checked that five
cards were named -- and cg-bid--discard removes cards with
cl-set-difference, which silently ignores cards not in the hand. A
client naming five cards it did not hold kept all fifteen and played
on with more cards than the rules allow.
New cg-bid--net-holds-p checks every named card against the seat's
hand, respecting multiplicity (five copies of one held card do not
pass). The check joins the phase/contractor/arity checks that already
live in the discard branch of cg-net-apply-move.
Test cgt-bid-net-discard-checked fails against the previous code: the
foreign discard was accepted (returned t) and the game advanced to
play with a 15-card hand.
Finding 1 of the 2026-07-29 security review: hosting bound 0.0.0.0 --
every interface -- with no password and no encryption, so anyone who
could reach the machine's port was handed a seat and the game state.
New defcustom cg-net-host-address defaults to 127.0.0.1; the wide-open
value remains available and its docstring says exactly what choosing it
means. This converts "anyone who can reach you" into "someone you
deliberately let in", and bounds Finding 4's unauthenticated
memory-exhaustion route to local callers along the way.
Test cgt-net-host-loopback-default checks both the default value and
the actual bound address (process-contact :local). Against the
previous code it fails with (void-variable cg-net-host-address); the
old bind, captured before the patch: [0 0 0 0 PORT].
Finding 2 of the 2026-07-29 security review (CONFIRMED there): text
properties survive the prin1/read round trip the protocol is built on,
so a malicious host could send a client strings whose properties rebind
keys or carry expressions evaluated during redisplay.
New cg-net--scrub walks a decoded message and passes every string
through substring-no-properties; conses and vectors are copied, shared
and circular structure is tolerated. It is applied inside
cg-net--filter -- the one decode point both the host and the client
read through -- so both directions are covered at the boundary rather
than at each use site.
Test cgt-net-strips-properties (loopback, both directions) fails
against the previous code with properties intact: value (keymap (keymap)).
* card-games.el (card-games-treatment, card-games--svg-card-vars,
card-games--full-svg-vars, card-games-set-treatment): switch all games
between UNICODE, SVG cards, and the full-window SVG table by toggling the
per-game display vars together. (card-game--cycle-treatment, card-game):
a clickable control in the chooser.
* test/card-games-tests.el: cgt-treatment-set.
* NEWS: note the treatment selector.
* cg-core.el (cg-ai-level): new difficulty defcustom. (cg-red-suit-p):
return a normalised boolean so two red suits compare equal by colour --
diamond and heart were wrongly treated as opposite colours, letting a red
card build on another red card in the alternating-colour games.
* cg-crapette.el (cg-crap--ai-greedy-move, cg-crap--ai-play): honour easy
/ normal / hard.
* cg-trick.el (cg-trick--run): easy plays a random legal card.
* card-games.el (card-game): clickable AI-level control.
(card-game--cycle-ai, card-games-set-ai-level): change it.
* test/card-games-tests.el: cgt-red-suit-boolean, cgt-ai-level-easy-crapette;
bind cg-ai-level in cgt-crap-ai-enabling.
* NEWS.
* cg-svg.el (cg-svg-card-back): default to random; add lattice/waves/diamond
and the emacs/emacs-classic/gnu/splash logo backs. (cg-svg--back-*,
cg-svg--draw-back): draw them. (cg-svg--card-backs, cg-svg--random-back,
cg-svg--roll-back, cg-svg-shuffle-card-back, cg-svg--effective-back): random
machinery.
* card-games.el (card-game): re-roll the random back on each menu visit.
* test/card-games-tests.el: add cgt-svg-logo-smoke, cgt-svg-card-back-smoke.
* NEWS: note the card backs and random default.
* cg-cribbage.el (cg-crib--svg, cg-crib--render-text): board with a peg-track,
the starter, the pegging count/cards, the crib, and the hand.
board with the rubber/contract, four seats with the dummy exposed, the trick,
and the interactive hand.
* test/card-games-tests.el: add cgt-crib-svg-smoke, cgt-bridge-svg-smoke.
* NEWS: note the boards; every game now has a full SVG board.
discard, suit in play, stock, and the hand.
* cg-spite.el (cg-spite--board-svg, cg-spite--render-text): centre piles,
goal, discards, and hand.
* cg-match.el (cg-om--svg, cg-om--render-text, cg-render-apply): Old Maid
board with clickable target cards; wire mouse-1, zoom, and cg-current-game.
* test/card-games-tests.el: add cgt-eights-svg-smoke, cgt-spite-svg-smoke,
cgt-om-svg-smoke.
* NEWS: note the new boards.
* cg-rummy.el (cg-rummy--board-svg): add :stock-label and a :discard 'none
books and your hand/foot.
* cg-scopa.el (cg-fish--svg, cg-fish--render-text): board with the table
* NEWS: note the new boards.
* cg-trick.el (cg-trick--svg, cg-trick--draw-backs): draw the whole table
as one SVG -- three opponents, the trick in a diamond, the South hand
fanned with cursor, legal-play hints, and pass marks; hand cards carry
(hand . INDEX) click regions. (cg-trick--render-text): the text fallback.
(cg-render): dispatch SVG on a graphical display, else text. Add a footer
legend; refresh the help. Covers Hearts, Spades, Whist, Oh Hell, and the
Euchre/Pitch/Briscola subclasses.
* test/card-games-tests.el: add cgt-trick-svg-smoke, cgt-trick-svg-bidding.
* NEWS: note the trick-taking SVG table.
* cg-crapette.el (cg-crap--house-move): honour an optional WANT count on
an empty-house destination. (cg-crap--do-move): thread it through.
(cg-crap--hold-adjust, cg-crap-hold-less, cg-crap-hold-more): [ and ] set
the held count. (cg-crap--holding-line): show the run with the held
cards marked. (cg-crap-act): default the held count to the run length.
Bind [ and ]; update the legend and help.
* test/card-games-tests.el: add cgt-crap-sequence-partial, cgt-crap-partial-space.
* NEWS: note choosing the group size.
* cg-crapette.el (cg-crapette-stops): new defcustom.
(cg-crap--stop): call STOP (end your turn) or block, per the custom, when
you skip an available foundation play; wired into drop, draw, and end.
(cg-crap--builds-down-p, cg-crap--house-run, cg-crap--free-houses,
cg-crap--capacity, cg-crap--house-move, cg-crap--do-move): move a legal
sequence between houses, limited by the number of empty houses.
* test/card-games-tests.el: add cgt-crap-house-run, -sequence-move,
-sequence-space, -stop.
* NEWS: note the stop rule and sequenced moves.
* cg-crapette.el: new game on cg-core — eight foundations (up by suit,
two decks), eight shared houses (down, alternating colour), per-player
reserve/waste/hand; foundation priority; loading onto the opponent; a
greedy AI; text UI with cursor pick/drop, undo, and q-to-menu.
* card-games.el: require cg-crapette and add the chooser entry.
* Makefile (EL): add cg-crapette.el.
* test/card-games-tests.el: 5 ERT (deal, legality, win, AI turn, AI foundation).
Full-SVG 500 made mouse-operable for newcomers: kitty Discard button and
five-card cap, on-table phase banner, ? Help/Rules overlay with the bid
legend, legal-play dimming, card-size slider, and a layout pass that
moves the Help and size controls into the log panel so nothing overlaps.
Bump all files to 1.0.90, add NEWS, a README testing quick-start, and
make the shared engine files checkdoc-clean.
The last three keyboard-only games are now click-to-play, so every game
in the package responds to the mouse.
Crazy Eights and President route their hands through the shared
cg-regions click map (President maps each rank group to a click); the
patience boards (Golf, TriPeaks, Pyramid) follow the solitaire keystone,
mapping exposed slots plus the waste and stock to their spots. Each adds
a card-size slider, [mouse-1], and +/-/0 zoom.
Add cgt-mouse-regions asserting the SVG click maps build (suite -> 116).
Rummy 500 gains the signature below-the-top discard pickup (T): take the
chosen card plus every card above it, melding or laying off the chosen
card at once. New `deep-pickup' class slot keeps Basic Rummy top-only;
the AI takes a buried card when it completes a new meld; the pile renders
with 0=top depth indices.
Hand & Foot gains its three missing rules: pick up the discard pile (p)
by melding the top card with two matching naturals; red threes as bonus
cards (auto-collected with a replacement draw, +100 each / +200 all four);
and the round-by-round go-down minimum (50/90/120/150) enforced as one
atomic initial meld.
Add four ERT tests (suite 111->115); refresh the README and in-file
commentary; fix cg-rum500.el's truncated file footer.
Shared hand row gains a region-tag: tagged hands carry a cg-regions click
map (cards -> (hand . i)) and a card-size slider in the same image.
cg-core adds cg-mouse-action, cg-card-click, zoom commands, cg-card-scale
(folded into cg-scale), and a cg-render-apply base for scale/zoom. Seven
hand games are now click-to-position (Scopa/Casino/Spite click-to-play),
with [mouse-1] and +/-/0 bound. Adds cgt-hand-regions; suite 111/111.
cg-renderer gains a regions slot; the text/svg treatments get real
draw/hit methods; cg-regions-hit + cg-render-apply complete the loop.
Prototype on solitaire: cg-sol--svg returns (image . regions), redisplay
goes through cg-render-game, and [mouse-1] selects-and-acts by reusing the
keyboard pick-up/drop. Adds cgt-keystone-regions; suite 110/110.
New cg-bridge.el: a four-handed Bridge game (you are South, partnering
North against East and West) on the shared cg-game base.
* Auction: level/strain bids plus pass, double, and redouble, with the
three-pass end rule, pass-outs, doubling state, and declarer
determination (first of the side to name the strain).
* Play: follow-suit with the dummy exposed after the opening lead; the
declarer plays both hands. Trick resolution honours trump and no-trump.
* Scoring: classic rubber -- trick points below the line toward game;
overtricks, slam, insult, and undertrick penalties above; vulnerability
and the rubber bonus. Verified against known results.
* A small natural bidding AI (openings, NT, raises with a fit, simple
overcalls) that always terminates the auction, plus a greedy
card-play AI.
Wire cg-bridge into the chooser, the Makefile, and the README, and add
two ERT tests (scoring math and a dozen full AI-driven deals). The suite
is now 109/109 and every file byte-compiles cleanly.
Euchre, Pitch, Briscola, and Spite & Malice
Five new files, each reusing or extending an existing engine.
* cg-match.el: Go Fish and Old Maid, matching games on a shared
helper set (completes the original wishlist).
* cg-cribbage.el: two-handed Cribbage to 121 -- the crib, the cut,
pegging, and a full show scorer (fifteens, pairs, runs, flush, nobs).
* cg-scopa.el: a capture-by-sum engine driving Scopa (40-card, sette
bello, primiera, scopas) and Casino (pairs and sums, big/little
casino, aces, sweeps). Casino omits builds.
* cg-trick-ext.el: Euchre (24-card with both bowers), Auction Pitch
(bid, pitch sets trump, High/Low/Jack/Game), and Briscola (fixed
trump, no follow), as subclasses of the cg-trick engine.
* cg-spite.el: Spite & Malice, a competitive patience to empty the
goal pile onto shared Ace-to-Queen centre piles; Kings are wild.
Wire all nine commands into the card-game chooser, extend the Makefile
EL list, and add README sections. Add ten ERT tests covering each
game's engine and a full AI-driven game; the suite is now 107/107 and
every file byte-compiles cleanly.
New files at Version 1.0.60 to match the tree; post-1.0.60 work
toward 1.0.90.
Introduce a shared meld engine and four games built on it, all on
cg-core/EIEIO with console UNICODE rendering.
* cg-rummy.el: the meld engine and Gin Rummy. Set/run validation,
candidate-meld enumeration, a bitmask-DP minimum-deadwood partition,
and a layoff finder, plus the abstract cg-rummy-game base and shared
render helpers. Gin is two-handed with draw/take/discard/knock, gin
and undercut bonuses, opponent layoffs, and play to 100.
* cg-rum500.el: the abstract cg-tablemeld-game (one mode and command
set, dispatching on the subclass) driving Basic Rummy (meld out;
score the cards left in other hands; to 100) and Rummy 500 (score the
cards you lay down, lose those left in hand; ace high and worth 15;
to 500).
* cg-handfoot.el: Hand & Foot, a partnership Canasta cousin. Hand and
foot packets, Twos and Jokers wild, rank books with clean/dirty piles,
go-out bonus, and partnership scoring to 5000. Deliberately
Host-authoritative networked 500: host sits South, joiners take W/N/E,
open seats filled by AI. Per-seat rotated state hides other hands and the
kitty; clients reuse the single-player renderer/commands via :around advice,
so cg-bid.el/cg-bid-ui.el are untouched. Adds cg-bid-host/cg-bid-join, a
start-now lobby with auto-start at four players, and cg-bid-shuffle-partners.
cg-net.el gains cg-net-connect-functions. Verified: clean byte-compile,
checkdoc, 34/34 ERT (incl. 3 new net tests) and a two-process TCP game.