        var playing = false ;
        var playButton;
        var chaseContainer;
        var scoreDiv;
        var bugSprites ;
        var answerSprites ;
        var timerId ;
        var me ;
        var NO_REBOUND = 0;
        var RANDOM_REBOUND = 1;
        var FLY_THRU_EDGES = 2;
        var GET_OUT = 3;
        var userVelocity = 5.5;
        var TWO_PI = Math.PI * 2.0;
        var gameLevel ;
        var score ;
        var timerDelay = 100;
        var problem ;
        var paralyzed = false ;
        var livesLeft ;
 
        function createMeImg() {
             var img = document.createElement( 'img' );
             img.src = 'images/me.gif';
             img.alt = '';
             img.id = 'me';
             img.className = 'sprite';
             return img ;
        }       
        
        function startGame() {
            playButton = document.getElementById( "playButton" );
            playButton.style.visibility = 'visible';
            playButton.focus();
            chaseContainer = document.getElementById( "chaseContainer" );
            scoreDiv = document.getElementById( "score" );

            gameLevel = 1;
            score = 0;

 
            var meImg = createMeImg();
            me = new Sprite( meImg, new Point( 220, 190 ) );
            me.edgeRebound = NO_REBOUND ;
            me.velocity = 0;
            recalcMovement( me );
            chaseContainer.appendChild( meImg );
            
            setLivesLeft( 4 );

            answerSprites = new Array();
            answerSprites[0] = newAnt( "ant1.gif" );
            answerSprites[1] = newAnt( "ant2.gif" );
            answerSprites[2] = newAnt( "ant3.gif" );
            answerSprites[3] = newAnt( "ant4.gif" );
                        
            bugSprites = new Array();
            
            startNextProblem();

            playGame();
        }

        function setLivesLeft( numLives ) {
            livesLeft = numLives ;

            var life ;
            for ( life=1; life<=numLives; life++ ) {
                var lifeImg = document.getElementById( "life" + life );
                lifeImg.style.display = 'inline' ;
            }
            while ( true ) {
                var lifeImg = document.getElementById( "life" + life );
                if ( lifeImg == null ) break;
                lifeImg.style.display = 'none' ;
                life++;
            }
            playButton.focus();
        }
        
        function startNextLevel() {
            gameLevel ++ ;
            if ( userVelocity < 15 ) {
                timerDelay = Math.round( timerDelay * 0.9 );
                userVelocity=userVelocity*1.1;
            }
            //debug( 'userVelocity=' + userVelocity );
            if ( gameLevel < 4 ) {
                 bugSprites[bugSprites.length] = newFly() ;
            }
        }
        
        function startNextProblem() {
              problem = getNextProblem();
              assignBugs( problem );
              showProblem( problem );
              var i; 
              for ( i = 0; i<answerSprites.length; i++ ) {
                   var answerSprite = answerSprites[i];
                   if ( answerSprite.object.parentNode != chaseContainer ) {
                       chaseContainer.appendChild( answerSprite.object );
                       bugSprites[bugSprites.length] = answerSprite ;
                   }
                   
                   setAntPersonality( answerSprite );
              }

              // after every four correct answers
              if ( (numCorrect % 4) == 3 ) {
                  startNextLevel();
              }
        }
        
        
        
       function assignBugs( problem ) {
            shuffleQuestionOrder();
            for ( var i=0; i<4; i++ ) {
                var q = questionOrder[i];
                if ( q == 3 ) {
                    // correct answer is at order i
                    problem.correctAnswer.order = i;
                    problem.correctAnswer.bug = answerSprites[i] ;
                }
                else {
                    // incorrect answer q is at order i
                    problem.incorrectAnswers[q].order = i;
                    problem.incorrectAnswers[q].bug = answerSprites[i];
                }
            }
        }

        var questionOrder = [ 0, 1, 2, 3 ];
        
        function shuffleQuestionOrder() {
            var i=0;
            for ( i=0; i<questionOrder.length; i++ ) {
                var r = Math.floor(Math.random() * 4) % 4;
                var tmp = questionOrder[r];
                questionOrder[r] = questionOrder[i];
                questionOrder[i] = tmp;
            }
        }
                
        
        
        function showProblem( problem ) {
              document.getElementById( 'questionHeading' ).innerHTML = problem.questionHeading;
              document.getElementById( 'question' ).innerHTML = problem.question;
              document.getElementById( 'answerHeading' ).innerHTML = problem.answerHeading;
              showPossibleAnswer( problem.correctAnswer.order, problem.correctAnswer.text );
              for ( var a = 0; a<problem.incorrectAnswers.length; a++ ) {
                   var incorrectAnswer = problem.incorrectAnswers[a] ;
                   showPossibleAnswer( incorrectAnswer.order, incorrectAnswer.text );
              }
        }
        
        function showPossibleAnswer( i, text ) {
              var answerObj = document.getElementById( 'answer' + i );
              answerObj.className = 'answer';
              answerObj.innerHTML = text;
              answerObj.style.textDecoration = '';
              answerObj.parentNode.style.display = '' ;
              answerObj.parentNode.style.backgroundColor = '';
              
        }
              
        
        function Sprite( object, centerPoint ) {
            this.object = object;
            this.center = centerPoint ;
            this.xIncrement = 0.0;
            this.yIncrement = 0.0;
            this.directionRadians = 0 ;
            this.velocity = 3.25;
            this.wiggleFrequency = 0;
            this.wiggleRadians = 0;
            this.edgeRebound = RANDOM_REBOUND ;
            this.killerInstinct = 0.0;
            this.scaredInstinct = 0.0;
            this.schizoInstinct = 0.0;
        }

        function newFly() {
            var img = document.createElement( 'img' );
            img.className = "sprite" ;
            img.src = "images/fly1_0.gif" ;
            img.alt = "fly" ;
            var fly = new Sprite( img, new Point( Math.random() * chaseContainer.clientWidth, Math.random() * 50 ) );
            fly.wiggleFrequency = Math.random() / 3.0;
            fly.wiggleRadians = Math.random() * 1.5;
            changeDirection( fly, 0 );
            fly.edgeRebound = FLY_THRU_EDGES ;
            chaseContainer.appendChild( img );
            return fly ;
        }
        
        
        function newAnt( fileName ) {
            var img = document.createElement( 'img' );
            img.className = "sprite" ;
            img.src = "images/" + fileName ;
            img.alt = fileName ;
            var ant = new Sprite( img, new Point( Math.random() * chaseContainer.clientWidth, Math.random() * 50 ) );
            ant.wiggleFrequency = Math.random() / 3.0;
            ant.wiggleRadians = Math.random() * Math.PI;
            changeDirection( ant, 0 );
            ant.edgeRebound = RANDOM_REBOUND ;
            return ant ;
        }
        
        function setAntPersonality( ant ) {
            if ( gameLevel > 10 ) {
                ant.schizoInstinct = 0.05;            
            }
            else {
                ant.wiggleFrequency = Math.random() / 3.0;
                ant.wiggleRadians = Math.random() * Math.PI;
                ant.killerInstinct = 0.0;
                ant.scaredInstinct = 0.0;
                ant.schizoInstinct = 0.0;
            }
        }
        
        
        function Point( x, y ) {
            this.x = x ;
            this.y = y ;
        }

        function Line( point1, point2 ) {
            this.point1 = point1 ;
            this.point2 = point2 ;
        }
        
        function togglePlay() {
           if ( playing ) {
               pauseGame();
           }
           else {
               playGame();
           }
        }
        
        function pauseGame() {
            playing = false ;
            playButton.innerHTML = 'Play';
            clearTimeout( timerId );
            timerId = null;
        }
        
        function playGame() {
            playing = true ;
            playButton.innerHTML = 'Pause';
            timerId = 1;
            moveObjects( true );
        }
        
       function endGame() {
            markCorrectAnswer()
            me.object.style.display = 'none';
           for ( var i=0; i<bugSprites.length; i++ ) {
               var bugSprite = bugSprites[i];
               bugSprite.edgeRebound = GET_OUT ;
               bugSprite.killerInstinct = 0.0;
               bugSprite.scaredInstinct = 1.0;
               bugSprite.schizoInstinct = 0.0;
           }
           timerDelay = 0;
           setTimeout( "gameOver()", 4000 );
           document.getElementById( 'gameOver' ).style.display = 'block';
       }
       
       function gameOver() {
           playButton.onblur='';
           playButton.style.display = 'none';
           debug( 'gameOver' );
           clearTimeout( timerId );
           timerId = null;
            
           for ( var i=0; i<bugSprites.length; i++ ) {
               var bugSprite = bugSprites[i];
               bugSprite.object.parentNode.removeChild( bugSprite.object );
           }
           setTimeout( "onGameOver( score )", 500  );
       }
        


        var userLeft = false;
        var userRight = false ;
        var userUp = false ;
        var userDown = false ;

        function isGameKey( key ) {
            return playing && ( key == LEFT_ARROW_KEY  || key == RIGHT_ARROW_KEY || 
                 key == UP_ARROW_KEY || key == DOWN_ARROW_KEY  ||
                 key == J_KEY || key == K_KEY ||
                 key == L_KEY || key == H_KEY );
        }        
        
        function debug( msg ) {
        }
        
        function keyChange( event, keyDown ) {
            var key = keyCode( event );
            //debug( key + " " + keyDown );
            if ( isGameKey( key ) ) {
                if ( !paralyzed ) {
                    if ( key == LEFT_ARROW_KEY || key == H_KEY ) {
                        userLeft = keyDown ;
                    } else if ( key == RIGHT_ARROW_KEY || key == L_KEY ) {
                        userRight = keyDown ;
                    } else if ( key == UP_ARROW_KEY || key == J_KEY ) {
                        userUp = keyDown ;
                    } else if ( key == DOWN_ARROW_KEY || key == K_KEY ) {
                        userDown = keyDown;
                    }
                    recalcUserMovement();
                }
                return murderEvent( event ) ;
            }
        }
        
        function filterGameKey( event ) {
            var key = keyCode( event );
            if ( isGameKey( key ) ) {
                return murderEvent( event ); ;
            }
        }
        
        
        function murderEvent(evt) { 
            evt.cancel=true; 
            evt.returnValue=false; 
            evt.cancelBubble=true; 
            if (evt.stopPropagation) evt.stopPropagation(); 
            if (evt.preventDefault) evt.preventDefault(); 
            return false;
        }        
        
        function changeDirection( sprite, radians ) {
            sprite.directionRadians += radians ;
            while ( sprite.directionRadians > TWO_PI ) {
                sprite.directionRadians -= TWO_PI ;
            }
            while ( sprite.directionRadians < 0 ) {
                 sprite.directionRadians += TWO_PI ;
            }
            recalcMovement( sprite );
            var pos = sprite.object.src.indexOf( '_' ); 
            if ( pos > 0 ) {
                 // calc d as PI percent rounded to nearest 25% of PI
                 var dir25 = Math.round(Math.round( (sprite.directionRadians / Math.PI) * 100) / 25) * 25;
                 if ( dir25 == 200 ) { dir25 = 0; }
                 sprite.object.src = sprite.object.src.substring( 0, pos ) + '_' + dir25 + '.gif';
            }
        }
        
        function changeSpeed( sprite, percentChange ) {
            sprite.velocity = sprite.velocity * percentChange ;
            recalcMovement( sprite );
        }
        
         
        var UP_RADIANS = Math.PI ;
        var DOWN_RADIANS = 0;
        var RIGHT_RADIANS = (Math.PI / 2.0);
        var LEFT_RADIANS =  (RIGHT_RADIANS + Math.PI) ;
        
        function recalcUserMovement() {
            var radians = 0;
            me.velocity = userVelocity;
            
            if ( userUp && !userDown && userLeft==userRight ) {
                 radians = UP_RADIANS ;
            }
            else if ( userDown && !userUp && userLeft==userRight ) {
                 radians = DOWN_RADIANS ;
            }
            else if ( userLeft && !userRight && userUp==userDown ) {
               radians = LEFT_RADIANS ;
            }
            else if ( userRight && !userLeft && userUp==userDown ) {
               radians = RIGHT_RADIANS ;
            }
            else if ( userRight && userUp && !userLeft && !userDown ) {
               radians = (UP_RADIANS + RIGHT_RADIANS) / 2;
            }
            else if ( userRight && userDown && !userLeft && !userUp ) {
                radians = (DOWN_RADIANS + RIGHT_RADIANS) / 2;
            }
            else if ( userLeft && userUp && !userRight && !userDown ) {
               radians = (UP_RADIANS + LEFT_RADIANS) / 2 ;
            }
            else if ( userLeft && userDown && !userRight && !userUp ) {
               radians = (DOWN_RADIANS - RIGHT_RADIANS) / 2;
            }
            else {
                me.velocity = 0;
            }            
            
            me.directionRadians = radians ;
            recalcMovement( me );
            //debug( 'rad=' + radians + ' x=' + me.xIncrement + ' y=' + me.yIncrement );
        }
        
        function recalcMovement( sprite ) {
            sprite.xIncrement = Math.sin( sprite.directionRadians ) * sprite.velocity ;
            sprite.yIncrement = Math.cos( sprite.directionRadians ) * sprite.velocity ;
        }
                        
        function checkBounds( sprite, containerDiv ) {
             if ( sprite.edgeRebound == GET_OUT ) {
                 return ;
             }
             var hitEdge = false ;
             var flyThruX = sprite.center.x ;
             var flyThruY = sprite.center.y ;
             var maxX = containerDiv.clientWidth - sprite.object.clientWidth  ;
             var maxY = containerDiv.clientHeight - sprite.object.clientHeight ;
             
             if ( sprite.center.x < 0 ) {
                sprite.center.x = 0 ;
                hitEdge = true ;
                flyThruX = maxX ;
             }
             if ( sprite.center.y < 0 ) {
                sprite.center.y = 0 ;
                hitEdge = true ;
                flyThruY = maxY
             }
             if ( sprite.center.x > maxX ) {
                sprite.center.x = maxX ;
                hitEdge = true ;
                flyThruX = 0 ;
             }
             if ( sprite.center.y > maxY ) {
                sprite.center.y = maxY ;
                hitEdge = true ;
                flyThruY = 0 ;
             }
             if ( hitEdge ) {
                 if ( sprite.edgeRebound == RANDOM_REBOUND ) {
                    changeDirection( sprite, (Math.random() * Math.PI * 2) );
                 }
                 else if ( sprite.edgeRebound == FLY_THRU_EDGES ) {
                     sprite.center.y = flyThruY ;
                     sprite.center.x = flyThruX ;
                 }
             }
        }
        
        function changePersonality( sprite ) {
                var r = Math.random();
                if ( r < 0.33 ) {
                    // change to killer
                    sprite.killerInstinct = 0.9;
                    sprite.scaredInstinct = 0.0 ;
                    sprite.wiggleFrequency = 0.0;
                }
                else if ( r < 0.66 ) {
                    // change to scared
                    sprite.killerInstinct = 0.0;
                    sprite.scaredInstinct = 0.1 ;
                    sprite.wiggleFrequency = 0.0;
                }
                else {
                    sprite.killerInstinct = 0.0;
                    sprite.scaredInstinct = 0.0 ;
                    sprite.wiggleFrequency = 0.5;
                }
        }

        function useKillerInstinct( sprite ) {
               if ( sprite.center.x == me.center.x ) {
                    sprite.xIncrement = 0;
               }
               else {
                    sprite.xIncrement = ( sprite.center.x < me.center.x ) ? 1 : -1;
               }
               if ( sprite.center.y == me.center.y ) {
                    sprite.yIncrement = 0;
               }
               else {
                    sprite.yIncrement = ( sprite.center.y < me.center.y ) ? 1 : -1;
               }
        }

        function useScaredInstinct( sprite ) {
               if ( sprite.center.x == me.center.x ) {
                    sprite.xIncrement = 0;
               }
               else {
                    sprite.xIncrement = ( sprite.center.x > me.center.x ) ? 1 : -1;
               }
               if ( sprite.center.y == me.center.y ) {
                    sprite.yIncrement = 0;
               }
               else {
                    sprite.yIncrement = ( sprite.center.y > me.center.y ) ? 1 : -1;
               }
        }

                
        function moveSprite( sprite ) {
           if ( sprite.schizoInstinct > 0 && Math.random() < sprite.schizoInstinct ) {
               changePersonality( sprite ) ;
           }
           if ( sprite.wiggleFrequency > 0 && Math.random() < sprite.wiggleFrequency ) {
                changeDirection( sprite, (Math.random() * sprite.wiggleRadians) - (sprite.wiggleRadians/2.0) );
           }
           if ( sprite.killerInstinct > 0 && Math.random() < sprite.killerInstinct ) {
               useKillerInstinct( sprite );
           }
           if ( sprite.scaredInstinct > 0 && Math.random() < sprite.scaredInstinct ) {
               useScaredInstinct( sprite );
           }
           sprite.center.x = sprite.center.x + sprite.xIncrement ;
           sprite.center.y = sprite.center.y + sprite.yIncrement ;
           checkBounds( sprite, chaseContainer );
           moveSpriteImage( sprite );
        }
        
        function moveSpriteImage( sprite ) {
           sprite.object.style.left = Math.round( sprite.center.x ) + "px";
           sprite.object.style.top = Math.round( sprite.center.y ) + "px";
        }
        
        function moveObjects( repeat ) {
            if ( repeat ) {
                timerId = setTimeout( "moveObjects( true )",  timerDelay );
            }
            var meMovementLine = new Line( me.center, 
                                           new Point( me.center.x + me.xIncrement,
                                                      me.center.y + me.yIncrement ) );
            for ( var i=0; i<bugSprites.length; i++ ) {
               var bug = bugSprites[i];
               var bugCenter = bug.center ;
               moveSprite( bug );
               if ( intersects( meMovementLine, 
                                new Line( bugCenter, new Point( bugCenter.x + bug.xIncrement, bugCenter.y + bug.yIncrement ) ) ) ) {
                    try {
                        onhit( bug );
                    }
                    catch ( e ) {
                    }
               }  
            }
            moveSprite( me );
        }
        
        function intersects( line1, line2 ){
            return ( (Math.round( line1.point2.x / 40 ) == Math.round( line2.point2.x / 40 ) ) &&
                     (Math.round( line1.point2.y / 40 ) == Math.round( line2.point2.y / 40 ) ) );
            /***
            var rise;
            var run;
            
            // slope = rise over run
            rise = (line1.point1.y  - line1.point2.y)
            run =  (line1.point1.x  - line1.point2.x); 
            var m1 =  rise / run ;

            rise = (line2.point1.y  - line2.point2.y)
            run =  (line2.point1.x  - line2.point2.x); 
            var m2 =  rise / run ;
            
            // y = mx + b, so b = y - mx;
            var b1 = line1.point1.y - m1 * line1.point1.x ;
            var b2 = line2.point1.y - m2 * line2.point1.x ;

            if ( m1 == m2 ) {
                // same slopes (don't let division by zero happen)
                if ( b1 != b2 ) {
                    // lines are parallel but not overlapping
                    return false ;
                }
                else {
                    // see if these line segments overlap
                    return false ;
                }
            }
            else {
                x = (b2 - b1) / (m1 - m2);
                y = m1 * x + b1;
                
                // x,y is where the lines intersect, now check to see if 
                // this point lies on both line segments
                if ( Math.min( line1.point1.y, line1.point2.y ) <= y <= Math.max( line1.point1.y, line1.point2.y ) &&
                     Math.min( line1.point1.x, line1.point2.x ) <= x <= Math.max( line1.point1.x, line1.point2.x ) &&
                     Math.min( line2.point1.y, line2.point2.y ) <= y <= Math.max( line2.point1.y, line2.point2.y ) &&
                     Math.min( line2.point1.x, line2.point2.x ) <= x <= Math.max( line2.point1.x, line2.point2.x ) ) {
                     return true ;
                }
                else {
                     return false ;
                }
            }
            ***/
        }
        
        
        function removeSprite( sprite ) {
            // remove the image from the screen
            sprite.object.parentNode.removeChild( sprite.object );
            
            // remove the sprite from our array
            var i;
            for ( i=0; i<bugSprites.length; i++ ) {
                if ( bugSprites[i] == sprite ) {
                     bugSprites.splice( i, 1 );
                }
            }
        }

       var numCorrect = 0;
       var hits = 0;
       
       function onhit( bug ) {
           hits++;
           if ( bug == problem.correctAnswer.bug ) {
                 onCorrectAnswer( bug );
           }
           else {
                 onIncorrectAnswer( bug );
           }
       }


       function onCorrectAnswer( bug ) {
             numCorrect++ ;

             removeSprite( bug );
             scareAnswers();
             incrementScore( 100 + 10 * gameLevel ) ;
             setTimeout( "hideIncorrectAnswers()", 500 );
             markCorrectAnswer();
             

             // move bug far away, so we don't hit him right away
             if ( bug.center.y < chaseContainer.clientHeight/2 ) {
                  bug.center.y = chaseContainer.clientHeight - bug.object.height ;
             } 
             else {
                  bug.center.y = bug.object.clientHeight / 2;
             }
             moveSpriteImage( bug );
             
             setTimeout( "startNextProblem()",  2000 );
 
             if ( onAteBug ) {
                onAteBug();
             } 
       }
       
       function markCorrectAnswer() {
           var answerDiv = document.getElementById( 'answer' + problem.correctAnswer.order ) ;
           answerDiv.className = answerDiv.className + ' correctAnswer' ; 
       }
       
       function markIncorrectAnswer( ansNum ) {
           var answer = document.getElementById( 'answer' + ansNum );
           answer.style.textDecoration = 'line-through';
           answer.parentNode.style.backgroundColor = '#bbbbbb';
       }

       function incrementScore( value ) {
           score += value ;
           if ( score < 0 ) {
               score = 0;
           }
           scoreDiv.innerHTML = score ;
       }
       
       function hideIncorrectAnswers() {
           var i;
           for ( i=0; i<problem.incorrectAnswers.length; i++ ) {
               var ansNum = problem.incorrectAnswers[i].order ;
               var answer = document.getElementById( 'answer' + ansNum );
               answer.parentNode.style.display = 'none' ;
           }
       }

       function onIncorrectAnswer( bug ) {
           var badBugIndex = findBadBug( bug );
           debug( 'incorrect ' + badBugIndex );
           if ( badBugIndex >= 0 ) {
               incrementScore( -50 ) ;       
               // strike out wrong answer
               markIncorrectAnswer( problem.incorrectAnswers[badBugIndex].order );
               removeSprite( bug );
               me.object.src = 'images/sadme.gif';
               paralyzeMe( 800 );
               
               if ( livesLeft == 0 ) {
                   endGame();
               }
               else {
                   setLivesLeft( livesLeft - 1 );
               }
               
           }
           else {
                 if ( !paralyzed ) {
                      incrementScore( -3 );
                 }
                 // must have just hit a non problem bug
                 paralyzeMe( 100 );
           }
       }

       
       function paralyzeMe( paralyzeTime ) {
             paralyzed = true ;
             me.velocity = 0;
             userUp = userDown = userLeft = userRight = false ;
             recalcMovement( me );

             setTimeout( "unparalyzeMe()", paralyzeTime );
       }
       
              
       function unparalyzeMe() {
           paralyzed = false ;
           me.object.src = 'images/me.gif';
       }
       
       function scareAnswers() {
           var i=0;
           for ( i=0; i<answerSprites.length; i++ ) {
               var answerSprite = answerSprites[i];
               answerSprite.killerInstinct = 0.0;
               answerSprite.scaredInstinct = 1.0;
               answerSprite.schizoInstinct = 0.0;
           }       
       }
       

       function findBadBug( bug ) {
            var i=0;
            for ( i=0; i<problem.incorrectAnswers.length; i++ ) {
                if ( problem.incorrectAnswers[i].bug == bug ) {
                     return i;
                }
            }
            return -1;
       }       
