croupier: Add SyncbaseEcho
Modified the Makefile and Croupier organization slightly to add a new
'game' called SyncbaseEcho.
SyncbaseEcho and SyncbaseEchoImpl essentially do what the original Sky
example in roadmap/mojo/syncbase does.
Note: These had to be split up, or else make test wouldn't work anymore.
Some of the embedder code won't run in a stand-alone VM.
To run with mojo_shell use `make start-with-mojo` instead of the usual
`make start` or `make install`. It also doesn't run on Android at the moment.
Improved the Makefile too, by making things PHONY-er.
Change-Id: I7e7a3d921e02b587b1d7047633fbcc84b1094892
diff --git a/Makefile b/Makefile
index 6bcebad..cbd61ea 100644
--- a/Makefile
+++ b/Makefile
@@ -1,29 +1,93 @@
+# This beginning section is used to setup the environment for running with mojo_shell.
+ETHER_DIR := $(V23_ROOT)/roadmap/mojo/syncbase
+CROUPIER_DIR := $(shell pwd)
+SHELL := /bin/bash -euo pipefail
+
+ifdef ANDROID
+ MOJO_ANDROID_FLAGS := --android
+
+ MOJO_BUILD_DIR := $(MOJO_DIR)/src/out/android_Debug
+ SKY_BUILD_DIR := $(SKY_DIR)/src/out/android_Debug
+ ETHER_BUILD_DIR := $(ETHER_DIR)/gen/mojo/android
+
+ SYNCBASE_DATA_DIR := /data/data/org.chromium.mojo.shell/app_home/syncbase_data
+else
+ MOJO_BUILD_DIR := $(MOJO_DIR)/src/out/Debug
+ SKY_BUILD_DIR := $(SKY_DIR)/src/out/Debug
+ ETHER_BUILD_DIR := $(ETHER_DIR)/gen/mojo/linux_amd64
+
+ SYNCBASE_DATA_DIR := /tmp/syncbase_data
+endif
+
+# NOTE(nlacasse): Running Go Mojo services requires passing the
+# --enable-multiprocess flag to mojo_shell. This is because the Go runtime is
+# very large, and can interfere with C++ memory if they are in the same
+# process.
+MOJO_SHELL_FLAGS := -v --enable-multiprocess \
+ --config-alias MOJO_BUILD_DIR=$(MOJO_BUILD_DIR) \
+ --config-alias SKY_DIR=$(SKY_DIR) \
+ --config-alias SKY_BUILD_DIR=$(SKY_BUILD_DIR) \
+ --config-alias ETHER_DIR=$(ETHER_DIR) \
+ --config-alias ETHER_BUILD_DIR=$(ETHER_BUILD_DIR) \
+ --config-alias CROUPIER_DIR=$(CROUPIER_DIR)
+
+
+.DELETE_ON_ERROR:
+
# Get the packages used by the dart project, according to pubspec.yaml
# Can also use `pub get`, but Sublime occasionally reverts me to an ealier version.
# Only `pub upgrade` can escape such a thing.
-get-packages: pubspec.yaml
+packages: pubspec.yaml
pub upgrade
-TEST_FILES := $(shell find test -name *.dart ! -name *.part.dart)
+DART_LIB_FILES := $(shell find lib -name *.dart ! -name *.part.dart)
+DART_TEST_FILES := $(shell find test -name *.dart ! -name *.part.dart)
-check-fmt:
- dartfmt -n lib/main.dart $(TEST_FILES)
+.PHONY: dartfmt
+dartfmt:
+ dartfmt -w $(DART_LIB_FILES) $(DART_TEST_FILES)
+.PHONY: lint
lint:
dartanalyzer lib/main.dart
- dartanalyzer $(TEST_FILES)
+ dartanalyzer $(DART_TEST_FILES)
+.PHONY: start
start:
./packages/sky/sky_tool start
-install: get-packages
+.PHONY: install
+install: packages
./packages/sky/sky_tool start --install
-# Could use `pub run test` too, but I like seeing every assertion print out.
-test:
- dart --checked $(TEST_FILES)
+.PHONY: env-check
+env-check:
+ifndef MOJO_DIR
+ $(error MOJO_DIR is not set)
+endif
+ifndef SKY_DIR
+ $(error SKY_DIR is not set)
+endif
+ifndef V23_ROOT
+ $(error V23_ROOT is not set)
+endif
+ifeq ($(wildcard $(MOJO_BUILD_DIR)),)
+ $(error ERROR: $(MOJO_BUILD_DIR) does not exist. Please see README.md for instructions on compiling Mojo resources.)
+endif
+# Run the Sky program with mojo shell. This allows use of Syncbase and Mojo.
+# If syncbase doesn't load, it could be that port 4002 is still in use; try fuser 4002/tcp.
+.PHONY: start-with-mojo
+start-with-mojo: env-check packages
+ $(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) 'mojo:window_manager https://croupier.v.io/lib/main.dart'
+
+# Could use `pub run test` too, but I like seeing every assertion print out.
+# TODO(alexfandrianto): I split off the syncbase logic from game.dart because it
+# would not run in a stand-alone VM. We will need to add mojo_test eventually.
+.PHONY: test
+test: packages
+ dart --checked $(DART_TEST_FILES)
+
+.PHONY: clean
clean:
rm -rf packages
-
-.PHONY: check-fmt lint start install test clean
\ No newline at end of file
diff --git a/lib/components/board.dart b/lib/components/board.dart
index 8a5aaeb..2e1ea9a 100644
--- a/lib/components/board.dart
+++ b/lib/components/board.dart
@@ -22,27 +22,30 @@
switch (posMod) {
case 0:
widgetsList.add(new widgets.Transform(
- transform: new vector_math.Matrix4.identity().rotateZ(math.PI).translate(0.0, -cardHeight / 2),
- child: new Card(logic_card.Card.All[cards[i]], true)
- ));
+ transform: new vector_math.Matrix4.identity()
+ .rotateZ(math.PI)
+ .translate(0.0, -cardHeight / 2),
+ child: new Card(logic_card.Card.All[cards[i]], true)));
break;
case 1:
widgetsList.add(new widgets.Transform(
- transform: new vector_math.Matrix4.identity().rotateZ(math.PI/2.0).translate(0.0, cardWidth/2),
- child: new Card(logic_card.Card.All[cards[i]], true)
- ));
+ transform: new vector_math.Matrix4.identity()
+ .rotateZ(math.PI / 2.0)
+ .translate(0.0, cardWidth / 2),
+ child: new Card(logic_card.Card.All[cards[i]], true)));
break;
case 2:
widgetsList.add(new widgets.Transform(
- transform: new vector_math.Matrix4.identity().translate(-cardWidth, cardWidth / 2),
- child: new Card(logic_card.Card.All[cards[i]], true)
- ));
+ transform: new vector_math.Matrix4.identity().translate(
+ -cardWidth, cardWidth / 2),
+ child: new Card(logic_card.Card.All[cards[i]], true)));
break;
case 3:
widgetsList.add(new widgets.Transform(
- transform: new vector_math.Matrix4.identity().rotateZ(math.PI/2.0).translate(0.0, -cardHeight/2),
- child: new Card(logic_card.Card.All[cards[i]], true)
- ));
+ transform: new vector_math.Matrix4.identity()
+ .rotateZ(math.PI / 2.0)
+ .translate(0.0, -cardHeight / 2),
+ child: new Card(logic_card.Card.All[cards[i]], true)));
break;
}
}
@@ -57,9 +60,10 @@
widgets.Widget build() {
List<widgets.Positioned> cards = [];
for (int i = 0; i < count; i++) {
- cards.add(new widgets.Positioned(child: new Card(logic_card.Card.All[0], false),
- top: 0.0,
- left: cardWidth*i/2.0));
+ cards.add(new widgets.Positioned(
+ child: new Card(logic_card.Card.All[0], false),
+ top: 0.0,
+ left: cardWidth * i / 2.0));
}
return new widgets.Stack(cards);
}
@@ -69,46 +73,42 @@
CardCluster centerCluster;
List<PlayerHand> hands; // counts of cards in players hands, in clockwise order
- Board(int firstCardPlayedPosition, List<int> cards, List<int> playerHandCount) :
- centerCluster = new CardCluster(firstCardPlayedPosition, cards) {
- assert(playerHandCount.length == 4);
- hands = new List<PlayerHand>();
- for (int count in playerHandCount) {
- hands.add(new PlayerHand(count));
- }
+ Board(int firstCardPlayedPosition, List<int> cards, List<int> playerHandCount)
+ : centerCluster = new CardCluster(firstCardPlayedPosition, cards) {
+ assert(playerHandCount.length == 4);
+ hands = new List<PlayerHand>();
+ for (int count in playerHandCount) {
+ hands.add(new PlayerHand(count));
+ }
}
widgets.Widget build() {
return new widgets.Container(
- decoration: new widgets.BoxDecoration(backgroundColor: colors.Pink[500]),
- child: new widgets.Stack(
- [
- new widgets.Positioned(child: hands[0],
- top: 0.0,
- left: 250.0),
- new widgets.Positioned(child: new widgets.Transform(
- transform: new vector_math.Matrix4.identity().rotateZ(math.PI/2.0),
- child: hands[1]
- ),
- left: 100.0,
- top: 400.0),
- new widgets.Positioned(child: new widgets.Transform(
+ decoration: new widgets.BoxDecoration(
+ backgroundColor: colors.Pink[500]),
+ child: new widgets.Stack([
+ new widgets.Positioned(child: hands[0], top: 0.0, left: 250.0),
+ new widgets.Positioned(
+ child: new widgets.Transform(
+ transform: new vector_math.Matrix4.identity()
+ .rotateZ(math.PI / 2.0),
+ child: hands[1]),
+ left: 100.0,
+ top: 400.0),
+ new widgets.Positioned(
+ child: new widgets.Transform(
transform: new vector_math.Matrix4.identity().rotateZ(math.PI),
- child: hands[2]
- ),
- top: 820.0,
- left: 350.0),
- new widgets.Positioned(child: new widgets.Transform(
- transform: new vector_math.Matrix4.identity().rotateZ(math.PI/2.0),
- child: hands[3]
- ),
- left: 500.0,
- top: 400.0),
- new widgets.Positioned(child: centerCluster,
- top: 400.0,
- left: 300.0),
- ]
- )
- );
+ child: hands[2]),
+ top: 820.0,
+ left: 350.0),
+ new widgets.Positioned(
+ child: new widgets.Transform(
+ transform: new vector_math.Matrix4.identity()
+ .rotateZ(math.PI / 2.0),
+ child: hands[3]),
+ left: 500.0,
+ top: 400.0),
+ new widgets.Positioned(child: centerCluster, top: 400.0, left: 300.0),
+ ]));
}
}
diff --git a/lib/components/card.dart b/lib/components/card.dart
index 9554583..5f7ee4b 100644
--- a/lib/components/card.dart
+++ b/lib/components/card.dart
@@ -8,16 +8,15 @@
Card(this.card, this.faceUp);
widgets.Widget build() {
- return new widgets.Listener(
- child: imageFromCard(card, faceUp)
- );
+ return new widgets.Listener(child: imageFromCard(card, faceUp));
}
static widgets.Widget imageFromCard(logic_card.Card c, bool faceUp) {
// TODO(alexfandrianto): If we allow an optional prefix in front of this,
// we would be able to have multiple skins of the same deck.
// TODO(alexfandrianto): Better card organization?
- String imageName = "${c.deck}/${faceUp ? 'up' : 'down'}/${c.identifier}.png";
+ String imageName =
+ "${c.deck}/${faceUp ? 'up' : 'down'}/${c.identifier}.png";
return new widgets.NetworkImage(src: imageName);
}
}
diff --git a/lib/components/card_collection.dart b/lib/components/card_collection.dart
index 0058cce..3ad62f8 100644
--- a/lib/components/card_collection.dart
+++ b/lib/components/card_collection.dart
@@ -5,9 +5,7 @@
import 'package:sky/widgets.dart' show DragTarget;
import 'package:sky/theme/colors.dart' as colors;
-enum Orientation {
- vert, horz, fan, show1
-}
+enum Orientation { vert, horz, fan, show1 }
class CardCollectionComponent extends StatefulComponent {
List<logic_card.Card> cards;
@@ -17,7 +15,8 @@
String status = 'bar';
- CardCollectionComponent(this.cards, this.faceUp, this.orientation, this.parentCallback);
+ CardCollectionComponent(
+ this.cards, this.faceUp, this.orientation, this.parentCallback);
void syncConstructorArguments(CardCollectionComponent other) {
cards = other.cards;
@@ -35,19 +34,21 @@
List<Widget> flexCards(List<Widget> cardWidgets) {
List<Widget> flexWidgets = new List<Widget>();
- cardWidgets.forEach((cardWidget) => flexWidgets.add(new Flexible(child: cardWidget)));
+ cardWidgets.forEach(
+ (cardWidget) => flexWidgets.add(new Flexible(child: cardWidget)));
return flexWidgets;
}
Widget wrapCards(List<Widget> cardWidgets) {
switch (this.orientation) {
case Orientation.vert:
- return new Flex(flexCards(cardWidgets), direction: FlexDirection.vertical);
+ return new Flex(flexCards(cardWidgets),
+ direction: FlexDirection.vertical);
case Orientation.horz:
return new Flex(flexCards(cardWidgets));
case Orientation.fan:
- // unimplemented, so we'll fall through to show1, for now.
- // Probably a Stack + Positioned
+ // unimplemented, so we'll fall through to show1, for now.
+ // Probably a Stack + Positioned
case Orientation.show1:
return new Stack(cardWidgets);
default:
@@ -60,30 +61,28 @@
List<Widget> cardComponents = new List<Widget>();
cardComponents.add(new Text(status));
for (int i = 0; i < cards.length; i++) {
- cardComponents.add(new Draggable<Card>(new Card(cards[i], faceUp))); // flex
+ cardComponents
+ .add(new Draggable<Card>(new Card(cards[i], faceUp))); // flex
}
// Let's draw a stack of cards with DragTargets.
// TODO(alexfandrianto): In many cases, card collections shouldn't have draggable cards.
// Additionally, it may be worthwhile to restrict it to 1 at a time.
return new DragTarget<Card>(
- onAccept: _handleAccept,
- builder: (List<Card> data, _) {
- print(this.cards.length);
- print(data);
- return new Container(
+ onAccept: _handleAccept, builder: (List<Card> data, _) {
+ print(this.cards.length);
+ print(data);
+ return new Container(
decoration: new BoxDecoration(
- border: new Border.all(
- width: 3.0,
- color: data.isEmpty ? colors.white : colors.Blue[500]
- ),
- backgroundColor: data.isEmpty ? colors.Grey[500] : colors.Green[500]
- ),
+ border: new Border.all(
+ width: 3.0,
+ color: data.isEmpty ? colors.white : colors.Blue[500]),
+ backgroundColor: data.isEmpty
+ ? colors.Grey[500]
+ : colors.Green[500]),
height: 80.0,
margin: new EdgeDims.all(10.0),
- child: wrapCards(cardComponents)
- );
- }
- );
+ child: wrapCards(cardComponents));
+ });
}
}
diff --git a/lib/components/croupier.dart b/lib/components/croupier.dart
index 7313003..cf46d45 100644
--- a/lib/components/croupier.dart
+++ b/lib/components/croupier.dart
@@ -16,7 +16,8 @@
croupier = other.croupier;
}
- Function setStateCallbackFactory(logic_croupier.CroupierState s, [var data = null]) {
+ Function setStateCallbackFactory(logic_croupier.CroupierState s,
+ [var data = null]) {
return () => setState(() {
croupier.setState(s, data);
});
@@ -27,57 +28,53 @@
case logic_croupier.CroupierState.Welcome:
// in which we show them a UI to start a new game, join a game, or change some settings.
return new Container(
- padding: new EdgeDims.only(top: sky.view.paddingTop),
- child: new Flex([
- new FlatButton(
+ padding: new EdgeDims.only(top: sky.view.paddingTop),
+ child: new Flex([
+ new FlatButton(
child: new Text('Create Game'),
- onPressed: setStateCallbackFactory(logic_croupier.CroupierState.ChooseGame)
- ),
- new FlatButton(
- child: new Text('Join Game')
- ),
- new FlatButton(
- child: new Text('Settings')
- )
- ], direction: FlexDirection.vertical
- )
- );
+ onPressed: setStateCallbackFactory(
+ logic_croupier.CroupierState.ChooseGame)),
+ new FlatButton(child: new Text('Join Game')),
+ new FlatButton(child: new Text('Settings'))
+ ], direction: FlexDirection.vertical));
case logic_croupier.CroupierState.Settings:
return null; // in which we let them pick an avatar, name, and color. And return to the previous screen after (NOT IMPLEMENTED YET)
case logic_croupier.CroupierState.ChooseGame:
// in which we let them pick a game out of the many possible games... There aren't that many.
return new Container(
- padding: new EdgeDims.only(top: sky.view.paddingTop),
- child: new Flex([
- new FlatButton(
+ padding: new EdgeDims.only(top: sky.view.paddingTop),
+ child: new Flex([
+ new FlatButton(
child: new Text('Proto'),
- onPressed: setStateCallbackFactory(logic_croupier.CroupierState.PlayGame, logic_game.GameType.Proto)
- ),
- new FlatButton(
+ onPressed: setStateCallbackFactory(
+ logic_croupier.CroupierState.PlayGame,
+ logic_game.GameType.Proto)),
+ new FlatButton(
child: new Text('Hearts'),
- onPressed: setStateCallbackFactory(logic_croupier.CroupierState.PlayGame, logic_game.GameType.Hearts)
- ),
- new FlatButton(
- child: new Text('Poker')
- ),
- new FlatButton(
- child: new Text('Solitaire')
- )
- ], direction: FlexDirection.vertical
- )
- );
+ onPressed: setStateCallbackFactory(
+ logic_croupier.CroupierState.PlayGame,
+ logic_game.GameType.Hearts)),
+ new FlatButton(child: new Text('Poker')),
+ new FlatButton(child: new Text('Solitaire')),
+ new FlatButton(
+ child: new Text('Syncbase Echo'),
+ onPressed: setStateCallbackFactory(
+ logic_croupier.CroupierState.PlayGame,
+ logic_game.GameType.SyncbaseEcho))
+ ], direction: FlexDirection.vertical));
case logic_croupier.CroupierState.AwaitGame:
return null; // in which players wait for game invitations to arrive.
case logic_croupier.CroupierState.ArrangePlayers:
return null; // If needed, lists the players around and what devices they'd like to use.
case logic_croupier.CroupierState.PlayGame:
return new Container(
- padding: new EdgeDims.only(top: sky.view.paddingTop),
- child: new GameComponent(croupier.game) // Asks the game UI to draw itself.
- );
+ padding: new EdgeDims.only(top: sky.view.paddingTop),
+ child: new GameComponent(
+ croupier.game) // Asks the game UI to draw itself.
+ );
default:
assert(false);
return null;
}
}
-}
\ No newline at end of file
+}
diff --git a/lib/components/draggable.dart b/lib/components/draggable.dart
index ad4cbec..55406fb 100644
--- a/lib/components/draggable.dart
+++ b/lib/components/draggable.dart
@@ -16,14 +16,14 @@
widgets.Widget build() {
return new widgets.Listener(
- onPointerDown: _startDrag,
- onPointerMove: _updateDrag,
- onPointerCancel: _cancelDrag,
- onPointerUp: _drop,
- child: new widgets.Transform(
- transform: new vector_math.Matrix4.identity().translate(displacement.dx, displacement.dy),
- child: child)
- );
+ onPointerDown: _startDrag,
+ onPointerMove: _updateDrag,
+ onPointerCancel: _cancelDrag,
+ onPointerUp: _drop,
+ child: new widgets.Transform(
+ transform: new vector_math.Matrix4.identity().translate(
+ displacement.dx, displacement.dy),
+ child: child));
}
widgets.EventDisposition _startDrag(sky.PointerEvent event) {
diff --git a/lib/components/game.dart b/lib/components/game.dart
index 5eb4c5b..94c5bca 100644
--- a/lib/components/game.dart
+++ b/lib/components/game.dart
@@ -1,15 +1,17 @@
import '../logic/card.dart' show Card;
-import '../logic/game.dart' show Game, GameType, Viewer, HeartsGame, HeartsPhase;
+import '../logic/game.dart'
+ show Game, GameType, Viewer, HeartsGame, HeartsPhase;
+import '../logic/syncbase_echo_impl.dart' show SyncbaseEchoImpl;
import 'board.dart' show Board;
import 'card_collection.dart' show CardCollectionComponent, Orientation;
import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets.dart' show FlatButton;
+import 'package:sky/widgets.dart' show FlatButton, RaisedButton;
import 'package:sky/theme/colors.dart' as colors;
-
class GameComponent extends StatefulComponent {
Game game;
+ SyncbaseEchoImpl s;
GameComponent(this.game) {
game.updateCallback = update;
@@ -29,10 +31,15 @@
return buildProto();
case GameType.Hearts:
return buildHearts();
+ case GameType.SyncbaseEcho:
+ if (s == null) {
+ s = new SyncbaseEchoImpl(game);
+ }
+ return buildSyncbaseEcho();
case GameType.Board:
// Does NOT work in checked mode since it has a Stack of Positioned Stack with Positioned Widgets.
// Issue and possible workaround? https://github.com/domokit/sky_engine/issues/732
- return new Board(1, [2,3,4], [1, 2, 3, 4]);
+ return new Board(1, [2, 3, 4], [1, 2, 3, 4]);
default:
return null; // unsupported
}
@@ -48,7 +55,7 @@
setState(() {
try {
game.move(card, dest);
- } catch(e) {
+ } catch (e) {
print("You can't do that! ${e.toString()}");
game.debugString = e.toString();
}
@@ -62,33 +69,30 @@
for (int i = 0; i < 4; i++) {
List<Card> cards = game.cardCollections[i];
- CardCollectionComponent c = new CardCollectionComponent(cards, game.playerNumber == i, Orientation.horz, _updateGameCallback);
+ CardCollectionComponent c = new CardCollectionComponent(
+ cards, game.playerNumber == i, Orientation.horz, _updateGameCallback);
cardCollections.add(c); // flex
}
cardCollections.add(new Container(
- decoration: new BoxDecoration(backgroundColor: colors.Green[500], borderRadius: 5.0),
- child: new CardCollectionComponent(game.cardCollections[4], true, Orientation.show1, _updateGameCallback)
- ));
+ decoration: new BoxDecoration(
+ backgroundColor: colors.Green[500], borderRadius: 5.0),
+ child: new CardCollectionComponent(game.cardCollections[4], true,
+ Orientation.show1, _updateGameCallback)));
cardCollections.add(new FlatButton(
- child: new Text('Switch View'),
- onPressed: _switchPlayersCallback
- ));
+ child: new Text('Switch View'), onPressed: _switchPlayersCallback));
return new Container(
- decoration: new BoxDecoration(backgroundColor: colors.Pink[500]),
- child: new Flex(cardCollections, direction: FlexDirection.vertical)
- );
+ decoration: new BoxDecoration(backgroundColor: colors.Pink[500]),
+ child: new Flex(cardCollections, direction: FlexDirection.vertical));
}
- Widget _makeSwitchViewButton() =>_makeButton('Switch View', _switchPlayersCallback);
+ Widget _makeSwitchViewButton() =>
+ _makeButton('Switch View', _switchPlayersCallback);
Widget _makeButton(String text, Function callback) {
- return new FlatButton(
- child: new Text(text),
- onPressed: callback
- );
+ return new FlatButton(child: new Text(text), onPressed: callback);
}
Widget buildHearts() {
@@ -97,13 +101,12 @@
switch (game.phase) {
case HeartsPhase.Deal:
return new Container(
- decoration: new BoxDecoration(backgroundColor: colors.Pink[500]),
- child: new Flex([
- new Text('Player ${game.playerNumber}'),
- _makeButton('Deal', game.dealCards),
- _makeSwitchViewButton()
- ], direction: FlexDirection.vertical)
- );
+ decoration: new BoxDecoration(backgroundColor: colors.Pink[500]),
+ child: new Flex([
+ new Text('Player ${game.playerNumber}'),
+ _makeButton('Deal', game.dealCards),
+ _makeSwitchViewButton()
+ ], direction: FlexDirection.vertical));
case HeartsPhase.Pass:
case HeartsPhase.Take:
case HeartsPhase.Play:
@@ -115,6 +118,20 @@
}
}
+ Widget buildSyncbaseEcho() {
+ return new Container(
+ decoration: const BoxDecoration(
+ backgroundColor: const Color(0xFF00ACC1)),
+ child: new Flex([
+ new RaisedButton(child: new Text('doEcho'), onPressed: s.doEcho),
+ new Text('sendMsg: ${s.sendMsg}'),
+ new Text('recvMsg: ${s.recvMsg}'),
+ new RaisedButton(child: new Text('doPutGet'), onPressed: s.doPutGet),
+ new Text('putStr: ${s.putStr}'),
+ new Text('getStr: ${s.getStr}')
+ ], direction: FlexDirection.vertical));
+ }
+
Widget showBoard() {
HeartsGame game = this.game as HeartsGame;
@@ -124,23 +141,22 @@
for (int i = 0; i < 4; i++) {
List<Card> cards = game.cardCollections[i];
- CardCollectionComponent c = new CardCollectionComponent(cards, game.playerNumber == i, Orientation.horz, _updateGameCallback);
+ CardCollectionComponent c = new CardCollectionComponent(
+ cards, game.playerNumber == i, Orientation.horz, _updateGameCallback);
cardCollections.add(c); // flex
}
cardCollections.add(new Container(
- decoration: new BoxDecoration(backgroundColor: colors.Green[500], borderRadius: 5.0),
- child: new CardCollectionComponent(game.cardCollections[4], true, Orientation.show1, _updateGameCallback)
- ));
+ decoration: new BoxDecoration(
+ backgroundColor: colors.Green[500], borderRadius: 5.0),
+ child: new CardCollectionComponent(game.cardCollections[4], true,
+ Orientation.show1, _updateGameCallback)));
cardCollections.add(new FlatButton(
- child: new Text('Switch View'),
- onPressed: _switchPlayersCallback
- ));
+ child: new Text('Switch View'), onPressed: _switchPlayersCallback));
return new Container(
- decoration: new BoxDecoration(backgroundColor: colors.Pink[500]),
- child: new Flex(cardCollections, direction: FlexDirection.vertical)
- );
+ decoration: new BoxDecoration(backgroundColor: colors.Pink[500]),
+ child: new Flex(cardCollections, direction: FlexDirection.vertical));
}
}
diff --git a/lib/logic/card.dart b/lib/logic/card.dart
index 3f53685..4dbba8a 100644
--- a/lib/logic/card.dart
+++ b/lib/logic/card.dart
@@ -5,7 +5,9 @@
final String identifier;
Card(this.deck, this.identifier);
- Card.fromString(String cardData) : deck = cardData.split(" ")[0], identifier = cardData.split(" ")[1];
+ Card.fromString(String cardData)
+ : deck = cardData.split(" ")[0],
+ identifier = cardData.split(" ")[1];
bool operator ==(Object other) {
if (other is! Card) return false;
diff --git a/lib/logic/croupier.dart b/lib/logic/croupier.dart
index 9a548cc..494e29e 100644
--- a/lib/logic/croupier.dart
+++ b/lib/logic/croupier.dart
@@ -1,7 +1,12 @@
import 'game.dart' show Game, GameType;
enum CroupierState {
- Welcome, Settings, ChooseGame, AwaitGame, ArrangePlayers, PlayGame
+ Welcome,
+ Settings,
+ ChooseGame,
+ AwaitGame,
+ ArrangePlayers,
+ PlayGame
}
class Croupier {
@@ -62,4 +67,4 @@
// Settings.load(String data) {}
// String save() { return null; }
-}
\ No newline at end of file
+}
diff --git a/lib/logic/game.dart b/lib/logic/game.dart
index b5f7d36..142ec30 100644
--- a/lib/logic/game.dart
+++ b/lib/logic/game.dart
@@ -1,12 +1,11 @@
import 'card.dart' show Card;
import 'dart:math' as math;
+import 'syncbase_echo.dart' show SyncbaseEcho;
// Note: Proto and Board are "fake" games intended to demonstrate what we can do.
// Proto is just a drag cards around "game".
// Board is meant to show how one _could_ layout a game of Hearts. This one is not hooked up very well yet.
-enum GameType {
- Proto, Hearts, Poker, Solitaire, Board
-}
+enum GameType { Proto, Hearts, Poker, Solitaire, Board, SyncbaseEcho }
/// A game consists of multiple decks and tracks a single deck of cards.
/// It also handles events; when cards are dragged to and from decks.
@@ -28,12 +27,20 @@
return new ProtoGame(pn);
case GameType.Hearts:
return new HeartsGame(pn);
+ case GameType.SyncbaseEcho:
+ return new SyncbaseEcho();
default:
assert(false);
return null;
}
}
+ // A public super constructor that doesn't really do anything.
+ // TODO(alexfandrianto): The proper way to handle this would be to use 'parts'.
+ // That way, I can have all the game logic split up across multiple files and
+ // still access private constructors.
+ Game.dummy(this.gameType) {}
+
// A super constructor, don't call this unless you're a subclass.
Game._create(this.gameType, this.playerNumber, int numCollections) {
gamelog.setGame(this);
@@ -112,9 +119,7 @@
}
}
-enum HeartsPhase {
- Deal, Pass, Take, Play, Score
-}
+enum HeartsPhase { Deal, Pass, Take, Play, Score }
class HeartsGame extends Game {
static const PLAYER_A = 0;
@@ -161,7 +166,8 @@
List<int> scores = [0, 0, 0, 0];
List<bool> ready;
- HeartsGame(int playerNumber) : super._create(GameType.Hearts, playerNumber, 16) {
+ HeartsGame(int playerNumber)
+ : super._create(GameType.Hearts, playerNumber, 16) {
resetGame();
}
@@ -181,7 +187,8 @@
}
int get passTarget {
- switch (roundNumber % 4) { // is a 4-cycle
+ switch (roundNumber % 4) {
+ // is a 4-cycle
case 0:
return (playerNumber - 1) % 4; // passLeft
case 1:
@@ -197,7 +204,8 @@
}
int get takeTarget => _getTakeTarget(playerNumber);
int _getTakeTarget(takerId) {
- switch (roundNumber % 4) { // is a 4-cycle
+ switch (roundNumber % 4) {
+ // is a 4-cycle
case 0:
return (takerId + 1) % 4; // takeRight
case 1:
@@ -255,14 +263,12 @@
bool hasSuit(int player, String suit) {
Card matchesSuit = this.cardCollections[player + OFFSET_HAND].firstWhere(
- (Card element) => (getCardSuit(element) == suit),
- orElse: () => null
- );
+ (Card element) => (getCardSuit(element) == suit), orElse: () => null);
return matchesSuit != null;
}
Card get leadingCard {
- if(this.numPlayed >= 1) {
+ if (this.numPlayed >= 1) {
return cardCollections[this.lastTrickTaker + OFFSET_PLAY][0];
}
return null;
@@ -280,18 +286,18 @@
bool get hasGameEnded => this.scores.reduce(math.max) >= HeartsGame.MAX_SCORE;
bool get allDealt => cardCollections[PLAYER_A].length == 13 &&
- cardCollections[PLAYER_B].length == 13 &&
- cardCollections[PLAYER_C].length == 13 &&
- cardCollections[PLAYER_D].length == 13;
+ cardCollections[PLAYER_B].length == 13 &&
+ cardCollections[PLAYER_C].length == 13 &&
+ cardCollections[PLAYER_D].length == 13;
bool get allPassed => cardCollections[PLAYER_A_PASS].length == 3 &&
- cardCollections[PLAYER_B_PASS].length == 3 &&
- cardCollections[PLAYER_C_PASS].length == 3 &&
- cardCollections[PLAYER_D_PASS].length == 3;
+ cardCollections[PLAYER_B_PASS].length == 3 &&
+ cardCollections[PLAYER_C_PASS].length == 3 &&
+ cardCollections[PLAYER_D_PASS].length == 3;
bool get allTaken => cardCollections[PLAYER_A_PASS].length == 0 &&
- cardCollections[PLAYER_B_PASS].length == 0 &&
- cardCollections[PLAYER_C_PASS].length == 0 &&
- cardCollections[PLAYER_D_PASS].length == 0;
+ cardCollections[PLAYER_B_PASS].length == 0 &&
+ cardCollections[PLAYER_C_PASS].length == 0 &&
+ cardCollections[PLAYER_D_PASS].length == 0;
bool get allPlayed => this.numPlayed == 4;
bool get allReady => ready[0] && ready[1] && ready[2] && ready[3];
@@ -344,14 +350,17 @@
int i = findCard(card);
if (i == -1) {
- throw new StateError('card does not exist or was not dealt: ${card.toString()}');
+ throw new StateError(
+ 'card does not exist or was not dealt: ${card.toString()}');
}
int destId = cardCollections.indexOf(dest);
if (destId == -1) {
- throw new StateError('destination list does not exist: ${dest.toString()}');
+ throw new StateError(
+ 'destination list does not exist: ${dest.toString()}');
}
if (destId != playerNumber + OFFSET_PLAY) {
- throw new StateError('player ${playerNumber} is not playing to the correct list: ${destId}');
+ throw new StateError(
+ 'player ${playerNumber} is not playing to the correct list: ${destId}');
}
gamelog.add(new HeartsCommand.play(playerNumber, card));
@@ -405,7 +414,8 @@
if (!heartsBroken && isHeartsCard(play[0])) {
heartsBroken = true;
}
- this.cardCollections[winner + OFFSET_TRICK].addAll(play); // or add(play[0])
+ this.cardCollections[winner + OFFSET_TRICK]
+ .addAll(play); // or add(play[0])
play.clear();
}
@@ -435,7 +445,7 @@
// Returns null or the reason that the player cannot play the card.
String canPlay(int player, Card c) {
if (phase != HeartsPhase.Play) {
- return "It is not the Play phase of Hearts.";
+ return "It is not the Play phase of Hearts.";
}
if (!cardCollections[player].contains(c)) {
return "Player ${player} does not have the card (${c.toString()})";
@@ -455,7 +465,9 @@
if (this.leadingCard != null) {
String leadingSuit = getCardSuit(this.leadingCard);
String otherSuit = getCardSuit(c);
- if (this.numPlayed >= 1 && leadingSuit != otherSuit && hasSuit(player, leadingSuit)) {
+ if (this.numPlayed >= 1 &&
+ leadingSuit != otherSuit &&
+ hasSuit(player, leadingSuit)) {
return "Must follow with a ${leadingSuit}.";
}
}
@@ -470,7 +482,8 @@
Card c = cardCollections[i + OFFSET_PLAY][0];
int value = this.getCardValue(c);
String suit = this.getCardSuit(c);
- if (suit == leadingSuit && (highestIndex == null || highestValue < value)) {
+ if (suit == leadingSuit &&
+ (highestIndex == null || highestValue < value)) {
highestIndex = i;
highestValue = value;
}
@@ -492,7 +505,8 @@
for (int i = 0; i < 4; i++) {
int delta = computeScore(i);
this.scores[i] += delta;
- if (delta == 26) { // Shot the moon!
+ if (delta == 26) {
+ // Shot the moon!
shotMoon = i;
}
}
@@ -525,7 +539,6 @@
}
}
-
class GameLog {
Game game;
List<GameCommand> log = new List<GameCommand>();
@@ -561,20 +574,18 @@
HeartsCommand(this.data);
// The following constructors are used for the player generating the HeartsCommand.
- HeartsCommand.deal(int playerId, List<Card> cards) :
- this.data = computeDeal(playerId, cards);
+ HeartsCommand.deal(int playerId, List<Card> cards)
+ : this.data = computeDeal(playerId, cards);
- HeartsCommand.pass(int senderId, List<Card> cards) :
- this.data = computePass(senderId, cards);
+ HeartsCommand.pass(int senderId, List<Card> cards)
+ : this.data = computePass(senderId, cards);
- HeartsCommand.take(int takerId) :
- this.data = computeTake(takerId);
+ HeartsCommand.take(int takerId) : this.data = computeTake(takerId);
- HeartsCommand.play(int playerId, Card c) :
- this.data = computePlay(playerId, c);
+ HeartsCommand.play(int playerId, Card c)
+ : this.data = computePlay(playerId, c);
- HeartsCommand.ready(int playerId) :
- this.data = computeReady(playerId);
+ HeartsCommand.ready(int playerId) : this.data = computeReady(playerId);
static computeDeal(int playerId, List<Card> cards) {
StringBuffer buff = new StringBuffer();
@@ -608,7 +619,8 @@
switch (parts[0]) {
case "Deal":
if (game.phase != HeartsPhase.Deal) {
- throw new StateError("Cannot process deal commands when not in Deal phase");
+ throw new StateError(
+ "Cannot process deal commands when not in Deal phase");
}
// Deal appends cards to playerId's hand.
int playerId = int.parse(parts[1]);
@@ -625,7 +637,8 @@
return;
case "Pass":
if (game.phase != HeartsPhase.Pass) {
- throw new StateError("Cannot process pass commands when not in Pass phase");
+ throw new StateError(
+ "Cannot process pass commands when not in Pass phase");
}
// Pass moves a set of cards from senderId to receiverId.
int senderId = int.parse(parts[1]);
@@ -646,7 +659,8 @@
return;
case "Take":
if (game.phase != HeartsPhase.Take) {
- throw new StateError("Cannot process take commands when not in Take phase");
+ throw new StateError(
+ "Cannot process take commands when not in Take phase");
}
int takerId = int.parse(parts[1]);
int senderPile = game._getTakeTarget(takerId) + HeartsGame.OFFSET_PASS;
@@ -657,7 +671,8 @@
return;
case "Play":
if (game.phase != HeartsPhase.Play) {
- throw new StateError("Cannot process play commands when not in Play phase");
+ throw new StateError(
+ "Cannot process play commands when not in Play phase");
}
// Play the card from the player's hand to their play pile.
@@ -671,16 +686,19 @@
// If the card isn't valid, then we have an error.
String reason = game.canPlay(playerId, c);
if (reason != null) {
- throw new StateError("Player ${playerId} cannot play ${c.toString()} because ${reason}");
+ throw new StateError(
+ "Player ${playerId} cannot play ${c.toString()} because ${reason}");
}
this.transfer(hand, discard, c);
return;
case "Ready":
if (game.hasGameEnded) {
- throw new StateError("Game has already ended. Start a new one to play again.");
+ throw new StateError(
+ "Game has already ended. Start a new one to play again.");
}
if (game.phase != HeartsPhase.Score) {
- throw new StateError("Cannot process ready commands when not in Score phase");
+ throw new StateError(
+ "Cannot process ready commands when not in Score phase");
}
int playerId = int.parse(parts[1]);
game.setReady(playerId);
@@ -693,7 +711,8 @@
void transfer(List<Card> sender, List<Card> receiver, Card c) {
if (!sender.contains(c)) {
- throw new StateError("Sender ${sender.toString()} lacks Card ${c.toString()}");
+ throw new StateError(
+ "Sender ${sender.toString()} lacks Card ${c.toString()}");
}
sender.remove(c);
receiver.add(c);
@@ -707,15 +726,15 @@
ProtoCommand(this.data);
// The following constructors are used for the player generating the ProtoCommand.
- ProtoCommand.deal(int playerId, List<Card> cards) :
- this.data = computeDeal(playerId, cards);
+ ProtoCommand.deal(int playerId, List<Card> cards)
+ : this.data = computeDeal(playerId, cards);
// TODO: receiverId is actually implied by the game round. So it may end up being removable.
- ProtoCommand.pass(int senderId, int receiverId, List<Card> cards) :
- this.data = computePass(senderId, receiverId, cards);
+ ProtoCommand.pass(int senderId, int receiverId, List<Card> cards)
+ : this.data = computePass(senderId, receiverId, cards);
- ProtoCommand.play(int playerId, Card c) :
- this.data = computePlay(playerId, c);
+ ProtoCommand.play(int playerId, Card c)
+ : this.data = computePlay(playerId, c);
static computeDeal(int playerId, List<Card> cards) {
StringBuffer buff = new StringBuffer();
diff --git a/lib/logic/syncbase_echo.dart b/lib/logic/syncbase_echo.dart
new file mode 100644
index 0000000..2befa0b
--- /dev/null
+++ b/lib/logic/syncbase_echo.dart
@@ -0,0 +1,5 @@
+import 'game.dart' show Game, GameType;
+
+class SyncbaseEcho extends Game {
+ SyncbaseEcho() : super.dummy(GameType.SyncbaseEcho);
+}
\ No newline at end of file
diff --git a/lib/logic/syncbase_echo_impl.dart b/lib/logic/syncbase_echo_impl.dart
new file mode 100644
index 0000000..4343d57
--- /dev/null
+++ b/lib/logic/syncbase_echo_impl.dart
@@ -0,0 +1,91 @@
+import 'dart:async';
+import 'dart:convert' show UTF8;
+
+import 'game.dart' show Game;
+
+import 'package:sky/mojo/embedder.dart' show embedder;
+
+import 'package:ether/echo_client.dart' show EchoClient;
+import 'package:ether/syncbase_client.dart'
+ show Perms, SyncbaseClient, SyncbaseTable;
+
+log(String msg) {
+ DateTime now = new DateTime.now();
+ print('$now $msg');
+}
+
+Perms emptyPerms() => new Perms()..json = '{}';
+
+class SyncbaseEchoImpl {
+ final EchoClient _echoClient;
+ final SyncbaseClient _syncbaseClient;
+ final Game game;
+
+ SyncbaseEchoImpl(this.game)
+ : _echoClient = new EchoClient(
+ embedder.connectToService, 'https://mojo.v.io/echo_server.mojo'),
+ _syncbaseClient = new SyncbaseClient(embedder.connectToService,
+ 'https://mojo.v.io/syncbase_server.mojo');
+
+ int seq = 0;
+ SyncbaseTable tb;
+ String sendMsg, recvMsg, putStr, getStr;
+
+ Future doEcho() async {
+ log('DemoApp.doEcho');
+
+ sendMsg = seq.toString();
+ recvMsg = '';
+ seq++;
+ log('setState sendMsg done');
+
+ String recvMsgAsync = await _echoClient.echo(sendMsg);
+
+ recvMsg = recvMsgAsync;
+ log('setState recvMsg done');
+
+ game.updateCallback(); // tell the UI to set/update state.
+ }
+
+ Future doSyncbaseInit() async {
+ log('DemoApp.doSyncbaseInit');
+ if (tb != null) {
+ log('syncbase already initialized');
+ return;
+ }
+ var app = _syncbaseClient.app('app');
+ if (!(await app.exists())) {
+ await app.create(emptyPerms());
+ }
+ var db = app.noSqlDatabase('db');
+ if (!(await db.exists())) {
+ await db.create(emptyPerms());
+ }
+ var table = db.table('table');
+ if (!(await table.exists())) {
+ await table.create(emptyPerms());
+ }
+ tb = table;
+ log('syncbase is now initialized');
+ }
+
+ Future doPutGet() async {
+ log('DemoApp.doPutGet');
+ await doSyncbaseInit();
+
+ putStr = seq.toString();
+ getStr = '';
+ seq++;
+ log('setState putStr done');
+
+ // TODO(sadovsky): Switch to tb.put/get once they exist.
+ var row = tb.row('key');
+ await row.put(UTF8.encode(putStr));
+ var getBytes = await row.get();
+
+ getStr = UTF8.decode(getBytes);
+ log('setState getStr done');
+
+ game.updateCallback(); // tell the UI to set/update state.
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index 718f454..ae59599 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -12,15 +12,12 @@
Widget build() {
return new Container(
- decoration: new BoxDecoration(
- backgroundColor: const Color(0xFF6666FF),
- borderRadius: 5.0
- ),
- child: new CroupierComponent(this.croupier)
- );
+ decoration: new BoxDecoration(
+ backgroundColor: const Color(0xFF6666FF), borderRadius: 5.0),
+ child: new CroupierComponent(this.croupier));
}
}
void main() {
runApp(new CroupierApp());
-}
\ No newline at end of file
+}
diff --git a/mojoconfig b/mojoconfig
new file mode 100644
index 0000000..23a16b5
--- /dev/null
+++ b/mojoconfig
@@ -0,0 +1,41 @@
+# This describes how we access our mojo services, including the sky_viewer.
+
+{
+ 'dev_servers': [
+ {
+ 'host': 'https://croupier.v.io/',
+ 'mappings': [
+ ('packages/', [
+ # For croupier packages.
+ '@{CROUPIER_DIR}/packages',
+ ]),
+ ('', [
+ # For croupier/lib/main.dart.
+ '@{CROUPIER_DIR}',
+ ]),
+ ]
+ },
+ {
+ 'host': 'https://mojo.v.io/',
+ 'mappings': [
+ ('', [
+ # For echo_server.mojo and syncbase_server.mojo.
+ '@{ETHER_BUILD_DIR}',
+ ]),
+ ],
+ },
+ {
+ 'host': 'https://sky/',
+ 'mappings': [
+ ('', [
+ # For sky_viewer.mojo.
+ '@{SKY_BUILD_DIR}'
+ ]),
+ ],
+ }
+ ],
+
+ 'content_handlers': {
+ 'application/dart': 'https://sky/sky_viewer.mojo',
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 298989f..d23cca5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,4 +2,8 @@
dependencies:
sky: any
sky_tools: any
- test: any
\ No newline at end of file
+ test: any
+ ether: any
+dependency_overrides:
+ ether:
+ path: ../../mojo/syncbase/dart
\ No newline at end of file
diff --git a/test/hearts_test.dart b/test/hearts_test.dart
index 85fc692..744245e 100644
--- a/test/hearts_test.dart
+++ b/test/hearts_test.dart
@@ -11,22 +11,54 @@
game.dealCards(); // What the dealer actually runs to get cards to everybody.
// By virtue of creating the game, HeartsGame should have 4 collections with 13 cards and 8 collections with 0 cards each.
- expect(game.cardCollections[HeartsGame.PLAYER_A + HeartsGame.OFFSET_HAND].length, equals(13), reason: "Dealt 13 cards to A");
- expect(game.cardCollections[HeartsGame.PLAYER_B + HeartsGame.OFFSET_HAND].length, equals(13), reason: "Dealt 13 cards to B");
- expect(game.cardCollections[HeartsGame.PLAYER_C + HeartsGame.OFFSET_HAND].length, equals(13), reason: "Dealt 13 cards to C");
- expect(game.cardCollections[HeartsGame.PLAYER_D + HeartsGame.OFFSET_HAND].length, equals(13), reason: "Dealt 13 cards to D");
- expect(game.cardCollections[HeartsGame.PLAYER_A + HeartsGame.OFFSET_PLAY].length, equals(0), reason: "Not playing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_B + HeartsGame.OFFSET_PLAY].length, equals(0), reason: "Not playing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_C + HeartsGame.OFFSET_PLAY].length, equals(0), reason: "Not playing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_D + HeartsGame.OFFSET_PLAY].length, equals(0), reason: "Not playing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_A + HeartsGame.OFFSET_PASS].length, equals(0), reason: "Not passing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_B + HeartsGame.OFFSET_PASS].length, equals(0), reason: "Not passing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_C + HeartsGame.OFFSET_PASS].length, equals(0), reason: "Not passing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_D + HeartsGame.OFFSET_PASS].length, equals(0), reason: "Not passing yet");
- expect(game.cardCollections[HeartsGame.PLAYER_A + HeartsGame.OFFSET_TRICK].length, equals(0), reason: "No tricks yet");
- expect(game.cardCollections[HeartsGame.PLAYER_B + HeartsGame.OFFSET_TRICK].length, equals(0), reason: "No tricks yet");
- expect(game.cardCollections[HeartsGame.PLAYER_C + HeartsGame.OFFSET_TRICK].length, equals(0), reason: "No tricks yet");
- expect(game.cardCollections[HeartsGame.PLAYER_D + HeartsGame.OFFSET_TRICK].length, equals(0), reason: "No tricks yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_A + HeartsGame.OFFSET_HAND].length, equals(13),
+ reason: "Dealt 13 cards to A");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_B + HeartsGame.OFFSET_HAND].length, equals(13),
+ reason: "Dealt 13 cards to B");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_C + HeartsGame.OFFSET_HAND].length, equals(13),
+ reason: "Dealt 13 cards to C");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_D + HeartsGame.OFFSET_HAND].length, equals(13),
+ reason: "Dealt 13 cards to D");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_A + HeartsGame.OFFSET_PLAY].length, equals(0),
+ reason: "Not playing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_B + HeartsGame.OFFSET_PLAY].length, equals(0),
+ reason: "Not playing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_C + HeartsGame.OFFSET_PLAY].length, equals(0),
+ reason: "Not playing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_D + HeartsGame.OFFSET_PLAY].length, equals(0),
+ reason: "Not playing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_A + HeartsGame.OFFSET_PASS].length, equals(0),
+ reason: "Not passing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_B + HeartsGame.OFFSET_PASS].length, equals(0),
+ reason: "Not passing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_C + HeartsGame.OFFSET_PASS].length, equals(0),
+ reason: "Not passing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_D + HeartsGame.OFFSET_PASS].length, equals(0),
+ reason: "Not passing yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_A + HeartsGame.OFFSET_TRICK].length, equals(0),
+ reason: "No tricks yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_B + HeartsGame.OFFSET_TRICK].length, equals(0),
+ reason: "No tricks yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_C + HeartsGame.OFFSET_TRICK].length, equals(0),
+ reason: "No tricks yet");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_D + HeartsGame.OFFSET_TRICK].length, equals(0),
+ reason: "No tricks yet");
});
});
@@ -44,7 +76,8 @@
new Card("classic", "h4")
];
- expect(game.computeScore(HeartsGame.PLAYER_A), equals(4), reason: "Player A has 4 hearts");
+ expect(game.computeScore(HeartsGame.PLAYER_A), equals(4),
+ reason: "Player A has 4 hearts");
// In this alternative situation, what's the score?
game.cardCollections[HeartsGame.PLAYER_B_TRICK] = <Card>[
@@ -60,14 +93,16 @@
new Card("classic", "s2")
];
- expect(game.computeScore(HeartsGame.PLAYER_B), equals(8), reason: "Player B has 8 hearts.");
+ expect(game.computeScore(HeartsGame.PLAYER_B), equals(8),
+ reason: "Player B has 8 hearts.");
// Should prepare C as well.
game.cardCollections[HeartsGame.PLAYER_C_TRICK] = <Card>[
new Card("classic", "h5"),
new Card("classic", "sq")
];
- expect(game.computeScore(HeartsGame.PLAYER_C), equals(14), reason: "Player C has 1 heart and the queen of spades.");
+ expect(game.computeScore(HeartsGame.PLAYER_C), equals(14),
+ reason: "Player C has 1 heart and the queen of spades.");
// Now, update the score, modifying game.scores.
game.updateScore();
@@ -116,7 +151,8 @@
void runCommand() {
String c = commands[commandIndex];
commandIndex++;
- if (c == "" || c[0] == "#") { // Essentially, this case allows empty lines and comments.
+ if (c == "" || c[0] == "#") {
+ // Essentially, this case allows empty lines and comments.
runCommand();
} else {
game.gamelog.add(new HeartsCommand(c));
@@ -133,10 +169,18 @@
runCommand();
// Confirm cards in hands.
- List<Card> expectedAHand = new List<Card>.from(Card.All.getRange(26, 26+5))..addAll(Card.All.getRange(13+5, 26));
- List<Card> expectedBHand = new List<Card>.from(Card.All.getRange(13, 13+5))..addAll(Card.All.getRange(39+5, 52));
- List<Card> expectedCHand = new List<Card>.from(Card.All.getRange(39, 39+5))..addAll(Card.All.getRange(0+5, 13));
- List<Card> expectedDHand = new List<Card>.from(Card.All.getRange(0, 0+5))..addAll(Card.All.getRange(26+5, 39));
+ List<Card> expectedAHand =
+ new List<Card>.from(Card.All.getRange(26, 26 + 5))
+ ..addAll(Card.All.getRange(13 + 5, 26));
+ List<Card> expectedBHand =
+ new List<Card>.from(Card.All.getRange(13, 13 + 5))
+ ..addAll(Card.All.getRange(39 + 5, 52));
+ List<Card> expectedCHand =
+ new List<Card>.from(Card.All.getRange(39, 39 + 5))
+ ..addAll(Card.All.getRange(0 + 5, 13));
+ List<Card> expectedDHand =
+ new List<Card>.from(Card.All.getRange(0, 0 + 5))
+ ..addAll(Card.All.getRange(26 + 5, 39));
expect(game.cardCollections[HeartsGame.PLAYER_A], equals(expectedAHand));
expect(game.cardCollections[HeartsGame.PLAYER_B], equals(expectedBHand));
expect(game.cardCollections[HeartsGame.PLAYER_C], equals(expectedCHand));
@@ -152,22 +196,38 @@
runCommand();
// Confirm cards in hands and passes.
- List<Card> expectedAHand = new List<Card>.from(Card.All.getRange(26+3, 26+5))..addAll(Card.All.getRange(13+5, 26));
- List<Card> expectedBHand = new List<Card>.from(Card.All.getRange(13+3, 13+5))..addAll(Card.All.getRange(39+5, 52));
- List<Card> expectedCHand = new List<Card>.from(Card.All.getRange(39+3, 39+5))..addAll(Card.All.getRange(0+5, 13));
- List<Card> expectedDHand = new List<Card>.from(Card.All.getRange(0+3, 0+5))..addAll(Card.All.getRange(26+5, 39));
- List<Card> expectedAPass = new List<Card>.from(Card.All.getRange(26, 26+3));
- List<Card> expectedBPass = new List<Card>.from(Card.All.getRange(13, 13+3));
- List<Card> expectedCPass = new List<Card>.from(Card.All.getRange(39, 39+3));
- List<Card> expectedDPass = new List<Card>.from(Card.All.getRange(0, 0+3));
+ List<Card> expectedAHand =
+ new List<Card>.from(Card.All.getRange(26 + 3, 26 + 5))
+ ..addAll(Card.All.getRange(13 + 5, 26));
+ List<Card> expectedBHand =
+ new List<Card>.from(Card.All.getRange(13 + 3, 13 + 5))
+ ..addAll(Card.All.getRange(39 + 5, 52));
+ List<Card> expectedCHand =
+ new List<Card>.from(Card.All.getRange(39 + 3, 39 + 5))
+ ..addAll(Card.All.getRange(0 + 5, 13));
+ List<Card> expectedDHand =
+ new List<Card>.from(Card.All.getRange(0 + 3, 0 + 5))
+ ..addAll(Card.All.getRange(26 + 5, 39));
+ List<Card> expectedAPass =
+ new List<Card>.from(Card.All.getRange(26, 26 + 3));
+ List<Card> expectedBPass =
+ new List<Card>.from(Card.All.getRange(13, 13 + 3));
+ List<Card> expectedCPass =
+ new List<Card>.from(Card.All.getRange(39, 39 + 3));
+ List<Card> expectedDPass =
+ new List<Card>.from(Card.All.getRange(0, 0 + 3));
expect(game.cardCollections[HeartsGame.PLAYER_A], equals(expectedAHand));
expect(game.cardCollections[HeartsGame.PLAYER_B], equals(expectedBHand));
expect(game.cardCollections[HeartsGame.PLAYER_C], equals(expectedCHand));
expect(game.cardCollections[HeartsGame.PLAYER_D], equals(expectedDHand));
- expect(game.cardCollections[HeartsGame.PLAYER_A_PASS], equals(expectedAPass));
- expect(game.cardCollections[HeartsGame.PLAYER_B_PASS], equals(expectedBPass));
- expect(game.cardCollections[HeartsGame.PLAYER_C_PASS], equals(expectedCPass));
- expect(game.cardCollections[HeartsGame.PLAYER_D_PASS], equals(expectedDPass));
+ expect(game.cardCollections[HeartsGame.PLAYER_A_PASS],
+ equals(expectedAPass));
+ expect(game.cardCollections[HeartsGame.PLAYER_B_PASS],
+ equals(expectedBPass));
+ expect(game.cardCollections[HeartsGame.PLAYER_C_PASS],
+ equals(expectedCPass));
+ expect(game.cardCollections[HeartsGame.PLAYER_D_PASS],
+ equals(expectedDPass));
});
test("Take Phase", () {
expect(game.phase, equals(HeartsPhase.Take));
@@ -180,23 +240,26 @@
// Confirm cards in hands again.
// Note: I will eventually want to do a sorted comparison or set comparison instead.
- List<Card> expectedAHand = new List<Card>.from(Card.All.getRange(26+3, 26+5))
- ..addAll(Card.All.getRange(13+5, 26))
- ..addAll(Card.All.getRange(13, 13+3));
- List<Card> expectedBHand = new List<Card>.from(Card.All.getRange(13+3, 13+5))
- ..addAll(Card.All.getRange(39+5, 52))
- ..addAll(Card.All.getRange(39, 39+3));
- List<Card> expectedCHand = new List<Card>.from(Card.All.getRange(39+3, 39+5))
- ..addAll(Card.All.getRange(0+5, 13))
- ..addAll(Card.All.getRange(0, 0+3));
- List<Card> expectedDHand = new List<Card>.from(Card.All.getRange(0+3, 0+5))
- ..addAll(Card.All.getRange(26+5, 39))
- ..addAll(Card.All.getRange(26, 26+3));
+ List<Card> expectedAHand =
+ new List<Card>.from(Card.All.getRange(26 + 3, 26 + 5))
+ ..addAll(Card.All.getRange(13 + 5, 26))
+ ..addAll(Card.All.getRange(13, 13 + 3));
+ List<Card> expectedBHand =
+ new List<Card>.from(Card.All.getRange(13 + 3, 13 + 5))
+ ..addAll(Card.All.getRange(39 + 5, 52))
+ ..addAll(Card.All.getRange(39, 39 + 3));
+ List<Card> expectedCHand =
+ new List<Card>.from(Card.All.getRange(39 + 3, 39 + 5))
+ ..addAll(Card.All.getRange(0 + 5, 13))
+ ..addAll(Card.All.getRange(0, 0 + 3));
+ List<Card> expectedDHand =
+ new List<Card>.from(Card.All.getRange(0 + 3, 0 + 5))
+ ..addAll(Card.All.getRange(26 + 5, 39))
+ ..addAll(Card.All.getRange(26, 26 + 3));
expect(game.cardCollections[HeartsGame.PLAYER_A], equals(expectedAHand));
expect(game.cardCollections[HeartsGame.PLAYER_B], equals(expectedBHand));
expect(game.cardCollections[HeartsGame.PLAYER_C], equals(expectedCHand));
expect(game.cardCollections[HeartsGame.PLAYER_D], equals(expectedDHand));
-
});
test("Play Phase - Trick 1", () {
expect(game.phase, equals(HeartsPhase.Play));
@@ -208,8 +271,10 @@
runCommand();
// Confirm the winner of the round.
- expect(game.lastTrickTaker, equals(3), reason: "Player 3 played 4 of Clubs");
- expect(game.cardCollections[HeartsGame.PLAYER_D_TRICK].length, equals(4), reason: "Player 3 won 1 trick.");
+ expect(game.lastTrickTaker, equals(3),
+ reason: "Player 3 played 4 of Clubs");
+ expect(game.cardCollections[HeartsGame.PLAYER_D_TRICK].length, equals(4),
+ reason: "Player 3 won 1 trick.");
});
test("Play Phase - Trick 2", () {
expect(game.phase, equals(HeartsPhase.Play));
@@ -221,10 +286,12 @@
runCommand();
// Confirm the winner of the round.
- expect(game.lastTrickTaker, equals(2), reason: "Player 2 played Ace of Clubs");
- expect(game.cardCollections[HeartsGame.PLAYER_C_TRICK].length, equals(4), reason: "Player 2 won 1 trick.");
- expect(game.cardCollections[HeartsGame.PLAYER_D_TRICK].length, equals(4), reason: "Player 3 won 1 trick.");
-
+ expect(game.lastTrickTaker, equals(2),
+ reason: "Player 2 played Ace of Clubs");
+ expect(game.cardCollections[HeartsGame.PLAYER_C_TRICK].length, equals(4),
+ reason: "Player 2 won 1 trick.");
+ expect(game.cardCollections[HeartsGame.PLAYER_D_TRICK].length, equals(4),
+ reason: "Player 3 won 1 trick.");
});
test("Play Phase - Trick 13", () {
expect(game.phase, equals(HeartsPhase.Play));
@@ -236,17 +303,33 @@
}
// Assert that hands/plays/passes are empty.
- expect(game.cardCollections[HeartsGame.PLAYER_A + HeartsGame.OFFSET_HAND].length, equals(0), reason: "Played all cards");
- expect(game.cardCollections[HeartsGame.PLAYER_B + HeartsGame.OFFSET_HAND].length, equals(0), reason: "Played all cards");
- expect(game.cardCollections[HeartsGame.PLAYER_C + HeartsGame.OFFSET_HAND].length, equals(0), reason: "Played all cards");
- expect(game.cardCollections[HeartsGame.PLAYER_D + HeartsGame.OFFSET_HAND].length, equals(0), reason: "Played all cards");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_A + HeartsGame.OFFSET_HAND].length, equals(0),
+ reason: "Played all cards");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_B + HeartsGame.OFFSET_HAND].length, equals(0),
+ reason: "Played all cards");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_C + HeartsGame.OFFSET_HAND].length, equals(0),
+ reason: "Played all cards");
+ expect(game.cardCollections[
+ HeartsGame.PLAYER_D + HeartsGame.OFFSET_HAND].length, equals(0),
+ reason: "Played all cards");
// Check that all 52 cards are in tricks.
- expect(game.lastTrickTaker, equals(0), reason: "Player 0 won the last trick.");
- expect(game.cardCollections[HeartsGame.PLAYER_A_TRICK].length, equals(4*8), reason: "Player 0 won 8 tricks.");
- expect(game.cardCollections[HeartsGame.PLAYER_B_TRICK].length, equals(4*2), reason: "Player 1 won 2 tricks.");
- expect(game.cardCollections[HeartsGame.PLAYER_C_TRICK].length, equals(4*2), reason: "Player 2 won 2 tricks.");
- expect(game.cardCollections[HeartsGame.PLAYER_D_TRICK].length, equals(4), reason: "Player 3 won 1 trick.");
+ expect(game.lastTrickTaker, equals(0),
+ reason: "Player 0 won the last trick.");
+ expect(
+ game.cardCollections[HeartsGame.PLAYER_A_TRICK].length, equals(4 * 8),
+ reason: "Player 0 won 8 tricks.");
+ expect(
+ game.cardCollections[HeartsGame.PLAYER_B_TRICK].length, equals(4 * 2),
+ reason: "Player 1 won 2 tricks.");
+ expect(
+ game.cardCollections[HeartsGame.PLAYER_C_TRICK].length, equals(4 * 2),
+ reason: "Player 2 won 2 tricks.");
+ expect(game.cardCollections[HeartsGame.PLAYER_D_TRICK].length, equals(4),
+ reason: "Player 3 won 1 trick.");
});
test("Score Phase", () {
expect(game.phase, equals(HeartsPhase.Score));
@@ -274,29 +357,41 @@
for (int i = 0; i < 68; i++) {
runCommand();
}
- expect(game.scores, equals([21+0, 3+26, 2+26, 0+26]));
+ 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, 4 ready
for (int i = 0; i < 68; i++) {
runCommand();
}
- expect(game.scores, equals([21+0+0, 3+26+26, 2+26+26, 0+26+26]));
+ 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, 4 ready
for (int i = 0; i < 60; i++) {
runCommand();
}
- expect(game.scores, equals([21+0+0+0, 3+26+26+26, 2+26+26+26, 0+26+26+26]));
+ expect(game.scores, equals([
+ 21 + 0 + 0 + 0,
+ 3 + 26 + 26 + 26,
+ 2 + 26 + 26 + 26,
+ 0 + 26 + 26 + 26
+ ]));
expect(game.hasGameEnded, isFalse);
// 5th round: 4 deal, 4 pass, 4 take, 52 play. Game is over, so no ready phase.
for (int i = 0; i < 64; i++) {
runCommand();
}
- expect(game.scores, equals([21+0+0+0+0, 3+26+26+26+26, 2+26+26+26+26, 0+26+26+26+26]));
- expect(game.hasGameEnded, isTrue); // assumes game ends after about 100 points.
+ expect(game.scores, equals([
+ 21 + 0 + 0 + 0 + 0,
+ 3 + 26 + 26 + 26 + 26,
+ 2 + 26 + 26 + 26 + 26,
+ 0 + 26 + 26 + 26 + 26
+ ]));
+ expect(game.hasGameEnded,
+ isTrue); // assumes game ends after about 100 points.
});
});
@@ -305,53 +400,66 @@
expect(() {
HeartsGame game = new HeartsGame(0);
game.phase = HeartsPhase.Score;
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
}, throwsA(new isInstanceOf<StateError>()));
});
test("Dealing - missing card", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, <Card>[new Card("fake", "not real")]));
+ game.gamelog.add(
+ new HeartsCommand.deal(0, <Card>[new Card("fake", "not real")]));
}, throwsA(new isInstanceOf<StateError>()));
});
test("Dealing - too many cards dealt", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 15))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 15))));
}, throwsA(new isInstanceOf<StateError>()));
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 5))));
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(5, 15))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 5))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(5, 15))));
}, throwsA(new isInstanceOf<StateError>()));
});
test("Passing - wrong phase", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
- game.gamelog.add(new HeartsCommand.pass(0, new List<Card>.from(Card.All.getRange(0, 4))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.pass(
+ 0, new List<Card>.from(Card.All.getRange(0, 4))));
}, throwsA(new isInstanceOf<StateError>()));
});
test("Passing - missing card", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
game.phase = HeartsPhase.Pass;
- game.gamelog.add(new HeartsCommand.pass(0, new List<Card>.from(Card.All.getRange(13, 16))));
+ game.gamelog.add(new HeartsCommand.pass(
+ 0, new List<Card>.from(Card.All.getRange(13, 16))));
}, throwsA(new isInstanceOf<StateError>()));
});
test("Passing - wrong number of cards", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
game.phase = HeartsPhase.Pass;
- game.gamelog.add(new HeartsCommand.pass(0, new List<Card>.from(Card.All.getRange(0, 2))));
+ game.gamelog.add(new HeartsCommand.pass(
+ 0, new List<Card>.from(Card.All.getRange(0, 2))));
}, throwsA(new isInstanceOf<StateError>()));
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
game.phase = HeartsPhase.Pass;
- game.gamelog.add(new HeartsCommand.pass(0, new List<Card>.from(Card.All.getRange(0, 4))));
+ game.gamelog.add(new HeartsCommand.pass(
+ 0, new List<Card>.from(Card.All.getRange(0, 4))));
}, throwsA(new isInstanceOf<StateError>()));
});
test("Taking - wrong phase", () {
@@ -363,14 +471,16 @@
test("Playing - wrong phase", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
}, throwsA(new isInstanceOf<StateError>()));
});
test("Playing - missing card", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
+ 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.play(0, Card.All[13]));
}, throwsA(new isInstanceOf<StateError>()));
@@ -378,7 +488,8 @@
test("Playing - invalid card (not 2 of clubs as first card)", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
@@ -389,10 +500,14 @@
// But the odds are miniscule, so this rule will be enforced.
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
- game.gamelog.add(new HeartsCommand.deal(1, new List<Card>.from(Card.All.getRange(13, 26))));
- game.gamelog.add(new HeartsCommand.deal(2, new List<Card>.from(Card.All.getRange(26, 39))));
- game.gamelog.add(new HeartsCommand.deal(3, new List<Card>.from(Card.All.getRange(39, 52))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 1, new List<Card>.from(Card.All.getRange(13, 26))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 2, new List<Card>.from(Card.All.getRange(26, 39))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
@@ -403,43 +518,58 @@
test("Playing - wrong turn", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 13))));
- game.gamelog.add(new HeartsCommand.deal(1, new List<Card>.from(Card.All.getRange(13, 26))));
- game.gamelog.add(new HeartsCommand.deal(2, new List<Card>.from(Card.All.getRange(26, 39))));
- game.gamelog.add(new HeartsCommand.deal(3, new List<Card>.from(Card.All.getRange(39, 52))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 0, new List<Card>.from(Card.All.getRange(0, 13))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 1, new List<Card>.from(Card.All.getRange(13, 26))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 2, new List<Card>.from(Card.All.getRange(26, 39))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
- game.gamelog.add(new HeartsCommand.play(1, Card.All[13])); // player 0's turn, not player 1's.
+ game.gamelog.add(new HeartsCommand.play(
+ 1, Card.All[13])); // player 0's turn, not player 1's.
}, throwsA(new isInstanceOf<StateError>()));
});
test("Playing - invalid card (suit mismatch)", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 12))..add(Card.All[25])));
- game.gamelog.add(new HeartsCommand.deal(1, new List<Card>.from(Card.All.getRange(12, 25))));
- game.gamelog.add(new HeartsCommand.deal(2, new List<Card>.from(Card.All.getRange(26, 39))));
- game.gamelog.add(new HeartsCommand.deal(3, new List<Card>.from(Card.All.getRange(39, 52))));
+ game.gamelog.add(new HeartsCommand.deal(0,
+ new List<Card>.from(Card.All.getRange(0, 12))..add(Card.All[25])));
+ game.gamelog.add(new HeartsCommand.deal(
+ 1, new List<Card>.from(Card.All.getRange(12, 25))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 2, new List<Card>.from(Card.All.getRange(26, 39))));
+ game.gamelog.add(new HeartsCommand.deal(
+ 3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
- game.gamelog.add(new HeartsCommand.play(0, Card.All[13])); // should play 12
+ game.gamelog
+ .add(new HeartsCommand.play(0, Card.All[13])); // should play 12
}, throwsA(new isInstanceOf<StateError>()));
});
test("Playing - invalid card (hearts not broken yet)", () {
expect(() {
HeartsGame game = new HeartsGame(0);
- game.gamelog.add(new HeartsCommand.deal(0, new List<Card>.from(Card.All.getRange(0, 12))..add(Card.All[38])));
- game.gamelog.add(new HeartsCommand.deal(1, new List<Card>.from(Card.All.getRange(13, 26))));
- game.gamelog.add(new HeartsCommand.deal(2, new List<Card>.from(Card.All.getRange(26, 38))..add(Card.All[12])));
- game.gamelog.add(new HeartsCommand.deal(3, new List<Card>.from(Card.All.getRange(39, 52))));
+ game.gamelog.add(new HeartsCommand.deal(0,
+ new List<Card>.from(Card.All.getRange(0, 12))..add(Card.All[38])));
+ game.gamelog.add(new HeartsCommand.deal(
+ 1, new List<Card>.from(Card.All.getRange(13, 26))));
+ game.gamelog.add(new HeartsCommand.deal(2,
+ new List<Card>.from(Card.All.getRange(26, 38))..add(Card.All[12])));
+ game.gamelog.add(new HeartsCommand.deal(
+ 3, new List<Card>.from(Card.All.getRange(39, 52))));
game.phase = HeartsPhase.Play;
game.lastTrickTaker = 0;
game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
game.gamelog.add(new HeartsCommand.play(2, Card.All[12])); // 2 won!
game.gamelog.add(new HeartsCommand.play(3, Card.All[39]));
- game.gamelog.add(new HeartsCommand.play(2, Card.All[26])); // But 2 can't lead with a hearts.
+ game.gamelog.add(new HeartsCommand.play(
+ 2, Card.All[26])); // But 2 can't lead with a hearts.
}, throwsA(new isInstanceOf<StateError>()));
});
});
-}
\ No newline at end of file
+}