The Fun Part
Hello, loves!
I think this morning we get to do the fun part with our new YieldStepper. Ants??
But first, we need to put in the view-handling part. When stepping the build, we need to update the view on every step. And in the BuildStepper, there’s some special code at the end of things, but maybe we’ll do that differently. To begin, however, I think we need to stick closely to the BuildStepper, which goes like this:
class BuildStepper:
def __init__(self, table, layout, dungeon ):
self.table = table
self.index = 0
self.layout = layout
self.dungeon = dungeon
def run_to_completion(self, view=None):
while self._more_to_do():
self._do_one_step(view)
self._update_view(view)
def maker_single_step(self, view=None):
if self._more_to_do():
self._do_one_step(view)
self._update_view(view)
return self._more_to_do()
def _update_view(self, view):
if view:
sub = self.layout.subregion
self.layout.clear_subregions()
view.update_KSL()
self.layout.subregion = sub
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)
self.index += 1
The YieldStepper, so far, looks like this:
class YieldStepper:
def __init__(self, steps):
self.steps = steps
def run_to_completion(self):
for _ in self.steps:
pass
def maker_single_step(self):
try:
next(self.steps)
return True
except StopIteration:
return False
I think that we may find that we can pass the view as an optional construction parameter, but for now let’s stick to the existing style. Since this code is driving a view, I’m not going to TDD it, as tests of views are difficult, slow, and anyway I don’t want to.
def run_to_completion(self, view=None):
for _ in self.steps:
pass
self._update_view(view)
def maker_single_step(self, view=None):
try:
next(self.steps)
self._update_view(view)
return True
except StopIteration:
return False
def _update_view(self, view):
if view is not None:
sub = self.layout.subregion
self.layout.clear_subregions()
view.update_KSL()
self.layout.subregion = sub
PyCharm points out that we don’t have a layout to talk to. Let’s add that as an optional parameter as well, this time to the constructor.
class YieldStepper:
def __init__(self, steps, layout=None):
self.steps = steps
self.layout = layout
PyCharm is happy, the tests are happy, and I want a save point for what’s next. Commit: adding layout and view to YieldStepper.
So far, all this code is in the test file. We’ll leave it there but now we have to change ‘main’ substantially, to use the new stepper.
To minimize work and change, I use the old methods to draw some rooms:
def yield_table_steps(layout):
make_a_diamond_room(layout, None)
yield
make_a_diamond_room(layout, None)
make_a_cave_room(layout, None)
yield
finish(layout, None)
yield
And I bash main like this:
def main():
layout = DungeonLayout(55, 55)
dungeon = Dungeon(layout)
# stepper = make_build_table(layout, dungeon)
stepper = YieldStepper(yield_table_steps(), layout)
screen_multiplier = 16
screen_width = screen_multiplier*dungeon.max_x
screen_height = screen_multiplier*dungeon.max_y
arcade.Window(screen_width + 8*screen_multiplier, screen_height, 'Caveat Emptor')
view = DungeonView(dungeon)
# for single stepping comment out next TWO lines
stepper.run_to_completion(view) #
dungeon.run()
# for single stepping comment out previous TWO lines
view.run(stepper)
This is enough to find out why it doesn’t work. Forgot layout:
stepper = YieldStepper(yield_table_steps(layout), layout)
There were squiggles, I just didn’t notice them. Now when I run it, the game comes up with an empty no rooms visible dungeon, but the starting message scrolls.
We’ll drop in some prints to see where we’re going.
My second run shows a dungeon that looks as I expect. What happened the first time, I’m rather sure, is that the few rooms I created just didn’t show up in the full view, and I never zoomed out.
Let me try the single stepping. When I comment out the two magic lines that turn on the stepper, I notice that one of them is dungeon(run), which I think was somehow left out of YieldStepper. But we should be able to see things happening step by step anyway.
Stepping works, and the dungeon does run. Ah. I forgot, we built that into the KeyPress code:
elif symbol == arcade.key.D:
more = self.stepper.maker_single_step(self.view)
if not more and not self.called_run:
self.dungeon.run()
self.called_run = True
I think it’s actually working! I expected at least some difficulty with the untested bits but we seem to have found an acorn. Excuse me for a moment while I set up the whole build process from BuildStepper, in the YieldStepper.
I’ll spare you the whole thing, but it goes like this:
def yield_table_steps(layout, dungeon):
bottom_left(layout)
make_a_diamond_room(layout)
make_a_diamond_room(layout)
make_a_cave_room(layout)
make_a_diamond_room(layout)
make_a_diamond_room(layout)
finish(layout)
yield
top_left(layout)
make_a_round_room(layout)
...
populate(layout, dungeon)
yield
add_content(layout, dungeon)
yield
We see already what is nice about this scheme: we can put in as many or as few yield statements as we want. So I elected to draw each section’s rooms in one go, then the connecting paths between them. Let’s have a movie of what happens now.
That is very much exactly what is supposed to happen. With the YieldStepper, we can build up a dungeon definition as above, with no yield statements, and then, if we want to observe some part of the build, we can insert just the ones we want. And, of course, it’s now easier to insert print statements where we may want them, and, for those who like to run the debugger, we could set a breakpoint right in the middle of the build.
The changes to ‘main’ are destructive to the BuildStepper style, and this is working, so I’ll clean out the BuildStepper bits and commit this: converted to YieldStepper.
Summary
So this has gone just as I had thought (and hoped) that it would. The yield approach makes defining a dungeon simpler and more flexible. We can use existing functions, as in the example above, or we could write out more detailed code right in line. (Of course we would soon refactor it back out into useful functions, right? Right?)
We can move yield statements around, adding and subtracting them so that we can run chunks that we don’t want to observe, then insert lots of yields around code that we do want to observe.
I think that practicing using yield with those tests probably made things go more smoothly, and I’m sure that TDDing the basics of YieldStepper was helpful, as it left the stepper in a condition very close to its final form, so that adding the view-related bits was unlikely to break anything.
It went smoothly, the stepping capability is far more flexible and convenient, and best of all let’s face it, doing it with yield is quite nifty. Kind of like real programming.
I commented to my fellow Slackers the other day:
One thing that I don’t always like about small steps is that one rarely gets the pleasure of solving a big complex problem. Of course the art lies in never solving big complex problems, instead turning them into small simple problems. But still, one remembers those few times when one solved the whole shebang in one massive mental effort. If that ever really happened. But seriously, when you’re in the flow of solving something with small steps, one just sees a bit, carves it off, solves it, carves another. Everything is like “oh wow that’s hard”, snip snip snip, “oh wow that was easy”. None of that god-like feeling. I kind of miss it, though my god-like days are probably long in the past anyway.
The amazing thing for me about the small step approach is that the whole solution never really comes into my mind as a “whole”. Instead, the code gets more and more capable, and often nicer and nicer, and it can end up in a very good place, and yet all I really did was make tiny things better. I’m moving in a useful direction, and that does require some decent intuition about what would be better.
You were promised ants.
But I feel like an ant. I go around carrying stuff from place to place and then one day I look up and we have this amazing system around us. But the ant could “know” that there are lots of ants making the system happen. If ants knew things. But here chez Ron, there’s just me. And still, things happen.
This particular step was a bit more than just carrying crumbs from place to place: I had a vision of what the YieldStepper could be, and actually built it with that in mind. Obviously no big deal, it only took a couple of hours.
But it has a different feeling than bit-by-bit code improvement. It’s like I called my shot, three rails, eight ball in the corner, and made it.
I don’t know. It feels good whenever I make progress, but today’s is just a bit more tasty than some days. Not a full on Callooh Callay, but at least a bit of a Yippee!
See you next time!