From 23c0804a542d4c15e63ae577bc2032ba136de692 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Sat, 6 Jun 2026 20:13:09 -0600 Subject: [PATCH] feat(farkel): add set-aside dice support and improve score-all - Extend selection scoring to include set-aside dice combined with currently selected dice - Replace greedy score-all with exhaustive search over all subsets of rolled dice, finding the optimal combination with set-aside dice - Update turn score display to correctly show combined points during pick phase --- public/src/games/farkel/FarkelGame.js | 41 +++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/public/src/games/farkel/FarkelGame.js b/public/src/games/farkel/FarkelGame.js index ca4bdcc..956a4af 100644 --- a/public/src/games/farkel/FarkelGame.js +++ b/public/src/games/farkel/FarkelGame.js @@ -425,8 +425,8 @@ export default class FarkelGame extends Phaser.Scene { const hasPending = this.gs.turn.kept > 0 || (this.gs.phase === 'awaitPick' && this.selectionScore() > 0); const showTurn = (seat === cur && !isGameOver(this.gs) && hasPending); if (showTurn) { - const sel = (this.gs.phase === 'awaitPick') ? this.selectionScore() : 0; - row.score.setText(`${p.score} +${this.gs.turn.kept + sel}`); + const total = (this.gs.phase === 'awaitPick') ? this.selectionScore() : this.gs.turn.kept; + row.score.setText(`${p.score} +${total}`); } else { row.score.setText(String(p.score)); } @@ -498,8 +498,16 @@ export default class FarkelGame extends Phaser.Scene { // ── selection helpers ───────────────────────────────────────────────────────── selectionValues() { return [...this.selected].map((i) => this.gs.turn.rolled[i]); } - selectionValid() { return scoreSelection(this.selectionValues()).valid; } - selectionScore() { const r = scoreSelection(this.selectionValues()); return r.valid ? r.points : 0; } + selectionValid() { + const combined = [...this.gs.turn.setAsideDice, ...this.selectionValues()]; + return scoreSelection(combined).valid; + } + selectionScore() { + const combined = [...this.gs.turn.setAsideDice, ...this.selectionValues()]; + if (combined.length === 0) return 0; + const r = scoreSelection(combined); + return r.valid ? r.points : 0; + } // ── input ────────────────────────────────────────────────────────────────────── onDieClick(i) { @@ -512,8 +520,29 @@ export default class FarkelGame extends Phaser.Scene { onScoreAll() { if (this.busy || !this.isHumanTurn() || this.gs.phase !== 'awaitPick') return; - const best = bestScoring(this.gs.turn.rolled); - this.selected = new Set(best.indices); + const rolled = this.gs.turn.rolled; + const kept = this.gs.turn.setAsideDice; + + // Try all subsets of rolled dice, combine with kept, find best total score + let bestScore = 0; + let bestIndices = new Set(); + for (let mask = 0; mask < (1 << rolled.length); mask++) { + const subset = []; + for (let i = 0; i < rolled.length; i++) { + if (mask & (1 << i)) subset.push(rolled[i]); + } + const combined = [...kept, ...subset]; + const result = scoreSelection(combined); + if (result.valid && result.points > bestScore) { + bestScore = result.points; + bestIndices = new Set(); + for (let i = 0; i < rolled.length; i++) { + if (mask & (1 << i)) bestIndices.add(i); + } + } + } + + this.selected = bestIndices; playSound(this, SFX.PIECE_CLICK); this.render(); }