Python Asteroids+Invaders on GitHub

Now for something completely different!

Yesterday, I mused:

I wonder … could we perhaps get some substantial code compression by factoring out differences in display and sound and explosion style and such? Is there some way of really collapsing our current code down? That would be really quite fine, to discover a substantial code reduction at this late date. I don’t know if it’s possible or not … but it’s worth thinking about!

This morning, while waking up, I had an idea for a completely different way that this game might work.

In our present design, the game “emerges” from the individual decisions made by independent game objects, invaders, asteroids, players, shots, whatever. During each game cycle, in addition to opportunities to move and draw themselves, the objects have an opportunity to interact with any other object in the game, broken out by type. So a PlayerShot gets a chance to interact with any other object, including any existing InvaderShot instances, or (via a trick) with any existing Invader. And any existing InvaderShot gets a chance to interact with any existing PlayerShot.

So the PlayerShot gets a chance to see that it is hitting the InvaderShot, and if it is, it destroys itself. And the InvaderShot gets to see that it is hitting a PlayerShot, and, if it is, it destroys itself and causes a small noisy explosion. Between them, they implement the desired game behavior in the situation where a player shot and an invader shot collide.

I am admittedly in love with this design, and find it fascinating how the individual behaviors add up to complex game behavior. And I freely grant that it takes a certain twist of thinking to work out some of the situations that come up in the game, to fit it into this scheme of independent agents just doing their own things.

The inevitable result of this design is that we get a lot of very small objects, mostly with quite specialized behaviors, often somewhat similar and yet simply different. So what if there was a completely different way of working? Something like this …

The Situation

Suppose we were designing the tame of Space Invaders. We might begin to describe what I’ll call “situations”.

  • player shot hits shield
  • player shot hits invader shot (1)
  • player shot hits invader
  • player shot hits saucer
  • player shot hits top of screen
  • invader shot hits player shot (1)
  • invader shot hits player
  • invader shot hits shield
  • invader shot hits bottom line

We might notice some situations that are “the same” (1), just seen from a different point of view. We would probably want to keep track of any of those duplications.

On the face of it, it seems that we could design some kind of game engine that managed our various entities and checked for these interesting situations and dealt with them. We might enhance our list of situations with sublists of things that should happen when the situation arises:

  • player shot hits shield
    • remove player shot
    • damage shield
  • player shot hits invader shot (1)
    • remove player shot
    • remove invader shot
    • make explosion
  • player shot hits invader
    • remove player shot
    • remove invader
    • make explosion
    • accrue score for invader type
  • player shot hits saucer
    • remove player shot
    • remove saucer
    • make explosion
    • accrue mystery score
  • player shot hits top of screen
    • remove player shot
    • make explosion
  • invader shot hits player shot (1)
    • handled elsewhere - make no situation?
  • invader shot hits player
    • remove invader shot
    • remove player
    • explode player
  • invader shot hits shield
    • remove invader shot
    • damage shield
  • invader shot hits bottom line
    • remove invader shot
    • damage bottom line
  • no player on screen
    • delay two seconds
    • activate reserve player or end game …
  • player score exists
    • display score at top of screen
  • game is over
    • display game over
    • run robot player

As we fill out our list, we would discover new situations, like “no player on screen” and new actions. We might try to get all the situations and actions figured out. Certainly we would want to do enough thinking to be reasonably sure that we could implement what we were writing down. And, because we are incrementalists, we would start implementing as soon as we were reasonably certain that we could make it all work.

The Prospects

This “design” seems to have some interesting characteristics, some of them quite different from our current design. The Situation Design seems to take a somewhat more global view of things than our current design with small independent objects. It seems to allow for a different grouping of ideas, although we do see some very similar elementary operations like “remove player shot”.

We might argue that the Situation Design makes it easier for a game designer to define a game, although we can see that the definition still threatens to get fairly long and to be rather redundant. And, at least with this much thinking in, some issues seem difficult. The “delay two seconds” leaps out at me. There’s some tricky timing, with delays built in, that the Situation Design may have difficulty expressing. But there’s always something, isn’t there? We’re software developers and we’re pretty sure we can do what needs to be done.

Some of the design issues seem quite interesting, by which I mean “hmm, I’m not sure how I’d so that”. It’s pretty clear that there will be some kind of “actions”, which look to me like a verb “remove” and an object “player shot”. Hm, and the objects mentioned in any situation are usually objects mentioned in the situation:

  • invader shot hits player
    • remove invader shot
    • remove player
    • explode player

But how might we represent situations? It would be “interesting” if there was a kind of “little language”, a data structure much like the outline we’re showing here, and the game just assessed all the (active?) situations and if they were true, executed the actions. (Possibly there would be a false condition for situations as well. If we can do the one we can do the other.)

You Want It Darker Harder

But we have the game engine that we have, with its Fleets object and the game cycle of update, begin, interact, end, tick, draw, or whatever it is. Could we use a Situation Design under that engine?

And … could we evolve the existing Space Invaders game over to a Situation Design, while embedded in the current overall design?

My answers are yes, we could probably build a Situation Design Space Invaders under the existing engine. And maybe we could evolve the existing design incrementally from independent objects to Situation Design. Maybe.

You know we’re going to try!

Rank Speculation

While I do espouse moving to code as quickly as possible, and even more quickly than that, I also think quite a lot about design, before, during, and after coding. Right now happens to be “before”.

I imagine that a situation will consist of some kind of “check”, like “is hitting”, and one or more objects to be checked. And each check will evaluate to a boolean True/False, and will contain one or two sets of actions, one set to be executed if True, one if False. Perhaps there is a third set to be done in both cases. Save that idea for later. An action will consist of some kind of action verb, like “remove” or “explode”, with zero or more parameters (nouns?) to say what is removed or what explodes or whatever.

The collection of situations will almost certainly be dynamic, either by some kind of “is active” flag on a fixed set of situations, or, more likely, with individual situations being added or removed as the game progresses.

If we think of the situation-action objects as operations in some abstract game machine, there might be an interpreter that, at appropriate intervals, executes each existing situation, which then runs and does whatever it does. In that context, a situation might be a concrete subclass of a superclass Situation, which implements (at least) one method: execute.

How will a situation refer to its arguments? There are at least these possibilities:

  1. The situation is passed the overall universe of objects, a Fleets instance in our case, and it can interrogate the instance to find out whatever it needs to know;
  2. There is some intermediate object that fields interactions from Fleets and executes situations that are interested in that information. (This probably still requires a lookup in Fleets for other parameters.)
  3. A new dispatch cycle could be built underneath Fleets, that partitions the game objects as it needs to. This could even be a wrapper for Fleets. It could replace Fleets, but that seems to violate our arbitrary rule that all the games run on the same high level objects, so a wrapper is probably better.

Doubtless there are variations on these ideas, and perhaps there are other schemes that keep Fleets intact and operating, but support the Situation Design.

For now, I’m just going to let these thoughts perk. I think I know how to do the actions, so they’re not very interesting to me, and I do not as yet quite see what I’d spike for the situations. When I get an idea for situations that I could implement … you’ll be among the first to know!

See you next time!