Assess, Improve, Plan
I think Arcade is in place. Do we like it? Is the code where we want it? What are our longer-term plans?
Skipped tests, darn it!
Oh, right. There was that skipped test. In fact there are two: I don’t even remember what the other one was. Here’s the keyboard one:
class TestHandlers:
@pytest.mark.skip("needs revision")
def test_keydown(self):
fake = FakeDungeon()
view = DungeonView(fake, testing=True)
event = FakeEvent(pygame.K_UP)
view.handle_keydown(event)
assert fake.received == 'north'
view.handle_keyup(event)
...
I think we could do without that. I’m inclined to remove the entire file. If we need keyboard tests later, we can do new ones.
I do hate to remove tests, though. It makes me feel kind of dirty somehow.
Let’s make this work and then maybe let it stick around. OK< the first thing is that with Arcade, we have on_key_press and on_key_release. So let’s change those with a quick multi-cursor edit. I’m afraid to rename, lest it do something weird. In fact, PyCharm won’t rename here. Edit.
The Arcade version wants modifiers. Add , 0) to all the mentions of event). We don’t need an event, we need a key, which is just an int in Arcade. Change all those.
The test looks like this now:
class TestHandlers:
def test_keydown(self):
fake = FakeDungeon()
view = DungeonView(fake, testing=True)
event = arcade.key.UP
view.on_key_press(event, 0)
assert fake.received == 'north'
view.on_key_release(event, 0)
...
And I had to modify DungeonView not to super init when testing, which makes me grumble a bit.
class DungeonView(arcade.View):
def __init__(self, dungeon, testing=False):
if not testing:
super().__init__()
self.dungeon = dungeon
self.key_lock = None
self.shape_list = None
if testing:
return
The test runs green. Now we can save it, avoiding that dirty feeling, even though it’s not really bearing much weight. Commit: revamp test.
What else is skipped?
@pytest.mark.skip('old style')
def test_border_characterization(self):
layout = DungeonLayout(5, 5)
cell = layout.at(3,3)
cell1 = layout.at(2,3)
room_1 = Room([cell, cell1], '1')
layout.add_room(room_1)
cell2 = layout.at(4,3)
layout.add_room(Room([cell2], '2'))
c_33 = layout.at(3,3)
layout.make_borders()
dungeon = Dungeon(layout)
view = RoomView(dungeon, room_1)
borders = view.layout.get_borders(c_33)
assert len(borders) == 4
assert borders["n"] == "wall"
assert borders["e"] == "door"
assert borders["w"] == "open"
assert borders["s"] == "wall"
Well aside from “door” needing to be “partition”, what’s wrong with that? I’ll unskip it and see how it fails. Ah. BorderList isn’t subscriptable that way any more. And we don’t use “n” any more, we use the step increments (0, -1) and the like. We can fix this up, I think.
def test_border_characterization(self):
layout = DungeonLayout(5, 5)
cell = layout.at(3,3)
cell1 = layout.at(2,3)
room_1 = Room([cell, cell1], '1')
layout.add_room(room_1)
cell2 = layout.at(4,3)
layout.add_room(Room([cell2], '2'))
c_33 = layout.at(3,3)
layout.make_borders()
dungeon = Dungeon(layout)
view = RoomView(dungeon, room_1)
borders = view.layout.get_borders(c_33)
assert len(borders) == 4
assert self.get_borders(borders,(0, -1)) == "wall"
assert self.get_borders(borders,(1,0)) == "partition"
assert self.get_borders(borders,(-1,0)) == "open"
assert self.get_borders(borders,(0,1)) == "wall"
def get_borders(self, borders, side):
return next((border.boundary for border in borders if border.side == side))
We are green, and feeling particularly morally good. Commit: revamp test.
Is pygame gone?
I’ll just do a quick search for references to pygame. The only occurrence is in this commented-out code:
def draw_contents(self):
adventurer_cell = self.dungeon.player_cell
cx = adventurer_cell.x*cell_size + cell_size//2
cy = adventurer_cell.y*cell_size + cell_size//2
arcade.draw_circle_filled(cx, cy, cell_size//2, arcade.color.RED)
# x,y = adventurer_cell.x, adventurer_cell.y
# txt = f'({x},{y})'
# font = pygame.font.SysFont("monospace", 20)
# surf = font.render(txt, True, 'white')
# self.screen.blit(surf, (cx, cy - cell_size))
That was there to display Dot’s position above her. We’ll recreate that if we ever want it. Remove, commit: tidying.
Anything to clean up?
We only import arcade in DungeonView, RoomView, main, and TestHandlers. I wonder about main. It just has this …
def main():
...
dungeon.place_player_at(chosen_cell)
window = arcade.Window(screen_width, screen_height, 'Caveat Emptor')
view = DungeonView(dungeon)
view.setup()
window.show_view(view)
arcade.run()
I’d bet that we could find a better place for that, and perhaps we will. I’d like to review some more of the Arcade examples, to see how they arrange things: I don’t have a good sense of overall organization yet. For how, I don’t see anything that needs cleaning up.
In a previous article we spoke briefly about building a “shim”, a little class that belongs to us, that covers the arcade class, so that we’d call a method on our class and inside that class would be the only knowledge of how that thing is done via arcade. That might still be a good idea, and we’ll keep it in mind.
But for now, I think we’re in pretty good shape. I’m calling the replacement of pygame with arcade “done”, and calling the code “good enough for now”. As we add to it, I’m sure we’ll find things to improve.
What’s next?
We have reached the advertised “longer-term ideas” part of the article. I started out calling it “longer-term plans” but since my plans never come to fruition in their original form, “ideas” is closer to correct. Here are some, more or less at random:
-
Arcade allows multiple views in the window. We could learn how those work and use them to lay out a dungeon game with different sections, map, mini-map, scoring, and so on.
-
There are GUI widgets in Arcade, which may come in handy for some of those possible views.
-
Arcade has the notion of a Camera, which can occupy some fraction of the window, and which displays something? with the ability to pan and zoom. If we can figure out how that works, cameras might be useful for a mini-map, and for a zoomed-in view of the map, where actual game play takes place.
-
There are over 800 assets built into Arcade, and more can be added, so we likely have plenty of opportunity to display player and monsters. It seems that some of the character textures include animation frames, so there may be help for that sort of thing.
-
When I did the Codea Lua dungeon program, I wrote around 300 articles about it. That represents a lot of time and a lot of capability. I have other activities in mind, so I’m not sure how far we’ll go with this thing. I guess until it isn’t fun any more.
-
I was thinking about a dungeon layer capability, something like this: using the existing room makers and maybe new ones, allow the dungeon builder to lay out a particular room arrangement, and then “reserve” the cells around it, probably in a rectangular area, then use the existing makers to fill in the rest of the dungeon space in their typically random fashion. This would let the builder set up a final confrontation arrangement, after the player toils through the rest of the dungeon.
-
I am also concerned that while the current dungeons are fun to look at, they may be too large for enjoyable play: perhaps smaller layers are more fun. We’ll see. It’s easy enough to build less.
I think what we’ll do for a while is experiment with things like multiple views and cameras, exploring the Arcade examples, and seeing what we can use in our program. Along the way we might do things like give Dot a more interesting appearance, add some other creatures to the dungeon, and so on.
I hope you’ll join me!