feat(engine): hammer booster removes one cell, no scoring

This commit is contained in:
2026-06-18 12:02:55 +09:00
parent 4cda34f0b7
commit 5aee503c09
2 changed files with 60 additions and 0 deletions
+11
View File
@@ -216,4 +216,15 @@ class GameEngine {
}
_phase = GamePhase.lost;
}
/// Booster: empties one filled cell. No move/score/combo/objective effect.
/// Allowed only mid-attempt (playing or stuck). Returns false on an empty
/// cell or finished attempt so the caller keeps the booster.
bool useHammer(int x, int y) {
if (_phase == GamePhase.won || _phase == GamePhase.lost) return false;
if (!_grid.isOccupied(x, y)) return false;
_grid = _grid.withCell(x, y, const Cell(CellType.empty));
_checkStuck();
return true;
}
}
+49
View File
@@ -0,0 +1,49 @@
import 'package:block_seasons/game/engine/game_engine.dart';
import 'package:block_seasons/game/models/stage.dart';
import 'package:flutter_test/flutter_test.dart';
StageConfig _stage() => StageConfig.fromJson({
'id': 'b_test',
'seed': 1,
'moveLimit': 20,
'preset': [
{'x': 0, 'y': 0, 't': 'filled', 'c': 3},
{'x': 1, 'y': 0, 't': 'filled', 'c': 4},
],
'objectives': [
{'type': 'reachScore', 'target': 100000},
],
'stars': {
'two': {'movesLeft': 5},
'three': {'movesLeft': 10},
},
});
void main() {
test('useHammer empties a filled cell without scoring or spending a move', () {
final e = GameEngine(_stage());
final score0 = e.score;
final moves0 = e.movesUsed;
expect(e.grid.isOccupied(0, 0), isTrue);
final ok = e.useHammer(0, 0);
expect(ok, isTrue);
expect(e.grid.isOccupied(0, 0), isFalse);
expect(e.score, score0, reason: 'no points from a booster');
expect(e.movesUsed, moves0, reason: 'no move spent');
});
test('useHammer on an empty cell returns false and changes nothing', () {
final e = GameEngine(_stage());
expect(e.grid.isOccupied(5, 5), isFalse);
expect(e.useHammer(5, 5), isFalse);
expect(e.grid.isOccupied(5, 5), isFalse);
});
test('useHammer is rejected once the stage is won or lost', () {
final e = GameEngine(_stage());
e.declineAndLose();
expect(e.useHammer(0, 0), isFalse);
});
}