| Index: client/samples/dartcombat/state.dart
|
| ===================================================================
|
| --- client/samples/dartcombat/state.dart (revision 3705)
|
| +++ client/samples/dartcombat/state.dart (working copy)
|
| @@ -1,307 +0,0 @@
|
| -// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -/**
|
| - * Stores the actual data on a player's boat grid, the UI representation for its
|
| - * grid and the status of each shot. Acts as a controller handling isolate
|
| - * messages (from the main isolate message and shots from the enemy), and UI
|
| - * events.
|
| - */
|
| -// TODO(sigmund): move UI setup out of here, e.g. into a controller class.
|
| -class PlayerState extends Isolate {
|
| -
|
| - /** internal id for this player. */
|
| - int _id;
|
| -
|
| - /** Set up of boats on the board. */
|
| - BoatGrid boats;
|
| -
|
| - /** State of shots taken by the enemy on this player's board. */
|
| - GridState localGrid;
|
| -
|
| - /** State of shots this player has taken on the enemy. */
|
| - GridState enemyGrid;
|
| -
|
| - /** Total shots made. */
|
| - int totalShots;
|
| -
|
| - /** Total hits. */
|
| - int totalHits;
|
| -
|
| - /** Total misses. */
|
| - int totalMisses;
|
| -
|
| - /** Total boats that we have sunk. */
|
| - int boatsSunk;
|
| -
|
| - /** Interface to interact with the enemy. */
|
| - Enemy enemy;
|
| -
|
| - /** UI representation of this player's grid. */
|
| - PlayerGridView _ownView;
|
| -
|
| - /** UI representation of the enemy's grid. */
|
| - EnemyGridView _enemyView;
|
| -
|
| - /** Port used for testing purposes. */
|
| - SendPort _portForTest;
|
| -
|
| - // This can take no arguments for now (wait for isolate redesign).
|
| - PlayerState() : super.light() {}
|
| -
|
| - void main() {
|
| - this.port.receive((message, SendPort replyTo) {
|
| - dispatch(message, replyTo);
|
| - });
|
| - }
|
| -
|
| - /** dispatches all messages that are expected in this isolate. */
|
| - void dispatch(var message, SendPort replyTo) {
|
| - int action = message['action'];
|
| - List args = message['args'];
|
| - switch (action) {
|
| - // message from the main program, setting this as player 1 or 2
|
| - case MessageIds.SETUP:
|
| - handleSetup(args[0]);
|
| - break;
|
| - // message from the main program, giving port to talk with other player
|
| - case MessageIds.SET_ENEMY:
|
| - enemy = new Enemy(args[0]);
|
| - break;
|
| - // message from the other player indicating it's ready to play.
|
| - case MessageIds.ENEMY_IS_READY:
|
| - _enemyView.setEnemyReady();
|
| - replyTo.send([true], null);
|
| - break;
|
| - // message from the other player indicating a shot.
|
| - case MessageIds.SHOOT:
|
| - List res = handleShot(args[0], args[1]);
|
| - replyTo.send([true, res], null);
|
| - break;
|
| - // message from the unit test (used to make tests deterministic)
|
| - case MessageIds.SET_PORT_FOR_TEST:
|
| - _portForTest = args[0];
|
| - replyTo.send([true], null);
|
| - break;
|
| - default:
|
| - break;
|
| - }
|
| - }
|
| -
|
| - /** Handles a message from the main isolate to setup this player. */
|
| - void handleSetup(int id) {
|
| - _id = id;
|
| - boats = new BoatGrid();
|
| - localGrid = new GridState();
|
| - enemyGrid = new GridState();
|
| - totalShots = 0;
|
| - totalHits = 0;
|
| - totalMisses = 0;
|
| - boatsSunk = 0;
|
| -
|
| - _ownView = new PlayerGridView(this, document.query("#p${id}own"));
|
| - _enemyView = new EnemyGridView(this, document.query("#p${id}enemy"));
|
| - if (_portForTest != null) {
|
| - _portForTest.call(["_TEST:handleSetup", id]);
|
| - }
|
| - }
|
| -
|
| - /** Handles a shot message from the enemy player. */
|
| - List handleShot(int x, int y) {
|
| - List res = boats.shoot(localGrid, x, y);
|
| - switch (res[0]) {
|
| - case Constants.MISS:
|
| - _ownView.addMiss(x, y);
|
| - break;
|
| - case Constants.HIT:
|
| - _ownView.addHit(x, y);
|
| - break;
|
| - case Constants.SUNK:
|
| - _ownView.addHit(x, y);
|
| - break;
|
| - }
|
| - if (_portForTest != null) {
|
| - _portForTest.call(["_TEST:handleShot", _id, res[0], x, y]);
|
| - }
|
| - return res;
|
| - }
|
| -
|
| - /** local action to add a boat in the grid. */
|
| - void addBoat(Boat boat) {
|
| - boats.placeBoats([boat]);
|
| - assert(enemy != null);
|
| - enemy.ready();
|
| - }
|
| -
|
| - /** local action to generate an asynchronous shot at the enemy. */
|
| - void shoot(int x, int y) {
|
| - superShot(x, y, _id % 2 == 0);
|
| - }
|
| -
|
| - /** A single shot on (x, y). */
|
| - void singleShot(int x, int y) {
|
| - if (_canShoot(x, y)) {
|
| - _recordPendingShot(x, y);
|
| - Future<int> res = enemy.shoot(x, y); // async shot!
|
| - res.then((int result) {
|
| - _recordShotResult(result, x, y);
|
| - });
|
| - res.handleException((String error) {
|
| - _recordFailedShot(x, y);
|
| - return true;
|
| - });
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Takes 1 shot, if it's a hit, it then shoots to each of the 4 cardinal
|
| - * directions until a boat is sunk. When [parallel] all directions are
|
| - * explored in parallel.
|
| - */
|
| - void superShot(int x, int y, bool parallel) {
|
| - if (_canShoot(x, y)) {
|
| - _recordPendingShot(x, y);
|
| - Future<int> firstShot = enemy.shoot(x, y);
|
| - firstShot.then((int res) {
|
| - _recordShotResult(res, x, y);
|
| - if (res == Constants.HIT) {
|
| - // no miss, but no sunk, search around
|
| - _exploreAllDirections(x, y, parallel);
|
| - }
|
| - });
|
| - firstShot.handleException((String error) {
|
| - _recordFailedShot(x, y);
|
| - return true;
|
| - });
|
| - }
|
| - }
|
| -
|
| - static final LEFT_DIR = const [-1, 0];
|
| - static final RIGHT_DIR = const [1, 0];
|
| - static final UP_DIR = const [0, -1];
|
| - static final DOWN_DIR = const [0, 1];
|
| -
|
| - Future<bool> _exploreAllDirections(int x, int y, bool parallel) {
|
| - Completer<bool> superShot_ = new Completer<bool>();
|
| - if (parallel) {
|
| - final arr = new List<Future<bool>>();
|
| - arr.add(_exploreDirectionHelper(LEFT_DIR, x, y));
|
| - arr.add(_exploreDirectionHelper(RIGHT_DIR, x, y));
|
| - arr.add(_exploreDirectionHelper(UP_DIR, x, y));
|
| - arr.add(_exploreDirectionHelper(DOWN_DIR, x, y));
|
| - Futures.wait(arr).then((arrValues) {
|
| - superShot_.complete(true);
|
| - });
|
| - } else {
|
| - _seqExploreDirectionHelper(LEFT_DIR, x, y, superShot_,
|
| - _seqExploreDirectionHelper(RIGHT_DIR, x, y, superShot_,
|
| - _seqExploreDirectionHelper(UP_DIR, x, y, superShot_,
|
| - _seqExploreDirectionHelper(DOWN_DIR, x, y, superShot_, null))))(false);
|
| - }
|
| - return superShot_.future;
|
| - }
|
| - Function _seqExploreDirectionHelper(List<int> dir, int x, int y,
|
| - Completer<bool> seq, void _next(bool res)) {
|
| - return (bool res) {
|
| - if (res) {
|
| - seq.complete(true);
|
| - } else {
|
| - _exploreDirectionHelper(dir, x, y).then(
|
| - (_next != null) ? _next : (void _(v) {seq.complete(false);}));
|
| - }
|
| - };
|
| - }
|
| -
|
| - Future<bool> _exploreDirectionHelper(List<int> dir, int x, int y) {
|
| - Completer<bool> sunk = new Completer<bool>();
|
| - _followDir(x + dir[0], y + dir[1], dir[0], dir[1], sunk);
|
| - return sunk.future;
|
| - }
|
| -
|
| - void _followDir(int x, int y, int incX, int incY, Completer<bool> sunk) {
|
| - if (_canShoot(x, y)) {
|
| - _recordPendingShot(x, y);
|
| - Future<int> shot = enemy.shoot(x, y);
|
| - shot.then((int res) {
|
| - _recordShotResult(res, x, y);
|
| - switch (res) {
|
| - case Constants.HIT:
|
| - if (!sunk.future.isComplete) {
|
| - _followDir(x + incX, y + incY, incX, incY, sunk);
|
| - }
|
| - break;
|
| - case Constants.SUNK:
|
| - sunk.complete(true);
|
| - break;
|
| - case Constants.MISS:
|
| - sunk.complete(false);
|
| - break;
|
| - }
|
| - });
|
| - shot.handleException((String error) {
|
| - _recordFailedShot(x, y);
|
| - sunk.completeException(error);
|
| - return true;
|
| - });
|
| - // We don't actually chain sunk.cancel with shot.cancel because individual
|
| - // shots can't be cancelled.
|
| - } else {
|
| - sunk.complete(false);
|
| - }
|
| - }
|
| -
|
| - /** checks that a shot is in range and has not been done before. */
|
| - bool _canShoot(int x, int y) {
|
| - return _inRange(x, y) && enemyGrid.valueAt(x, y) == null;
|
| - }
|
| -
|
| - /** checks that a shot is in range. */
|
| - bool _inRange(int x, int y) {
|
| - return x >= 0 && y >=0 && x < Constants.SIZE && y < Constants.SIZE;
|
| - }
|
| -
|
| - /** register a pending shot in the local enemyGrid state and update the UI. */
|
| - void _recordPendingShot(int x, int y) {
|
| - totalShots++;
|
| - _enemyView.statusBar.updateStatus();
|
| - _enemyView.addMaybeHit(x, y);
|
| - enemyGrid.pending(x, y);
|
| - }
|
| -
|
| - /** record a cancelled shot in the local enemyGrid state and update the UI. */
|
| - void _recordCancelledShot(int x, int y) {
|
| - totalShots--;
|
| - _enemyView.removeMaybeHit(x, y);
|
| - _enemyView.statusBar.updateStatus();
|
| - enemyGrid.clear(x, y);
|
| - }
|
| -
|
| - /** record a failing shot in the local enemyGrid state and update the UI. */
|
| - void _recordFailedShot(int x, int y) {
|
| - _recordCancelledShot(x, y);
|
| - }
|
| -
|
| - /** register the result of a shot and update the UI. */
|
| - void _recordShotResult(int shotResult, int x, int y) {
|
| - switch(shotResult) {
|
| - case Constants.MISS:
|
| - totalMisses++;
|
| - _enemyView.addMiss(x, y);
|
| - enemyGrid.miss(x, y);
|
| - break;
|
| - case Constants.HIT:
|
| - totalHits++;
|
| - _enemyView.addHit(x, y);
|
| - enemyGrid.hit(x, y);
|
| - break;
|
| - case Constants.SUNK:
|
| - totalHits++;
|
| - boatsSunk++;
|
| - _enemyView.addHit(x, y);
|
| - enemyGrid.hit(x, y);
|
| - break;
|
| - }
|
| - _enemyView.statusBar.updateStatus();
|
| - }
|
| -}
|
|
|