From df2ce91b063daab7cad9ec75fd726626372de4ff Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Tue, 8 Mar 2022 19:23:10 -0300 Subject: [PATCH 1/3] feat: adding bonus letter feature state management --- lib/game/bloc/game_bloc.dart | 12 +++++ lib/game/bloc/game_event.dart | 9 ++++ lib/game/bloc/game_state.dart | 10 +++- test/game/bloc/game_bloc_test.dart | 62 +++++++++++++++++++--- test/game/bloc/game_event_test.dart | 17 ++++++ test/game/bloc/game_state_test.dart | 29 ++++++++-- test/game/components/ball_test.dart | 2 +- test/game/view/pinball_game_page_test.dart | 2 +- 8 files changed, 127 insertions(+), 16 deletions(-) diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 71c527a8..3cfc521f 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -9,6 +9,7 @@ class GameBloc extends Bloc { GameBloc() : super(const GameState.initial()) { on(_onBallLost); on(_onScored); + on(_onBonusLetterActivated); } void _onBallLost(BallLost event, Emitter emit) { @@ -22,4 +23,15 @@ class GameBloc extends Bloc { emit(state.copyWith(score: state.score + event.points)); } } + + void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) { + emit( + state.copyWith( + bonusLetter: [ + ...state.bonusLetter, + event.letter, + ], + ), + ); + } } diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index 88ef265b..417f6322 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -24,3 +24,12 @@ class Scored extends GameEvent { @override List get props => [points]; } + +class BonusLetterActivated extends GameEvent { + const BonusLetterActivated(this.letter); + + final String letter; + + @override + List get props => [letter]; +} diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 1a0568f7..8454bab7 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -8,12 +8,14 @@ class GameState extends Equatable { const GameState({ required this.score, required this.balls, + required this.bonusLetter, }) : 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; + balls = 3, + bonusLetter = const []; /// The current score of the game. final int score; @@ -23,6 +25,9 @@ class GameState extends Equatable { /// When the number of balls is 0, the game is over. final int balls; + /// Active bonus letters + final List bonusLetter; + /// Determines when the game is over. bool get isGameOver => balls == 0; @@ -32,6 +37,7 @@ class GameState extends Equatable { GameState copyWith({ int? score, int? balls, + List? bonusLetter, }) { assert( score == null || score >= this.score, @@ -41,6 +47,7 @@ class GameState extends Equatable { return GameState( score: score ?? this.score, balls: balls ?? this.balls, + bonusLetter: bonusLetter ?? this.bonusLetter, ); } @@ -48,5 +55,6 @@ class GameState extends Equatable { List get props => [ score, balls, + bonusLetter, ]; } diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index 2676a286..3dc5dda7 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -21,9 +21,9 @@ void main() { } }, expect: () => [ - const GameState(score: 0, balls: 2), - const GameState(score: 0, balls: 1), - const GameState(score: 0, balls: 0), + const GameState(score: 0, balls: 2, bonusLetter: []), + const GameState(score: 0, balls: 1, bonusLetter: []), + const GameState(score: 0, balls: 0, bonusLetter: []), ], ); }); @@ -37,8 +37,8 @@ void main() { ..add(const Scored(points: 2)) ..add(const Scored(points: 3)), expect: () => [ - const GameState(score: 2, balls: 3), - const GameState(score: 5, balls: 3), + const GameState(score: 2, balls: 3, bonusLetter: []), + const GameState(score: 5, balls: 3, bonusLetter: []), ], ); @@ -53,9 +53,55 @@ void main() { bloc.add(const Scored(points: 2)); }, expect: () => [ - const GameState(score: 0, balls: 2), - const GameState(score: 0, balls: 1), - const GameState(score: 0, balls: 0), + const GameState(score: 0, balls: 2, bonusLetter: []), + const GameState(score: 0, balls: 1, bonusLetter: []), + const GameState(score: 0, balls: 0, bonusLetter: []), + ], + ); + }); + + group('BonusLetterActivated', () { + blocTest( + '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( + score: 0, + balls: 3, + bonusLetter: ['G'], + ), + const GameState( + score: 0, + balls: 3, + bonusLetter: ['G', 'O'], + ), + const GameState( + score: 0, + balls: 3, + bonusLetter: ['G', 'O', 'O'], + ), + const GameState( + score: 0, + balls: 3, + bonusLetter: ['G', 'O', 'O', 'G'], + ), + const GameState( + score: 0, + balls: 3, + bonusLetter: ['G', 'O', 'O', 'G', 'L'], + ), + const GameState( + score: 0, + balls: 3, + bonusLetter: ['G', 'O', 'O', 'G', 'L', 'E'], + ), ], ); }); diff --git a/test/game/bloc/game_event_test.dart b/test/game/bloc/game_event_test.dart index e839ab56..0e7a0f71 100644 --- a/test/game/bloc/game_event_test.dart +++ b/test/game/bloc/game_event_test.dart @@ -40,5 +40,22 @@ void main() { expect(() => Scored(points: 0), throwsAssertionError); }); }); + + group('BonusLetterActivated', () { + test('can be instantiated', () { + expect(const BonusLetterActivated('A'), isNotNull); + }); + + test('supports value equality', () { + expect( + BonusLetterActivated('A'), + equals(BonusLetterActivated('A')), + ); + expect( + BonusLetterActivated('B'), + isNot(equals(BonusLetterActivated('A'))), + ); + }); + }); }); } diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index 59cc0d1d..e50acbcd 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -7,14 +7,24 @@ void main() { group('GameState', () { test('supports value equality', () { expect( - GameState(score: 0, balls: 0), - equals(const GameState(score: 0, balls: 0)), + GameState( + score: 0, + balls: 0, + bonusLetter: const [], + ), + equals( + const GameState( + score: 0, + balls: 0, + bonusLetter: [], + ), + ), ); }); group('constructor', () { test('can be instantiated', () { - expect(const GameState(score: 0, balls: 0), isNotNull); + expect(const GameState(score: 0, balls: 0, bonusLetter: []), isNotNull); }); }); @@ -23,7 +33,7 @@ void main() { 'when balls are negative', () { expect( - () => GameState(balls: -1, score: 0), + () => GameState(balls: -1, score: 0, bonusLetter: const []), throwsAssertionError, ); }, @@ -34,7 +44,7 @@ void main() { 'when score is negative', () { expect( - () => GameState(balls: 0, score: -1), + () => GameState(balls: 0, score: -1, bonusLetter: const []), throwsAssertionError, ); }, @@ -47,6 +57,7 @@ void main() { const gameState = GameState( balls: 0, score: 0, + bonusLetter: [], ); expect(gameState.isGameOver, isTrue); }); @@ -57,6 +68,7 @@ void main() { const gameState = GameState( balls: 1, score: 0, + bonusLetter: [], ); expect(gameState.isGameOver, isFalse); }); @@ -70,6 +82,7 @@ void main() { const gameState = GameState( balls: 1, score: 0, + bonusLetter: [], ); expect(gameState.isLastBall, isTrue); }, @@ -82,6 +95,7 @@ void main() { const gameState = GameState( balls: 2, score: 0, + bonusLetter: [], ); expect(gameState.isLastBall, isFalse); }, @@ -96,6 +110,7 @@ void main() { const gameState = GameState( balls: 0, score: 2, + bonusLetter: [], ); expect( () => gameState.copyWith(score: gameState.score - 1), @@ -111,6 +126,7 @@ void main() { const gameState = GameState( balls: 0, score: 2, + bonusLetter: [], ); expect( gameState.copyWith(), @@ -126,10 +142,12 @@ void main() { const gameState = GameState( score: 2, balls: 0, + bonusLetter: [], ); final otherGameState = GameState( score: gameState.score + 1, balls: gameState.balls + 1, + bonusLetter: const ['A'], ); expect(gameState, isNot(equals(otherGameState))); @@ -137,6 +155,7 @@ void main() { gameState.copyWith( score: otherGameState.score, balls: otherGameState.balls, + bonusLetter: otherGameState.bonusLetter, ), equals(otherGameState), ); diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index 7ac3ceff..9885b310 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -135,7 +135,7 @@ void main() { whenListen( gameBloc, const Stream.empty(), - initialState: const GameState(score: 10, balls: 1), + initialState: const GameState(score: 10, balls: 1, bonusLetter: []), ); await game.ready(); diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index be418c1d..eacee734 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -67,7 +67,7 @@ void main() { 'renders a game over dialog when the user has lost', (tester) async { final gameBloc = MockGameBloc(); - const state = GameState(score: 0, balls: 0); + const state = GameState(score: 0, balls: 0, bonusLetter: []); whenListen( gameBloc, Stream.value(state), From de25974f8c9b50bc31fd416f28a08bab0445a98a Mon Sep 17 00:00:00 2001 From: Erick Date: Wed, 9 Mar 2022 09:48:09 -0300 Subject: [PATCH 2/3] Update lib/game/bloc/game_state.dart Co-authored-by: Alejandro Santiago --- lib/game/bloc/game_state.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 8454bab7..09207b86 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -25,7 +25,7 @@ class GameState extends Equatable { /// When the number of balls is 0, the game is over. final int balls; - /// Active bonus letters + /// Active bonus letters. final List bonusLetter; /// Determines when the game is over. From e4cd4342c0c2bc40724cfed8deac93caf3abffa0 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 9 Mar 2022 09:53:52 -0300 Subject: [PATCH 3/3] feat: pr suggestions --- lib/game/bloc/game_bloc.dart | 4 +-- lib/game/bloc/game_state.dart | 12 ++++----- test/game/bloc/game_bloc_test.dart | 28 +++++++++---------- test/game/bloc/game_state_test.dart | 31 ++++++++++++---------- test/game/components/ball_test.dart | 6 ++++- test/game/view/pinball_game_page_test.dart | 2 +- 6 files changed, 45 insertions(+), 38 deletions(-) diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 3cfc521f..3b5c16b0 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -27,8 +27,8 @@ class GameBloc extends Bloc { void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) { emit( state.copyWith( - bonusLetter: [ - ...state.bonusLetter, + bonusLetters: [ + ...state.bonusLetters, event.letter, ], ), diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 09207b86..8a5ab298 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -8,14 +8,14 @@ class GameState extends Equatable { const GameState({ required this.score, required this.balls, - required this.bonusLetter, + required this.bonusLetters, }) : 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, - bonusLetter = const []; + bonusLetters = const []; /// The current score of the game. final int score; @@ -26,7 +26,7 @@ class GameState extends Equatable { final int balls; /// Active bonus letters. - final List bonusLetter; + final List bonusLetters; /// Determines when the game is over. bool get isGameOver => balls == 0; @@ -37,7 +37,7 @@ class GameState extends Equatable { GameState copyWith({ int? score, int? balls, - List? bonusLetter, + List? bonusLetters, }) { assert( score == null || score >= this.score, @@ -47,7 +47,7 @@ class GameState extends Equatable { return GameState( score: score ?? this.score, balls: balls ?? this.balls, - bonusLetter: bonusLetter ?? this.bonusLetter, + bonusLetters: bonusLetters ?? this.bonusLetters, ); } @@ -55,6 +55,6 @@ class GameState extends Equatable { List get props => [ score, balls, - bonusLetter, + bonusLetters, ]; } diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index 3dc5dda7..bd669397 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -21,9 +21,9 @@ void main() { } }, expect: () => [ - const GameState(score: 0, balls: 2, bonusLetter: []), - const GameState(score: 0, balls: 1, bonusLetter: []), - const GameState(score: 0, balls: 0, bonusLetter: []), + const GameState(score: 0, balls: 2, bonusLetters: []), + const GameState(score: 0, balls: 1, bonusLetters: []), + const GameState(score: 0, balls: 0, bonusLetters: []), ], ); }); @@ -37,8 +37,8 @@ void main() { ..add(const Scored(points: 2)) ..add(const Scored(points: 3)), expect: () => [ - const GameState(score: 2, balls: 3, bonusLetter: []), - const GameState(score: 5, balls: 3, bonusLetter: []), + const GameState(score: 2, balls: 3, bonusLetters: []), + const GameState(score: 5, balls: 3, bonusLetters: []), ], ); @@ -53,9 +53,9 @@ void main() { bloc.add(const Scored(points: 2)); }, expect: () => [ - const GameState(score: 0, balls: 2, bonusLetter: []), - const GameState(score: 0, balls: 1, bonusLetter: []), - const GameState(score: 0, balls: 0, bonusLetter: []), + const GameState(score: 0, balls: 2, bonusLetters: []), + const GameState(score: 0, balls: 1, bonusLetters: []), + const GameState(score: 0, balls: 0, bonusLetters: []), ], ); }); @@ -75,32 +75,32 @@ void main() { const GameState( score: 0, balls: 3, - bonusLetter: ['G'], + bonusLetters: ['G'], ), const GameState( score: 0, balls: 3, - bonusLetter: ['G', 'O'], + bonusLetters: ['G', 'O'], ), const GameState( score: 0, balls: 3, - bonusLetter: ['G', 'O', 'O'], + bonusLetters: ['G', 'O', 'O'], ), const GameState( score: 0, balls: 3, - bonusLetter: ['G', 'O', 'O', 'G'], + bonusLetters: ['G', 'O', 'O', 'G'], ), const GameState( score: 0, balls: 3, - bonusLetter: ['G', 'O', 'O', 'G', 'L'], + bonusLetters: ['G', 'O', 'O', 'G', 'L'], ), const GameState( score: 0, balls: 3, - bonusLetter: ['G', 'O', 'O', 'G', 'L', 'E'], + bonusLetters: ['G', 'O', 'O', 'G', 'L', 'E'], ), ], ); diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index e50acbcd..7345d3bd 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -10,13 +10,13 @@ void main() { GameState( score: 0, balls: 0, - bonusLetter: const [], + bonusLetters: const [], ), equals( const GameState( score: 0, balls: 0, - bonusLetter: [], + bonusLetters: [], ), ), ); @@ -24,7 +24,10 @@ void main() { group('constructor', () { test('can be instantiated', () { - expect(const GameState(score: 0, balls: 0, bonusLetter: []), isNotNull); + expect( + const GameState(score: 0, balls: 0, bonusLetters: []), + isNotNull, + ); }); }); @@ -33,7 +36,7 @@ void main() { 'when balls are negative', () { expect( - () => GameState(balls: -1, score: 0, bonusLetter: const []), + () => GameState(balls: -1, score: 0, bonusLetters: const []), throwsAssertionError, ); }, @@ -44,7 +47,7 @@ void main() { 'when score is negative', () { expect( - () => GameState(balls: 0, score: -1, bonusLetter: const []), + () => GameState(balls: 0, score: -1, bonusLetters: const []), throwsAssertionError, ); }, @@ -57,7 +60,7 @@ void main() { const gameState = GameState( balls: 0, score: 0, - bonusLetter: [], + bonusLetters: [], ); expect(gameState.isGameOver, isTrue); }); @@ -68,7 +71,7 @@ void main() { const gameState = GameState( balls: 1, score: 0, - bonusLetter: [], + bonusLetters: [], ); expect(gameState.isGameOver, isFalse); }); @@ -82,7 +85,7 @@ void main() { const gameState = GameState( balls: 1, score: 0, - bonusLetter: [], + bonusLetters: [], ); expect(gameState.isLastBall, isTrue); }, @@ -95,7 +98,7 @@ void main() { const gameState = GameState( balls: 2, score: 0, - bonusLetter: [], + bonusLetters: [], ); expect(gameState.isLastBall, isFalse); }, @@ -110,7 +113,7 @@ void main() { const gameState = GameState( balls: 0, score: 2, - bonusLetter: [], + bonusLetters: [], ); expect( () => gameState.copyWith(score: gameState.score - 1), @@ -126,7 +129,7 @@ void main() { const gameState = GameState( balls: 0, score: 2, - bonusLetter: [], + bonusLetters: [], ); expect( gameState.copyWith(), @@ -142,12 +145,12 @@ void main() { const gameState = GameState( score: 2, balls: 0, - bonusLetter: [], + bonusLetters: [], ); final otherGameState = GameState( score: gameState.score + 1, balls: gameState.balls + 1, - bonusLetter: const ['A'], + bonusLetters: const ['A'], ); expect(gameState, isNot(equals(otherGameState))); @@ -155,7 +158,7 @@ void main() { gameState.copyWith( score: otherGameState.score, balls: otherGameState.balls, - bonusLetter: otherGameState.bonusLetter, + bonusLetters: otherGameState.bonusLetters, ), equals(otherGameState), ); diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index 9885b310..bd2cbcfc 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -135,7 +135,11 @@ void main() { whenListen( gameBloc, const Stream.empty(), - initialState: const GameState(score: 10, balls: 1, bonusLetter: []), + initialState: const GameState( + score: 10, + balls: 1, + bonusLetters: [], + ), ); await game.ready(); diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index eacee734..d578a1db 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -67,7 +67,7 @@ void main() { 'renders a game over dialog when the user has lost', (tester) async { final gameBloc = MockGameBloc(); - const state = GameState(score: 0, balls: 0, bonusLetter: []); + const state = GameState(score: 0, balls: 0, bonusLetters: []); whenListen( gameBloc, Stream.value(state),