feat: adding bonus logic to the game bloc

pull/24/head
Erick Zanardo 4 years ago
parent 360b5876cf
commit fd7d731dda

@ -6,12 +6,17 @@ part 'game_event.dart';
part 'game_state.dart'; part 'game_state.dart';
class GameBloc extends Bloc<GameEvent, GameState> { class GameBloc extends Bloc<GameEvent, GameState> {
GameBloc() : super(const GameState.initial()) { GameBloc({
int bonusLettersCount = 5,
}) : _bonusLettersCount = bonusLettersCount,
super(const GameState.initial()) {
on<BallLost>(_onBallLost); on<BallLost>(_onBallLost);
on<Scored>(_onScored); on<Scored>(_onScored);
on<BonusLetterActivated>(_onBonusLetterActivated); on<BonusLetterActivated>(_onBonusLetterActivated);
} }
final int _bonusLettersCount;
void _onBallLost(BallLost event, Emitter emit) { void _onBallLost(BallLost event, Emitter emit) {
if (state.balls > 0) { if (state.balls > 0) {
emit(state.copyWith(balls: state.balls - 1)); emit(state.copyWith(balls: state.balls - 1));
@ -25,13 +30,24 @@ class GameBloc extends Bloc<GameEvent, GameState> {
} }
void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) { void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) {
emit( final updatedList = [...state.bonusLetters, event.letter];
state.copyWith(
bonusLetters: [ if (updatedList.length == _bonusLettersCount) {
...state.bonusLetters, emit(
event.letter, state.copyWith(
], bonusLetters: [],
), bonusHistory: [
); ...state.bonusHistory,
GameBonuses.letterSequence,
],
),
);
} else {
emit(
state.copyWith(
bonusLetters: updatedList,
),
);
}
} }
} }

@ -28,7 +28,7 @@ class Scored extends GameEvent {
class BonusLetterActivated extends GameEvent { class BonusLetterActivated extends GameEvent {
const BonusLetterActivated(this.letter); const BonusLetterActivated(this.letter);
final String letter; final int letter;
@override @override
List<Object?> get props => [letter]; List<Object?> get props => [letter];

@ -1,5 +1,11 @@
part of 'game_bloc.dart'; part of 'game_bloc.dart';
/// Enum to describe all the available bonuses
/// on the game
enum GameBonuses {
letterSequence,
}
/// {@template game_state} /// {@template game_state}
/// Represents the state of the pinball game. /// Represents the state of the pinball game.
/// {@endtemplate} /// {@endtemplate}
@ -9,13 +15,15 @@ class GameState extends Equatable {
required this.score, required this.score,
required this.balls, required this.balls,
required this.bonusLetters, required this.bonusLetters,
required this.bonusHistory,
}) : assert(score >= 0, "Score can't be negative"), }) : assert(score >= 0, "Score can't be negative"),
assert(balls >= 0, "Number of balls can't be negative"); assert(balls >= 0, "Number of balls can't be negative");
const GameState.initial() const GameState.initial()
: score = 0, : score = 0,
balls = 3, balls = 3,
bonusLetters = const []; bonusLetters = const [],
bonusHistory = const [];
/// The current score of the game. /// The current score of the game.
final int score; final int score;
@ -26,7 +34,11 @@ class GameState extends Equatable {
final int balls; final int balls;
/// Active bonus letters. /// Active bonus letters.
final List<String> bonusLetters; final List<int> bonusLetters;
/// Holds the history of all the bonuses
/// that the palyer earned during the play
final List<GameBonuses> bonusHistory;
/// Determines when the game is over. /// Determines when the game is over.
bool get isGameOver => balls == 0; bool get isGameOver => balls == 0;
@ -37,7 +49,8 @@ class GameState extends Equatable {
GameState copyWith({ GameState copyWith({
int? score, int? score,
int? balls, int? balls,
List<String>? bonusLetters, List<int>? bonusLetters,
List<GameBonuses>? bonusHistory,
}) { }) {
assert( assert(
score == null || score >= this.score, score == null || score >= this.score,
@ -48,6 +61,7 @@ class GameState extends Equatable {
score: score ?? this.score, score: score ?? this.score,
balls: balls ?? this.balls, balls: balls ?? this.balls,
bonusLetters: bonusLetters ?? this.bonusLetters, bonusLetters: bonusLetters ?? this.bonusLetters,
bonusHistory: bonusHistory ?? this.bonusHistory,
); );
} }
@ -56,5 +70,6 @@ class GameState extends Equatable {
score, score,
balls, balls,
bonusLetters, bonusLetters,
bonusHistory,
]; ];
} }

@ -21,9 +21,24 @@ void main() {
} }
}, },
expect: () => [ expect: () => [
const GameState(score: 0, balls: 2, bonusLetters: []), const GameState(
const GameState(score: 0, balls: 1, bonusLetters: []), score: 0,
const GameState(score: 0, balls: 0, bonusLetters: []), 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: 2))
..add(const Scored(points: 3)), ..add(const Scored(points: 3)),
expect: () => [ expect: () => [
const GameState(score: 2, balls: 3, bonusLetters: []), const GameState(
const GameState(score: 5, balls: 3, bonusLetters: []), 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)); bloc.add(const Scored(points: 2));
}, },
expect: () => [ expect: () => [
const GameState(score: 0, balls: 2, bonusLetters: []), const GameState(
const GameState(score: 0, balls: 1, bonusLetters: []), score: 0,
const GameState(score: 0, balls: 0, bonusLetters: []), 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', 'adds the letter to the state',
build: GameBloc.new, build: GameBloc.new,
act: (bloc) => bloc act: (bloc) => bloc
..add(const BonusLetterActivated('G')) ..add(const BonusLetterActivated(0))
..add(const BonusLetterActivated('O')) ..add(const BonusLetterActivated(1))
..add(const BonusLetterActivated('O')) ..add(const BonusLetterActivated(2)),
..add(const BonusLetterActivated('G')) expect: () => const [
..add(const BonusLetterActivated('L')) GameState(
..add(const BonusLetterActivated('E')),
expect: () => [
const GameState(
score: 0, score: 0,
balls: 3, balls: 3,
bonusLetters: ['G'], bonusLetters: [0],
bonusHistory: [],
), ),
const GameState( GameState(
score: 0, score: 0,
balls: 3, balls: 3,
bonusLetters: ['G', 'O'], bonusLetters: [0, 1],
bonusHistory: [],
), ),
const GameState( GameState(
score: 0, score: 0,
balls: 3, balls: 3,
bonusLetters: ['G', 'O', 'O'], bonusLetters: [0, 1, 2],
bonusHistory: [],
), ),
const GameState( ],
);
blocTest<GameBloc, GameState>(
'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, score: 0,
balls: 3, balls: 3,
bonusLetters: ['G', 'O', 'O', 'G'], bonusLetters: [0],
bonusHistory: [],
), ),
const GameState( GameState(
score: 0, score: 0,
balls: 3, balls: 3,
bonusLetters: ['G', 'O', 'O', 'G', 'L'], bonusLetters: [0, 1],
bonusHistory: [],
), ),
const GameState( GameState(
score: 0, score: 0,
balls: 3, balls: 3,
bonusLetters: ['G', 'O', 'O', 'G', 'L', 'E'], bonusLetters: [],
bonusHistory: [GameBonuses.letterSequence],
), ),
], ],
); );

