package gizmoball.game;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import physics.Vect;

/**
 * Unit Test for class GameBoard
 *
 *
 * Created: Sat Apr 24 09:04:42 2004
 *
 * @author <a href="mailto:drkp@mit.edu">Dan R. K. Ports</a>
 * @version $Id: GameBoardTest.java,v 1.7 2004/04/27 08:47:25 dan Exp $
 */
public class GameBoardTest extends TestCase {
    public static final double TOLERANCE = 0.001;

    /**
     * A test stub gizmo for testing the GameBoard. It simply keeps track
     * of the number of times and amount of time it has been simulated,
     * and the number of times it has been triggered.
     *
     * @author <a href="mailto:drkp@mit.edu">Dan R. K. Ports</a>
     * @version $Id: GameBoardTest.java,v 1.7 2004/04/27 08:47:25 dan Exp $
     */
    private static class StubbyGizmo extends AbstractGizmo {
        public int numTickCalls = 0;
        public double totalTickTime = 0.0;
        public int numTriggerCalls = 0;
    
        public StubbyGizmo(String name, Vect position) {
            super(name, position);
        } // StubbyGizmo constructor
        
        // Implementation of gizmoball.game.AbstractGizmo

        public boolean tick(double d) {
            numTickCalls++;
            totalTickTime += d;        
            return false;
        }

        public void fireTrigger(AbstractTrigger abstractTrigger) {
            numTriggerCalls++;
        }

        public double timeUntilCollisionWith(AbstractGizmo abstractGizmo) {
            return Double.POSITIVE_INFINITY;
        }

        public void collide(AbstractGizmo abstractGizmo) {
        
        }

        public Collidability canCollideWith(AbstractGizmo otherGizmo) {
            return Collidability.CAN_COLLIDE;
        }
        
    } // StubbyGizmo

    
    /** 
     * Creates a new <code>GameBoardTest</code> instance.
     *
     * @param name test name
     */
    public GameBoardTest (String name){
        super(name);
    }

    /**
     * @return a <code>TestSuite</code>
     */
    public static TestSuite suite(){
        return new TestSuite(GameBoardTest.class);
    }

    /** 
     * Entry point 
     */ 
    public static void main(String[] args) {
        junit.textui.TestRunner.run(suite());
    }


    GameBoard gb;

    protected void setUp() {
        gb = new GameBoard();
    }

    public void testDefaultDimensionsAndWalls() {
        assertEquals(new Vect(20, 20), gb.getDimensions());
        
        assertEquals(1, gb.getGizmos().size());
        AbstractGizmo gizmo = (AbstractGizmo) gb.getGizmos().iterator().next();
        assertTrue(gizmo instanceof OuterWallsGizmo);
        OuterWallsGizmo walls = (OuterWallsGizmo) gizmo;
        assertEquals(new Vect(20, 20), walls.getDimensions());
    }
    
    public void testNonDefaultDimensionsAndWalls() {
        gb = new GameBoard(new Vect(17, 42));
        assertEquals(new Vect(17, 42), gb.getDimensions());
        
        assertEquals(1, gb.getGizmos().size());
        AbstractGizmo gizmo = (AbstractGizmo) gb.getGizmos().iterator().next();
        assertTrue(gizmo instanceof OuterWallsGizmo);
        OuterWallsGizmo walls = (OuterWallsGizmo) gizmo;
        assertEquals(new Vect(17, 42), walls.getDimensions());
    }
    
