croupier: Augmented Board View Interactions
Multiple Related Changes were added in this CL
* The Game now requires an "Ask" command to occur before a card can be
"Play"ed.
* The Board will now show a "Play" button when it is your turn so that
you can play the card you've queued up.
* The Board also shows "Take" to take the trick when you've won it.
Note: The player's device will no longer show this unless in split
view.
* When playing without a table, "Ask" is not required.
* Board was moved around a bit to make space for these buttons.
Change-Id: I47368f90c63bd166a5cd53329d30ec61ab95d438
diff --git a/lib/components/board.dart b/lib/components/board.dart
index 09a3b68..3ed6e9e 100644
--- a/lib/components/board.dart
+++ b/lib/components/board.dart
@@ -79,7 +79,7 @@
}
class HeartsBoardState extends State<HeartsBoard> {
- static const double PROFILE_SIZE = 0.18; // multiplier of config.height
+ static const double PROFILE_SIZE = 0.17; // multiplier of config.height
// Every time the counter changes, a sound will be played.
// For example, in the pass/take phase, the counter does this:
@@ -392,65 +392,121 @@
}
Widget _buildBoardLayout() {
- return new Container(
- height: config.height,
- width: config.width,
- child: new Column([
- new Flexible(child: _playerProfile(2, PROFILE_SIZE), flex: 0),
- new Flexible(child: _showTrickText(2), flex: 0),
- new Flexible(
- child: new Row([
- new Flexible(child: _playerProfile(1, PROFILE_SIZE), flex: 0),
- new Flexible(child: _showTrickText(1), flex: 0),
- new Flexible(child: _buildCenterCards(), flex: 1),
- new Flexible(child: _showTrickText(3), flex: 0),
- new Flexible(child: _playerProfile(3, PROFILE_SIZE), flex: 0)
- ],
- alignItems: FlexAlignItems.center,
- justifyContent: FlexJustifyContent.spaceAround),
- flex: 1),
- new Flexible(child: _showTrickText(0), flex: 0),
- new Flexible(child: _playerProfile(0, PROFILE_SIZE), flex: 0)
- ],
- alignItems: FlexAlignItems.center,
- justifyContent: FlexJustifyContent.spaceAround));
+ int activePlayer = config.game.allPlayed
+ ? config.game.determineTrickWinner()
+ : config.game.whoseTurn;
+
+ return new GestureDetector(
+ onTap: config.game.asking ? null : config.game.askUI,
+ child: new Container(
+ height: config.height,
+ width: config.width,
+ decoration: new BoxDecoration(
+ border: new Border(
+ top: new BorderSide(
+ color: activePlayer == 2
+ ? style.theme.accentColor
+ : style.transparentColor,
+ width: 5.0),
+ right: new BorderSide(
+ color: activePlayer == 3
+ ? style.theme.accentColor
+ : style.transparentColor,
+ width: 5.0),
+ left: new BorderSide(
+ color: activePlayer == 1
+ ? style.theme.accentColor
+ : style.transparentColor,
+ width: 5.0),
+ bottom: new BorderSide(
+ color: activePlayer == 0
+ ? style.theme.accentColor
+ : style.transparentColor,
+ width: 5.0))),
+ child: new Column([
+ new Flexible(child: _playerProfile(2, PROFILE_SIZE), flex: 0),
+ new Flexible(child: _showTrickText(2), flex: 0),
+ new Flexible(
+ child: new Row([
+ new Flexible(
+ child: _playerProfile(1, PROFILE_SIZE), flex: 0),
+ new Flexible(child: _showTrickText(1), flex: 0),
+ new Flexible(
+ child: new Center(child: _buildCenterCards()), flex: 1),
+ new Flexible(child: _showTrickText(3), flex: 0),
+ new Flexible(
+ child: _playerProfile(3, PROFILE_SIZE), flex: 0)
+ ],
+ alignItems: FlexAlignItems.center,
+ justifyContent: FlexJustifyContent.spaceAround),
+ flex: 1),
+ new Flexible(child: _showTrickText(0), flex: 0),
+ new Flexible(child: _playerProfile(0, PROFILE_SIZE), flex: 0)
+ ],
+ alignItems: FlexAlignItems.center,
+ justifyContent: FlexJustifyContent.spaceAround)));
}
Widget _buildCenterCards() {
- bool wide = (config.width >= config.height);
+ //bool wide = (config.width >= config.height);
- if (wide) {
- return new Row([
- _buildCenterCard(1),
- new Column([_buildCenterCard(2), _buildCenterCard(0)],
- alignItems: FlexAlignItems.center,
- justifyContent: FlexJustifyContent.spaceAround),
- _buildCenterCard(3)
- ],
- alignItems: FlexAlignItems.center,
- justifyContent: FlexJustifyContent.spaceAround);
- } else {
- return new Column([
- _buildCenterCard(2),
- new Row([_buildCenterCard(1), _buildCenterCard(3)],
- alignItems: FlexAlignItems.center,
- justifyContent: FlexJustifyContent.spaceAround),
- _buildCenterCard(0)
- ],
- alignItems: FlexAlignItems.center,
- justifyContent: FlexJustifyContent.spaceAround);
+ double height = config.cardHeight * this._centerScaleFactor;
+ double width = config.cardWidth * this._centerScaleFactor;
+ Widget centerPiece =
+ new Container(height: height, width: width, child: new Block([]));
+ if (config.game.allPlayed) {
+ int rotateNum = config.game.determineTrickWinner();
+
+ centerPiece = _rotate(
+ new Container(
+ height: height,
+ width: width,
+ child: new RaisedButton(
+ child: new Text("Take", style: style.Text.largeStyle),
+ onPressed: config.game.takeTrickUI,
+ color: style.theme.accentColor)),
+ rotateNum);
}
+
+ return new Column([
+ new Flexible(
+ child: new Row([
+ new Flexible(child: new Block([])),
+ new Flexible(child: new Center(child: _buildCenterCard(2))),
+ new Flexible(child: new Block([])),
+ ],
+ alignItems: FlexAlignItems.center,
+ justifyContent: FlexJustifyContent.center)),
+ new Flexible(
+ child: new Row([
+ new Flexible(child: new Center(child: _buildCenterCard(1))),
+ new Flexible(child: new Block([centerPiece])),
+ new Flexible(child: new Center(child: _buildCenterCard(3))),
+ ],
+ alignItems: FlexAlignItems.center,
+ justifyContent: FlexJustifyContent.center)),
+ new Flexible(
+ child: new Row([
+ new Flexible(child: new Block([])),
+ new Flexible(child: new Center(child: _buildCenterCard(0))),
+ new Flexible(child: new Block([])),
+ ],
+ alignItems: FlexAlignItems.center,
+ justifyContent: FlexJustifyContent.center))
+ ],
+ alignItems: FlexAlignItems.center,
+ justifyContent: FlexJustifyContent.center);
}
double get _centerScaleFactor {
bool wide = (config.width >= config.height);
- double heightUsage = (1 - 2 * PROFILE_SIZE);
+ double heightUsed = 2 * PROFILE_SIZE;
if (wide) {
- return config.height * heightUsage / (config.cardHeight * 3);
+ return config.height * (1 - heightUsed) / (config.cardHeight * 4);
} else {
- return (config.width - config.height * heightUsage) /
- (config.cardWidth * 3);
+ return (config.width - (1.5 * config.height * heightUsed)) /
+ (config.cardWidth * 4);
}
}
@@ -462,13 +518,38 @@
bool hasPlayed = cards.length > 0;
bool isTurn = game.whoseTurn == playerNumber && !hasPlayed;
- return new CardCollectionComponent(
- cards, true, CardCollectionOrientation.show1,
- widthCard: config.cardWidth * this._centerScaleFactor,
- heightCard: config.cardHeight * this._centerScaleFactor,
- rotation: _rotationAngle(playerNumber),
- useKeys: true,
- backgroundColor: isTurn ? style.theme.accentColor : null);
+ double height = config.cardHeight * this._centerScaleFactor;
+ double width = config.cardWidth * this._centerScaleFactor;
+
+ List<Widget> stackWidgets = <Widget>[
+ new Positioned(
+ top: 0.0,
+ left: 0.0,
+ child: new CardCollectionComponent(
+ cards, true, CardCollectionOrientation.show1,
+ widthCard: width - 6,
+ heightCard: height - 6,
+ rotation: _rotationAngle(playerNumber),
+ useKeys: true))
+ ];
+
+ if (isTurn) {
+ stackWidgets.add(new Positioned(
+ top: 0.0,
+ left: 0.0,
+ child: _rotate(
+ new Container(
+ height: height,
+ width: width,
+ child: new RaisedButton(
+ child: new Text("Play", style: style.Text.largeStyle),
+ onPressed: config.game.asking ? null : config.game.askUI,
+ color: style.theme.accentColor)),
+ playerNumber)));
+ }
+
+ return new Container(
+ height: height, width: width, child: new Stack(stackWidgets));
}
// The off-screen cards consist of trick cards and play cards.
@@ -500,7 +581,7 @@
}
return new CardCollectionComponent(
- cards, isPlay, CardCollectionOrientation.show1,
+ cards, alreadyPlaying, CardCollectionOrientation.show1,
widthCard: config.cardWidth * sizeFactor,
heightCard: config.cardHeight * sizeFactor,
useKeys: true,
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index f69b819..066cbec 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -30,12 +30,14 @@
// If someone sat at the table, they would have the value 4.
// If nobody sat at the table, then we should show the split view.
- if (!config.croupier.players_found.values.contains(4)) {
+ if (!_isBoardPresent) {
_showSplitView = true;
}
_reset();
}
+ bool get _isBoardPresent => config.croupier.players_found.values.contains(4);
+
@override
void _reset() {
super._reset();
@@ -51,9 +53,11 @@
bool get _shouldUnbuffer {
HeartsGame game = config.game;
+ bool hasPermission = game.asking || !_isBoardPresent;
return game.whoseTurn == game.playerNumber &&
bufferedPlay.length > 0 &&
- !bufferedPlaying;
+ !bufferedPlaying &&
+ hasPermission;
}
@override
@@ -263,6 +267,10 @@
_clearBufferedPlay();
bufferedPlay.add(card);
} else {
+ // Automatically ask (for when there is no board).
+ if (!game.asking && !_isBoardPresent) {
+ game.askUI();
+ }
game.move(card, dest);
config.sounds.play("whooshOut");
}
@@ -454,7 +462,8 @@
switch (game.phase) {
case HeartsPhase.Play:
if (game.allPlayed &&
- game.determineTrickWinner() == game.playerNumber) {
+ game.determineTrickWinner() == game.playerNumber &&
+ _showSplitView) {
statusBarWidgets.add(new Flexible(
flex: 0,
child: new GestureDetector(
diff --git a/lib/logic/hearts/hearts_command.part.dart b/lib/logic/hearts/hearts_command.part.dart
index fe6826b..6bb709a 100644
--- a/lib/logic/hearts/hearts_command.part.dart
+++ b/lib/logic/hearts/hearts_command.part.dart
@@ -30,6 +30,9 @@
: super("Play", computePlay(playerId, c),
simultaneity: SimulLevel.TURN_BASED);
+ HeartsCommand.ask()
+ : super("Ask", computeAsk(), simultaneity: SimulLevel.TURN_BASED);
+
HeartsCommand.takeTrick()
: super("TakeTrick", computeTakeTrick(),
simultaneity: SimulLevel.TURN_BASED);
@@ -48,6 +51,8 @@
return SimulLevel.INDEPENDENT;
case "Play":
return SimulLevel.TURN_BASED;
+ case "Ask":
+ return SimulLevel.TURN_BASED;
case "TakeTrick":
return SimulLevel.TURN_BASED;
case "Ready":
@@ -83,6 +88,10 @@
return "${playerId}:${c.toString()}:END";
}
+ static String computeAsk() {
+ return "END";
+ }
+
static String computeTakeTrick() {
return "END";
}
@@ -155,6 +164,11 @@
return false;
}
+ // Can play if the game is asking for a card to be played.
+ if (!game.asking) {
+ return false;
+ }
+
// Play the card from the player's hand to their play pile.
int playerId = int.parse(parts[0]);
int targetId = playerId + HeartsGame.OFFSET_PLAY;
@@ -170,6 +184,14 @@
}
bool canTransfer = this.transferCheck(hand, discard, c);
return canTransfer;
+ case "Ask":
+ if (game.phase != HeartsPhase.Play) {
+ return false;
+ }
+ if (game.allPlayed) {
+ return false;
+ }
+ return !game.asking; // Can ask if you're not asking.
case "TakeTrick":
if (game.phase != HeartsPhase.Play) {
return false;
@@ -257,6 +279,11 @@
"Cannot process play commands when not in Play phase");
}
+ // Can play if the game is asking for a card to be played.
+ if (!game.asking) {
+ throw new StateError("Cannot play if not asking");
+ }
+
// Play the card from the player's hand to their play pile.
int playerId = int.parse(parts[0]);
int targetId = playerId + HeartsGame.OFFSET_PLAY;
@@ -272,6 +299,20 @@
"Player ${playerId} cannot play ${c.toString()} because ${reason}");
}
this.transfer(hand, discard, c);
+ game.asking = false;
+ return;
+ case "Ask":
+ if (game.phase != HeartsPhase.Play) {
+ throw new StateError(
+ "Cannot process ask commands when not in Play phase");
+ }
+ if (game.asking) {
+ throw new StateError("Cannot ask while already asking");
+ }
+ if (game.allPlayed) {
+ throw new StateError("Cannot ask if all cards are played");
+ }
+ game.asking = true;
return;
case "TakeTrick":
if (game.phase != HeartsPhase.Play) {
diff --git a/lib/logic/hearts/hearts_game.part.dart b/lib/logic/hearts/hearts_game.part.dart
index f4bb0e5..7e1b708 100644
--- a/lib/logic/hearts/hearts_game.part.dart
+++ b/lib/logic/hearts/hearts_game.part.dart
@@ -60,6 +60,7 @@
int lastTrickTaker;
bool heartsBroken;
int trickNumber;
+ bool asking; // Is the game ready to play a card?
// Used by the score screen to track scores and see which players are ready to continue to the next round.
List<int> scores = [0, 0, 0, 0];
@@ -78,6 +79,7 @@
heartsBroken = false;
lastTrickTaker = null;
trickNumber = 0;
+ asking = false;
}
void dealCards() {
@@ -278,6 +280,16 @@
}
// Note that this will be called by the UI.
+ void askUI() {
+ assert(phase == HeartsPhase.Play);
+ if (this.asking) {
+ print("Already asked...");
+ return; // just don't call it again.
+ }
+ gamelog.add(new HeartsCommand.ask());
+ }
+
+ // Note that this will be called by the UI.
void takeTrickUI() {
assert(phase == HeartsPhase.Play);
assert(this.allPlayed);
diff --git a/test/game_log_hearts_test.txt b/test/game_log_hearts_test.txt
index 7a3a9db..67126c1 100644
--- a/test/game_log_hearts_test.txt
+++ b/test/game_log_hearts_test.txt
@@ -22,93 +22,145 @@
# 3 has all hearts except for c4 and c5
# Trick 1 (2 leads with 2 of clubs)
+Ask|END
Play|2:classic c2:END
+Ask|END
Play|3:classic c4:END
+Ask|END
Play|0:classic d1:END
+Ask|END
Play|1:classic s1:END
TakeTrick|END
# Trick 2 (3 won last round with 4 of clubs)
+Ask|END
Play|3:classic c5:END
+Ask|END
Play|0:classic d2:END
+Ask|END
Play|1:classic s2:END
+Ask|END
Play|2:classic c1:END
TakeTrick|END
# Trick 3 (2 won with ace of clubs)
+Ask|END
Play|2:classic s4:END
+Ask|END
Play|3:classic h1:END
+Ask|END
Play|0:classic h5:END
+Ask|END
Play|1:classic s3:END
TakeTrick|END
# Trick 4 (2 won with s4)
+Ask|END
Play|2:classic s5:END
+Ask|END
Play|3:classic hk:END
+Ask|END
Play|0:classic h4:END
+Ask|END
Play|1:classic sk:END
TakeTrick|END
# Trick 5 (1 won with sk)
+Ask|END
Play|1:classic d5:END
+Ask|END
Play|2:classic ck:END
+Ask|END
Play|3:classic hq:END
+Ask|END
Play|0:classic d3:END
TakeTrick|END
# Trick 6 (1 won with d5)
+Ask|END
Play|1:classic d4:END
+Ask|END
Play|2:classic cq:END
+Ask|END
Play|3:classic hj:END
+Ask|END
Play|0:classic dk:END
TakeTrick|END
# Trick 7 (0 won with dk)
+Ask|END
Play|0:classic dq:END
+Ask|END
Play|1:classic sq:END
+Ask|END
Play|2:classic c3:END
+Ask|END
Play|3:classic h2:END
TakeTrick|END
# Trick 8 (0 won with dq)
+Ask|END
Play|0:classic dj:END
+Ask|END
Play|1:classic sj:END
+Ask|END
Play|2:classic cj:END
+Ask|END
Play|3:classic h3:END
TakeTrick|END
# Trick 9 (0 won with dj)
+Ask|END
Play|0:classic d10:END
+Ask|END
Play|1:classic s10:END
+Ask|END
Play|2:classic c10:END
+Ask|END
Play|3:classic h10:END
TakeTrick|END
# Trick 10 (0 won with d10)
+Ask|END
Play|0:classic d9:END
+Ask|END
Play|1:classic s9:END
+Ask|END
Play|2:classic c9:END
+Ask|END
Play|3:classic h9:END
TakeTrick|END
# Trick 11 (0 won with d9)
+Ask|END
Play|0:classic d8:END
+Ask|END
Play|1:classic s8:END
+Ask|END
Play|2:classic c8:END
+Ask|END
Play|3:classic h8:END
TakeTrick|END
# Trick 12 (0 won with d8)
+Ask|END
Play|0:classic d7:END
+Ask|END
Play|1:classic s7:END
+Ask|END
Play|2:classic c7:END
+Ask|END
Play|3:classic h7:END
TakeTrick|END
# Trick 13 (0 won with d7)
+Ask|END
Play|0:classic d6:END
+Ask|END
Play|1:classic s6:END
+Ask|END
Play|2:classic c6:END
+Ask|END
Play|3:classic h6:END
TakeTrick|END
@@ -141,93 +193,145 @@
Take|3:END
# Trick 1
+Ask|END
Play|0:classic c2:END
+Ask|END
Play|1:classic d2:END
+Ask|END
Play|2:classic d4:END
+Ask|END
Play|3:classic s2:END
TakeTrick|END
# Trick 2
+Ask|END
Play|0:classic c3:END
+Ask|END
Play|1:classic d3:END
+Ask|END
Play|2:classic h3:END
+Ask|END
Play|3:classic s3:END
TakeTrick|END
# Trick 3
+Ask|END
Play|0:classic c4:END
+Ask|END
Play|1:classic s4:END
+Ask|END
Play|2:classic h2:END
+Ask|END
Play|3:classic h4:END
TakeTrick|END
# Trick 4
+Ask|END
Play|0:classic c5:END
+Ask|END
Play|1:classic d5:END
+Ask|END
Play|2:classic h5:END
+Ask|END
Play|3:classic s5:END
TakeTrick|END
# Trick 5
+Ask|END
Play|0:classic c6:END
+Ask|END
Play|1:classic d6:END
+Ask|END
Play|2:classic h6:END
+Ask|END
Play|3:classic s6:END
TakeTrick|END
# Trick 6
+Ask|END
Play|0:classic c7:END
+Ask|END
Play|1:classic d7:END
+Ask|END
Play|2:classic h7:END
+Ask|END
Play|3:classic s7:END
TakeTrick|END
# Trick 7
+Ask|END
Play|0:classic c8:END
+Ask|END
Play|1:classic d8:END
+Ask|END
Play|2:classic h8:END
+Ask|END
Play|3:classic s8:END
TakeTrick|END
# Trick 8
+Ask|END
Play|0:classic c9:END
+Ask|END
Play|1:classic d9:END
+Ask|END
Play|2:classic h9:END
+Ask|END
Play|3:classic s9:END
TakeTrick|END
# Trick 9
+Ask|END
Play|0:classic c1:END
+Ask|END
Play|1:classic d1:END
+Ask|END
Play|2:classic h1:END
+Ask|END
Play|3:classic s1:END
TakeTrick|END
# Trick 10
+Ask|END
Play|0:classic c10:END
+Ask|END
Play|1:classic d10:END
+Ask|END
Play|2:classic h10:END
+Ask|END
Play|3:classic s10:END
TakeTrick|END
# Trick 11
+Ask|END
Play|0:classic cj:END
+Ask|END
Play|1:classic dj:END
+Ask|END
Play|2:classic hj:END
+Ask|END
Play|3:classic sj:END
TakeTrick|END
# Trick 12
+Ask|END
Play|0:classic cq:END
+Ask|END
Play|1:classic dq:END
+Ask|END
Play|2:classic hq:END
+Ask|END
Play|3:classic sq:END
TakeTrick|END
# Trick 13
+Ask|END
Play|0:classic ck:END
+Ask|END
Play|1:classic dk:END
+Ask|END
Play|2:classic hk:END
+Ask|END
Play|3:classic sk:END
TakeTrick|END
@@ -258,93 +362,145 @@
Take|3:END
# Trick 1
+Ask|END
Play|0:classic c2:END
+Ask|END
Play|1:classic d2:END
+Ask|END
Play|2:classic d4:END
+Ask|END
Play|3:classic s2:END
TakeTrick|END
# Trick 2
+Ask|END
Play|0:classic c3:END
+Ask|END
Play|1:classic d3:END
+Ask|END
Play|2:classic h3:END
+Ask|END
Play|3:classic s3:END
TakeTrick|END
# Trick 3
+Ask|END
Play|0:classic c4:END
+Ask|END
Play|1:classic s4:END
+Ask|END
Play|2:classic h2:END
+Ask|END
Play|3:classic h4:END
TakeTrick|END
# Trick 4
+Ask|END
Play|0:classic c5:END
+Ask|END
Play|1:classic d5:END
+Ask|END
Play|2:classic h5:END
+Ask|END
Play|3:classic s5:END
TakeTrick|END
# Trick 5
+Ask|END
Play|0:classic c6:END
+Ask|END
Play|1:classic d6:END
+Ask|END
Play|2:classic h6:END
+Ask|END
Play|3:classic s6:END
TakeTrick|END
# Trick 6
+Ask|END
Play|0:classic c7:END
+Ask|END
Play|1:classic d7:END
+Ask|END
Play|2:classic h7:END
+Ask|END
Play|3:classic s7:END
TakeTrick|END
# Trick 7
+Ask|END
Play|0:classic c8:END
+Ask|END
Play|1:classic d8:END
+Ask|END
Play|2:classic h8:END
+Ask|END
Play|3:classic s8:END
TakeTrick|END
# Trick 8
+Ask|END
Play|0:classic c9:END
+Ask|END
Play|1:classic d9:END
+Ask|END
Play|2:classic h9:END
+Ask|END
Play|3:classic s9:END
TakeTrick|END
# Trick 9
+Ask|END
Play|0:classic c1:END
+Ask|END
Play|1:classic d1:END
+Ask|END
Play|2:classic h1:END
+Ask|END
Play|3:classic s1:END
TakeTrick|END
# Trick 10
+Ask|END
Play|0:classic c10:END
+Ask|END
Play|1:classic d10:END
+Ask|END
Play|2:classic h10:END
+Ask|END
Play|3:classic s10:END
TakeTrick|END
# Trick 11
+Ask|END
Play|0:classic cj:END
+Ask|END
Play|1:classic dj:END
+Ask|END
Play|2:classic hj:END
+Ask|END
Play|3:classic sj:END
TakeTrick|END
# Trick 12
+Ask|END
Play|0:classic cq:END
+Ask|END
Play|1:classic dq:END
+Ask|END
Play|2:classic hq:END
+Ask|END
Play|3:classic sq:END
TakeTrick|END
# Trick 13
+Ask|END
Play|0:classic ck:END
+Ask|END
Play|1:classic dk:END
+Ask|END
Play|2:classic hk:END
+Ask|END
Play|3:classic sk:END
TakeTrick|END
@@ -363,93 +519,145 @@
Deal|3:classic s1:classic s2:classic s3:classic h4:classic s5:classic s6:classic s7:classic s8:classic s9:classic s10:classic sj:classic sq:classic sk:END
# Trick 1
+Ask|END
Play|0:classic c2:END
+Ask|END
Play|1:classic d2:END
+Ask|END
Play|2:classic d4:END
+Ask|END
Play|3:classic s2:END
TakeTrick|END
# Trick 2
+Ask|END
Play|0:classic c3:END
+Ask|END
Play|1:classic d3:END
+Ask|END
Play|2:classic h3:END
+Ask|END
Play|3:classic s3:END
TakeTrick|END
# Trick 3
+Ask|END
Play|0:classic c4:END
+Ask|END
Play|1:classic s4:END
+Ask|END
Play|2:classic h2:END
+Ask|END
Play|3:classic h4:END
TakeTrick|END
# Trick 4
+Ask|END
Play|0:classic c5:END
+Ask|END
Play|1:classic d5:END
+Ask|END
Play|2:classic h5:END
+Ask|END
Play|3:classic s5:END
TakeTrick|END
# Trick 5
+Ask|END
Play|0:classic c6:END
+Ask|END
Play|1:classic d6:END
+Ask|END
Play|2:classic h6:END
+Ask|END
Play|3:classic s6:END
TakeTrick|END
# Trick 6
+Ask|END
Play|0:classic c7:END
+Ask|END
Play|1:classic d7:END
+Ask|END
Play|2:classic h7:END
+Ask|END
Play|3:classic s7:END
TakeTrick|END
# Trick 7
+Ask|END
Play|0:classic c8:END
+Ask|END
Play|1:classic d8:END
+Ask|END
Play|2:classic h8:END
+Ask|END
Play|3:classic s8:END
TakeTrick|END
# Trick 8
+Ask|END
Play|0:classic c9:END
+Ask|END
Play|1:classic d9:END
+Ask|END
Play|2:classic h9:END
+Ask|END
Play|3:classic s9:END
TakeTrick|END
# Trick 9
+Ask|END
Play|0:classic c1:END
+Ask|END
Play|1:classic d1:END
+Ask|END
Play|2:classic h1:END
+Ask|END
Play|3:classic s1:END
TakeTrick|END
# Trick 10
+Ask|END
Play|0:classic c10:END
+Ask|END
Play|1:classic d10:END
+Ask|END
Play|2:classic h10:END
+Ask|END
Play|3:classic s10:END
TakeTrick|END
# Trick 11
+Ask|END
Play|0:classic cj:END
+Ask|END
Play|1:classic dj:END
+Ask|END
Play|2:classic hj:END
+Ask|END
Play|3:classic sj:END
TakeTrick|END
# Trick 12
+Ask|END
Play|0:classic cq:END
+Ask|END
Play|1:classic dq:END
+Ask|END
Play|2:classic hq:END
+Ask|END
Play|3:classic sq:END
TakeTrick|END
# Trick 13
+Ask|END
Play|0:classic ck:END
+Ask|END
Play|1:classic dk:END
+Ask|END
Play|2:classic hk:END
+Ask|END
Play|3:classic sk:END
TakeTrick|END
@@ -480,93 +688,145 @@
Take|3:END
# Trick 1
+Ask|END
Play|0:classic c2:END
+Ask|END
Play|1:classic d2:END
+Ask|END
Play|2:classic d4:END
+Ask|END
Play|3:classic s2:END
TakeTrick|END
# Trick 2
+Ask|END
Play|0:classic c3:END
+Ask|END
Play|1:classic d3:END
+Ask|END
Play|2:classic h3:END
+Ask|END
Play|3:classic s3:END
TakeTrick|END
# Trick 3
+Ask|END
Play|0:classic c4:END
+Ask|END
Play|1:classic s4:END
+Ask|END
Play|2:classic h2:END
+Ask|END
Play|3:classic h4:END
TakeTrick|END
# Trick 4
+Ask|END
Play|0:classic c5:END
+Ask|END
Play|1:classic d5:END
+Ask|END
Play|2:classic h5:END
+Ask|END
Play|3:classic s5:END
TakeTrick|END
# Trick 5
+Ask|END
Play|0:classic c6:END
+Ask|END
Play|1:classic d6:END
+Ask|END
Play|2:classic h6:END
+Ask|END
Play|3:classic s6:END
TakeTrick|END
# Trick 6
+Ask|END
Play|0:classic c7:END
+Ask|END
Play|1:classic d7:END
+Ask|END
Play|2:classic h7:END
+Ask|END
Play|3:classic s7:END
TakeTrick|END
# Trick 7
+Ask|END
Play|0:classic c8:END
+Ask|END
Play|1:classic d8:END
+Ask|END
Play|2:classic h8:END
+Ask|END
Play|3:classic s8:END
TakeTrick|END
# Trick 8
+Ask|END
Play|0:classic c9:END
+Ask|END
Play|1:classic d9:END
+Ask|END
Play|2:classic h9:END
+Ask|END
Play|3:classic s9:END
TakeTrick|END
# Trick 9
+Ask|END
Play|0:classic c1:END
+Ask|END
Play|1:classic d1:END
+Ask|END
Play|2:classic h1:END
+Ask|END
Play|3:classic s1:END
TakeTrick|END
# Trick 10
+Ask|END
Play|0:classic c10:END
+Ask|END
Play|1:classic d10:END
+Ask|END
Play|2:classic h10:END
+Ask|END
Play|3:classic s10:END
TakeTrick|END
# Trick 11
+Ask|END
Play|0:classic cj:END
+Ask|END
Play|1:classic dj:END
+Ask|END
Play|2:classic hj:END
+Ask|END
Play|3:classic sj:END
TakeTrick|END
# Trick 12
+Ask|END
Play|0:classic cq:END
+Ask|END
Play|1:classic dq:END
+Ask|END
Play|2:classic hq:END
+Ask|END
Play|3:classic sq:END
TakeTrick|END
# Trick 13
+Ask|END
Play|0:classic ck:END
+Ask|END
Play|1:classic dk:END
+Ask|END
Play|2:classic hk:END
+Ask|END
Play|3:classic sk:END
TakeTrick|END
diff --git a/test/hearts_test.dart b/test/hearts_test.dart
index d5300e1..95ed019 100644
--- a/test/hearts_test.dart
+++ b/test/hearts_test.dart
@@ -304,11 +304,10 @@
test("Play Phase - Trick 1", () {
expect(game.phase, equals(HeartsPhase.Play));
- // Play Trick 1 consists of 4 play commands + 1 take trick command.
- runCommand();
- runCommand();
- runCommand();
- runCommand();
+ // Play Trick 1 consists of 4 ask + 4 play + 1 take trick command.
+ for (int i = 0; i < 8; i++) {
+ runCommand();
+ }
// Confirm that everyone has played before taking the trick
expect(game.allPlayed, isTrue);
@@ -323,11 +322,10 @@
test("Play Phase - Trick 2", () {
expect(game.phase, equals(HeartsPhase.Play));
- // Play Trick 2 consists of 4 play commands + 1 take trick command.
- runCommand();
- runCommand();
- runCommand();
- runCommand();
+ // Play Trick 2 consists of 4 ask + 4 play + 1 take trick command.
+ for (int i = 0; i < 8; i++) {
+ runCommand();
+ }
// Confirm that everyone has played before taking the trick
expect(game.allPlayed, isTrue);
@@ -344,9 +342,9 @@
test("Play Phase - Trick 13", () {
expect(game.phase, equals(HeartsPhase.Play));
- // Play Trick 13 consists of 44 play commands + 11 take trick command.
+ // Play Trick 13 consists of 44 ask + 44 play + 11 take trick command.
// Read line by line until the game is "over".
- for (int i = 10; i < 65; i++) {
+ for (int i = 18; i < 117; i++) {
runCommand();
}
@@ -409,24 +407,24 @@
test("Score Phase - end of game", () {
expect(game.hasGameEnded, isFalse);
- // 2nd Round: 4 deal, 4 pass, 4 take, 52 play, 13 take trick, 4 ready
+ // 2nd Round: 4 deal, 4 pass, 4 take, 52 ask, 52 play, 13 take trick, 4 ready
// Player A will shoot the moon for all remaining games (for simplicity).
- for (int i = 0; i < 81; i++) {
+ for (int i = 0; i < 133; i++) {
runCommand();
}
expect(game.scores, equals([21 + 0, 3 + 26, 2 + 26, 0 + 26]));
expect(game.hasGameEnded, isFalse);
- // 3rd Round: 4 deal, 4 pass, 4 take, 52 play, 13 take trick, 4 ready
- for (int i = 0; i < 81; i++) {
+ // 3rd Round: 4 deal, 4 pass, 4 take, 52 ask, 52 play, 13 take trick, 4 ready
+ for (int i = 0; i < 133; i++) {
runCommand();
}
expect(game.scores,
equals([21 + 0 + 0, 3 + 26 + 26, 2 + 26 + 26, 0 + 26 + 26]));
expect(game.hasGameEnded, isFalse);
- // 4th Round: 4 deal, 52 play, 13 take trick, 4 ready
- for (int i = 0; i < 73; i++) {
+ // 4th Round: 4 deal, 52 ask, 52 play, 13 take trick, 4 ready
+ for (int i = 0; i < 125; i++) {
runCommand();
}
expect(
@@ -440,8 +438,9 @@
expect(game.deltaScores, equals([0, 26, 26, 26]));
expect(game.hasGameEnded, isFalse);
- // 5th round: 4 deal, 4 pass, 4 take, 52 play, 13 take trick. Game is over, so no ready phase.
- for (int i = 0; i < 77; i++) {
+ // 5th round: 4 deal, 4 pass, 4 take, 52 ask, 52 play, 13 take trick.
+ // Game is over, so no ready phase.
+ for (int i = 0; i < 129; i++) {
runCommand();
}
expect(
@@ -541,6 +540,26 @@
game.gamelog.add(new HeartsCommand.take(3));
}, throwsA(new isInstanceOf<StateError>()));
});
+ test("Asking - wrong phase", () {
+ HeartsGame game = new HeartsGame()..playerNumber = 0;
+ game.phase = HeartsPhase.Deal;
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
+ expect(() {
+ game.gamelog.add(new HeartsCommand.ask());
+ }, throwsA(new isInstanceOf<StateError>()));
+ });
+ test("Asking - already asking", () {
+ HeartsGame game = new HeartsGame()..playerNumber = 0;
+ game.phase = HeartsPhase.Deal;
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.phase = HeartsPhase.Play;
+ game.gamelog.add(new HeartsCommand.ask());
+ expect(() {
+ game.gamelog.add(new HeartsCommand.ask());
+ }, throwsA(new isInstanceOf<StateError>()));
+ });
test("Playing - wrong phase", () {
HeartsGame game = new HeartsGame()..playerNumber = 0;
game.phase = HeartsPhase.Deal;
@@ -550,12 +569,23 @@
game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
}, throwsA(new isInstanceOf<StateError>()));
});
+ test("Playing - not asking", () {
+ HeartsGame game = new HeartsGame()..playerNumber = 0;
+ game.phase = HeartsPhase.Deal;
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.phase = HeartsPhase.Play;
+ expect(() {
+ game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
+ }, throwsA(new isInstanceOf<StateError>()));
+ });
test("Playing - missing card", () {
HeartsGame game = new HeartsGame()..playerNumber = 0;
game.phase = HeartsPhase.Deal;
game.gamelog.add(new HeartsCommand.deal(
0, new List<Card>.from(Card.All.getRange(0, 13))));
game.phase = HeartsPhase.Play;
+ game.gamelog.add(new HeartsCommand.ask());
expect(() {
game.gamelog.add(new HeartsCommand.play(0, Card.All[13]));
}, throwsA(new isInstanceOf<StateError>()));
@@ -567,6 +597,7 @@
0, new List<Card>.from(Card.All.getRange(0, 13))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
+ game.gamelog.add(new HeartsCommand.ask());
expect(() {
game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
}, throwsA(new isInstanceOf<StateError>()));
@@ -586,8 +617,11 @@
3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+ game.gamelog.add(new HeartsCommand.ask());
expect(() {
game.gamelog.add(new HeartsCommand.play(2, Card.All[26]));
}, throwsA(new isInstanceOf<StateError>()));
@@ -605,6 +639,7 @@
3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
+ game.gamelog.add(new HeartsCommand.ask());
expect(() {
game.gamelog.add(new HeartsCommand.play(
1, Card.All[13])); // player 0's turn, not player 1's.
@@ -623,7 +658,9 @@
3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+ game.gamelog.add(new HeartsCommand.ask());
expect(() {
game.gamelog
.add(new HeartsCommand.play(0, Card.All[13])); // should play 12
@@ -642,17 +679,22 @@
3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(2, Card.All[12])); // 2 won!
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(3, Card.All[39]));
game.gamelog.add(new HeartsCommand.takeTrick());
+ game.gamelog.add(new HeartsCommand.ask());
expect(() {
game.gamelog.add(new HeartsCommand.play(
2, Card.All[26])); // But 2 can't lead with a hearts.
}, throwsA(new isInstanceOf<StateError>()));
});
- test("Playing - trick not taken yet", () {
+ test("Asking - trick not taken yet", () {
HeartsGame game = new HeartsGame()..playerNumber = 0;
game.phase = HeartsPhase.Deal;
game.gamelog.add(new HeartsCommand.deal(
@@ -671,15 +713,18 @@
3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(2, Card.All[12])); // 2 won!
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(3, Card.All[39]));
- // Do not take trick. Play a card instead.
+ // Do not take trick. Ask instead.
expect(() {
- game.gamelog.add(new HeartsCommand.play(
- 2, Card.All[11])); // But 2 can't play until trick is taken
+ game.gamelog.add(new HeartsCommand.ask());
}, throwsA(new isInstanceOf<StateError>()));
});
test("Playing - take trick wrong phase", () {
@@ -702,9 +747,13 @@
3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+ game.gamelog.add(new HeartsCommand.ask());
game.gamelog.add(new HeartsCommand.play(2, Card.All[12]));
+ game.gamelog.add(new HeartsCommand.ask());
expect(() {
game.gamelog.add(new HeartsCommand.takeTrick()); // too soon
}, throwsA(new isInstanceOf<StateError>()));