import 'package:block_seasons/core/rng.dart'; import 'package:block_seasons/game/engine/game_engine.dart'; import 'package:block_seasons/game/models/grid.dart'; import 'package:block_seasons/game/models/stage.dart'; import 'package:flutter_test/flutter_test.dart'; StageConfig _endlessStage(int seed) => StageConfig.fromJson({ 'id': 'stress_$seed', 'seed': seed, 'moveLimit': 40, 'preset': const >[], 'objectives': [ {'type': 'reachScore', 'target': 99999999}, ], 'stars': { 'two': {'movesLeft': 5}, 'three': {'movesLeft': 10}, }, 'generatorProfile': 'mid', }); void main() { test('100 seeded random games run to a terminal state without violations', () { for (var seed = 0; seed < 100; seed++) { final engine = GameEngine(_endlessStage(seed)); final rng = SeededRng(seed * 7919 + 1); var guard = 0; while (engine.phase == GamePhase.playing) { guard++; expect(guard, lessThan(300), reason: 'seed $seed: game never ended'); // Enumerate all legal moves; the engine promises at least one exists // while phase == playing. final moves = <(int, int, int)>[]; for (var i = 0; i < engine.tray.length; i++) { for (var y = 0; y < GridState.size; y++) { for (var x = 0; x < GridState.size; x++) { if (engine.tryPlaceWouldSucceed(i, x, y)) moves.add((i, x, y)); } } } expect(moves, isNotEmpty, reason: 'seed $seed: playing phase but no legal move'); final (i, x, y) = moves[rng.nextInt(moves.length)]; final result = engine.tryPlace(i, x, y); expect(result, isNotNull, reason: 'seed $seed: legal move rejected'); // Invariants after every move. expect(engine.tray.length, inInclusiveRange(1, 3), reason: 'seed $seed'); expect(engine.movesUsed, lessThanOrEqualTo(40), reason: 'seed $seed'); expect(engine.score, greaterThanOrEqualTo(0), reason: 'seed $seed'); } // The score objective is unreachable, so every game ends stuck. expect(engine.phase, GamePhase.stuck, reason: 'seed $seed'); expect(engine.stuckReason, isNotNull, reason: 'seed $seed'); } }); }