    public void testAddAndRemoveGizmos() {
        AbstractGizmo g1 = new StubbyGizmo("g1", Vect.X_HAT);
        AbstractGizmo g2 = new StubbyGizmo("g2", Vect.X_HAT);

        assertEquals(1, gb.getGizmos().size());
        assertFalse(gb.getGizmos().contains(g1));
        assertFalse(gb.getGizmos().contains(g2));

        gb.addGizmo(g1);
        assertEquals(2, gb.getGizmos().size());
        assertTrue(gb.getGizmos().contains(g1));
        assertFalse(gb.getGizmos().contains(g2));
        assertSame(gb, g1.getGameBoard());        

        gb.addGizmo(g2);
        assertEquals(3, gb.getGizmos().size());
        assertTrue(gb.getGizmos().contains(g1));
        assertTrue(gb.getGizmos().contains(g2));
        assertSame(gb, g1.getGameBoard());
        assertSame(gb, g2.getGameBoard());
        
        assertTrue(gb.removeGizmo(g2));
        assertEquals(2, gb.getGizmos().size());
        assertTrue(gb.getGizmos().contains(g1));
        assertFalse(gb.getGizmos().contains(g2));
        assertSame(gb, g1.getGameBoard());        

        try {
            gb.addGizmo(null);
            fail("expected an IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass
        } // end of try-catch

        try {
            gb.removeGizmo(null);
            fail("expected an IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass
        } // end of try-catch
    }

    public void testAddAndRemoveTriggers()
    {
        AbstractGizmo g1 = new StubbyGizmo("g1", Vect.X_HAT);
        AbstractGizmo g2 = new StubbyGizmo("g2", Vect.X_HAT);
        AbstractGizmo g3 = new StubbyGizmo("g3", Vect.X_HAT);

        gb.addGizmo(g1);
        gb.addGizmo(g2);
        gb.addGizmo(g3);

        assertEquals(0, gb.getTriggers().size());
        assertEquals(0, gb.getCollisionTriggers().size());
        assertEquals(0, gb.getKeypressTriggers().size());
        assertEquals(0, gb.getIncomingTriggersForGizmo(g1).size());       
        assertEquals(0, gb.getOutgoingTriggersForGizmo(g1).size());       
        assertEquals(0, gb.getIncomingTriggersForGizmo(g2).size());       
        assertEquals(0, gb.getOutgoingTriggersForGizmo(g2).size());       
        assertEquals(0, gb.getIncomingTriggersForGizmo(g3).size());       
        assertEquals(0, gb.getOutgoingTriggersForGizmo(g3).size());       
        assertEquals(gb.getIncomingTriggersForGizmo(g1), g1.getIncomingTriggers());
        assertEquals(gb.getIncomingTriggersForGizmo(g2), g2.getIncomingTriggers());
        assertEquals(gb.getIncomingTriggersForGizmo(g3), g3.getIncomingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g1), g1.getOutgoingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g2), g2.getOutgoingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g3), g3.getOutgoingTriggers());

        AbstractTrigger t1 = new CollisionTrigger(g1, g2);
        AbstractTrigger t2 = new CollisionTrigger(g1, g3);
        AbstractTrigger t3 = new KeypressTrigger(1, g2);

        gb.addTrigger(t1);
        gb.addTrigger(t2);
        gb.addTrigger(t3);

