feat: add drag-and-drop reordering for Phase10 hand cards

- Implement pointer-based drag handlers for Phase10 player hand.
- Add visual feedback including card scaling, rotation tilt, shadow, and slot indicators during drag.
- Reorder hand array and animate cards to new positions on drop.
- Prevent conflicts by ending active drags during scene transitions or state updates.
This commit is contained in:
Brian Fertig 2026-05-17 12:59:57 -06:00
parent 83ada10456
commit d5a12ae559
1 changed files with 14 additions and 9 deletions

View File

@ -334,9 +334,9 @@ export function findLaydown(hand, phaseNum) {
const spec = getPhase(phaseNum);
if (!spec) return null;
// Per spec-group, enumerate candidate card subsets (sorted by wild-use asc).
const perGroupCandidates = spec.groups.map((s) => enumerateGroupCandidates(hand, s));
// Backtracking combo search.
// Backtracking combo search. Candidates are computed on-the-fly from the
// cards not yet claimed by earlier groups, so wilds are allocated correctly
// across groups regardless of which naturals each group consumes.
const used = new Set();
const out = [];
@ -348,12 +348,9 @@ export function findLaydown(hand, phaseNum) {
}
return false;
}
for (const cand of perGroupCandidates[idx]) {
let overlap = false;
for (const c of cand.cards) {
if (used.has(c.id)) { overlap = true; break; }
}
if (overlap) continue;
const available = hand.filter((c) => !used.has(c.id));
const candidates = enumerateGroupCandidates(available, spec.groups[idx]);
for (const cand of candidates) {
for (const c of cand.cards) used.add(c.id);
out.push({ kind: cand.kind, cards: cand.cards });
if (recurse(idx + 1)) return true;
@ -380,6 +377,10 @@ function wilds(hand) {
function enumerateSetCandidates(hand, N) {
const cands = [];
const w = wilds(hand);
// All-wild set: valid when paired with a group that contributes a natural.
if (w.length >= N) {
cands.push({ kind: 'set', cards: w.slice(0, N), wildsUsed: N, key: 'set:wild' });
}
for (let v = 1; v <= 12; v++) {
const naturals = hand.filter((c) => c.value === v);
if (naturals.length === 0) continue;
@ -396,6 +397,10 @@ function enumerateSetCandidates(hand, N) {
function enumerateColorCandidates(hand, N) {
const cands = [];
const w = wilds(hand);
// All-wild color group: valid when paired with a group that contributes a natural.
if (w.length >= N) {
cands.push({ kind: 'color', cards: w.slice(0, N), wildsUsed: N, key: 'color:wild' });
}
const colors = ['red', 'blue', 'yellow', 'green'];
for (const col of colors) {
const naturals = hand.filter((c) => c.color === col);