Coverage Report - com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.AnimateManager
 
Classes in this File Line Coverage Branch Coverage Complexity
AnimateManager
0%
0/247
0%
0/91
5.519
AnimateManager$1
0%
0/3
N/A
5.519
AnimateManager$EventListCommand
0%
0/7
N/A
5.519
AnimateManager$MoveTimerTask
0%
0/135
0%
0/76
5.519
AnimateManager$PointOffset
0%
0/3
N/A
5.519
 
 1  
 /******************************************************************************
 2  
  * AnimateManager.java - Manage displaying game moves as they happen
 3  
  * $Id$
 4  
  * 
 5  
  * BuckoFIBS - Backgammon by BuckoSoft
 6  
  * Copyright© 2011 - Dick Balaska - BuckoSoft, Corp.
 7  
  * 
 8  
  * $Log$
 9  
  * Revision 1.9  2013/09/12 06:42:28  dick
 10  
  * Animating the double cube.
 11  
  *
 12  
  * Revision 1.8  2013/09/10 00:22:54  dick
 13  
  * Spline2D and Flasher move MoveSpline2D and MoveFlasher
 14  
  *
 15  
  * Revision 1.7  2011/07/16 03:52:55  dick
 16  
  * Add YouDouble
 17  
  *
 18  
  * Revision 1.6  2011/07/04 03:43:53  dick
 19  
  * Add FirstRoll handling.
 20  
  *
 21  
  * Revision 1.5  2011/06/18 19:32:58  dick
 22  
  * Set the resigning points in the AcceptAndWin object.
 23  
  *
 24  
  * Revision 1.4  2011/06/05 06:55:43  dick
 25  
  * Handle AcceptAndWin.
 26  
  *
 27  
  * Revision 1.3  2011/06/02 19:17:48  dick
 28  
  * If the last event on the queue is removed and it isGui(), then keep it special off the queue,
 29  
  * so that BoardGui knows what kind of gui event to deal with (but we don't run the queue).
 30  
  *
 31  
  * Deal with Resign events.
 32  
  *
 33  
  * Revision 1.2  2011/05/23 05:59:12  dick
 34  
  * AnimateManager is responsible for playing game sounds as they come up.
 35  
  *
 36  
  * Revision 1.1  2011/05/22 22:56:08  dick
 37  
  * c.b.f.B.g.boardTab.board becomes c.b.f.B.g.boardTab.boardPane .
 38  
  *
 39  
  * Revision 1.13  2011/05/22 05:23:28  dick
 40  
  * Add "Please" handling, RollOrDouble and AcceptRejectDouble.
 41  
  * All GameEvent objects are named starting with GameEvent.
 42  
  *
 43  
  * Revision 1.12  2011/05/21 20:18:30  dick
 44  
  * Sorting dice works.
 45  
  *
 46  
  * Revision 1.11  2011/05/21 06:09:12  dick
 47  
  * Put the dice back in the board in the correct place.
 48  
  *
 49  
  * Revision 1.10  2011/05/21 05:06:35  dick
 50  
  * Handle the PleaseMove event.
 51  
  *
 52  
  * Revision 1.9  2011/05/18 05:54:43  dick
 53  
  * Animating uses a clone of the board so that when we muck with it,
 54  
  * we don't mess up the game boards (in case we replay them).
 55  
  *
 56  
  * Revision 1.8  2011/05/17 22:52:38  dick
 57  
  * extraCheckers is only used for the drawing, and should not be used in calculations; it is not at the same time as the calcs.
 58  
  *
 59  
  * Revision 1.7  2011/05/16 21:25:32  dick
 60  
  * Handle Home and Bar during Move.
 61  
  *
 62  
  * Revision 1.6  2011/05/16 17:03:34  dick
 63  
  * Erase the dice after a CantMove.
 64  
  *
 65  
  * Revision 1.5  2011/05/16 15:59:01  dick
 66  
  * Add finalize().
 67  
  * Clear dice from the board during the roll animation.
 68  
  *
 69  
  * Revision 1.4  2011/05/16 14:17:58  dick
 70  
  * Add support for Type.CantMove.
 71  
  *
 72  
  * Revision 1.3  2011/05/16 11:40:57  dick
 73  
  * All access for the event list goes through only one synchronized function.
 74  
  *
 75  
  * Revision 1.2  2011/05/15 04:37:30  dick
 76  
  * All access to eventList is through one synchronized function.
 77  
  * Calculate the checkers positions from lastBoard thru the moves on the queue.
 78  
  *
 79  
  * Revision 1.1  2011/05/15 02:17:54  dick
 80  
  * Move the AnimateEvents to their own package.
 81  
  *
 82  
  * Revision 1.4  2011/05/14 04:43:01  dick
 83  
  * I needed to deal with some lightweight Boards.
 84  
  * So finally make the primary document Document
 85  
  * and demote just the Board handling to a domain object.
 86  
  *
 87  
  * Revision 1.3  2011/05/14 00:12:21  dick
 88  
  * Redraw the board after erasing extraCheckers.
 89  
  *
 90  
  * Revision 1.2  2011/05/13 18:25:28  dick
 91  
  * Debugging animation.  Still needs help with the Y checker position.
 92  
  * Also need to do bar/home positions.
 93  
  *
 94  
  * Revision 1.1  2011/05/13 14:28:13  dick
 95  
  * Manage displaying game moves as they happen
 96  
  *
 97  
  */
 98  
 
 99  
 /* 
 100  
  * This program is free software: you can redistribute it and/or modify
 101  
  * it under the terms of the GNU General Public License as published by
 102  
  * the Free Software Foundation, either version 3 of the License, or
 103  
  * (at your option) any later version.
 104  
  *
 105  
  * This program is distributed in the hope that it will be useful,
 106  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 107  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 108  
  * GNU General Public License for more details.
 109  
  *
 110  
  * You should have received a copy of the GNU General Public License
 111  
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 112  
  *
 113  
  * The Original Code is BuckoFIBS, <http://www.buckosoft.com/BuckoFIBS/>.
 114  
  * The Initial Developer of the Original Code is Dick Balaska and BuckoSoft, Corp.
 115  
  * 
 116  
  */
 117  
 package com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane;
 118  
 
 119  
 import java.util.Iterator;
 120  
 import java.util.LinkedList;
 121  
 import java.util.Timer;
 122  
 import java.util.TimerTask;
 123  
 
 124  
 import javax.swing.ImageIcon;
 125  
 
 126  
 import org.slf4j.Logger;
 127  
 import org.slf4j.LoggerFactory;
 128  
 
 129  
 import com.buckosoft.fibs.BuckoFIBS.AudioManager;
 130  
 import com.buckosoft.fibs.BuckoFIBS.BFProperties;
 131  
 import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.BoardGui;
 132  
 import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.animateType.DoubleSpline2D;
 133  
 import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.animateType.MoveFlasher;
 134  
 import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.animateType.MoveSpline2D;
 135  
 import com.buckosoft.fibs.domain.Board;
 136  
 import com.buckosoft.fibs.domain.gameEvent.GameEvent;
 137  
 import com.buckosoft.fibs.domain.gameEvent.GameEvent.Type;
 138  
 import com.buckosoft.fibs.domain.gameEvent.GameEventAcceptAndWin;
 139  
 import com.buckosoft.fibs.domain.gameEvent.GameEventBoard;
 140  
 import com.buckosoft.fibs.domain.gameEvent.GameEventCantMove;
 141  
 import com.buckosoft.fibs.domain.gameEvent.GameEventDouble;
 142  
 import com.buckosoft.fibs.domain.gameEvent.GameEventFirstRoll;
 143  
 import com.buckosoft.fibs.domain.gameEvent.GameEventMove;
 144  
 import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseAcceptOrRejectDouble;
 145  
 import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseAcceptOrRejectResign;
 146  
 import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseMove;
 147  
 import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseRollOrDouble;
 148  
 import com.buckosoft.fibs.domain.gameEvent.GameEventRejectResign;
 149  
 import com.buckosoft.fibs.domain.gameEvent.GameEventResign;
 150  
 import com.buckosoft.fibs.domain.gameEvent.GameEventRoll;
 151  
 
 152  
 /** Manage animating game events
 153  
  * @author Dick Balaska
 154  
  * @since 2011/05/12
 155  
  * @version $Revision$ <br> $Date$
 156  
  * @see <a href="http://cvs.buckosoft.com/Projects/BuckoFIBS/BuckoFIBS/src/main/java/com/buckosoft/fibs/BuckoFIBS/gui/boardTab/boardPane/AnimateManager.java">cvs AnimateManager.java</a>
 157  
  */
 158  0
 public class AnimateManager {
 159  
         private        final static boolean DEBUG = true;
 160  
         private        final static boolean DEBUGqueue = false;
 161  
         private        final static boolean DEBUGmove = false;
 162  
         
 163  0
     private Logger logger = LoggerFactory.getLogger(getClass());
 164  
         private        BoardPane                boardPane;
 165  
         private        BoardGui                boardGui;
 166  
         private        BFProperties        bfProps;
 167  0
         private        ImageIcon                resignIcon = null;
 168  0
         private        ImageIcon                acceptIcon = null;
 169  
 
 170  
         /** The last Board processed by the TimerTask. */
 171  
         private        Board                        lastBoard;
 172  
 
 173  
         /** 4 checkers and 4 positions and 4 zero spares for in process compound moves.
 174  
          * These are the translucent checkers in transit between Boards. 
 175  
          * These are pairs; 0/1 = point/offset */
 176  0
         protected        int[]                extraCheckers = new int[12];
 177  
         /** The number of used checkers in the extraCheckers array */
 178  0
         protected        int                        extraCheckersIndex = 0;
 179  
         /** Who's extra Checkers (are in home) */
 180  0
         protected        int                        extraCheckersWho = 0;
 181  
 
 182  
         /** Default constructor */
 183  0
         public AnimateManager() {}
 184  
 
 185  
         /** Shut down the timer queue on cleanup. 
 186  
          * Failing to do so causes the app to keep running...
 187  
          */
 188  
         @Override
 189  
         public void finalize() {
 190  0
                 accessEventList(EventListCommand.removeAll, null, null);
 191  0
                 if (moveTimer != null)
 192  0
                         moveTimer.cancel();
 193  0
                 if (moveTimerTask != null)
 194  0
                         moveTimerTask.cancel();
 195  0
                 moveTimer = null;
 196  0
                 moveTimerTask = null;                
 197  0
         }
 198  
 
 199  
         /** Set the back reference to the boardGui for notifications 
 200  
          * @param boardPane The BoardPane
 201  
          */
 202  
         public void setBoardPane(BoardPane boardPane) {
 203  0
                 this.boardGui = (BoardGui)boardPane;
 204  0
                 this.boardPane = boardPane;                // yeah they are the same object, but logically separate.
 205  0
         }
 206  
 
 207  
         /** Set the reference to the properties
 208  
          * @param bfProperties
 209  
          */
 210  
         public void setProperties(BFProperties bfProperties) {
 211  0
                 this.bfProps = bfProperties;
 212  0
         }
 213  
 
 214  
         /** Return the first {@link AnimateEvent} in the queue. 
 215  
          * Does not remove the element.
 216  
          * @return The head of the queue or null if the queue is empty.
 217  
          */
 218  
         public AnimateEvent        getHeadEvent() {
 219  0
                 return(accessEventList(EventListCommand.head, null, null));
 220  
         }
 221  
 
 222  
         /** Queue this animation event for display.<br>
 223  
          * Note that if !props.isAnimateMoves() then we just discard the moves 
 224  
          *    except for Board, which is important.
 225  
          *    
 226  
          * @param gameEvent The event to queue and eventually display
 227  
          */
 228  
         public void addEvent(GameEvent gameEvent) {
 229  
                 if (DEBUGqueue || DEBUG)
 230  0
                         logger.info("addEvent: " + gameEvent.getType().toString() + ":: " + gameEvent.toString());
 231  0
                 if (gameEvent.getType() == Type.Board)
 232  0
                         addBoard((GameEventBoard)gameEvent);
 233  
                 else {
 234  0
                         if (!bfProps.isAnimateMoves())
 235  0
                                 return;
 236  0
                         switch (gameEvent.getType()) {
 237  
                                 case Move:
 238  0
                                         addMove((GameEventMove)gameEvent);
 239  0
                                         break;
 240  
                                 case Roll:
 241  0
                                         addRoll((GameEventRoll)gameEvent);
 242  0
                                         break;
 243  
                                 case CantMove:
 244  0
                                         addCantMove((GameEventCantMove)gameEvent);
 245  0
                                         break;
 246  
                                 case PleaseMove:
 247  0
                                         addPleaseMove((GameEventPleaseMove)gameEvent);
 248  0
                                         break;
 249  
                                 case PleaseRollOrDouble:
 250  0
                                         addPleaseRollOrDouble((GameEventPleaseRollOrDouble)gameEvent);
 251  0
                                         break;
 252  
                                 case PleaseAcceptOrRejectDouble:
 253  0
                                         addPleaseAcceptOrRejectDouble((GameEventPleaseAcceptOrRejectDouble)gameEvent);
 254  0
                                         break;
 255  
                                 case Resign:
 256  0
                                         addResign((GameEventResign)gameEvent);
 257  0
                                         break;
 258  
                                 case PleaseAcceptOrRejectResign:
 259  0
                                         addPleaseAcceptOrRejectResign((GameEventPleaseAcceptOrRejectResign)gameEvent);
 260  0
                                         break;
 261  
                                 case RejectResign:
 262  0
                                         addRejectResign((GameEventRejectResign)gameEvent);
 263  0
                                         break;
 264  
                                 case AcceptAndWin:
 265  0
                                         addAcceptAndWin((GameEventAcceptAndWin)gameEvent);
 266  0
                                         break;
 267  
                                 case FirstRoll:
 268  0
                                         addFirstRoll((GameEventFirstRoll)gameEvent);
 269  0
                                         break;
 270  
                                 case Double:
 271  0
                                         addDouble((GameEventDouble)gameEvent);
 272  0
                                         break;
 273  
                                 //case OpponentDouble:
 274  
                                 //        addPleaseAcceptOrRejectDouble((GameEventPleaseAcceptOrRejectDouble)gameEvent);
 275  
                                 //        break;
 276  
                                 default:
 277  0
                                         logger.warn("unhandled addEvent: " + gameEvent.getType().toString() + ":: " + gameEvent.toString());
 278  
                         }
 279  
                 }
 280  0
         }
 281  
 
 282  
         // put the home and bar values at the end of the point array for convienence
 283  
         private final static int paHOME = 26;
 284  
         private final static int paBAR = 28;
 285  0
         private        int[] pointArray = new int[paBAR+2];
 286  
         
 287  
         private void addBoard(GameEventBoard gameBoard) {
 288  
 //                this.board = boardPane.board;
 289  0
                 AnimateEventBoard aeb = new AnimateEventBoard();
 290  0
                 aeb.setBoard(gameBoard.getBoard().clone());
 291  0
                 queueEvent(aeb);
 292  0
                 if (gameBoard.getPostEvent() != null)
 293  0
                         addEvent(gameBoard.getPostEvent());
 294  0
         }
 295  
         private        void addCantMove(GameEventCantMove gameCantMove) {
 296  0
                 AnimateEventCantMove acm = new AnimateEventCantMove();
 297  0
                 acm.setWho(gameCantMove.getWho());
 298  0
                 queueEvent(acm);
 299  0
         }
 300  
         private        void addRoll(GameEventRoll gameRoll) {
 301  0
                 AnimateEventDiceRoll aedr = new AnimateEventDiceRoll();
 302  0
                 aedr.setWho(gameRoll.getWho());
 303  0
                 int[] dice = gameRoll.getDice();
 304  0
                 maybeSortDice(dice);
 305  0
                 aedr.dice[0] = dice[0];
 306  0
                 aedr.dice[1] = dice[1];
 307  
                 if (DEBUG)
 308  0
                         logger.info("addRoll: dice=" + dice[0] + "-" + dice[1] + " aedr=" + aedr.dice[0] + "-" + aedr.dice[1]);
 309  0
                 queueEvent(aedr);
 310  0
         }
 311  
         private void addFirstRoll(GameEventFirstRoll gameFirstRoll) {
 312  0
                 AnimateEventFirstRoll aefr = new AnimateEventFirstRoll();
 313  0
                 aefr.setWhiteDie(gameFirstRoll.getDice()[0]);
 314  0
                 aefr.setBlackDie(gameFirstRoll.getDice()[1]);
 315  0
                 aefr.setWhitePlayer(gameFirstRoll.getPlayerNames()[0]);
 316  0
                 aefr.setBlackPlayer(gameFirstRoll.getPlayerNames()[1]);
 317  0
                 queueEvent(aefr);
 318  0
         }
 319  
         private        void addPleaseMove(GameEventPleaseMove gamePleaseMove) {
 320  0
                 AnimateEventPleaseMove aepm = new AnimateEventPleaseMove();
 321  0
                 aepm.setCheckersToMove(gamePleaseMove.getCheckersToMove());
 322  0
                 aepm.setDice(gamePleaseMove.getDice());
 323  0
                 maybeSortDice(aepm.getDice());
 324  0
                 queueEvent(aepm);
 325  0
         }
 326  
         private void addPleaseRollOrDouble(GameEventPleaseRollOrDouble unused) {
 327  0
                 AnimateEventPleaseRollOrDouble aeprd = new AnimateEventPleaseRollOrDouble();
 328  0
                 queueEvent(aeprd);
 329  0
         }
 330  
         private void addPleaseAcceptOrRejectDouble(GameEventPleaseAcceptOrRejectDouble gepaord) {
 331  
                 // XXX
 332  0
                 AnimateEventPleaseAcceptOrRejectDouble aepard = new AnimateEventPleaseAcceptOrRejectDouble();
 333  0
                 aepard.setGui(gepaord.isGuiEvent());
 334  0
                 aepard.setWhoDoubled(gepaord.getWhoDoubled());
 335  0
                 queueEvent(aepard);
 336  0
         }
 337  
         private void addResign(GameEventResign gameEventResign) {
 338  0
                 if (resignIcon == null)
 339  0
                         resignIcon = new ImageIcon(getClass().getResource("/g/resign_flag-366x353.png"));
 340  0
                 AnimateEventResign aer = new AnimateEventResign();
 341  0
                 aer.setIcon(resignIcon);
 342  0
                 aer.setWho(gameEventResign.getWho());
 343  0
                 aer.setResigningPoints(gameEventResign.getResigningPoints());
 344  0
                 queueEvent(aer);
 345  0
         }
 346  
         private void addPleaseAcceptOrRejectResign(GameEventPleaseAcceptOrRejectResign gameEventPleaseAcceptRejectResign) {
 347  0
                 AnimateEventPleaseAcceptOrRejectResign aeparr = new AnimateEventPleaseAcceptOrRejectResign();
 348  0
                 if (resignIcon == null)
 349  0
                         resignIcon = new ImageIcon(getClass().getResource("/g/resign_flag-366x353.png"));
 350  0
                 aeparr.setIcon(resignIcon);
 351  0
                 aeparr.setWho(gameEventPleaseAcceptRejectResign.getWho());
 352  0
                 aeparr.setResigningPoints(gameEventPleaseAcceptRejectResign.getResigningPoints());
 353  0
                 queueEvent(aeparr);
 354  0
         }
 355  
         private void addRejectResign(GameEventRejectResign gameEventRejectResign) {
 356  0
                 AnimateEventRejectResign aerr = new AnimateEventRejectResign();
 357  0
                 if (resignIcon == null)
 358  0
                         resignIcon = new ImageIcon(getClass().getResource("/g/resign_flag-366x353.png"));
 359  0
                 aerr.setIcon(resignIcon);
 360  0
                 aerr.setWho(gameEventRejectResign.getWho() == Board.X ? Board.O : Board.X);
 361  0
                 aerr.setResigningPoints(gameEventRejectResign.getResigningPoints());
 362  0
                 queueEvent(aerr);
 363  0
         }
 364  
         private void addAcceptAndWin(GameEventAcceptAndWin gameEventAcceptAndWin) {
 365  0
                 AnimateEventAcceptAndWin aeaaw = new AnimateEventAcceptAndWin();
 366  0
                 if (acceptIcon == null)
 367  0
                         acceptIcon = new ImageIcon(getClass().getResource("/g/green-check-mark-217x250.png"));
 368  0
                 aeaaw.setIcon(acceptIcon);
 369  0
                 aeaaw.setWho(gameEventAcceptAndWin.getWho() == Board.X ? Board.O : Board.X);
 370  0
                 aeaaw.setResigningPoints(gameEventAcceptAndWin.getResigningPoints());
 371  0
                 queueEvent(aeaaw);
 372  0
         }
 373  
         // XXX
 374  
         private void addDouble(GameEventDouble gameDouble) {
 375  0
                 AnimateEventDouble aed = createAnimateEventDouble();
 376  0
                 aed.setBoardPane(boardPane);
 377  0
                 aed.setCubeBefore(gameDouble.getCubeBefore());
 378  0
                 aed.setWhoDoubled(gameDouble.getWhoDoubled());
 379  0
                 queueEvent(aed);
 380  0
         }
 381  
 
 382  
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 383  
         /** A little helper class for keeping track of moving checkers
 384  
          * @author dick
 385  
          */
 386  
         private class PointOffset {
 387  0
                 PointOffset(int point, int offset) {
 388  0
                         this.point = point; this.offset = offset;
 389  0
                 }
 390  
                 public int        point;
 391  
                 public int        offset;
 392  
         }
 393  
 
 394  
         private void addMove(GameEventMove gameMove) {
 395  0
                 int[] m = gameMove.getMoves();
 396  0
                 accessEventList(EventListCommand.calcCheckers, null, pointArray);
 397  
                 if (DEBUGmove)
 398  
                         logger.info("who=" + gameMove.getWho() 
 399  
                                         + " bar=" + pointArray[paBAR+gameMove.getWho()] + " home=" + pointArray[paHOME+gameMove.getWho()]);
 400  
 
 401  0
                 LinkedList<PointOffset>        movingCheckers = new LinkedList<PointOffset>();
 402  
                 Iterator<PointOffset> iter;
 403  
 
 404  0
                 for (int i=0; i<m.length; i+=2) {
 405  0
                         AnimateEventMove am = createAnimateEventMove();
 406  0
                         am.setBoardPane(boardPane);
 407  0
                         am.setWho(gameMove.getWho());
 408  0
                         int startPoint = m[i];
 409  0
                         int startOffset = Math.abs(pointArray[startPoint]);
 410  0
                         startOffset--;
 411  0
                         iter = movingCheckers.iterator();
 412  0
                         while (iter.hasNext()) {
 413  0
                                 PointOffset po = iter.next();
 414  0
                                 if (po.point == startPoint) {
 415  0
                                         startOffset = po.offset;
 416  0
                                         iter.remove();
 417  
                                 }
 418  0
                         }
 419  0
                         if (m[i] == Board.Bar) {
 420  0
                                 startOffset = pointArray[paBAR+gameMove.getWho()]-1;
 421  0
                                 pointArray[paBAR+gameMove.getWho()]--;
 422  
                         } else {
 423  0
                                 pointArray[m[i]] -= gameMove.getDepth();
 424  
                         }
 425  0
                         am.setStartPointAndOffset(m[i], startOffset);
 426  0
                         int endPoint = m[i+1];
 427  0
                         int endOffset = Math.abs(pointArray[m[i+1]]);
 428  0
                         for (PointOffset po : movingCheckers) {
 429  0
                                 if (po.point == endPoint) {
 430  0
                                         endOffset = po.offset+1;
 431  0
                                         break;
 432  
                                 }
 433  0
                         }
 434  0
                         if (m[i+1] == Board.Home) {
 435  0
                                 endOffset = pointArray[paHOME+gameMove.getWho()];
 436  0
                                 pointArray[paHOME+gameMove.getWho()]++;
 437  
                         } else
 438  0
                                 pointArray[m[i+1]] += gameMove.getDepth();
 439  0
                         am.setEndPointAndOffset(endPoint, endOffset);
 440  0
                         movingCheckers.addFirst(new PointOffset(endPoint, endOffset));
 441  
                         if (DEBUGmove) {
 442  
                                 logger.info("addMove: checkers on start point[" + m[i] + "]=" + pointArray[m[i]]);
 443  
                                 logger.info("addMove: checkers on end   point[" + m[i+1] + "]=" + pointArray[m[i+1]]);
 444  
                                 logger.info("addMove: start=" + am.pointNumberStart + "/" + am.checkerNumberStart + " end=" 
 445  
                                                 + am.pointNumberEnd + "/" + am.checkerNumberEnd);
 446  
                         }
 447  0
                         am.calculate();
 448  0
                         queueEvent(am);
 449  
                 }
 450  0
         }
 451  
 
 452  
         private void maybeSortDice(int[] dice) {
 453  0
                 if (this.bfProps.isHighDieLeft() && dice[0] < dice[1]) {
 454  
                         int x;
 455  0
                         x = dice[0];
 456  0
                         dice[0] = dice[1];
 457  0
                         dice[1] = x;
 458  
                 }
 459  0
         }
 460  
 
 461  
         /** Get a new AnimateMove object depending on the user's preference 
 462  
          * of which kind to use.
 463  
          * @return The new AnimateEventMove
 464  
          */
 465  
         private        AnimateEventMove createAnimateEventMove() {
 466  0
                 AnimateEventMove am = null;
 467  0
                 switch (bfProps.getAnimateType()) {
 468  
                 case AnimateEventMove.ANIMATE_FLASHER:
 469  0
                         am = new MoveFlasher();
 470  0
                         break;
 471  
                 case AnimateEventMove.ANIMATE_SPLINE2D:
 472  0
                         am = new MoveSpline2D();
 473  
                         break;
 474  
                 }
 475  0
                 return(am);
 476  
         }
 477  
 
 478  
         /** Get a new AnimateMove object depending on the user's preference 
 479  
          * of which kind to use.
 480  
          * @return The new AnimateEventMove
 481  
          */
 482  
         private        AnimateEventDouble createAnimateEventDouble() {
 483  0
                 AnimateEventDouble ad = null;
 484  0
                 switch (bfProps.getAnimateType()) {
 485  
                 case AnimateEventMove.ANIMATE_FLASHER:
 486  
 // XXX:                ad = new DoubleFlasher();
 487  0
                         ad = new DoubleSpline2D();        // XXX: Temp!
 488  0
                         break;
 489  
                 case AnimateEventMove.ANIMATE_SPLINE2D:
 490  0
                         ad = new DoubleSpline2D();
 491  
                         break;
 492  
                 }
 493  0
                 return(ad);
 494  
         }
 495  
 
 496  
         /** Queue this event for execution.  If the queue isn't running, then start it
 497  
          * @param am The Event to queue
 498  
          */
 499  
         private void queueEvent(AnimateEvent am) {
 500  
                 //boolean start = accessEventList(EventListCommand.head, null, null) == null;
 501  0
                 accessEventList(EventListCommand.add, am, null);
 502  0
                 if (moveTimer == null)
 503  0
                         startMoveList();
 504  0
         }
 505  
         
 506  
 ///////////////////////////////////////////////////////////////////////////////
 507  
 ///////////////////////////////////////////////////////////////////////////////
 508  
 ///////////////////////////////////////////////////////////////////////////////
 509  
 ///////////////////////////////////////////////////////////////////////////////
 510  0
         private LinkedList<AnimateEvent>        _private_eventList = new LinkedList<AnimateEvent>();
 511  0
         private        AnimateEvent                guiEvent = null;
 512  0
         private        Timer                                moveTimer = null;
 513  0
         private        MoveTimerTask                moveTimerTask = null;
 514  
         
 515  
         /** Commands handled by the synchronized list function below */
 516  0
         private enum EventListCommand {
 517  0
                 head,
 518  0
                 add,
 519  0
                 remove,
 520  0
                 calcCheckers,
 521  0
                 size,
 522  0
                 removeAll
 523  
         }
 524  
         /** provide access to the cross-thread eventList.
 525  
          * We perform several functions, based on {@link EventListCommand}<br>
 526  
          * head = return the first element of the list.<br>
 527  
          * add = queue this AnimateEvent at the end of the list.<br>
 528  
          * remove = remove the head element from the list.<br>
 529  
          * calcCheckers = fill out the derived "current" checkerPositions by walking the list.<br>
 530  
          * size = fill out checkerPositions[0] with the size of the list.<br>
 531  
          * removeAll = empty the list.
 532  
          * 
 533  
          * @param command The {@link EventListCommand} to execute on the _private_eventList
 534  
          * @param ae The AnimateEvent to queue
 535  
          * @param checkerPositions The checkerPositions to fill out
 536  
          * @return Some commands return the head of the list, most return null.
 537  
          */
 538  
         synchronized private AnimateEvent accessEventList(EventListCommand command, AnimateEvent ae, int[] checkerPositions) {
 539  0
                 switch (command) {
 540  
                 case remove:
 541  0
                         if (_private_eventList.isEmpty())
 542  0
                                 return(null);
 543  0
                         ae = _private_eventList.removeFirst();
 544  0
                         if (_private_eventList.size() == 0 && ae.isGui())
 545  0
                                 guiEvent = ae;
 546  0
                         return(ae);
 547  
                 case add:
 548  0
                         _private_eventList.add(ae);
 549  0
                         guiEvent = null;
 550  0
                         return(null);
 551  
                 case head:
 552  0
                         if (guiEvent != null)
 553  0
                                 return(guiEvent);
 554  0
                         if (_private_eventList.isEmpty())
 555  0
                                 return(null);
 556  0
                         return(_private_eventList.getFirst());
 557  
                 case calcCheckers:
 558  
                         int        i;
 559  0
                         if (lastBoard != null) {
 560  0
                                 for (i=0; i<25; i++)                // set some default positions
 561  0
                                         checkerPositions[i] = lastBoard.getPoints()[i];
 562  0
                                 checkerPositions[paHOME] = lastBoard.getHome()[0]; 
 563  0
                                 checkerPositions[paHOME+1] = lastBoard.getHome()[1]; 
 564  0
                                 checkerPositions[paBAR] = lastBoard.getBar()[0]; 
 565  0
                                 checkerPositions[paBAR+1] = lastBoard.getBar()[1]; 
 566  
                                 if (DEBUG && false) {
 567  
                                         logger.info("calcCheckers: home:" + checkerPositions[paHOME] + "-" + checkerPositions[paHOME+1]
 568  
                                                                 + " bar:" + checkerPositions[paBAR] + "-" + checkerPositions[paBAR+1]);
 569  
                                 }
 570  
                         }
 571  0
                         for (AnimateEvent e : _private_eventList) {
 572  0
                                 switch (e.getType()) {
 573  
                                 case Board:
 574  0
                                         AnimateEventBoard aeb = (AnimateEventBoard)e;
 575  0
                                         Board b = aeb.getBoard();
 576  0
                                         int[] p = b.getPoints();
 577  0
                                         for (i=0; i<25; i++)
 578  0
                                                 checkerPositions[i] = p[i];
 579  0
                                         checkerPositions[Board.Bar] = b.getBar()[aeb.getWho()]; 
 580  0
                                         checkerPositions[Board.Home] = b.getHome()[aeb.getWho()];
 581  
                                         if (DEBUG && false)
 582  
                                                 logger.info("calcCheckers: home=" + checkerPositions[Board.Home]
 583  
                                                              + " from " + b.getHome()[0] + "-" + b.getHome()[1]);
 584  0
                                         break;
 585  
                                 case Move:
 586  0
                                         AnimateEventMove aem = (AnimateEventMove)e;
 587  0
                                         if (checkerPositions[aem.pointNumberStart] < 0) {
 588  0
                                                 checkerPositions[aem.pointNumberStart]++;
 589  0
                                                 checkerPositions[aem.pointNumberEnd]--;
 590  
                                         } else {
 591  0
                                                 checkerPositions[aem.pointNumberStart]++;
 592  0
                                                 checkerPositions[aem.pointNumberEnd]--;                                        
 593  
                                         }
 594  
                                 }
 595  0
                         }
 596  0
                         return(null);
 597  
                 case size:
 598  0
                         checkerPositions[0] = _private_eventList.size();
 599  0
                         return(null);
 600  
                 case removeAll:
 601  0
                         _private_eventList.clear();
 602  0
                         return(null);
 603  
                 }
 604  0
                 logger.error("unhandled accessEventList command: " + command.toString());
 605  0
                 return(null);
 606  
         }
 607  
 ///////////////////////////////////////////////////////////////////////////////
 608  
 ///////////////////////////////////////////////////////////////////////////////
 609  
 ///////////////////////////////////////////////////////////////////////////////
 610  
 ///////////////////////////////////////////////////////////////////////////////
 611  
         
 612  0
         private        int                timerIncrement = 50;
 613  
 
 614  
         private void startMoveList() {
 615  0
                 moveTimer = new Timer();
 616  0
                 moveTimerTask = new MoveTimerTask();
 617  0
                 moveTimer.schedule(moveTimerTask, 0, timerIncrement);
 618  0
         }
 619  
 
 620  0
         private class MoveTimerTask extends TimerTask {
 621  0
                 private        double        offset = 0.0;
 622  0
                 private        int[]        queueLength = new int[1];
 623  0
                 private        double        timerIncrementD = (double)timerIncrement/1000.0;
 624  
 
 625  
                 @Override
 626  
                 public void run() {
 627  
                         
 628  0
                         AnimateEvent ae = accessEventList(EventListCommand.head, null, null);
 629  0
                         if (ae != null) {
 630  
                                 if (DEBUGqueue)
 631  
                                         logger.info("mtt: process Event: " + ae.getType().toString() + " " + ae.toString());
 632  
                                 // Figure out our time increment based on queueLength
 633  
                                 //timerIncrementD = (double)timerIncrement/1000.0;
 634  0
                                 timerIncrementD = (double)timerIncrement/(double)ae.getDuration();
 635  0
                                 accessEventList(EventListCommand.size, null, queueLength);
 636  0
                                 if (queueLength[0] > 7)
 637  0
                                         timerIncrementD *= 2;
 638  0
                                 if (queueLength[0] > 10)
 639  0
                                         timerIncrementD *= 2;
 640  0
                                 if (queueLength[0] > 12)
 641  0
                                         timerIncrementD *= 1.2;
 642  
                                 
 643  
                                 // Is this a board?  Special handling
 644  0
                                 if (ae.getType() == AnimateEvent.Type.Board) {
 645  0
                                         AnimateEventBoard aeb = (AnimateEventBoard)ae;
 646  0
                                         lastBoard = aeb.getBoard();
 647  0
                                         boardPane.setBoard(lastBoard);
 648  0
                                         accessEventList(EventListCommand.remove, null, null);
 649  
                                         if (DEBUGqueue)
 650  
                                                 logger.info("mtt: remove EventBoard: " + aeb.getType().toString());
 651  0
                                         offset = 0.0;
 652  0
                                         for (int i=0; i<extraCheckers.length; i++)
 653  0
                                                 extraCheckers[i] = 0;
 654  0
                                         extraCheckersIndex = 0;
 655  0
                                         boardPane.updateBoardTab();
 656  0
                                 } else {
 657  0
                                         ae.setOffset(offset);
 658  0
                                         switch (ae.getType()) {
 659  
                                         case Move:
 660  0
                                                 AnimateEventMove am = (AnimateEventMove)ae; 
 661  0
                                                 if (offset == 0.0) {
 662  0
                                                         extraCheckersWho = am.getWho();
 663  0
                                                         boolean extraFound = false;
 664  0
                                                         for (int i=0; i<extraCheckersIndex; i+=2) {
 665  0
                                                                 if (extraCheckers[i] == am.pointNumberStart) {
 666  0
                                                                         extraFound = true;
 667  0
                                                                         for (int j=i; j<extraCheckersIndex; j+=2) {
 668  0
                                                                                 extraCheckers[j] = extraCheckers[j+2];
 669  0
                                                                                 extraCheckers[j+1] = extraCheckers[j+3];
 670  
                                                                         }
 671  0
                                                                         extraCheckersIndex -= 2;
 672  0
                                                                         break;
 673  
                                                                 }
 674  
                                                         }
 675  0
                                                         if (!extraFound && lastBoard != null) {
 676  0
                                                                 if (lastBoard.getPoints()[am.pointNumberStart] < 0)
 677  0
                                                                         lastBoard.getPoints()[am.pointNumberStart]++;
 678  
                                                                 else
 679  0
                                                                         lastBoard.getPoints()[am.pointNumberStart]--;
 680  0
                                                                 if (am.pointNumberStart == Board.Bar)
 681  0
                                                                         lastBoard.getBar()[ae.getWho()]--;
 682  
                                                         }
 683  
         
 684  0
                                                 }
 685  
                                                 break;
 686  
                                         case Double:
 687  0
                                                 AnimateEventDouble aed = (AnimateEventDouble)ae; 
 688  0
                                                 if (offset == 0.0) {
 689  0
                                                         aed.calculate(lastBoard);
 690  
                                                 }
 691  
                                                 break;
 692  
                                         case Roll:
 693  0
                                                 if (lastBoard != null) {                        // clear out any dice in the board during roll
 694  0
                                                         int[][] d = lastBoard.getDice();
 695  0
                                                         d[Board.X][0] = 0;
 696  0
                                                         d[Board.X][1] = 0;
 697  0
                                                         d[Board.O][0] = 0;
 698  0
                                                         d[Board.O][1] = 0;
 699  0
                                                 }
 700  
                                                 break;
 701  
                                         case PleaseMove:
 702  0
                                                 AnimateEventPleaseMove aepm = (AnimateEventPleaseMove) ae;
 703  0
                                                 if (lastBoard != null) {
 704  0
                                                         int[][] d = lastBoard.getDice();
 705  0
                                                         d[Board.O][0] = aepm.getDice()[0]; 
 706  0
                                                         d[Board.O][1] = aepm.getDice()[1];
 707  0
                                                         boardGui.yourMove(aepm.getCheckersToMove());
 708  0
                                                         boardGui.playSound(AudioManager.Cue.YourTurn);
 709  
                                                         if (DEBUG)
 710  0
                                                                 logger.info("******DING YOUR TURN dice=" + d[Board.O][0] + "-" + d[Board.O][1]
 711  0
                                                                              + " who=" + aepm.getWho() + " offset=" + aepm.offset);
 712  
                                                 }
 713  0
                                                 offset = 1.0;
 714  0
                                                 break;
 715  
                                         case PleaseRollOrDouble:
 716  0
                                                 if (lastBoard != null) {
 717  0
                                                         lastBoard.setYourTurnToRollOrDouble(true);
 718  0
                                                         boardGui.playSound(AudioManager.Cue.RollOrDouble);
 719  
                                                 }
 720  0
                                                 offset = 1.0;
 721  0
                                                 break;
 722  
                                         case PleaseAcceptOrRejectDouble:
 723  0
                                                 if (lastBoard != null) {
 724  0
                                                         lastBoard.setAcceptDeclineDouble();
 725  0
                                                         boardGui.playSound(AudioManager.Cue.Doubled);
 726  
                                                 }
 727  0
                                                 offset = 1.0;
 728  0
                                                 break;
 729  
                                         case PleaseAcceptOrRejectResign:
 730  0
                                                 AnimateEventPleaseAcceptOrRejectResign ar = (AnimateEventPleaseAcceptOrRejectResign)ae; 
 731  0
                                                 if (lastBoard != null) {
 732  0
                                                         if (ar.getWho() != Board.O)
 733  0
                                                                 lastBoard.setAcceptDeclineResign(true);
 734  0
                                                         lastBoard.setWhoIsResigning(ar.getWho());
 735  0
                                                         lastBoard.setResigningPoints(ar.getResigningPoints());
 736  0
                                                         boardGui.playSound(AudioManager.Cue.Doubled);
 737  
                                                 }
 738  0
                                                 offset = 1.0;
 739  0
                                                 break;
 740  
                                         case RejectResign:
 741  0
                                                 if (lastBoard != null) {
 742  0
                                                         lastBoard.setAcceptDeclineResign(false);
 743  
                                                 }
 744  
                                                 break;
 745  
                                         case FirstRoll:
 746  0
                                                 AnimateEventFirstRoll aefr = (AnimateEventFirstRoll)ae;
 747  0
                                                 if (lastBoard == null) {
 748  0
                                                         lastBoard = new Board();
 749  0
                                                         lastBoard.setPlayerName(Board.O, aefr.getWhitePlayer());
 750  0
                                                         lastBoard.setPlayerName(Board.X, aefr.getBlackPlayer());
 751  
                                                 }
 752  
                                                 break;
 753  
                                         }
 754  
                                         /////////////////////////////////////////////////////////////////
 755  
                                         // Repaint the board and update
 756  0
                                         boardPane.repaint();
 757  0
                                         offset += timerIncrementD;
 758  
                                         //logger.info("event:" + ae + " "+ ae.getType().toString() + " offset=" + offset + " timerD=" + timerIncrementD);
 759  
 
 760  
                                         // If this event is terminating, maybe special handling.
 761  0
                                         if (offset > 1.0) {
 762  0
                                                 switch (ae.getType()) {
 763  
                                                 case Move:
 764  0
                                                         AnimateEventMove am = (AnimateEventMove)ae; 
 765  0
                                                         extraCheckers[extraCheckersIndex++] = am.pointNumberEnd;
 766  0
                                                         extraCheckers[extraCheckersIndex++] = am.checkerNumberEnd;
 767  0
                                                         if (lastBoard != null) {                        // clear out any dice in the board after move
 768  0
                                                                 int[][] d = lastBoard.getDice();
 769  0
                                                                 d[Board.X][0] = 0;
 770  0
                                                                 d[Board.X][1] = 0;
 771  0
                                                                 d[Board.O][0] = 0;
 772  0
                                                                 d[Board.O][1] = 0;
 773  0
                                                         }
 774  
                                                         break;
 775  
                                                 case Roll:
 776  0
                                                         AnimateEventDiceRoll ar = (AnimateEventDiceRoll)ae;
 777  0
                                                         if (lastBoard != null) {                        // restore the dice on the board so we don't flash the screen
 778  0
                                                                 int[][] d = lastBoard.getDice();
 779  0
                                                                 if (ar.getWho() == Board.X) {
 780  0
                                                                         d[Board.X][0] = ar.dice[0];
 781  0
                                                                         d[Board.X][1] = ar.dice[1];
 782  
                                                                 } else {
 783  0
                                                                         d[Board.O][0] = ar.dice[0];
 784  0
                                                                         d[Board.O][1] = ar.dice[1];
 785  
                                                                 }
 786  0
                                                         }
 787  
                                                         break;
 788  
                                                 case CantMove:
 789  0
                                                         if (lastBoard != null) {                        // clear out any dice in the board after cantmove
 790  0
                                                                 int[][] d = lastBoard.getDice();
 791  0
                                                                 d[Board.X][0] = 0;
 792  0
                                                                 d[Board.X][1] = 0;
 793  0
                                                                 d[Board.O][0] = 0;
 794  0
                                                                 d[Board.O][1] = 0;
 795  
                                                         }
 796  
                                                         break;
 797  
                                                 }
 798  0
                                                 AnimateEvent aer = accessEventList(EventListCommand.remove, null, null);
 799  0
                                                 offset = 0.0;
 800  
                                                 if (DEBUGqueue)
 801  
                                                         logger.info("mtt: remove Event: " + aer.getType().toString());
 802  
                                         }
 803  
                                 }
 804  
                         }
 805  
 
 806  
                         // if there are no events in the list, then shut down the timer.
 807  0
                         int[] i = new int[1];
 808  0
                         accessEventList(EventListCommand.size, null, i);
 809  0
                         if (i[0] == 0) {
 810  
                                 if (DEBUGqueue)
 811  
                                         logger.info("mtt: terminate timer");
 812  0
                                 if (moveTimer != null)
 813  0
                                         moveTimer.cancel();
 814  0
                                 if (moveTimerTask != null)
 815  0
                                         moveTimerTask.cancel();
 816  0
                                 moveTimer = null;
 817  0
                                 moveTimerTask = null;
 818  0
                                 return;
 819  
                         }
 820  0
                 }
 821  
         }
 822  
 }