GameKit

An environment for learning to program

 

How do you get students with no prior exposure excited about computer science?

 

When teaching Computer Science Boot Camp at MIT, Owen Ozier and I had to answer this question.  Our challenge was to teach college-level computer science to 40 bright high school students, most of whom had never programmed before.  Our syllabus combined topics typically found in courses on programming languages, computer graphics, software engineering, and algorithms. 

 

We wanted the students to be excited about computer science like we are, so they would experiment and have questions in mind that theory lectures would answer.  We also wanted to give them the tools to break the abstraction of a computer as an appliance and see it as a tool they could control.  Our solution was to create two programming environments and teach our course relative to these.  Having a friendly, free environment they could use at home let students experiment at their leisure. 

 

Lambda calculus

Our two environments are very different.  The first is a lambda calculus interpreter.  Yes, we taught high school students lambda calculus.  Unfortunately, the few students who already knew how to program in C were very confused.  I hope that by the end of the unit they had learned to think about programming in a different way.  Those who had never programmed before caught on very quickly and could write programs like APPEND, REVERSE, and MERGE-SORT.  We discussed Church numerals and the Y-combinator in class but didn’t use it as an assignment.  This experience convinced me that Scheme is a viable language for introducing students to programming language theory at the high-school level.  While in lambda calculus, we discussed linked lists, recursion, first class functions, and order of growth.

 

GameKit

The second environment is GameKit, which you can download here.  To cover high-level topics like computer graphics, artificial intelligence, user interfaces, event driven programming, and object oriented programming, game programming is a natural motivating example.  Game programming is one of the most difficult software fields, however.  I designed GameKit as a full integrated development environment for new programmers to play with high level game programming concepts without needing to know about C++ or OpenGL.  We prototyped GameKit during the class and performed more work later with two students who expressed continuing interest in games.

 

GameKit consists of an Emacs-like editor, a debugger, and several resource editors.  The debugger has controls like a VCR.  Play and stop buttons launch the interpreter with the current project or stop execution.  A pause button lets the programmer break the program execution to inspect variables and a program trace.  A speed control slider makes the program run slower or faster, so that the trace and variables can be watched as the program executes.

 

The language is minimalist and specially adapted to game programming.  Control flow is imperative, with branching constructs.  I decided against a functional programming style because games, like most simulation, are well suited to imperative programming with mutation.  The world is a series of state bits and every action changes them.

 

I consider GameKit a pure object oriented language—a kind of idealized AI environment.  There is no way for the programmer to bind arbitrary variables.  Classes are instantiated and the resulting objects thrown into the simulation engine.  Only when an event occurs does control return to the program.  Inside an event handler, only the relevant objects are bound to variables.  For example, in a UI event the keyboard and mouse are bound to variables.  In a collision event the two objects that collided are bound.  One exception to this is the notion of “counters”—global variables that accumulate values like the player’s score.

 

The syntax of GameKit is Java-like.  I wanted students to not be scared by C++ and Java syntax when they encountered them in school.  Braces are a convenient blocking operator.  To lessen the confusion where end-braces stack up, I required a comment on the closing brace.  For example, a conditional block begins with “If {“ and ends with “} If”.  I consider C++ semi-colons unnecessary syntax and eliminated them.  Because there are no binding constructs, most of the syntax for variable declaration, argument passing, and types was unnecessary.  Comments are Scheme-style, with a preceding semi-colon.  In fact, the whole language is really just Scheme disguised by a different syntax.

 

The parser and interpreter are designed as an expert system that recognizes common mistakes and explains the problem (e.g. “I think you’re missing a closing brace around line 10 because the program makes no sense to me after that point.”) rather than giving cryptic messages (e.g. “unexpected string literal on line 11”).  GameKit is incomplete and this is one area that is particularly underdeveloped.

 

Below is an implementation of Space Invaders in GameKit.  It includes title and ending screens.  There are about 100 lines of real code, expanded to 230 because of whitespace and comments.  This is written by me as an example; the best student game we had was a platform jumping adventure game that is included in the download.

 

