In this step, you implement few logical functions and the game state.
Keywords: library, syntax, collection api
Edit lib/risk.dart
, as follows.
library risk;
import 'dart:math';
import 'package:risk_engine/risk_engine.dart';
part 'src/map.dart';
part 'src/game.dart';
The editor complains about inexistant src/game.dart
file. We'll create it in the section below.
It also complains about unused imports. Don't worry about that, we'll be using dart:math
and the engine later in this step.
→ Under lib/src
, create a file named game.dart
.
To do this in Dart Editor, right-click the lib/src
directory, choose New File..., and specify the name game.dart
.
part of risk;
// TODO: implement RiskGameState
You've just added a new implementation file!
Key information:
library risk;
declares that this is a library namedrisk
.part 'src/game.dart';
adds an implementation file in the library located by the given path.part of risk;
indicates that the file is part of the libraryrisk
and that it shares the same scope.
Edit src/game.dart
, as follows.
→ Add the following classes:
/// Stores the state of a country.
class CountryStateImpl implements CountryState {
/// The countryId for this CountryState.
final String countryId;
/// The playerId who owns this country.
int playerId;
/// The number of armies in this country.
int armies;
CountryStateImpl(this.countryId, {this.playerId, this.armies: 0});
}
/// Stores the state of a player.
class PlayerStateImpl implements PlayerState {
/// The playerId for this CountryState.
final int playerId;
/// The player's name.
String name;
/// The player's avatar.
String avatar;
/// The player's color.
String color;
/// The number of available armies for the player.
int reinforcement;
/// True if the player lost the game.
bool dead;
PlayerStateImpl(this.playerId, this.name, this.avatar, this.color, {this.reinforcement: 0, this.dead: false});
}
/// Stores the Risk game state.
class RiskGameStateImpl implements RiskGameState {
/// Returns the countryId / country state map.
Map<String, CountryStateImpl> countries = {};
/// Returns the playerId / player state map.
Map<int, PlayerStateImpl> players = {};
/// Returns the players order.
List<int> playersOrder = [];
/// Returns the player who is playing.
int activePlayerId;
/// True if the game is started.
bool started = false;
/// True if the game is setuping player countries.
bool setupPhase = false;
/// Return the turn step of the active player (REINFORCEMENT, ATTACK, FORTIFICATION).
String turnStep;
/// Returns the history of all events
List<EngineEvent> events = [];
}
The editor complains about missing concrete implementation. We'll implement them right after.
Key information:
CountryState
stores the state of a country: the player who owns the country and the number of armies in place.PlayerState
stores the state of a player: the player's information, the number of armies he get at the beginning of his turn and if he's dead.RiskGameState
stores the state of the whole game: the board with the countries states, the players states and the progress of the game.- A class implements one or more interfaces by declaring them in an
implements
clause and then providing the APIs required by the interfaces. - You can explore the source code of the interfaces: right click on the interface name (e.g.
RiskGameState
), then Open Declaration.
RiskGameState
also provides few logical functions needed for the game engine and it is also capable of updating itself for an incoming EngineEvent
.
Continue to edit src/game.dart
.
→ Right click on the RiskGameStateImpl
class name, then Quick Fix and select Create 5 missing override(s). It should add new methods in the class:
// TODO: implement allCountryIds
/// Returns all possible countryIds
@override
List<String> get allCountryIds => null;
/// Returns neighbours ids for the given [countryId].
@override
List<String> countryNeighbours(String countryId) {
// TODO: implement countryNeighbours
}
/// Computes attacker loss comparing rolled [attacks] and [defends] dices.
/// This method assumes that [attacks] and [defends] are sorted in a descending order
@override
int computeAttackerLoss(List<int> attacks, List<int> defends) {
// TODO: implement computeAttackerLoss
}
/**
* Computes reinforcement for a [playerId] in this game.
* Reinforcement = (Number of countries player owns) / 3 + (Continent bonus)
* Continent bonus is added if player owns all the countries of a continent.
* If reinforcement is less than three, round up to three.
*/
@override
int computeReinforcement(int playerId) {
// TODO: implement computeReinforcement
}
/// Updates this Risk game state for the incoming [event].
@override
void update(EngineEvent event) {
// TODO: implement update
}
→ Implements those methods and run tests in test/s3_game_test.dart
to check if your implementation is correct.
Key information:
RiskGameStateImpl
implements an abstract class. Unlike Java there's no real interface in Dart. Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. So every class can be used inimplements
clause.@override
is an annotation that marks an instance member as overriding a superclass member with the same name.allCountryIds
returns all existing countries. UseCOUNTRIES
orCOUNTRY_BY_ID
to get them.countryNeighbours
computes the number of reinforcement armies at the beginning of a player turn. Follow the Risk rules.computeAttackerLoss
takes rolled dices in parameter in descending order. It returns the number of lost armies for the attacker according to the Risk rules.computeReinforcement
computes the number of reinforcement armies at the beginning of a player turn. Follow the Risk rules and use the collection API. Advise: create a methodplayerCountries
that returns the country ids owned by the player.update
updates the game state in function of the incoming event. Follow the Events bible and run the tests.
Check your code against the files in s3_game (diff).