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,67 @@
|
||||
import 'package:block_seasons/game/models/cell.dart';
|
||||
import 'package:block_seasons/game/models/grid.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('GridState', () {
|
||||
test('empty grid has no occupied cells', () {
|
||||
final grid = GridState.empty();
|
||||
expect(grid.occupiedCount, 0);
|
||||
expect(grid.fillRatio, 0.0);
|
||||
for (var y = 0; y < GridState.size; y++) {
|
||||
for (var x = 0; x < GridState.size; x++) {
|
||||
expect(grid.cellAt(x, y).type, CellType.empty);
|
||||
expect(grid.isOccupied(x, y), isFalse);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('withCell returns a new grid and leaves the original unchanged', () {
|
||||
final grid = GridState.empty();
|
||||
final next = grid.withCell(3, 4, const Cell(CellType.filled, colorId: 2));
|
||||
|
||||
expect(grid.isOccupied(3, 4), isFalse);
|
||||
expect(next.isOccupied(3, 4), isTrue);
|
||||
expect(next.cellAt(3, 4).type, CellType.filled);
|
||||
expect(next.cellAt(3, 4).colorId, 2);
|
||||
expect(next.occupiedCount, 1);
|
||||
});
|
||||
|
||||
test('gem cells count as occupied', () {
|
||||
final grid =
|
||||
GridState.empty().withCell(0, 0, const Cell(CellType.gem));
|
||||
expect(grid.isOccupied(0, 0), isTrue);
|
||||
expect(grid.occupiedCount, 1);
|
||||
});
|
||||
|
||||
test('clearing a cell back to empty updates occupancy', () {
|
||||
final grid = GridState.empty()
|
||||
.withCell(5, 5, const Cell(CellType.filled, colorId: 1))
|
||||
.withCell(5, 5, const Cell(CellType.empty));
|
||||
expect(grid.isOccupied(5, 5), isFalse);
|
||||
expect(grid.occupiedCount, 0);
|
||||
});
|
||||
|
||||
test('isRowFull and isColFull detect complete lines', () {
|
||||
var grid = GridState.empty();
|
||||
for (var x = 0; x < GridState.size; x++) {
|
||||
grid = grid.withCell(x, 2, const Cell(CellType.filled, colorId: 0));
|
||||
}
|
||||
for (var y = 0; y < GridState.size; y++) {
|
||||
grid = grid.withCell(6, y, const Cell(CellType.filled, colorId: 0));
|
||||
}
|
||||
expect(grid.isRowFull(2), isTrue);
|
||||
expect(grid.isRowFull(3), isFalse);
|
||||
expect(grid.isColFull(6), isTrue);
|
||||
expect(grid.isColFull(0), isFalse);
|
||||
});
|
||||
|
||||
test('fillRatio reflects occupied fraction', () {
|
||||
var grid = GridState.empty();
|
||||
for (var x = 0; x < GridState.size; x++) {
|
||||
grid = grid.withCell(x, 0, const Cell(CellType.filled, colorId: 0));
|
||||
}
|
||||
expect(grid.fillRatio, closeTo(8 / 64, 1e-9));
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user