Files
BlockSeasons/test/game/engine/scoring_test.dart
T
airkjw 0210c14858 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>
2026-06-11 13:05:55 +09:00

93 lines
2.6 KiB
Dart

import 'package:block_seasons/game/engine/scoring.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('lineClearBase', () {
test('escalates with simultaneous lines', () {
expect(lineClearBase(1), 100);
expect(lineClearBase(2), 300);
expect(lineClearBase(3), 600);
expect(lineClearBase(4), 1000);
});
});
group('comboMultiplier', () {
test('grows by 0.5 per streak step and caps at 8', () {
expect(comboMultiplier(1), 1.5);
expect(comboMultiplier(2), 2.0);
expect(comboMultiplier(8), 5.0);
expect(comboMultiplier(12), 5.0);
});
});
group('ComboState', () {
test('clearing placements grow the streak', () {
var combo = ComboState.initial;
combo = combo.advance(cleared: true);
expect(combo.streak, 1);
combo = combo.advance(cleared: true);
expect(combo.streak, 2);
});
test('one dry move is grace, streak survives', () {
var combo = ComboState.initial;
combo = combo.advance(cleared: true);
combo = combo.advance(cleared: false);
expect(combo.streak, 1);
combo = combo.advance(cleared: true);
expect(combo.streak, 2);
});
test('two consecutive dry moves reset the streak', () {
var combo = ComboState.initial;
combo = combo.advance(cleared: true);
combo = combo.advance(cleared: false);
combo = combo.advance(cleared: false);
expect(combo.streak, 0);
combo = combo.advance(cleared: true);
expect(combo.streak, 1);
});
});
group('scorePlacement', () {
test('placement without clear scores cell count only', () {
final delta = scorePlacement(
cellsPlaced: 4,
linesCleared: 0,
combo: ComboState.initial,
);
expect(delta.points, 4);
expect(delta.combo.streak, 0);
});
test('first clear applies x1.5 multiplier', () {
final delta = scorePlacement(
cellsPlaced: 5,
linesCleared: 1,
combo: ComboState.initial,
);
// 5 + round(100 * 1.5) = 155
expect(delta.points, 155);
expect(delta.combo.streak, 1);
});
test('streak compounds across placements', () {
var combo = ComboState.initial;
final first = scorePlacement(
cellsPlaced: 3,
linesCleared: 1,
combo: combo,
);
combo = first.combo;
final second = scorePlacement(
cellsPlaced: 3,
linesCleared: 2,
combo: combo,
);
// 3 + round(300 * 2.0) = 603
expect(second.points, 603);
expect(second.combo.streak, 2);
});
});
}