PROGRAM{

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Rules for object interaction

  CLASSES{

 

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;; The Player class.  Player is the object controlled

    ;; by the user.

   

    Player{

      NORMAL{  ; What to do normally

        ; If the Left arrow key is pressed, then move

        ; the player graphic left by ten grid points.

        If Keyboard.left = "true" {

          MoveLeft Me 10

        }If

 

        ; If the Right arrow key is pressed, then move

        ; the player graphic right by ten grid points.

        If Keyboard.right = "true" {

          MoveRight Me 10

        }If

 

        ; To keep too many missiles from getting fired at once, we

        ; will force the player to fire only 1 every 20 frames

        If counter$missile-delay = 0{

          ; If the space bar is pressed, fire a missile

          If Keyboard.space = "true" {

            Create Missile "Missile" "Missile" 90 3 Me.x Me.y 0

            set counter$missile-delay 12

          }If

        }else{

          ;; Count down to missile firing

          change counter$missile-delay -1

        }if

      }NORMAL

 

      COLLISION{  ; What to do if I hit a sprite

        If It.class = Alien {

          destroy It                ; Destroy whatever got hit

          Change Counter$armor -10  ; Take 10 away from the player's armor

        }If

      }COLLISION

 

      MAPCHANGE{  ; What to do if the map type underneath me changes

        ; Green squares are used on the map to mark the edges of

        ; where the player can move.  If the player moved onto

        ; a green square, move them back.

        If Me.green = "true" {

          Unmove Me

        }If

      }MAPCHANGE

     

    }Player

 

   

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;; The Missile class.  Missiles are fired by the user at the

    ;; invaders.

    Missile{

      NORMAL{

        ; Normally, just move the missile

        Move Me

      }NORMAL

 

      COLLISION{

        If It.class = Alien {

          ; If the missile collides with an alien, destroy

          ; both the alien and the missile, then give the

          ; player 10 points

          destroy Me

          destroy It

          Change Counter$Points +10

        }If

      }COLLISION

 

      MAPCHANGE{

        If Me.red = "true" {

          destroy Me

        }If

      }MAPCHANGE

 

      CREATE{

        MoveForward Me 10 ; move forward immediately

      }CREATE

    }Missile

 

    Alien{

      CREATE{

       

      }CREATE

      NORMAL{

        MoveForward Me 1

        If Counter$time = 10 {

          ChangeDirection Me 180  ; turn around every 10 frames

          MoveDown Me 1

        }If

      }NORMAL

 

      MAPCHANGE{

        If Me.red = "true" {

          SetState Die ; game over if they get to the bottom

        }If

      }MAPCHANGE

      collision{

        If It.class = missile {

          ; If the missile collides with an alien, destroy

          ; both the alien and the missile, then give the

          ; player 10 points

          destroy Me

          destroy It

          Change Counter$Points +10

        }If

      }collision

     

      CREATE{

        Change counter$AliensLeft +1

      }CREATE

 

      DESTROY{

        Change counter$AliensLeft -1

      }DESTROY

 

    }Alien

 

  }CLASSES

 

 

  STATES{

 

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;; The Begin state is where the program starts.  We put all of the

    ;; code that initializes the game here.

    Begin{

      INITIALIZE{

        Set counter$Lives 3

       

        SetState Title

      }INITIALIZE

    }Begin

 

    Title{

      Initialize{

        SetBackground game "title.jpg"

      }Initialize

     

      Loop{

        if keyboard.anykey = "true"{

          ; Play the game

          SetState MainGame

        }if

      }Loop

     

    }Title

   

    MainGame{

 

      INITIALIZE{

        DestroyAll     ; destroy any old sprites

 

        SetBackground game "space.bmp"

        SetMap game "Invaders.map"

 

        ; initialize counters

        Set    counter$AliensLeft 0 ; counts up using alien create

        Set    counter$Armor      100

        Set    counter$Points     0

        Set    counter$Time       5

        Set    counter$Missile-Delay 0

 

        Create Alien "Able"   "alien1" 0 .1  -50 80 0

        Create Alien "Basil"  "alien1" 0 .1  50 80  0

        Create Alien "Able"   "alien1" 0 .1  -15 80 0

        Create Alien "Basil"  "alien1" 0 .1  15 80  0

        Create Alien "Carry"  "alien1" 180 .1 -50 50 0

        Create Alien "Daniel" "alien1" 180 .1 50 50  0

        Create Alien "Carry"  "alien1" 180 .1 -15 50 0

        Create Alien "Daniel" "alien1" 180 .1 15 50  0

 

        Create Player "Player" "ship" 0 1 0 -80 0

      }INITIALIZE

 

      LOOP{

        Change counter$Time -1

 

        If counter$time < 0 {

          Set counter$Time 10

        }If

 

        If counter$armor < 0 {   ; got hit too many times

          SetState Die

        }If

 

        If counter$AliensLeft = 0{

          SetState Win

        }If

      }LOOP

 

    }MainGame

 

   

    Die{

      INITIALIZE{

        Change counter$Lives -1

 

        If counter$Lives = 0 {

          SetState End

        }else{

          ;      ShowMessage "Try again!" "again.bmp"

          SetState MainGame

        }If

      }INITIALIZE

    }Die

 

    Win{

      INITIALIZE{

        ;    ShowMessage "You Win!" "win.bmp"

        SetState End

      }INITIALIZE

    }WIN

 

    GameOver{

      INITIALIZE{

        ;  ShowMessage "Game Over!" "GameOver.bmp"

        DestroyAll                       ; good habit

        SetState End

        

      }INITIALIZE

      ; game over

    }GameOver

 

  }STATES

 

}PROGRAM

 

I think the design of radical domain-specific programming languages is fascinating and important research.  I think it is a reasonable goal to have a non-programming high school student learn to program in 2 weeks and implement a classic game like Space Invaders, Asteroids, Mario, or Pong in 100 lines of code.  Exploring these kinds of languages isn’t just for teaching.  If a new language can empower a non-programmer to make a simple game, why are experienced programmers stuck with languages that waste all of their time on common, obvious tasks, and not similarly empowered to focus on their applications?

 

Morgan McGuire

Brown University

 

Owen Ozier, Justin Miller, Laura Wollstadt contributed to this project.