Starting Spacewar

A chat about TDD inspires me to start the project today. I’ve written this program before, even once in Codea. Starting over, I’m going to try to start small, discovering everything, observing what happens, and refactoring good code out of whatever code I initially write.

One interesting question is TDD, and testing in general. How much should we do, and how should we do it? The best sign of insufficient testing are probably surprise bugs, and debugging to find them. Well try to use those clues to improve our practice and our code.

Here’s our first version

-- S3 Spacewar

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)

    -- Do your drawing here
    
    translate(0,0)
    ellipse(0,0,5,5)
    
end

All I did to the default program was add the translate and ellipse. And good news! The program is already wrong!

My circle, which looks like a dot, is at the lower left (0,0). I was thinking that it should be at the center. Also, I got far ahead of myself by using translate, which is a somewhat high-powered feature that lets you draw a figure at any desired position. I should at least have translated to the center. Let’s do that, by hand:

    -- Do your drawing here
    
    translate(WIDTH/2,HEIGHT/2)
    ellipse(0,0,10,10)

I move the drawing point to the middle of the screen. I also increased the size of my circle, mostly because I wondered if it was filled, but also so I could see it better. The running program looks like this now:

Ship

(It has taken me 15 minutes or more to figure out how to paste that picture in here, and I’ll still have to sort out sizing it and getting it in condition to publish. But let’s worry about that later.)

So I have a spaceship, which looks pretty round, in the middle of the screen, not moving. Super, this is a fascinating game so far. Also, I have the whole thing in the Main function, which surely isn’t right. We could certainly imagine a Ship object at this point, or a hierarchy of objects, and so on. Let’s not do that, though we certainly could. You’re up to it, and I probably am, but let’s let the code tell us what to do instead.

Let’s make the ship move across the screen left to right, upward. Basically we’ll just add (1,1) to the position each time through. How to do that? No need to be entirely stupid. Let’s imagine that the translate() moves to the ship position, so the ship still draws at (0,0). That will let us draw a more complex ship as if it is at (0,0) and put it wherever we wish. We can rotate it, too, though rotating our circle may not be too interesting.

So here’s some code that might do the job:

-- S3 Spacewar

function setup()
    pos = vec2(WIDTH/2, HEIGHT/2)
end

function draw()
    background(40, 40, 50)
    strokeWidth(5)
    
    pos = pos + vec2(1,1)
    translate(pos.x,pos.y)
    ellipse(0,0,10,10)
    
end

I’ve deleted all the “helpful” baby-step comments to make the code more readable. (To me, and I hope, to you.) In setup, we define pos to be a 2-d vector pointing to center-screen, and in the draw function, we update pos, translate to that location, and draw our (circular) ship.

This gives us a dot that moves up and to the right until it goes off screen, and leaves us with two problems. First, keep the dot on the screen. Second, saving a movie of the program running so you can see how we’re doing. OK, well, three problems, because pos is a global and it won’t do to keep all the points for all the ships and stuff in globals. This is our first clue that we are going to need a structure of some kind to represent our ship, and it even hints that we’ll need a structure of some kind to keep track of all our objects, ships, bullets, suns, stars, whatever.

Codea will let me make videos, so I’m not going to worry about convincing Byword to link them in, but this will surely cause me to do multiple passes of editing on various computers or something. That’s for later: let’s get the dot to stay on the screen.

-- S3 Spacewar

function setup()
    pos = vec2(WIDTH/2, HEIGHT/2)
end

function draw()
    background(40, 40, 50)
    strokeWidth(5)
    
    pos = clip_to_screen(pos + vec2(1,1))
    translate(pos:unpack())
    ellipse(0,0,10,10)
end

function clip_to_screen(vec)
    return vec2(vec.x%WIDTH, vec.y%HEIGHT)
end

I coded that “by intention”. I wrote the pos = clip_to_screen call, expressing what I wanted to do. Then I built the (trivial) function. This approach, which I learned from Kent Beck, gives me two advantages. First, when I express what I want, it lets me be clear in my mind what I’m up to. Second, when I write it that way, I automatically get some nice modularity: I now have a useful function that will clip any vector to the screen.

Naturally, I knew roughly what I wanted, namely to mod the x and y by WIDTH and HEIGHT respectively. I could have written it in line. I like this way better and offer it as something you might want to try if you don’t already do it.

There’s another little thing there. Notice the translate(pos:unpack()). That used to say pos.x, pos.y. The unpack method is a relatively new Codea feature that makes it a bit more convenient moving between vector functions and functions, like translate, and work in x, y, z. So I thought I’d give it a go.

Sure enough, our “ship” now goes up and to the right and enters back at the lower left, over and over again, just like we wanted. That’ll do for today’s start. We’ll see what we do next when we do it.