feat: adding bonus logic to the game bloc (#24)

* feat: adding bonus logic to the game bloc

* feat: PR suggestions

* Apply suggestions from code review

Co-authored-by: Alejandro Santiago <dev@alestiago.com>

* feat: pr suggestions

* chore: main rebase

* feat: pr suggestions

* Apply suggestions from code review

Co-authored-by: Alejandro Santiago <dev@alestiago.com>

* feat: pr suggestion

* feat: pr suggestions

* feat: pr suggestions

* Apply suggestions from code review

Co-authored-by: Alejandro Santiago <dev@alestiago.com>

* feat: pr suggestions

Co-authored-by: Alejandro Santiago <dev@alestiago.com>
pull/41/head
Erick 2 years ago committed by GitHub
parent d0756e0b60
commit 07d16fbac0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,6 +14,8 @@ class GameBloc extends Bloc<GameEvent, GameState> {
on<BonusLetterActivated>(_onBonusLetterActivated);
}
static const bonusWord = 'GOOGLE';
void _onBallLost(BallLost event, Emitter emit) {
if (state.balls > 0) {
emit(state.copyWith(balls: state.balls - 1));
@ -27,13 +29,25 @@ class GameBloc extends Bloc<GameEvent, GameState> {
}
void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) {
emit(
state.copyWith(
bonusLetters: [
...state.bonusLetters,
event.letter,
],
),
);
final newBonusLetters = [
...state.activatedBonusLetters,
event.letterIndex,
];
if (newBonusLetters.length == bonusWord.length) {
emit(
state.copyWith(
activatedBonusLetters: [],
bonusHistory: [
...state.bonusHistory,
GameBonus.word,
],
),
);
} else {
emit(
state.copyWith(activatedBonusLetters: newBonusLetters),
);
}
}
}

@ -34,10 +34,14 @@ class Scored extends GameEvent {
}
class BonusLetterActivated extends GameEvent {
const BonusLetterActivated(this.letter);
const BonusLetterActivated(this.letterIndex)
: assert(
letterIndex < GameBloc.bonusWord.length,
'Index must be smaller than the length of the word',
);
final String letter;
final int letterIndex;
@override
List<Object?> get props => [letter];
List<Object?> get props => [letterIndex];
}

@ -2,6 +2,13 @@
part of 'game_bloc.dart';
/// Defines bonuses that a player can gain during a PinballGame.
enum GameBonus {
/// Bonus achieved when the user activate all of the bonus
/// letters on the board, forming the bonus word
word,
}
/// {@template game_state}
/// Represents the state of the pinball game.
/// {@endtemplate}
@ -10,14 +17,16 @@ class GameState extends Equatable {
const GameState({
required this.score,
required this.balls,
required this.bonusLetters,
required this.activatedBonusLetters,
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 [];
activatedBonusLetters = const [],
bonusHistory = const [];
/// The current score of the game.
final int score;
@ -28,7 +37,11 @@ class GameState extends Equatable {
final int balls;
/// Active bonus letters.
final List<String> bonusLetters;
final List<int> activatedBonusLetters;
/// Holds the history of all the [GameBonus]es earned by the player during a
/// PinballGame.
final List<GameBonus> bonusHistory;
/// Determines when the game is over.
bool get isGameOver => balls == 0;
@ -39,7 +52,8 @@ class GameState extends Equatable {
GameState copyWith({
int? score,
int? balls,
List<String>? bonusLetters,
List<int>? activatedBonusLetters,
List<GameBonus>? bonusHistory,
}) {
assert(
score == null || score >= this.score,
@ -49,7 +63,9 @@ class GameState extends Equatable {
return GameState(
score: score ?? this.score,
balls: balls ?? this.balls,
bonusLetters: bonusLetters ?? this.bonusLetters,
activatedBonusLetters:
activatedBonusLetters ?? this.activatedBonusLetters,
bonusHistory: bonusHistory ?? this.bonusHistory,
);
}
@ -57,6 +73,7 @@ class GameState extends Equatable {
List<Object?> get props => [
score,
balls,
bonusLetters,
activatedBonusLetters,
bonusHistory,
];
}

@ -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,
activatedBonusLetters: [],
bonusHistory: [],
),
const GameState(
score: 0,
balls: 1,
activatedBonusLetters: [],
bonusHistory: [],
),
const GameState(
score: 0,
balls: 0,
activatedBonusLetters: [],
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,
activatedBonusLetters: [],
bonusHistory: [],
),
const GameState(
score: 5,
balls: 3,
activatedBonusLetters: [],
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,
activatedBonusLetters: [],
bonusHistory: [],
),
const GameState(
score: 0,
balls: 1,
activatedBonusLetters: [],
bonusHistory: [],
),
const GameState(
score: 0,
balls: 0,
activatedBonusLetters: [],
bonusHistory: [],
),
],
);
});
@ -65,42 +105,77 @@ 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'],
activatedBonusLetters: [0],
bonusHistory: [],
),
const GameState(
GameState(
score: 0,
balls: 3,
bonusLetters: ['G', 'O'],
activatedBonusLetters: [0, 1],
bonusHistory: [],
),
const GameState(
GameState(
score: 0,
balls: 3,
bonusLetters: ['G', 'O', 'O'],
activatedBonusLetters: [0, 1, 2],
bonusHistory: [],
),
const GameState(
],
);
blocTest<GameBloc, GameState>(
'adds the bonus when the bonusWord is completed',
build: GameBloc.new,
act: (bloc) => bloc
..add(const BonusLetterActivated(0))
..add(const BonusLetterActivated(1))
..add(const BonusLetterActivated(2))
..add(const BonusLetterActivated(3))
..add(const BonusLetterActivated(4))
..add(const BonusLetterActivated(5)),
expect: () => const [
GameState(
score: 0,
balls: 3,
bonusLetters: ['G', 'O', 'O', 'G'],
activatedBonusLetters: [0],
bonusHistory: [],
),
const GameState(
GameState(
score: 0,
balls: 3,
bonusLetters: ['G', 'O', 'O', 'G', 'L'],
activatedBonusLetters: [0, 1],
bonusHistory: [],
),
const GameState(
GameState(
score: 0,
balls: 3,
activatedBonusLetters: [0, 1, 2],
bonusHistory: [],
),
GameState(
score: 0,
balls: 3,
activatedBonusLetters: [0, 1, 2, 3],
bonusHistory: [],
),
GameState(
score: 0,
balls: 3,
activatedBonusLetters: [0, 1, 2, 3, 4],
bonusHistory: [],
),
GameState(
score: 0,
balls: 3,
bonusLetters: ['G', 'O', 'O', 'G', 'L', 'E'],
activatedBonusLetters: [],
bonusHistory: [GameBonus.word],
),
],
);

@ -43,19 +43,29 @@ 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))),
);
});
test(
'throws assertion error if index is bigger than the word length',
() {
expect(
() => BonusLetterActivated(8),
throwsAssertionError,
);
},
);
});
});
}

