Time to set a tentative direction and move that way. I am daunted before even starting. No code today.

Something about the world is making me feel that nothing is worth-while. I think I know how Nero felt. Anyway, sometimes you’ve just gotta go heads down and do what you can. Today is one of those days.

The “big objectives” that I have in mind include

  • Separate construction from execution;
  • Reduce size of large objects like GameRunner, Dungeon, and Tile;
  • (Maybe) move toward a separate Making App.

I’d also like to improve my separate apps that provide information about Codea projects, such as the ones that gave us the class-methods list we looked at yesterday.

Connections

One key component of code quality is coupling, the way that objects are connected together, who knows whom. Let’s look at a few of our key objects to see what they know. I’ll get this by reviewing the code of each, although we might also consider a tool that displays all the elements of the table that makes up each object. That would need to be done at run time, but could be useful. We’ll see. For now, let’s just reveiw the code.

  • Main (mostly setup)
    • Sprites
    • OperatingMode
    • Bus
    • GameRunner
    • Inventory
  • GameRunner
    • buttons
    • rooms
    • dungeon
    • monsters
    • player
    • ref: DungeonContents (a global?)
    • ref: Announcer
    • ref: Decor
    • draw: knows many things
  • GameRunner provides:
    • getDungeon
    • getPlayer
    • getTile(position)
    • hasMonsterNearPlayer
    • initiateCombatBetween(att,def)
    • mediates whose turn it is
    • mediates keyPress
    • produces path to WayDown
    • can position player
    • initial placement: Darkness, Lever, NPC, Player, WayDown, Spikes
    • direction to Player
    • manhattan distance to Player
    • determines if player turn is complete
    • return random room tile
    • remove a monster
    • reset visibility
    • scale the maps
  • Dungeon
    • pointer to Runner
    • knows dungeon dimensions
    • contains the rooms collection (GameRunner also?)
    • contains Map of tiles
    • return available tiles:
      • closer to player
      • further from player
      • closer/further from given tile
      • at given distance from player / tile
    • define corridors
    • determine what cells can be doors
    • manhattan distance between tiles/player
    • return or query neighbors of a tile
    • find random room or hallway tile
      • avoiding a room
      • avoiding an area

Wow. I’m already even more daunted than when I started out.

Let me get a bit more abstract (and possibly somewhat inaccurate) with a description.

GameRunner includes a lot of code for building the dungeon, and it often delegates to Dungeon to deal with the tiles. Dungeon keeps the tiles stored in a Map, which we created a while back to help us with our (stalled) implementation of Hex maps. Dungeon mostly returns useful information to requestors but does include room layout and corridor layout.

It seems reasonable to begin to separate out creating the dungeon from running the game. We can probably devise a sensible way to do that incrementally as time permits.

All those methods in Dungeon that provide tiles of interest and distances of interest all come down to one method:

function Dungeon:availableTilesAtDistanceFromTile(startingTile, targetTile, desiredDistance)
    local atCorrectDistance = function(tile)
        return targetTile:distanceFrom(tile) == desiredDistance
    end
    local tileIsFloor = function(tile)
        return tile:isFloor()
    end
    local neighbors = startingTile:reachableTilesInMap(self.map)
    local correctDistance = ar.filter(neighbors, atCorrectDistance)
    local floorTiles = ar.filter(correctDistance, tileIsFloor)
    return #floorTiles > 0 and floorTiles or { startingTile }
end

The reachableTilesInMap defers to the current Map, which returns the neighboring tiles to a given tile, depending on whether the map is hex or cartesian.

Starting Over

These are the moments that make a person want to start over. It’s not that I see a better solution: it’s just that I can’t see enough of this design to get a sense of how to make it better. Of course, even if I did start over, I wouldn’t have a better idea of how to manage the thing when it began to get this large. I don’t even have a good set of mistakes to avoid.

I think it’s just that I know how easy things are at the beginning, and I want things to be easy again. I do have an advantage in today’s situation, maybe even more than one.

The big advantage is that this is article number 255 in the dungeon series, so I can be pretty sure that if I did decide to rewrite the program, I’d have more than a working year to get back to where we are now. That’s a lot and I don’t want to do it. It would be easy to guess and say “We can do it over in only 30 days” or something, but in this case I’ve got facts, and the facts suggest the job would be huge.

A smaller advantage, but one that I am going to lean on is that I know1 that having lots of small objects makes for a better design than having a few huge objects, and small objects are easy to make, so it should be that we can improve this program step by step … inch by inch … with most of the work having that easy, smooth feeling that we’d like to reclaim2.

But there’s a bigger question here …

But Why?

I started this exploration yesterday because I had been wondering what I would do differently were I to start over with this program. At this writing, Lo! these many hours later, I think that I could only say a few rather general things:

  • Try to keep building and running separate;
  • Create many more small objects;
  • Start with publish-subscribe and use it a lot.

That’s about it. Oh and one more:

  • Don’t screw up.

Always good advice, right?

So here we are, with this big program out there in the market, or, worse, not ready for market, and we have a sense of uneasiness about its design. We’re worried. When we think about making the design better, we can’t see readily how to do it. What should we do?

If this is a product, we should probably just get back to work, with a few ideas about directions we’d like to move, such as separating building from running, or pushing methods down from large objects into smaller ones. We might even have a few somewhat specific notions of what we’d change:

  • Work toward a DungeonMaker component;
  • See whether we can avoid calling upward to Dungeon or GameRunner, when we encounter calls like that;

Then we turn back to building the capabilities that the game needs, and as we pass through code that needs improvement, we improve it. If a direction becomes clear, we lean that way. We leave each campground a bit cleaner than we find it.

Now this isn’t a product. Maybe it should be? Maybe I should treat it more like a product? Or will I learn more, and have more to offer, by undertaking larger choices?

I’m glad I asked that …

In “the real world”, as we like to call it, we don’t generally get a chance to redo some messy code. Much more often, almost always, we have to live with it. If we’re lucky and skilled, we can improve it a bit as we go.

So I cannot, in good conscience, do some big refactoring to make Dungeon smaller. It’s probably not even fair to do a series on doing that in small steps. Oh, that would at least show that a big thing can be done in small steps, but maybe the reality is that in “the real world” we’d never get to that big thing.

I can set some “big directions”, if I can see them. But the most accurate way to work on this thing will be to add the features that my product people ask for, and to improve the code that I have to work with, averting my eyes from the tragic plight of code that no longer gets worked on. If it works, and isn’t in the way, it’s good enough.

I’m not glad I asked that …

The answer to the question what I’d do if I were starting over? Well, I have a few candidates:

  1. Mu. Not an answer. Not even a question. Just mu.
  2. I’d screw up in similar but different ways.
  3. I’d probably take on too much and never get done.

And of course a few smaller answers that we’ve talked about above, the things that I think I’d do, but might not do, because I am still only human and fall into the same traps day after day. But sure, I’d try to make smaller objects. I’d try to test more. And so on.

Here’s the thing …

Day in and day out, in our program development work, I think we can do just a few things. Big changes aren’t in the cards. What I want to focus on are just two things:

  1. Try to leave the code a bit better today than it was yesterday.
  2. Try to leave the room a bit smarter today than I was yesterday.

If I can do even one of those things, it’s a good day. If I can do them both, it’s a great day.

Have a great day!



  1. When I say I “know” that, I mean that I, personally, along with the folks who hang out with me, prefer designs with lots of tiny objects. Whether they are objectively better, I can’t say. Whether “objectively better” is even a thing, I can’t say. 

  2. Slowly I turned …