Files
BlockSeasons/test/game/models/piece_library_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

61 lines
2.1 KiB
Dart

import 'package:block_seasons/game/models/piece.dart';
import 'package:block_seasons/game/models/piece_library.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('PieceLibrary', () {
test('has a rich fixed-orientation shape set', () {
expect(PieceLibrary.all.length, greaterThanOrEqualTo(35));
});
test('ids are unique', () {
final ids = PieceLibrary.all.map((p) => p.id).toSet();
expect(ids.length, PieceLibrary.all.length);
});
test('every piece is normalized to its top-left bounding box', () {
for (final piece in PieceLibrary.all) {
final minDx =
piece.offsets.map((o) => o.$1).reduce((a, b) => a < b ? a : b);
final minDy =
piece.offsets.map((o) => o.$2).reduce((a, b) => a < b ? a : b);
expect(minDx, 0, reason: '${piece.id} not normalized in x');
expect(minDy, 0, reason: '${piece.id} not normalized in y');
}
});
test('offsets are unique and fit in a 5x5 bounding box', () {
for (final piece in PieceLibrary.all) {
expect(piece.offsets.toSet().length, piece.offsets.length,
reason: '${piece.id} has duplicate offsets');
for (final (dx, dy) in piece.offsets) {
expect(dx, inInclusiveRange(0, 4), reason: piece.id);
expect(dy, inInclusiveRange(0, 4), reason: piece.id);
}
}
});
test('weights are positive and tiers are 1..3', () {
for (final piece in PieceLibrary.all) {
expect(piece.weight, greaterThan(0), reason: piece.id);
expect(piece.tier, inInclusiveRange(1, 3), reason: piece.id);
}
});
test('contains the staple shapes', () {
for (final id in ['mono', 'square2', 'square3', 'line5_h', 'line5_v']) {
expect(PieceLibrary.byId(id), isA<Piece>());
}
});
test('byId throws on unknown id', () {
expect(() => PieceLibrary.byId('nope'), throwsStateError);
});
test('small pieces exist for tight late-game boards', () {
final smalls = PieceLibrary.all.where((p) => p.size <= 2);
expect(smalls.length, greaterThanOrEqualTo(3));
});
});
}