@ -43,17 +43,17 @@ void main() {
group('BonusLetterActivated', () { group('BonusLetterActivated', () {
test('can be instantiated', () { test('can be instantiated', () {
expect(const BonusLetterActivated('A'), isNotNull); expect(const BonusLetterActivated(0), isNotNull);
}); });
test('supports value equality', () { test('supports value equality', () {
expect( expect(
BonusLetterActivated('A'), BonusLetterActivated(0),
equals(BonusLetterActivated('A')), equals(BonusLetterActivated(0)),
); );
expect( expect(
BonusLetterActivated('B'), BonusLetterActivated(0),
isNot(equals(BonusLetterActivated('A'))), isNot(equals(BonusLetterActivated(1))),
); );
}); });
}); });

@ -11,12 +11,14 @@ void main() {
score: 0, score: 0,
balls: 0, balls: 0,
bonusLetters: const [], bonusLetters: const [],
bonusHistory: const [],
), ),
equals( equals(
const GameState( const GameState(
score: 0, score: 0,
balls: 0, balls: 0,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
), ),
), ),
); );
@ -25,7 +27,12 @@ void main() {
group('constructor', () { group('constructor', () {
test('can be instantiated', () { test('can be instantiated', () {
expect( expect(
const GameState(score: 0, balls: 0, bonusLetters: []), const GameState(
score: 0,
balls: 0,
bonusLetters: [],
bonusHistory: [],
),
isNotNull, isNotNull,
); );
}); });
@ -36,7 +43,12 @@ void main() {
'when balls are negative', 'when balls are negative',
() { () {
expect( expect(
() => GameState(balls: -1, score: 0, bonusLetters: const []), () => GameState(
balls: -1,
score: 0,
bonusLetters: const [],
bonusHistory: const [],
),
throwsAssertionError, throwsAssertionError,
); );
}, },
@ -47,7 +59,12 @@ void main() {
'when score is negative', 'when score is negative',
() { () {
expect( expect(
() => GameState(balls: 0, score: -1, bonusLetters: const []), () => GameState(
balls: 0,
score: -1,
bonusLetters: const [],
bonusHistory: const [],
),
throwsAssertionError, throwsAssertionError,
); );
}, },
@ -61,6 +78,7 @@ void main() {
balls: 0, balls: 0,
score: 0, score: 0,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
); );
expect(gameState.isGameOver, isTrue); expect(gameState.isGameOver, isTrue);
}); });
@ -72,6 +90,7 @@ void main() {
balls: 1, balls: 1,
score: 0, score: 0,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
); );
expect(gameState.isGameOver, isFalse); expect(gameState.isGameOver, isFalse);
}); });
@ -86,6 +105,7 @@ void main() {
balls: 1, balls: 1,
score: 0, score: 0,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
); );
expect(gameState.isLastBall, isTrue); expect(gameState.isLastBall, isTrue);
}, },
@ -99,6 +119,7 @@ void main() {
balls: 2, balls: 2,
score: 0, score: 0,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
); );
expect(gameState.isLastBall, isFalse); expect(gameState.isLastBall, isFalse);
}, },
@ -114,6 +135,7 @@ void main() {
balls: 0, balls: 0,
score: 2, score: 2,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
); );
expect( expect(
() => gameState.copyWith(score: gameState.score - 1), () => gameState.copyWith(score: gameState.score - 1),
@ -130,6 +152,7 @@ void main() {
balls: 0, balls: 0,
score: 2, score: 2,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
); );
expect( expect(
gameState.copyWith(), gameState.copyWith(),
@ -146,11 +169,13 @@ void main() {
score: 2, score: 2,
balls: 0, balls: 0,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
); );
final otherGameState = GameState( final otherGameState = GameState(
score: gameState.score + 1, score: gameState.score + 1,
balls: gameState.balls + 1, balls: gameState.balls + 1,
bonusLetters: const ['A'], bonusLetters: const [0],
bonusHistory: const [GameBonuses.letterSequence],
); );
expect(gameState, isNot(equals(otherGameState))); expect(gameState, isNot(equals(otherGameState)));
@ -159,6 +184,7 @@ void main() {
score: otherGameState.score, score: otherGameState.score,
balls: otherGameState.balls, balls: otherGameState.balls,
bonusLetters: otherGameState.bonusLetters, bonusLetters: otherGameState.bonusLetters,
bonusHistory: otherGameState.bonusHistory,
), ),
equals(otherGameState), equals(otherGameState),
); );

@ -139,6 +139,7 @@ void main() {
score: 10, score: 10,
balls: 1, balls: 1,
bonusLetters: [], bonusLetters: [],
bonusHistory: [],
), ),
); );
await game.ready(); await game.ready();

@ -71,7 +71,13 @@ void main() {
'renders a game over dialog when the user has lost', 'renders a game over dialog when the user has lost',
(tester) async { (tester) async {
final gameBloc = MockGameBloc(); final gameBloc = MockGameBloc();
const state = GameState(score: 0, balls: 0, bonusLetters: []); const state = GameState(
score: 0,
balls: 0,
bonusLetters: [],
bonusHistory: [],
);
whenListen( whenListen(
gameBloc, gameBloc,
Stream.value(state), Stream.value(state),

Loading…
Cancel
Save