Hello, loves!

I do the wrong thing, then do the right thing. I tick myself off. I take a break. Tout le monde déteste l’IA.

Added in post:
These articles tell you, as well as I can, what really happens in my programming sessions. This morning went weird.

Original lead-in was this:

I usually have some rough semblance of a plan for these articles. Today I have less than that. Tout le monde déteste l’IA.

There are certainly things to work on, including:

  • Refactor something. There’s always something.
  • Provide a way to get into the debugging view without editing any code.
  • Provide a way to tell the stepper to just go ahead and run the rest of the setup.
  • Provide a way to change zooming from the tiny but see everything mode to a more playable mode.
  • Provide a mini-map, which might mean we don’t need special zooming, just a secret way to illuminate that map.
  • Start working on allocating things in the dungeon, which was the initial purpose for the debug view.
  • Work on something completely new. Encounters might be fun.

Having made that list, I’m sort of inclined to try the mini-map.

Belay that for a moment. I was testing some scaling in the view, to help me decide how to do the mini-map, and I discovered that if we try to step the stepper when it is exhausted, we get an error. Not good.

It appears to me that this is solid and cannot do anything when it’s off the end:

class BuildStepper:
    def __init__(self, table, layout, dungeon ):
        self.table = table
        self.index = 0
        self.layout = layout
        self.dungeon = dungeon

    def step(self, view=None):
        if self._more_to_do():
            self._do_one_step(view)
        return self._more_to_do()

    def _more_to_do(self):
        return self.index < len(self.table)

    def _do_one_step(self, view):
        self.table[self.index](self.layout, self.dungeon)
        if view:
            maker = KeyedSpriteListMaker(self.dungeon)
            maker.update(view)
        self.index += 1

So we’d best look at our D code:

        elif symbol == arcade.key.D:
            more = self.stepper.step(self.view)
            if not more:
                self.dungeon.run()

Right, that will tell the dungeon to run every time through. We shouldn’t oughta do that. We might also want to change Dungeon so that it won’t do anything on subsequent calls to run.

I think we’ll just fix the D command.

        elif symbol == arcade.key.D:
            more = self.stepper.step(self.view)
            if not more and not self.called_run:
                self.dungeon.run()
                self.called_run = True

The problem no longer occurs. Should we protect Dungeon as well? I think we should.

    def run(self):
        if self.running: return
        self.running = True
        self.layout.run(self.pub_sub)
        self.make_initial_announcement()

Why didn’t I write tests for these. Shouldn’t I?

To test the KeyPress code, we might create a method, or perhaps just send it a fake key (and a fake dungeon to check whether it was told twice to run). To test the Dungeon code … wow. We’d have to create a dungeon with layout, …

Maybe it’s not as hard as I thought.

    def test_cannot_run_twice(self):
        layout = CountingLayout()
        dungeon = Dungeon(layout)
        dungeon.run()
        assert layout.count == 1
        dungeon.run()
        assert layout.count == 1

class CountingLayout():
    def __init__(self):
        self.count = 0

    def run(self, ignored):
        self.count += 1

That passes. Dungeon is safe.

Now, for my sins, let’s test the [EXPLETIVE DELETED] KeyPress.

    def test_will_not_run_twice(self):
        fake = FakeDungeon()
        view = DungeonView(fake)
        stepper = BuildStepper([], None, fake)
        keys = KeyPress(stepper, view, fake, None)
        assert fake.runs == 0
        keys.on_key_press(arcade.key.D,0)
        keys.on_key_release(arcade.key.D,0)
        assert fake.runs == 1
        keys.on_key_press(arcade.key.D,0)
        keys.on_key_release(arcade.key.D,0)
        assert fake.runs == 1

Reflection

Right now, I’m feeling resentful and a bit angry. Why? Because I knew I should write those tests, not because of some rule, but because without them, someone might modify that code and break the system. Sometimes a test might just be a rote thing to do. These were more valuable than that, since a crash had occurred because of the multiple calls to run.

Then, while trying to justify my laziness and failure to realize that they weren’t even hard to write, I realized that I could do them, therefore should do them, and so I’m embarrassed to have been so flaky right here in front of you.

I could, of course, readily reorder the lines in the article and make it look like I did the right thing. But I didn’t do the right thing: I only did it so that I wouldn’t think ill of myself.

It was the wonderful Diana Larsen who first said, in my hearing “Don’t should all over yourself”. And she was right. I did less well than I might have. Then I recovered and did better. I should be pleased that I did the right thing. But I kind of feel that I was browbeaten (by myself no less) into doing what I should, what my principles say I will do, when it’s needed and a reasonable cost.

So [EXPLETIVE DELETED] it. I’m going to take a break and return to the original topic, what to do, probably mini-map, later.

Bah! <chuckle> People are weird. Present company included. See you next time!