| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file |  | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |  | 
|    3 // BSD-style license that can be found in the LICENSE file. |  | 
|    4  |  | 
|    5 /** |  | 
|    6  * Stores the actual data on a player's boat grid, the UI representation for its |  | 
|    7  * grid and the status of each shot. Acts as a controller handling isolate |  | 
|    8  * messages (from the main isolate message and shots from the enemy), and UI |  | 
|    9  * events. |  | 
|   10  */ |  | 
|   11 // TODO(sigmund): move UI setup out of here, e.g. into a controller class. |  | 
|   12 class PlayerState extends Isolate { |  | 
|   13  |  | 
|   14   /** internal id for this player. */ |  | 
|   15   int _id; |  | 
|   16  |  | 
|   17   /** Set up of boats on the board. */ |  | 
|   18   BoatGrid boats; |  | 
|   19  |  | 
|   20   /** State of shots taken by the enemy on this player's board. */ |  | 
|   21   GridState localGrid; |  | 
|   22  |  | 
|   23   /** State of shots this player has taken on the enemy. */ |  | 
|   24   GridState enemyGrid; |  | 
|   25  |  | 
|   26   /** Total shots made. */ |  | 
|   27   int totalShots; |  | 
|   28  |  | 
|   29   /** Total hits. */ |  | 
|   30   int totalHits; |  | 
|   31  |  | 
|   32   /** Total misses. */ |  | 
|   33   int totalMisses; |  | 
|   34  |  | 
|   35   /** Total boats that we have sunk. */ |  | 
|   36   int boatsSunk; |  | 
|   37  |  | 
|   38   /** Interface to interact with the enemy. */ |  | 
|   39   Enemy enemy; |  | 
|   40  |  | 
|   41   /** UI representation of this player's grid. */ |  | 
|   42   PlayerGridView _ownView; |  | 
|   43  |  | 
|   44   /** UI representation of the enemy's grid. */ |  | 
|   45   EnemyGridView _enemyView; |  | 
|   46  |  | 
|   47   /** Port used for testing purposes. */ |  | 
|   48   SendPort _portForTest; |  | 
|   49  |  | 
|   50   // This can take no arguments for now (wait for isolate redesign). |  | 
|   51   PlayerState() : super.light() {} |  | 
|   52  |  | 
|   53   void main() { |  | 
|   54     this.port.receive((message, SendPort replyTo) { |  | 
|   55       dispatch(message, replyTo); |  | 
|   56     }); |  | 
|   57   } |  | 
|   58  |  | 
|   59   /** dispatches all messages that are expected in this isolate. */ |  | 
|   60   void dispatch(var message, SendPort replyTo) { |  | 
|   61     int action = message['action']; |  | 
|   62     List args = message['args']; |  | 
|   63     switch (action) { |  | 
|   64       // message from the main program, setting this as player 1 or 2 |  | 
|   65       case MessageIds.SETUP: |  | 
|   66         handleSetup(args[0]); |  | 
|   67         break; |  | 
|   68       // message from the main program, giving port to talk with other player |  | 
|   69       case MessageIds.SET_ENEMY: |  | 
|   70         enemy = new Enemy(args[0]); |  | 
|   71         break; |  | 
|   72       // message from the other player indicating it's ready to play. |  | 
|   73       case MessageIds.ENEMY_IS_READY: |  | 
|   74         _enemyView.setEnemyReady(); |  | 
|   75         replyTo.send([true], null); |  | 
|   76         break; |  | 
|   77       // message from the other player indicating a shot. |  | 
|   78       case MessageIds.SHOOT: |  | 
|   79         List res = handleShot(args[0], args[1]); |  | 
|   80         replyTo.send([true, res], null); |  | 
|   81         break; |  | 
|   82       // message from the unit test (used to make tests deterministic) |  | 
|   83       case MessageIds.SET_PORT_FOR_TEST: |  | 
|   84         _portForTest = args[0]; |  | 
|   85         replyTo.send([true], null); |  | 
|   86         break; |  | 
|   87       default: |  | 
|   88         break; |  | 
|   89     } |  | 
|   90   } |  | 
|   91  |  | 
|   92   /** Handles a message from the main isolate to setup this player. */ |  | 
|   93   void handleSetup(int id) { |  | 
|   94     _id = id; |  | 
|   95     boats = new BoatGrid(); |  | 
|   96     localGrid = new GridState(); |  | 
|   97     enemyGrid = new GridState(); |  | 
|   98     totalShots = 0; |  | 
|   99     totalHits = 0; |  | 
|  100     totalMisses = 0; |  | 
|  101     boatsSunk = 0; |  | 
|  102  |  | 
|  103     _ownView = new PlayerGridView(this, document.query("#p${id}own")); |  | 
|  104     _enemyView = new EnemyGridView(this, document.query("#p${id}enemy")); |  | 
|  105     if (_portForTest != null) { |  | 
|  106       _portForTest.call(["_TEST:handleSetup", id]); |  | 
|  107     } |  | 
|  108   } |  | 
|  109  |  | 
|  110   /** Handles a shot message from the enemy player. */ |  | 
|  111   List handleShot(int x, int y) { |  | 
|  112     List res = boats.shoot(localGrid, x, y); |  | 
|  113     switch (res[0]) { |  | 
|  114       case Constants.MISS: |  | 
|  115         _ownView.addMiss(x, y); |  | 
|  116         break; |  | 
|  117       case Constants.HIT: |  | 
|  118         _ownView.addHit(x, y); |  | 
|  119         break; |  | 
|  120       case Constants.SUNK: |  | 
|  121         _ownView.addHit(x, y); |  | 
|  122         break; |  | 
|  123     } |  | 
|  124     if (_portForTest != null) { |  | 
|  125       _portForTest.call(["_TEST:handleShot", _id, res[0], x, y]); |  | 
|  126     } |  | 
|  127     return res; |  | 
|  128   } |  | 
|  129  |  | 
|  130   /** local action to add a boat in the grid. */ |  | 
|  131   void addBoat(Boat boat) { |  | 
|  132     boats.placeBoats([boat]); |  | 
|  133     assert(enemy != null); |  | 
|  134     enemy.ready(); |  | 
|  135   } |  | 
|  136  |  | 
|  137   /** local action to generate an asynchronous shot at the enemy. */ |  | 
|  138   void shoot(int x, int y) { |  | 
|  139     superShot(x, y, _id % 2 == 0); |  | 
|  140   } |  | 
|  141  |  | 
|  142   /** A single shot on (x, y). */ |  | 
|  143   singleShot(int x, int y) { |  | 
|  144     if (_canShoot(x, y)) { |  | 
|  145       _recordPendingShot(x, y); |  | 
|  146       try { // async shot! |  | 
|  147         _recordShotResult(await enemy.shoot(x, y), x, y); |  | 
|  148       } catch (var e) { |  | 
|  149         _recordFailedShot(x, y); |  | 
|  150       } |  | 
|  151     } |  | 
|  152   } |  | 
|  153  |  | 
|  154   /** |  | 
|  155    * Takes 1 shot, if it's a hit, it then shoots to each of the 4 cardinal |  | 
|  156    * directions until a boat is sunk. When [parallel] all directions are |  | 
|  157    * explored in parallel. |  | 
|  158    */ |  | 
|  159   superShot(int x, int y, bool parallel) { |  | 
|  160     if (_canShoot(x, y)) { |  | 
|  161       _recordPendingShot(x, y); |  | 
|  162       try { |  | 
|  163         int firstShot = await enemy.shoot(x, y); |  | 
|  164         _recordShotResult(firstShot, x, y); |  | 
|  165         if (firstShot == Constants.HIT) { |  | 
|  166           // no miss, but no sunk, search around |  | 
|  167           _exploreAllDirections(x, y, parallel); |  | 
|  168         } |  | 
|  169       } catch (var e) { |  | 
|  170         _recordFailedShot(x, y); |  | 
|  171       } |  | 
|  172     } |  | 
|  173   } |  | 
|  174  |  | 
|  175   static final LEFT_DIR = const [-1, 0]; |  | 
|  176   static final RIGHT_DIR = const [1, 0]; |  | 
|  177   static final UP_DIR = const [0, -1]; |  | 
|  178   static final DOWN_DIR = const [0, 1]; |  | 
|  179  |  | 
|  180   Future<bool> _exploreAllDirections(int x, int y, bool parallel) { |  | 
|  181     if (parallel) { |  | 
|  182       final arr = [ |  | 
|  183           _exploreDirectionHelper(LEFT_DIR, x, y), |  | 
|  184           _exploreDirectionHelper(RIGHT_DIR, x, y), |  | 
|  185           _exploreDirectionHelper(UP_DIR, x, y), |  | 
|  186           _exploreDirectionHelper(DOWN_DIR, x, y)]; |  | 
|  187       return (await Futures.wait(arr)).some((v) => v); |  | 
|  188     } else { |  | 
|  189       return await _exploreDirectionHelper(LEFT_DIR, x, y) |  | 
|  190           || await _exploreDirectionHelper(RIGHT_DIR, x, y) |  | 
|  191           || await _exploreDirectionHelper(UP_DIR, x, y) |  | 
|  192           || await _exploreDirectionHelper(DOWN_DIR, x, y); |  | 
|  193     } |  | 
|  194   } |  | 
|  195  |  | 
|  196   Future<bool> _exploreDirectionHelper(List<int> dir, int x, int y) { |  | 
|  197     bool res = await _followDir(x + dir[0], y + dir[1], dir[0], dir[1]); |  | 
|  198     return res; |  | 
|  199   } |  | 
|  200  |  | 
|  201   Future<bool> _followDir(int x, int y, int incX, int incY) { |  | 
|  202     while (_canShoot(x, y)) { |  | 
|  203       _recordPendingShot(x, y); |  | 
|  204       try { |  | 
|  205         int shot = await enemy.shoot(x, y); |  | 
|  206         _recordShotResult(shot, x, y); |  | 
|  207         // TODO(sigmund): make this into a switch when they are supported |  | 
|  208         if (shot == Constants.HIT) { |  | 
|  209             x += incX; |  | 
|  210             y += incY; |  | 
|  211         } else { |  | 
|  212           // TODO(sigmund): fix awaitc - this return statement is not |  | 
|  213           // transformed correctly because we currently prune the AST too |  | 
|  214           // agressively. We need an extra analysis phase that determines which |  | 
|  215           // statements have to be rewritten (returns anywhere, break/continue |  | 
|  216           // within loops that contain await, etc). |  | 
|  217           return shot == Constants.SUNK; |  | 
|  218         } |  | 
|  219       } catch (var e) { |  | 
|  220         _recordFailedShot(x, y); |  | 
|  221         throw e; |  | 
|  222       } |  | 
|  223     } |  | 
|  224     return false; |  | 
|  225   } |  | 
|  226  |  | 
|  227   /** checks that a shot is in range and has not been done before. */ |  | 
|  228   bool _canShoot(int x, int y) { |  | 
|  229     return _inRange(x, y) && enemyGrid.valueAt(x, y) == null; |  | 
|  230   } |  | 
|  231  |  | 
|  232   /** checks that a shot is in range. */ |  | 
|  233   bool _inRange(int x, int y) { |  | 
|  234     return x >= 0 && y >=0 && x < Constants.SIZE && y < Constants.SIZE; |  | 
|  235   } |  | 
|  236  |  | 
|  237   /** register a pending shot in the local enemyGrid state and update the UI. */ |  | 
|  238   void _recordPendingShot(int x, int y) { |  | 
|  239     totalShots++; |  | 
|  240     _enemyView.statusBar.updateStatus(); |  | 
|  241     _enemyView.addMaybeHit(x, y); |  | 
|  242     enemyGrid.pending(x, y); |  | 
|  243   } |  | 
|  244  |  | 
|  245   /** record a cancelled shot in the local enemyGrid state and update the UI. */ |  | 
|  246   void _recordCancelledShot(int x, int y) { |  | 
|  247     totalShots--; |  | 
|  248     _enemyView.removeMaybeHit(x, y); |  | 
|  249     _enemyView.statusBar.updateStatus(); |  | 
|  250     enemyGrid.clear(x, y); |  | 
|  251   } |  | 
|  252  |  | 
|  253   /** record a failing shot in the local enemyGrid state and update the UI. */ |  | 
|  254   void _recordFailedShot(int x, int y) { |  | 
|  255     _recordCancelledShot(x, y); |  | 
|  256   } |  | 
|  257  |  | 
|  258   /** register the result of a shot and update the UI. */ |  | 
|  259   void _recordShotResult(int shotResult, int x, int y) { |  | 
|  260     switch(shotResult) { |  | 
|  261       case Constants.MISS: |  | 
|  262         totalMisses++; |  | 
|  263         _enemyView.addMiss(x, y); |  | 
|  264         enemyGrid.miss(x, y); |  | 
|  265         break; |  | 
|  266       case Constants.HIT: |  | 
|  267         totalHits++; |  | 
|  268         _enemyView.addHit(x, y); |  | 
|  269         enemyGrid.hit(x, y); |  | 
|  270         break; |  | 
|  271       case Constants.SUNK: |  | 
|  272         totalHits++; |  | 
|  273         boatsSunk++; |  | 
|  274         _enemyView.addHit(x, y); |  | 
|  275         enemyGrid.hit(x, y); |  | 
|  276         break; |  | 
|  277     } |  | 
|  278     _enemyView.statusBar.updateStatus(); |  | 
|  279   } |  | 
|  280 } |  | 
| OLD | NEW |