From e7cd079a5d9c5415fc1d7374d2fb771bbcbabf77 Mon Sep 17 00:00:00 2001 From: airkjw Date: Thu, 18 Jun 2026 12:05:14 +0900 Subject: [PATCH] feat(engine): line-bomb booster clears a row or column --- lib/game/engine/game_engine.dart | 14 ++++++++++++ test/game/engine/booster_test.dart | 35 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/lib/game/engine/game_engine.dart b/lib/game/engine/game_engine.dart index 1427f92..bddb5e3 100644 --- a/lib/game/engine/game_engine.dart +++ b/lib/game/engine/game_engine.dart @@ -236,4 +236,18 @@ class GameEngine { _checkStuck(); return true; } + + /// Booster: empties one row or one column (exactly one of [row]/[col]). + /// No move/score/objective effect. Re-checks stuck. + bool useLineBomb({int? row, int? col}) { + if (_phase == GamePhase.won || _phase == GamePhase.lost) return false; + if ((row == null) == (col == null)) return false; // need exactly one + for (var i = 0; i < GridState.size; i++) { + final x = col ?? i; + final y = row ?? i; + _grid = _grid.withCell(x, y, const Cell(CellType.empty)); + } + _checkStuck(); + return true; + } } diff --git a/test/game/engine/booster_test.dart b/test/game/engine/booster_test.dart index a20c2b1..7dd7923 100644 --- a/test/game/engine/booster_test.dart +++ b/test/game/engine/booster_test.dart @@ -66,4 +66,39 @@ void main() { e.declineAndLose(); expect(e.useShuffle(), isFalse); }); + + test('useLineBomb(row:) empties that row, no scoring or move', () { + final e = GameEngine(_stage()); // row 0 has cells at x=0,1 + final score0 = e.score; + final moves0 = e.movesUsed; + + final ok = e.useLineBomb(row: 0); + + expect(ok, isTrue); + for (var x = 0; x < 8; x++) { + expect(e.grid.isOccupied(x, 0), isFalse, reason: 'col $x'); + } + expect(e.score, score0); + expect(e.movesUsed, moves0); + }); + + test('useLineBomb(col:) empties that column', () { + final e = GameEngine(_stage()); // (0,0) filled + expect(e.useLineBomb(col: 0), isTrue); + for (var y = 0; y < 8; y++) { + expect(e.grid.isOccupied(0, y), isFalse, reason: 'row $y'); + } + }); + + test('useLineBomb requires exactly one of row/col', () { + final e = GameEngine(_stage()); + expect(e.useLineBomb(), isFalse); + expect(e.useLineBomb(row: 0, col: 0), isFalse); + }); + + test('useLineBomb is rejected after the attempt finishes', () { + final e = GameEngine(_stage()); + e.declineAndLose(); + expect(e.useLineBomb(row: 0), isFalse); + }); }