From fd7d731ddabe8bcd9dc33cd1aebb41eaf5768bee Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 9 Mar 2022 13:31:33 -0300 Subject: [PATCH] feat: adding bonus logic to the game bloc --- lib/game/bloc/game_bloc.dart | 34 +++++-- lib/game/bloc/game_event.dart | 2 +- lib/game/bloc/game_state.dart | 21 +++- test/game/bloc/game_bloc_test.dart | 108 +++++++++++++++------ test/game/bloc/game_event_test.dart | 10 +- test/game/bloc/game_state_test.dart | 34 ++++++- test/game/components/ball_test.dart | 1 + test/game/view/pinball_game_page_test.dart | 8 +- 8 files changed, 168 insertions(+), 50 deletions(-) diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 3b5c16b0..a27f0199 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -6,12 +6,17 @@ part 'game_event.dart'; part 'game_state.dart'; class GameBloc extends Bloc { - GameBloc() : super(const GameState.initial()) { + GameBloc({ + int bonusLettersCount = 5, + }) : _bonusLettersCount = bonusLettersCount, + super(const GameState.initial()) { on(_onBallLost); on(_onScored); on(_onBonusLetterActivated); } + final int _bonusLettersCount; + void _onBallLost(BallLost event, Emitter emit) { if (state.balls > 0) { emit(state.copyWith(balls: state.balls - 1)); @@ -25,13 +30,24 @@ class GameBloc extends Bloc { } void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) { - emit( - state.copyWith( - bonusLetters: [ - ...state.bonusLetters, - event.letter, - ], - ), - ); + final updatedList = [...state.bonusLetters, event.letter]; + + if (updatedList.length == _bonusLettersCount) { + emit( + state.copyWith( + bonusLetters: [], + bonusHistory: [ + ...state.bonusHistory, + GameBonuses.letterSequence, + ], + ), + ); + } else { + emit( + state.copyWith( + bonusLetters: updatedList, + ), + ); + } } } diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index 417f6322..39ea2e7e 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -28,7 +28,7 @@ class Scored extends GameEvent { class BonusLetterActivated extends GameEvent { const BonusLetterActivated(this.letter); - final String letter; + final int letter; @override List get props => [letter]; diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 8a5ab298..786472db 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -1,5 +1,11 @@ part of 'game_bloc.dart'; +/// Enum to describe all the available bonuses +/// on the game +enum GameBonuses { + letterSequence, +} + /// {@template game_state} /// Represents the state of the pinball game. /// {@endtemplate} @@ -9,13 +15,15 @@ class GameState extends Equatable { required this.score, required this.balls, required this.bonusLetters, + required this.bonusHistory, }) : assert(score >= 0, "Score can't be negative"), assert(balls >= 0, "Number of balls can't be negative"); const GameState.initial() : score = 0, balls = 3, - bonusLetters = const []; + bonusLetters = const [], + bonusHistory = const []; /// The current score of the game. final int score; @@ -26,7 +34,11 @@ class GameState extends Equatable { final int balls; /// Active bonus letters. - final List bonusLetters; + final List bonusLetters; + + /// Holds the history of all the bonuses + /// that the palyer earned during the play + final List bonusHistory; /// Determines when the game is over. bool get isGameOver => balls == 0; @@ -37,7 +49,8 @@ class GameState extends Equatable { GameState copyWith({ int? score, int? balls, - List? bonusLetters, + List? bonusLetters, + List? bonusHistory, }) { assert( score == null || score >= this.score, @@ -48,6 +61,7 @@ class GameState extends Equatable { score: score ?? this.score, balls: balls ?? this.balls, bonusLetters: bonusLetters ?? this.bonusLetters, + bonusHistory: bonusHistory ?? this.bonusHistory, ); } @@ -56,5 +70,6 @@ class GameState extends Equatable { score, balls, bonusLetters, + bonusHistory, ]; } diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index bd669397..4ae8bd3d 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -21,9 +21,24 @@ void main() { } }, expect: () => [ - const GameState(score: 0, balls: 2, bonusLetters: []), - const GameState(score: 0, balls: 1, bonusLetters: []), - const GameState(score: 0, balls: 0, bonusLetters: []), + const GameState( + score: 0, + balls: 2, + bonusLetters: [], + bonusHistory: [], + ), + const GameState( + score: 0, + balls: 1, + bonusLetters: [], + bonusHistory: [], + ), + const GameState( + score: 0, + balls: 0, + bonusLetters: [], + bonusHistory: [], + ), ], ); }); @@ -37,8 +52,18 @@ void main() { ..add(const Scored(points: 2)) ..add(const Scored(points: 3)), expect: () => [ - const GameState(score: 2, balls: 3, bonusLetters: []), - const GameState(score: 5, balls: 3, bonusLetters: []), + const GameState( + score: 2, + balls: 3, + bonusLetters: [], + bonusHistory: [], + ), + const GameState( + score: 5, + balls: 3, + bonusLetters: [], + bonusHistory: [], + ), ], ); @@ -53,9 +78,24 @@ void main() { bloc.add(const Scored(points: 2)); }, expect: () => [ - const GameState(score: 0, balls: 2, bonusLetters: []), - const GameState(score: 0, balls: 1, bonusLetters: []), - const GameState(score: 0, balls: 0, bonusLetters: []), + const GameState( + score: 0, + balls: 2, + bonusLetters: [], + bonusHistory: [], + ), + const GameState( + score: 0, + balls: 1, + bonusLetters: [], + bonusHistory: [], + ), + const GameState( + score: 0, + balls: 0, + bonusLetters: [], + bonusHistory: [], + ), ], ); }); @@ -65,42 +105,56 @@ void main() { 'adds the letter to the state', build: GameBloc.new, act: (bloc) => bloc - ..add(const BonusLetterActivated('G')) - ..add(const BonusLetterActivated('O')) - ..add(const BonusLetterActivated('O')) - ..add(const BonusLetterActivated('G')) - ..add(const BonusLetterActivated('L')) - ..add(const BonusLetterActivated('E')), - expect: () => [ - const GameState( + ..add(const BonusLetterActivated(0)) + ..add(const BonusLetterActivated(1)) + ..add(const BonusLetterActivated(2)), + expect: () => const [ + GameState( score: 0, balls: 3, - bonusLetters: ['G'], + bonusLetters: [0], + bonusHistory: [], ), - const GameState( + GameState( score: 0, balls: 3, - bonusLetters: ['G', 'O'], + bonusLetters: [0, 1], + bonusHistory: [], ), - const GameState( + GameState( score: 0, balls: 3, - bonusLetters: ['G', 'O', 'O'], + bonusLetters: [0, 1, 2], + bonusHistory: [], ), - const GameState( + ], + ); + + blocTest( + 'adds the bonus when the sequence is completed', + build: () => GameBloc(bonusLettersCount: 3), + act: (bloc) => bloc + ..add(const BonusLetterActivated(0)) + ..add(const BonusLetterActivated(1)) + ..add(const BonusLetterActivated(2)), + expect: () => const [ + GameState( score: 0, balls: 3, - bonusLetters: ['G', 'O', 'O', 'G'], + bonusLetters: [0], + bonusHistory: [], ), - const GameState( + GameState( score: 0, balls: 3, - bonusLetters: ['G', 'O', 'O', 'G', 'L'], + bonusLetters: [0, 1], + bonusHistory: [], ), - const GameState( + GameState( score: 0, balls: 3, - bonusLetters: ['G', 'O', 'O', 'G', 'L', 'E'], + bonusLetters: [], + bonusHistory: [GameBonuses.letterSequence], ), ], ); diff --git a/test/game/bloc/game_event_test.dart b/test/game/bloc/game_event_test.dart index 0e7a0f71..c21ecfba 100644 --- a/test/game/bloc/game_event_test.dart +++ b/test/game/bloc/game_event_test.dart @@ -43,17 +43,17 @@ void main() { group('BonusLetterActivated', () { test('can be instantiated', () { - expect(const BonusLetterActivated('A'), isNotNull); + expect(const BonusLetterActivated(0), isNotNull); }); test('supports value equality', () { expect( - BonusLetterActivated('A'), - equals(BonusLetterActivated('A')), + BonusLetterActivated(0), + equals(BonusLetterActivated(0)), ); expect( - BonusLetterActivated('B'), - isNot(equals(BonusLetterActivated('A'))), + BonusLetterActivated(0), + isNot(equals(BonusLetterActivated(1))), ); }); }); diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index 7345d3bd..a23ae35f 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -11,12 +11,14 @@ void main() { score: 0, balls: 0, bonusLetters: const [], + bonusHistory: const [], ), equals( const GameState( score: 0, balls: 0, bonusLetters: [], + bonusHistory: [], ), ), ); @@ -25,7 +27,12 @@ void main() { group('constructor', () { test('can be instantiated', () { expect( - const GameState(score: 0, balls: 0, bonusLetters: []), + const GameState( + score: 0, + balls: 0, + bonusLetters: [], + bonusHistory: [], + ), isNotNull, ); }); @@ -36,7 +43,12 @@ void main() { 'when balls are negative', () { expect( - () => GameState(balls: -1, score: 0, bonusLetters: const []), + () => GameState( + balls: -1, + score: 0, + bonusLetters: const [], + bonusHistory: const [], + ), throwsAssertionError, ); }, @@ -47,7 +59,12 @@ void main() { 'when score is negative', () { expect( - () => GameState(balls: 0, score: -1, bonusLetters: const []), + () => GameState( + balls: 0, + score: -1, + bonusLetters: const [], + bonusHistory: const [], + ), throwsAssertionError, ); }, @@ -61,6 +78,7 @@ void main() { balls: 0, score: 0, bonusLetters: [], + bonusHistory: [], ); expect(gameState.isGameOver, isTrue); }); @@ -72,6 +90,7 @@ void main() { balls: 1, score: 0, bonusLetters: [], + bonusHistory: [], ); expect(gameState.isGameOver, isFalse); }); @@ -86,6 +105,7 @@ void main() { balls: 1, score: 0, bonusLetters: [], + bonusHistory: [], ); expect(gameState.isLastBall, isTrue); }, @@ -99,6 +119,7 @@ void main() { balls: 2, score: 0, bonusLetters: [], + bonusHistory: [], ); expect(gameState.isLastBall, isFalse); }, @@ -114,6 +135,7 @@ void main() { balls: 0, score: 2, bonusLetters: [], + bonusHistory: [], ); expect( () => gameState.copyWith(score: gameState.score - 1), @@ -130,6 +152,7 @@ void main() { balls: 0, score: 2, bonusLetters: [], + bonusHistory: [], ); expect( gameState.copyWith(), @@ -146,11 +169,13 @@ void main() { score: 2, balls: 0, bonusLetters: [], + bonusHistory: [], ); final otherGameState = GameState( score: gameState.score + 1, balls: gameState.balls + 1, - bonusLetters: const ['A'], + bonusLetters: const [0], + bonusHistory: const [GameBonuses.letterSequence], ); expect(gameState, isNot(equals(otherGameState))); @@ -159,6 +184,7 @@ void main() { score: otherGameState.score, balls: otherGameState.balls, bonusLetters: otherGameState.bonusLetters, + bonusHistory: otherGameState.bonusHistory, ), equals(otherGameState), ); diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index bd2cbcfc..d04e52a9 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -139,6 +139,7 @@ void main() { score: 10, balls: 1, bonusLetters: [], + bonusHistory: [], ), ); await game.ready(); diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index 746dc2c7..b47518ac 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -71,7 +71,13 @@ void main() { 'renders a game over dialog when the user has lost', (tester) async { final gameBloc = MockGameBloc(); - const state = GameState(score: 0, balls: 0, bonusLetters: []); + const state = GameState( + score: 0, + balls: 0, + bonusLetters: [], + bonusHistory: [], + ); + whenListen( gameBloc, Stream.value(state),