@ -10,13 +10,15 @@ void main() {
GameState(
score: 0,
balls: 0,
bonusLetters: const [],
activatedBonusLetters: const [],
bonusHistory: const [],
),
equals(
const GameState(
score: 0,
balls: 0,
bonusLetters: [],
activatedBonusLetters: [],
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,
activatedBonusLetters: [],
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,
activatedBonusLetters: 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,
activatedBonusLetters: const [],
bonusHistory: const [],
),
throwsAssertionError,
);
},
@ -60,7 +77,8 @@ void main() {
const gameState = GameState(
balls: 0,
score: 0,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
);
expect(gameState.isGameOver, isTrue);
});
@ -71,7 +89,8 @@ void main() {
const gameState = GameState(
balls: 1,
score: 0,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
);
expect(gameState.isGameOver, isFalse);
});
@ -85,7 +104,8 @@ void main() {
const gameState = GameState(
balls: 1,
score: 0,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
);
expect(gameState.isLastBall, isTrue);
},
@ -98,7 +118,8 @@ void main() {
const gameState = GameState(
balls: 2,
score: 0,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
);
expect(gameState.isLastBall, isFalse);
},
@ -113,7 +134,8 @@ void main() {
const gameState = GameState(
balls: 0,
score: 2,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
);
expect(
() => gameState.copyWith(score: gameState.score - 1),
@ -129,7 +151,8 @@ void main() {
const gameState = GameState(
balls: 0,
score: 2,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
);
expect(
gameState.copyWith(),
@ -145,12 +168,14 @@ void main() {
const gameState = GameState(
score: 2,
balls: 0,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
);
final otherGameState = GameState(
score: gameState.score + 1,
balls: gameState.balls + 1,
bonusLetters: const ['A'],
activatedBonusLetters: const [0],
bonusHistory: const [GameBonus.word],
);
expect(gameState, isNot(equals(otherGameState)));
@ -158,7 +183,8 @@ void main() {
gameState.copyWith(
score: otherGameState.score,
balls: otherGameState.balls,
bonusLetters: otherGameState.bonusLetters,
activatedBonusLetters: otherGameState.activatedBonusLetters,
bonusHistory: otherGameState.bonusHistory,
),
equals(otherGameState),
);

@ -133,7 +133,8 @@ void main() {
initialState: const GameState(
score: 10,
balls: 1,
bonusLetters: [],
activatedBonusLetters: [],
bonusHistory: [],
),
);
await game.ready();

@ -9,7 +9,12 @@ import '../../helpers/helpers.dart';
void main() {
group('GameHud', () {
late GameBloc gameBloc;
const initialState = GameState(score: 10, balls: 2, bonusLetters: []);
const initialState = GameState(
score: 10,
balls: 2,
activatedBonusLetters: [],
bonusHistory: [],
);
void _mockState(GameState state) {
whenListen(

@ -84,7 +84,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,
activatedBonusLetters: [],
bonusHistory: [],
);
whenListen(
gameBloc,
Stream.value(state),

Loading…
Cancel
Save