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
This commit is contained in:
Brian Fertig 2026-06-06 20:13:09 -06:00
parent 0849ea0446
commit 23c0804a54
1 changed files with 35 additions and 6 deletions

View File

@ -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();
}