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.
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 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?
Brown University
Owen Ozier, Justin Miller, Laura Wollstadt contributed to this project.