0210c14858
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>
93 lines
2.6 KiB
Dart
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);
|
|
});
|
|
});
|
|
}
|