cg-core: make cg-shuffle optionally seeded and reproducible

cg-shuffle gains an optional SEED and a `cg-shuffle-seed' dynamic var.
With a seed the order is deterministic and identical on every platform,
so a deal can be replayed, shared between players, or pinned in a test;
the index comes from secure-hash (spec-defined output everywhere, 7 hex
digits stay a fixnum even on 32-bit Emacs), so no bignum/overflow
behaviour can vary it.  With no seed the shuffle is the byte-for-byte
old system-random path -- normal play is unchanged.

Test cgt-shuffle-seeded pins determinism, permutation, override and a
cross-platform golden prefix; fails on the prior one-arg cg-shuffle.
Suite 159/159.
This commit is contained in:
Corwin Brust 2026-08-03 22:18:02 -05:00
parent 1a59003b13
commit 6379127e17
8 changed files with 180 additions and 15 deletions

View file

@ -199,7 +199,7 @@ strings inside MSG have their text properties stripped first, by
(lambda (proc string)
(let ((buf (concat (or (process-get proc 'cg-net-buf) "") string))
(start 0) nl)
(while (setq nl (string-search "\n" buf start))
(while (setq nl (cl-search "\n" buf :start2 start))
(let ((line (substring buf start nl)))
(setq start (1+ nl))
(unless (string-empty-p line)
@ -208,8 +208,7 @@ strings inside MSG have their text properties stripped first, by
(if (cg-net--valid-p msg types)
(funcall handler proc (cg-net--scrub msg))
(message "cg-net: dropped malformed message")))
(error (message "cg-net: bad message: %S" err)))))
)
(error (message "cg-net: bad message: %S" err))))))
(let ((rest (substring buf start)))
(if (> (length rest) cg-net-max-line)
(progn