GitHub Repo

The original game spawns more and more asteroids. I’m terrible at the game but I guess we need the feature. Just a little afternoon playing around.

The original game started with 4, then in each succeeding round went 6, 8, 10, and 11, sticking at 11. I think they were out of memory for more saucers. I’m pretty sure that eleven saucers is something that I’ll never see: I’m terrible at playing the game.

With our distributed game design, there is a question as to where to keep the information. Let’s start with a look at how WaveMaker works. Ah! It should be easy:

class WaveMaker(var numberToCreate: Int = 4): ISpaceObject, InteractingSpaceObject {
    private val oneShot = OneShot(4.0) { makeWave(it) }
    private var asteroidsMissing = true

    override fun update(deltaTime: Double, trans: Transaction) {}
    override fun callOther(other: InteractingSpaceObject, trans: Transaction) = Unit

    override val subscriptions = Subscriptions (
        beforeInteractions = { asteroidsMissing = true},
        interactWithAsteroid = { _, _ -> asteroidsMissing = false },
        afterInteractions = { if (asteroidsMissing) oneShot.execute(it) }
    )

    private fun makeWave(it: Transaction) {
        for (i in 1..this.numberToCreate) {
            it.add(Asteroid(U.randomEdgePoint()))
        }
    }
}

We never kill the WaveMaker, unlike the hand-off between the Checker/Maker design for the Ship. So we can just adjust the numberToCreate as needed:

    private fun makeWave(it: Transaction) {
        for (i in 1..this.numberToCreate) {
            it.add(Asteroid(U.randomEdgePoint()))
        }
        numberToCreate += 2
        if (numberToCreate > 11 ) numberToCreate = 11
    }

Well, that obviously works but let’s have a test to document the behavior and make sure.

    @Test
    fun `makes 4,6,8,10,11`() {
        val ck = WaveMaker()
        val t1 = Transaction()
        ck.makeWave(t1)
        assertThat(t1.adds.size).isEqualTo(4)
        val t2 = Transaction()
        ck.makeWave(t2)
        assertThat(t2.adds.size).isEqualTo(6)
    }

This is boring and tedious. Let’s imagine a new method in WaveMaker, and test it:

    @Test
    fun `calls for 4,6,8,10,11`() {
        val ck = WaveMaker()
        assertThat(ck.howMany()).isEqualTo(4)
        assertThat(ck.howMany()).isEqualTo(6)
        assertThat(ck.howMany()).isEqualTo(8)
        assertThat(ck.howMany()).isEqualTo(10)
        assertThat(ck.howMany()).isEqualTo(11)
        assertThat(ck.howMany()).isEqualTo(11)
    }

And …

    fun howMany(): Int {
        return numberToCreate.also {
            numberToCreate += 2
            if (numberToCreate > 11) numberToCreate = 11
        }
    }

I rather like Kotlin’s also for things like this where you’d like to return the number and then update it for next time.

I retained the first test because it checks to be reasonably confident that makeWave is actually calling howMany. It’s not proven, mind you. Should we write a test to prove it? No, let’s not.

We’re green and the game is now officially too hard for me. I need an easy mode. Another keystroke?

Anyway, commit: Asteroid waves are now 4,6,8,10,11,11 asteroids.

What else remains to be done? Let’s see:

  1. Small saucer (1000 points) appears after 30,000 points. (I’ll never see that.)
  2. Small saucer shot accuracy improves. (Needs research to see what the original game did.)
  3. Some write-ups say that small asteroids are faster. (My reading of the code says they are not, but might be random in a range.)
  4. Saucer zig-zagging could be a bit more random rather than once a second.
  5. Ship turning seems a bit slow. But will accuracy suffer with a larger rotation step?
  6. Ship acceleration seems sluggish.
  7. Hyperspace return away from edges for visibility.
  8. Allow for non-square windows?
  9. Improve generality of graphics, stroke width vs scale etc.
  10. Eliminate magic numbers, moving to Universe.
  11. Ability to change some settings from keyboard, i.e. make it so that I can win a few points.
  12. Timing could be improved to be more consistent. Different objects do it differently. OneShot might help?

Offhand, that’s all I can think of … except for sound, which I have yet to try but have received a hint on how to do it.

Summary

I just wanted to spend a few minutes with the code. Things are a bit better, with one new feature which I hate. The number of asteroids is too damn high.

It seems to me that the game design concept of independent objects in the mix has held up well. Everything has been pretty easy to do and usually does not involve too much jumping through one’s own orifice. Perhaps the most tricky bit is passing the ScoreKeeper back and forth between ShipChecker and ShipMaker, so that there’s always access to it. It could have been saved in a global or provided by interaction, I suppose, but I thought I’d try preserving it that way and it works nicely enough.

See you next time!