        assertEquals(3, gb.getTriggers().size());
        assertEquals(2, gb.getCollisionTriggers().size());
        assertTrue(gb.getCollisionTriggers().contains(t1));
        assertTrue(gb.getCollisionTriggers().contains(t2));
        assertEquals(1, gb.getKeypressTriggers().size());
        assertTrue(gb.getKeypressTriggers().contains(t3));
        assertEquals(0, gb.getIncomingTriggersForGizmo(g1).size());       
        assertEquals(2, gb.getOutgoingTriggersForGizmo(g1).size());       
        assertTrue(gb.getOutgoingTriggersForGizmo(g1).contains(t1));
        assertTrue(gb.getOutgoingTriggersForGizmo(g1).contains(t2));
        assertEquals(2, gb.getIncomingTriggersForGizmo(g2).size());       
        assertEquals(0, gb.getOutgoingTriggersForGizmo(g2).size());       
        assertTrue(gb.getIncomingTriggersForGizmo(g2).contains(t1));
        assertTrue(gb.getIncomingTriggersForGizmo(g2).contains(t3));
        assertEquals(1, gb.getIncomingTriggersForGizmo(g3).size());       
        assertEquals(0, gb.getOutgoingTriggersForGizmo(g3).size());       
        assertTrue(gb.getIncomingTriggersForGizmo(g3).contains(t2));
        assertEquals(gb.getIncomingTriggersForGizmo(g1), g1.getIncomingTriggers());
        assertEquals(gb.getIncomingTriggersForGizmo(g2), g2.getIncomingTriggers());
        assertEquals(gb.getIncomingTriggersForGizmo(g3), g3.getIncomingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g1), g1.getOutgoingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g2), g2.getOutgoingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g3), g3.getOutgoingTriggers());

        gb.removeTrigger(t2);
        
        assertEquals(2, gb.getTriggers().size());
        assertEquals(1, gb.getCollisionTriggers().size());
        assertTrue(gb.getCollisionTriggers().contains(t1));
        assertFalse(gb.getCollisionTriggers().contains(t2));
        assertEquals(1, gb.getKeypressTriggers().size());
        assertTrue(gb.getKeypressTriggers().contains(t3));
        assertEquals(0, gb.getIncomingTriggersForGizmo(g1).size());       
        assertEquals(1, gb.getOutgoingTriggersForGizmo(g1).size());       
        assertTrue(gb.getOutgoingTriggersForGizmo(g1).contains(t1));
        assertFalse(gb.getOutgoingTriggersForGizmo(g1).contains(t2));
        assertEquals(2, gb.getIncomingTriggersForGizmo(g2).size());       
        assertEquals(0, gb.getOutgoingTriggersForGizmo(g2).size());       
        assertTrue(gb.getIncomingTriggersForGizmo(g2).contains(t1));
        assertTrue(gb.getIncomingTriggersForGizmo(g2).contains(t3));
        assertEquals(0, gb.getIncomingTriggersForGizmo(g3).size());       
        assertEquals(0, gb.getOutgoingTriggersForGizmo(g3).size());       
        assertFalse(gb.getIncomingTriggersForGizmo(g3).contains(t2));
        assertEquals(gb.getIncomingTriggersForGizmo(g1), g1.getIncomingTriggers());
        assertEquals(gb.getIncomingTriggersForGizmo(g2), g2.getIncomingTriggers());
        assertEquals(gb.getIncomingTriggersForGizmo(g3), g3.getIncomingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g1), g1.getOutgoingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g2), g2.getOutgoingTriggers());
        assertEquals(gb.getOutgoingTriggersForGizmo(g3), g3.getOutgoingTriggers());
    }

    public void testTriggerDirectFiring()
    {
        StubbyGizmo g1 = new StubbyGizmo("g1", Vect.X_HAT);
        StubbyGizmo g2 = new StubbyGizmo("g2", Vect.X_HAT);
        StubbyGizmo g3 = new StubbyGizmo("g3", Vect.X_HAT);

        gb.addGizmo(g1);
        gb.addGizmo(g2);
        gb.addGizmo(g3);

        AbstractTrigger t1 = new CollisionTrigger(g1, g2);
        AbstractTrigger t2 = new CollisionTrigger(g1, g3);
        AbstractTrigger t3 = new KeypressTrigger(1, g2);

        gb.addTrigger(t1);
        gb.addTrigger(t2);
        gb.addTrigger(t3);

        assertEquals(0, g1.numTriggerCalls);
        assertEquals(0, g2.numTriggerCalls);
        assertEquals(0, g3.numTriggerCalls);

        t1.fire();
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(1, g2.numTriggerCalls);
        assertEquals(0, g3.numTriggerCalls);

        t2.fire();
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(1, g2.numTriggerCalls);
        assertEquals(1, g3.numTriggerCalls);

        t3.fire();
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(2, g2.numTriggerCalls);
        assertEquals(1, g3.numTriggerCalls);

        t1.fire();
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(3, g2.numTriggerCalls);
        assertEquals(1, g3.numTriggerCalls);
    }

    public void testKeypressTriggerFiring() {
        StubbyGizmo g1 = new StubbyGizmo("g1", Vect.X_HAT);
        StubbyGizmo g2 = new StubbyGizmo("g2", Vect.X_HAT);
        StubbyGizmo g3 = new StubbyGizmo("g3", Vect.X_HAT);

        gb.addGizmo(g1);
        gb.addGizmo(g2);
        gb.addGizmo(g3);

        AbstractTrigger t1 = new CollisionTrigger(g1, g2);
        AbstractTrigger t2 = new KeypressTrigger(1, g3);
        AbstractTrigger t3 = new KeypressTrigger(1, g2);
        AbstractTrigger t4 = new KeypressTrigger(2, g3);

        gb.addTrigger(t1);
        gb.addTrigger(t2);
        gb.addTrigger(t3);
        gb.addTrigger(t4);

        assertEquals(0, g1.numTriggerCalls);
        assertEquals(0, g2.numTriggerCalls);
        assertEquals(0, g3.numTriggerCalls);

        gb.fireKeypressTrigger(1);
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(1, g2.numTriggerCalls);
        assertEquals(1, g3.numTriggerCalls);
        
        gb.fireKeypressTrigger(1);
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(2, g2.numTriggerCalls);
        assertEquals(2, g3.numTriggerCalls);

        gb.fireKeypressTrigger(2);
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(2, g2.numTriggerCalls);
        assertEquals(3, g3.numTriggerCalls);

        gb.fireKeypressTrigger(3);
        assertEquals(0, g1.numTriggerCalls);
        assertEquals(2, g2.numTriggerCalls);
        assertEquals(3, g3.numTriggerCalls);
    }

    private class ScriptRecord {
        public double endTime;
        public double collisionTime;

        public ScriptRecord(double endTime, double collisionTime) {
            this.endTime = endTime;
            this.collisionTime = collisionTime;
        }
    }
    

    private class ScriptedGizmo extends AbstractGizmo {
        public int numTickCalls = 0;
        public double totalTickTime = 0.0;
        public int numTriggerCalls = 0;
        public int numCollideCalls = 0;
        public List script;
        public ScriptRecord curRec;
        public Iterator scriptIt;
        
        public ScriptedGizmo(String name, Vect position,
            List script) {
            super(name, position);
            this.script = script;
            scriptIt = script.iterator();
            curRec = (ScriptRecord) scriptIt.next();
        } // ScriptedGizmo constructor
        
        // Implementation of gizmoball.game.AbstractGizmo

        public boolean tick(double d) {
            System.out.println("    " + getName() + " tick: " + Double.toString(d));
            numTickCalls++;
            totalTickTime += d;
            if (totalTickTime > curRec.endTime) {
                curRec = (ScriptRecord) scriptIt.next();
            } // end of if (totalTickTime > curRec.endTime)
            
            return false;
        }

        public void fireTrigger(AbstractTrigger abstractTrigger) {
            numTriggerCalls++;
        }

        public double timeUntilCollisionWith(AbstractGizmo abstractGizmo) {
            System.out.println("    " + getName() + " timeToCollideWith " + abstractGizmo.getName() + ": " + Double.toString(curRec.collisionTime - totalTickTime));
            
            return curRec.collisionTime - totalTickTime;
        }

        public void collide(AbstractGizmo abstractGizmo) {
            System.out.println("    " + getName() + " collide with " + abstractGizmo.getName());
            curRec = (ScriptRecord) scriptIt.next();
            numCollideCalls++;
        }

        public Collidability canCollideWith(AbstractGizmo otherGizmo) {
            // This is ugly. I apologize.
            if (getName().equals("foo")) {
                if (otherGizmo.getName().equals("bar"))
                    return Collidability.CAN_COLLIDE;
                if (otherGizmo.getName().equals("baz"))
                    return Collidability.DEFER_COLLIDE;
                if (otherGizmo.getName().equals("quux"))
                    return Collidability.NEVER_COLLIDE;
            }
            if (getName().equals("bar")) {
                if (otherGizmo.getName().equals("foo"))
                    return Collidability.DEFER_COLLIDE;
                if (otherGizmo.getName().equals("baz"))
                    return Collidability.CAN_COLLIDE;
                if (otherGizmo.getName().equals("quux"))
                    return Collidability.NEVER_COLLIDE;
            }
            if (getName().equals("baz")) {
                if (otherGizmo.getName().equals("foo"))
                    return Collidability.CAN_COLLIDE;
                if (otherGizmo.getName().equals("bar"))
                    return Collidability.CAN_COLLIDE;
                if (otherGizmo.getName().equals("quux"))
                    return Collidability.DEFER_COLLIDE;
            }
            return Collidability.NEVER_COLLIDE;
        }
    }
    
    
    /**
     *<pre>
     * UNIT TESTING:
     *   A Half Second Play in Two Acts
     *   entirely divorced of all physical meaning.
     *
     * Dramatis personae:
     *   A GameBoard. The narrator.
     *   Four ScriptedGizmos, added in this order:
     *    foo: CAN_COLLIDE w/ bar, DEFER_COLLIDE w/ baz, NEVER_COLLIDE w/ quux
     *    bar: DEFER_COLLIDE w/ foo, CAN_COLLIDE w/ baz, NEVER_COLLIDE w/ quux
     *    baz: CAN_COLLIDE w/ foo, CAN_COLLIDE w/ bar, DEFER_COLLIDE w/ quux 
     *    quux: NEVER_COLLIDE w/ foo, bar, and baz
     *   ...forming the collision pairs:
     *     foo-bar, baz-foo, bar-baz
     *
     * ACT I: THE INITIALIZATION
     *
     * GameBoard: Foo, Bar, Baz, and Quux, join me as we traipse merrily
     *    through the scintillating world of unit testing!
     *
     * Foo: I'll collide with Bar in .15 seconds!
     * Bar: I'll collide with Baz in .25 seconds!
     * Baz: Foo and I are never going to collide, just you watch!
     *
     * OVERTURE ENDS
     *
     *
     * ACT II: SIMULATION
     * SCENE I: A .2 SECOND TICK
     *
     * GameBoard: Go forth and tick for .1 seconds.
     * Foo: I'll collide with Bar in .05 seconds! Aieee!
     * Bar: I'll collide with Baz in .15 seconds!
     * Baz: Foo and I are never going to collide, just you watch!
     *
     * GameBoard: Go forth and tick for .05 seconds!
     * Foo with Bar: <i>Clunk!</i>
     * Foo: I'll collide with Bar in .5 seconds.
     * Bar: I'll collide with Baz in .10 seconds!
     * Baz: Foo and I are never going to collide, just you watch!
     *
     * GameBoard: Go forth and tick for .05 seconds.
     * Foo: I'll collide with Bar in .45 seconds!
     * Bar: I'll collide with Baz in .05 seconds!
     * Baz: Foo and I are never going to collide, just you watch!
     *
     *
     * SCENE II: A .08 SECOND TICK
     *
     * GameBoard: Go forth and tick for .05 seconds!
     * Bar with Baz: <i>Sproing!</i>
     * Foo: I'll collide with Bar in .4 seconds.
     * Bar: I'm never colliding with Baz again! Try and make me!
     * Baz: Oh, the humanity! Now I'm going to collide with Foo in .15 s!
     *
     * GameBoard: Go forth and tick for .03 seconds!
     *
     *
     * SCENE III: A .22 SECOND TICK
     *
     * GameBoard: Go forth and tick for .07 seconds!
     * Foo: I'll collide with Bar in .3 seconds.
     * Bar: I'm never colliding with Baz again! Try and make me!
     * Baz: The end is near! I will collide with Foo in .05 s!
     *
     * GameBoard: Go forth and tick for .03 seconds.
     * Foo: I'll collide with Bar in .27 seconds.
     * Bar: I'm never colliding with Baz again! Try and make me!
     * Baz: The eschaton is imminent! I will collide with Foo in .02
     * seconds
     *
     * GameBoard: Go forth and tick for .02 seconds!
     * Baz with Foo: <i>Squark!</i>
     * Foo: I'll collide with Bar in .25 seconds!
     * Bar: I'm never colliding with Baz again! Try and make me!
     * Baz: I've had it, no more collisions with Foo!
     *
     * GameBoard: Go forth and tick for .08 seconds!
     * Foo: I'll collide with Bar in .17 seconds!
     * Bar: I'm never colliding with Baz again! Try and make me!
     * Baz: I've had it, no more collisions with Foo!
     *
     * GameBoard: Go forth and tick for .02 seconds!
     * Foo: I'll collide with Bar in .15 seconds!
     * Bar: I'm never colliding with Baz again! Try and make me!
     * Baz: I've had it, no more collisions with Foo!
     *
     * EXEUNT OMNES. Everyone is dereferenced and garbage-collected.
     * </pre>
     */
    public void testSimulation()
    {
        ScriptRecord[] fooScript =
             {
                 new ScriptRecord(.15, .15),
                 new ScriptRecord(.65, .65)
             };

        ScriptRecord[] barScript =
            {
                new ScriptRecord(.25, .25),
                new ScriptRecord(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)
            };
        
        ScriptRecord[] bazScript =
             {
                 new ScriptRecord(.25-TOLERANCE, Double.POSITIVE_INFINITY),
                 new ScriptRecord(.4, .4),
                 new ScriptRecord(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)
             };

        ScriptRecord[] quuxScript = 
            {
                new ScriptRecord(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)
            };
        

        ScriptedGizmo foo = new ScriptedGizmo("foo", Vect.X_HAT,
            Arrays.asList(fooScript));
        ScriptedGizmo bar = new ScriptedGizmo("bar", Vect.X_HAT,
            Arrays.asList(barScript));
        ScriptedGizmo baz = new ScriptedGizmo("baz", Vect.X_HAT,
            Arrays.asList(bazScript));
        ScriptedGizmo quux = new ScriptedGizmo("quux", Vect.X_HAT,
            Arrays.asList(barScript));

        gb.addGizmo(foo);
        gb.addGizmo(bar);
        gb.addGizmo(baz);
        gb.addGizmo(quux);
        gb.addTrigger(new CollisionTrigger(foo, bar));
        gb.addTrigger(new CollisionTrigger(bar, baz));
        gb.addTrigger(new CollisionTrigger(baz, quux));
        gb.addTrigger(new CollisionTrigger(quux, foo));

        assertEquals(0, foo.numTickCalls);
        assertEquals(0, bar.numTickCalls);  
        assertEquals(0, baz.numTickCalls);
        assertEquals(0, quux.numTickCalls);
        assertEquals(0, foo.numCollideCalls);
        assertEquals(0, bar.numCollideCalls);
        assertEquals(0, baz.numCollideCalls);
        assertEquals(0, quux.numCollideCalls);
        assertEquals(0.0, foo.totalTickTime, TOLERANCE);
        assertEquals(0.0, bar.totalTickTime, TOLERANCE);
        assertEquals(0.0, baz.totalTickTime, TOLERANCE);
        assertEquals(0.0, quux.totalTickTime, TOLERANCE);
        assertEquals(0, foo.numTriggerCalls);
        assertEquals(0, bar.numTriggerCalls);
        assertEquals(0, baz.numTriggerCalls);
        assertEquals(0, quux.numTriggerCalls);
        
        
        gb.tick(0.2);
        assertEquals(3, foo.numTickCalls);
        assertEquals(3, bar.numTickCalls);  
        assertEquals(3, baz.numTickCalls);
        assertEquals(3, quux.numTickCalls);
        assertEquals(1, foo.numCollideCalls);
        assertEquals(0, bar.numCollideCalls);
        assertEquals(0, baz.numCollideCalls);
        assertEquals(0, quux.numCollideCalls);
        assertEquals(0.2, foo.totalTickTime, TOLERANCE);
        assertEquals(0.2, bar.totalTickTime, TOLERANCE);
        assertEquals(0.2, baz.totalTickTime, TOLERANCE);
        assertEquals(0.2, quux.totalTickTime, TOLERANCE);
        assertEquals(0, foo.numTriggerCalls);
        assertEquals(1, bar.numTriggerCalls);
        assertEquals(1, baz.numTriggerCalls);
        assertEquals(0, quux.numTriggerCalls);

        gb.tick(0.08);
        assertEquals(5, foo.numTickCalls);
        assertEquals(5, bar.numTickCalls);  
        assertEquals(5, baz.numTickCalls);
        assertEquals(5, quux.numTickCalls);
        assertEquals(1, foo.numCollideCalls);
        assertEquals(1, bar.numCollideCalls);
        assertEquals(0, baz.numCollideCalls);
        assertEquals(0, quux.numCollideCalls);
        assertEquals(0.28, foo.totalTickTime, TOLERANCE);
        assertEquals(0.28, bar.totalTickTime, TOLERANCE);
        assertEquals(0.28, baz.totalTickTime, TOLERANCE);
        assertEquals(0.28, quux.totalTickTime, TOLERANCE);
        assertEquals(0, foo.numTriggerCalls);
        assertEquals(1, bar.numTriggerCalls);
        assertEquals(2, baz.numTriggerCalls);
        assertEquals(1, quux.numTriggerCalls);

        gb.tick(0.22);
        assertEquals(10, foo.numTickCalls);
        assertEquals(10, bar.numTickCalls);  
        assertEquals(10, baz.numTickCalls);
        assertEquals(10, quux.numTickCalls);
        assertEquals(1, foo.numCollideCalls);
        assertEquals(1, bar.numCollideCalls);
        assertEquals(1, baz.numCollideCalls);
        assertEquals(0, quux.numCollideCalls);
        assertEquals(0.5, foo.totalTickTime, TOLERANCE);
        assertEquals(0.5, bar.totalTickTime, TOLERANCE);
        assertEquals(0.5, baz.totalTickTime, TOLERANCE);
        assertEquals(0.5, quux.totalTickTime, TOLERANCE);
        assertEquals(0, foo.numTriggerCalls);
        assertEquals(2, bar.numTriggerCalls);
        assertEquals(2, baz.numTriggerCalls);
        assertEquals(2, quux.numTriggerCalls);
    }
} // GameBoardTest

