Add pure-Dart engine core: RNG, grid, placement, line clear, scoring, piece generator
PCG32 seeded RNG; immutable 8x8 GridState with occupancy bitmask; placement legality + anyPlacementExists; simultaneous row/col clears with single-count gem credit; combo scoring with one-move grace; weighted-bag generator with pity bias and depth-3 solvability nudge. All TDD, 51 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/// Pure scoring math: placement points, line-clear bonuses, combo streaks.
|
||||
library;
|
||||
|
||||
/// Base score for clearing [lines] rows/columns simultaneously.
|
||||
/// 1 -> 100, 2 -> 300, 3 -> 600, 4 -> 1000: simultaneous clears are
|
||||
/// superlinearly rewarded.
|
||||
int lineClearBase(int lines) => 100 * lines + 50 * (lines - 1) * lines;
|
||||
|
||||
/// Multiplier applied to clear score at combo [streak], capped at streak 8
|
||||
/// (x5.0) so late-game scores stay bounded.
|
||||
double comboMultiplier(int streak) => 1 + 0.5 * (streak > 8 ? 8 : streak);
|
||||
|
||||
/// Combo streak with one dry-move grace before reset.
|
||||
class ComboState {
|
||||
const ComboState({required this.streak, required this.dryMoves});
|
||||
|
||||
static const initial = ComboState(streak: 0, dryMoves: 0);
|
||||
|
||||
final int streak;
|
||||
final int dryMoves;
|
||||
|
||||
/// One dry move keeps the streak alive (grace); the second resets it.
|
||||
ComboState advance({required bool cleared}) {
|
||||
if (cleared) return ComboState(streak: streak + 1, dryMoves: 0);
|
||||
if (dryMoves + 1 >= 2) return initial;
|
||||
return ComboState(streak: streak, dryMoves: dryMoves + 1);
|
||||
}
|
||||
}
|
||||
|
||||
class ScoreDelta {
|
||||
const ScoreDelta({required this.points, required this.combo});
|
||||
|
||||
final int points;
|
||||
final ComboState combo;
|
||||
}
|
||||
|
||||
/// Computes the score delta for one placement and the resulting combo state.
|
||||
ScoreDelta scorePlacement({
|
||||
required int cellsPlaced,
|
||||
required int linesCleared,
|
||||
required ComboState combo,
|
||||
}) {
|
||||
final next = combo.advance(cleared: linesCleared > 0);
|
||||
var points = cellsPlaced;
|
||||
if (linesCleared > 0) {
|
||||
points += (lineClearBase(linesCleared) * comboMultiplier(next.streak))
|
||||
.round();
|
||||
}
|
||||
return ScoreDelta(points: points, combo: next);
|
||||
}
|
||||
Reference in New Issue
Block a user