Files
BlockSeasons/test/game/engine/auto_player_test.dart
T
airkjw 41c18c8bdd Add AutoPlayer bot, stage generator CLI, and calibrated Season 1 (60 stages)
Greedy bot with tray-survival lookahead and gem-line steering; generator
samples layouts along a difficulty curve, probes bot moves-to-win, sets
adaptive move budgets targeting win-rate bands, and derives star
thresholds from spare-move quantiles. Season 1 pack bundled in assets
with per-stage difficulty report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:56:54 +09:00

75 lines
2.3 KiB
Dart

import 'package:block_seasons/game/engine/auto_player.dart';
import 'package:block_seasons/game/models/stage.dart';
import 'package:flutter_test/flutter_test.dart';
StageConfig _easyClearLines() => StageConfig.fromJson({
'id': 'bot_easy',
'seed': 4242,
'moveLimit': 30,
'preset': const <Map<String, dynamic>>[],
'objectives': [
{'type': 'clearLines', 'count': 1},
],
'stars': {
'two': {'movesLeft': 10},
'three': {'movesLeft': 20},
},
'generatorProfile': 'mid',
});
StageConfig _gemStage() => StageConfig.fromJson({
'id': 'bot_gems',
'seed': 555,
'moveLimit': 35,
'preset': [
{'x': 1, 'y': 1, 't': 'gem'},
{'x': 6, 'y': 6, 't': 'gem'},
],
'objectives': [
{'type': 'clearGems', 'count': 2},
],
'stars': {
'two': {'movesLeft': 8},
'three': {'movesLeft': 16},
},
'generatorProfile': 'mid',
});
void main() {
test('bot terminates and reports a result on every attempt', () {
for (var attempt = 0; attempt < 10; attempt++) {
final run = AutoPlayer().play(_easyClearLines(), attempt: attempt);
expect(run.movesUsed, greaterThan(0));
expect(run.movesUsed, lessThanOrEqualTo(35));
if (run.won) {
expect(run.movesLeft, greaterThanOrEqualTo(0));
expect(run.stars, inInclusiveRange(1, 3));
}
}
});
test('bot wins a trivially easy clear-lines stage most of the time', () {
var wins = 0;
for (var attempt = 0; attempt < 20; attempt++) {
if (AutoPlayer().play(_easyClearLines(), attempt: attempt).won) wins++;
}
expect(wins, greaterThanOrEqualTo(16), reason: 'bot too weak: $wins/20');
});
test('bot pursues gem objectives without dead-ending the board', () {
var wins = 0;
for (var attempt = 0; attempt < 20; attempt++) {
if (AutoPlayer().play(_gemStage(), attempt: attempt).won) wins++;
}
expect(wins, greaterThanOrEqualTo(17), reason: 'gem play too weak: $wins/20');
});
test('same attempt number reproduces the same run', () {
final a = AutoPlayer().play(_gemStage(), attempt: 3);
final b = AutoPlayer().play(_gemStage(), attempt: 3);
expect(a.won, b.won);
expect(a.movesUsed, b.movesUsed);
expect(a.score, b.score);
});
}