62cbb4b16a
Sealed Objective types (clearGems/reachScore/clearLines) with JSON round-trip; StageConfig with preset cells and star thresholds; GameEngine orchestrating placement -> clear -> scoring -> objectives with stuck detection, one-shot rescue (continue / +5 moves), and deterministic per-attempt RNG. 100-game headless stress test and pure-Dart architecture guard. 76 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
89 lines
2.3 KiB
Dart
89 lines
2.3 KiB
Dart
import '../engine/game_event.dart';
|
|
|
|
/// Stage win condition with immutable progress tracking. New objective types
|
|
/// plug in as subclasses with a JSON tag.
|
|
sealed class Objective {
|
|
const Objective();
|
|
|
|
factory Objective.clearGems(int count) => ClearGemsObjective(count, 0);
|
|
factory Objective.reachScore(int target) => ReachScoreObjective(target, 0);
|
|
factory Objective.clearLines(int count) => ClearLinesObjective(count, 0);
|
|
|
|
factory Objective.fromJson(Map<String, dynamic> json) {
|
|
switch (json['type']) {
|
|
case 'clearGems':
|
|
return Objective.clearGems(json['count'] as int);
|
|
case 'reachScore':
|
|
return Objective.reachScore(json['target'] as int);
|
|
case 'clearLines':
|
|
return Objective.clearLines(json['count'] as int);
|
|
default:
|
|
throw FormatException('Unknown objective type: ${json['type']}');
|
|
}
|
|
}
|
|
|
|
int get target;
|
|
int get current;
|
|
bool get isComplete => current >= target;
|
|
|
|
Objective onEvent(GameEvent event);
|
|
|
|
Map<String, dynamic> toJson();
|
|
}
|
|
|
|
class ClearGemsObjective extends Objective {
|
|
const ClearGemsObjective(this.target, this.current);
|
|
|
|
@override
|
|
final int target;
|
|
@override
|
|
final int current;
|
|
|
|
@override
|
|
Objective onEvent(GameEvent event) => switch (event) {
|
|
LinesCleared(:final gems) =>
|
|
ClearGemsObjective(target, current + gems),
|
|
_ => this,
|
|
};
|
|
|
|
@override
|
|
Map<String, dynamic> toJson() => {'type': 'clearGems', 'count': target};
|
|
}
|
|
|
|
class ReachScoreObjective extends Objective {
|
|
const ReachScoreObjective(this.target, this.current);
|
|
|
|
@override
|
|
final int target;
|
|
@override
|
|
final int current;
|
|
|
|
@override
|
|
Objective onEvent(GameEvent event) => switch (event) {
|
|
ScoreChanged(:final total) => ReachScoreObjective(target, total),
|
|
_ => this,
|
|
};
|
|
|
|
@override
|
|
Map<String, dynamic> toJson() => {'type': 'reachScore', 'target': target};
|
|
}
|
|
|
|
class ClearLinesObjective extends Objective {
|
|
const ClearLinesObjective(this.target, this.current);
|
|
|
|
@override
|
|
final int target;
|
|
@override
|
|
final int current;
|
|
|
|
@override
|
|
Objective onEvent(GameEvent event) => switch (event) {
|
|
LinesCleared(:final lines) =>
|
|
ClearLinesObjective(target, current + lines),
|
|
_ => this,
|
|
};
|
|
|
|
@override
|
|
Map<String, dynamic> toJson() => {'type': 'clearLines', 'count': target};
|
|
}
|