20091227

Toki wo Kakeru Shoujo

congratulations to Toki wo Kakeru Shoujo for being about time travel and not confusing. I gave it a 9 because it had really good animation, it was hilarious and moving at the same time, and because it used time traveling well without sidestepping or ratholing on insane paradoxes.

At around the climax of the movie, both my wife and i were able to point out why something time-related happened, unlike the endings of most anime series, sadly.

20091224

Geometry Wars 2

Geometry Wars 2 is quite possibly my favorite game in the world, and for someone who plays as many games as I do, that's probably saying something. I like it this much because it has that perfect mix of strategy and twitch gaming that I also find attractive in other great games. Oh, and it's also gorgeous and mesmerizing to behold, graphically.

Every year I go to PAX, and without a doubt one of my favorite parts is playing GW2 in one of the console free-play rooms, with the chance of attracting a small crowd. This year I had the added bonus of having some live, stiff competition and meeting some pretty cool guys.

I was going to write a kind of mini-guide, at least for Deadline mode, but then I saw that another site had already done a pretty rad job. But then I thought, I don't see all of the advice I'd say written there (or stated the way I would), so maybe I should write some more anyway. I don't have all the pretty diagrams or videos at my disposal yet, but over time I may add those things.

They put the leaderboards up online, so you can go there and punch in knutaf to see where I sit. As if you care, heh.

General Advice

Before I talk about any of the specific modes, I want to say a few things about the game in general.

Possibly the most important thing that I can mention is seating arrangement. I read somewhere once that the optimal distance to sit away from your TV is about 1.5 times the width of your TV. So on my 50", that would be about 6 feet away. Sure enough, when I sit on the beanbag right in front of the TV, I consistently do much worse than when I sit on the couch back at the wall. I'm pretty sure this has a lot to do with peripheral vision and turning your head. As in, when I'm sitting at the proper distance, things like those jerk arrows coming from the sides of the screen are seen automatically without having to move my eyes or turn my head much.

Another pretty important thing is posture. Yes, like how you sit. Everyone has a different way that they sit to be effective when gaming, and you have to know yours. For instance, I tend to do better when I'm sitting up straight looking forward.

Learn how to "bobble" the right thumbstick. The point is to spread out your shots to cover a wider area. This is absolutely crucial. In many of the modes, you will do this pretty much constantly, so being able to do this without thinking will pay (in points!).

Shooting in the cardinal directions can be very useful if you are flying along a wall and need to shoot something directly in front of or behind you. It's altogether too easy to aim crookedly and have your shots go into the wall.

Enemy Types

I'll only say something about enemies for which I think the strategy isn't immediately obvious. For instance, I'm not going to tell you to watch out for pinwheels and "just shoot them" when they're in your way. You already know that. Also, some of the enemy types, like black holes and gates, will be covered in the context where they're useful.

Orange Arrows

There are a few things to know about these guys. They're actually pretty deadly, even under relatively mundane circumstances. You'll encounter five types of spawns, and you deal with them differently.

Single. A lone arrow that goes across the screen horizontally or vertically. As you're circling the board, it's best to try to take them out when they're moving perpendicularly to you. Once you're going parallel to them, the angle you have to aim at is less forgiving. These guys are tricky because it's easy for them to blindside you as you go by or overtake you from behind.

Half-row. This is half of one of the borders of the screen, all moving horizontally or vertically. If possible, the best way to deal with them is to fly to the empty space of the other half of the row, and fire parallel to them. If you do this right, the entire half-row will die virtually at the same time as it runs into your stream of bullets. See the following diagram for a visual aid.

Full-row. Just like a half row, except taking up a full border of the screen. If it's perpendicular to you, shoot a hole for yourself to go through and then try to deal with it like a half-row. If you're parallel to it, you'll have to shoot sideways to make a hole, then sneak through.

Sparse radial. These spawn around your ship in a ring, either pointing inwards or outwards. If they're pointing inwards, fly out through a hole between the "spokes" and shoot backwards. If you time it right and aim right, your shots will hit all of the arrows as they converge in the center of the circle. If they're pointing outwards, I don't really have a good suggestion.

Dense radial. Same as sparse radial, but packed too tightly to just fly out between the spokes. The technique is similar, though. Shoot a hole in one direction and fly straight out that way. Your aim and precision need to be good for this. Shooting in one of the cardinal directions might serve you well.

A video demonstration, in case you weren't impressed by my hand-made graphics.

Purple pods

You know what I'm talking about, right? The diamonds that break up into 3 little babies. Did you know these are the reason you see many players circle the playing field counter-clockwise? In Geometry Wars 1, the babies from purples only circled counter-clockwise, so if you were also flying in that direction, you had an easier time popping one and sneaking by the babies. Actually, I can't remember if it's that the babies went clockwise, and therefore circled into your stream of shots once they were free...

In any case, in GW2, the babies' direction is random. Nevertheless, your tactic for destroying them is similar. You fire into the purple and keep shooting, possibly bobbling the stick back and forth a bit. If all goes well, your follow-up shots will automatically take out the babies.

Snakes

They fixed the obnoxious bug (it had to be a bug) from the first game where snakes in the process of spawning in could kill you before they were fully spawned in. Not that you care, if you didn't play the first one.

If possible, shoot them as they're spawning in. They always spawn as a speck, and at this point, the head is vulnerable and an easy target. Once they start actually moving around, tracking the head can be hindered by their own body's occlusion or other snakes.

Green Assassins

The best way to deal with a mob of these guys, aside from running through a gate or triggering a black hole, is to trap them against the wall. If you narrow the angle of the stream of shots until you are shooting directly along the wall, normally you can halt their dodging instinct.

The principle on which this works is that the greenies move in the direction away from shots that are coming towards them. However, if you shoot exactly at them, they can't pick a direction to dodge, and just take it. The wall technique basically uses the wall as a guide to shoot along a straight line directly at them.

If you have a really huge swarm of greenies, there's a special way you can take them out, but it isn't generally applicable. It is fun to watch, though.

Deadline

Deadline is hands down my favorite mode. I love it because in a very tangible way, the game is adapting to you in real time. When you do well, the game "rewards" you with more and harder enemies. When you do badly, it punishes you by taking them away.

If you've taken the GRE computer-adaptive test, you may know what I'm talking about. It's taken on a computer, and it keeps track of whether you're getting questions right or wrong, and what kinds. If you rock, you'll keep getting harder and harder questions (which are worth more points); if you suck, you'll end up with weaksauce questions that don't help your score all that much. It's super stressful to keep thinking about whether each question was easier or harder than the previous one. Wow, what a tangent...

In Deadline, the faster you kill enemies, the faster enemies spawn. One way I think about it is that the game spawns a set of enemies all at the same time. Then it counts down some timer in its head till the next spawn. If you're fast enough to kill the entire spawn within that time, the next spawn will have more enemies than it would normally have. Not only that, but the next spawn will occur pretty much as soon as you've cleared the spawn before it, which buys you precious seconds. Apparently how fast and how many enemies are spawning is referred to as your "rank."

What's that? I didn't explain the premise? Woops. The goal in Deadline is to score the most points in 3 minutes as you can. You have some bombs, and infinite lives. The latter makes it a good mode for practicing, but if you're really going for a high score, let's not kid ourselves; if you die more than twice, you probably won't clear 15 million.

Dying is bad for two reasons: firstly, it will take several seconds to get lots of enemies back on the screen; and secondly, it lowers your rank. It actually feels a little more subtle. Let's consider your rank like a "level" in a typical RPG. You get some number of experience points, and once you cross some threshold, you level up, and at that instant, all of your stats raise. I think the rank here is the same way--a discrete function rather than a continuous one. So if you die once, you go back to the start of your current rank. You may not see too much of a difference in spawn rate. But if you die again too soon, you'll lose a whole rank, which will take time to gain back.

Tips and Strategies

The Robot Panic site does a reasonably good job with their tactics, so that's a good read. They mention 3 phases. At first, you want to kill enemies as quickly as possible. The way I think about it, this proves to the game that you've got the chops to take on more enemies. It accelerates the spawn rate. Eventually, you have a good number of enemies spawning regularly, so you want to make sure to suck up all the geoms possible, to raise your multiplier. Finally, when it's madness all over the place, you need to kill the mob of dudes following you using black holes and gates, which give a 5x multiplier to those kills.

The general method of playing this is to circle around the outer wall, shooting ahead or sometimes inwards.

Black Holes

There was a kind of switch that flipped in my head at one point, when I stopped considering black holes to be a bad thing (because they're a trap waiting to happen, and if they blow, they can spawn enemies that are faster than you are) and started thinking of them as a useful tool for clearing out enemies following me.

You'll want to get good at two skills here. The first, I like to call the "drive-by." The idea is to fly really close to the side of a black hole and pop in enough bullets in a split second to blow it up immediately. In particular, it's important that it blow up before it sucks in any enemies. Once it sucks something in, it starts to get out of control, and with it goes your ability to blow it up without being caught in the midst of flotsam on the way in.

The second technique is taking it to the next level: fly to the "far" side of it before doing the drive-by. When I say "far" side, I of course mean the side farthest from the huge mob of enemies following you. By doing this, more of the blast will take out the mob, giving you that delicious 5x boost.

I stole this diagram directy from the Robot Panic post because it's just that well done.

Gates

These are the other source of big money in Deadline. Dealing with these effectively is just as tricky as dealing with black holes. I have two related pieces of advice regarding these, and they have to do with how you go through the gate (duh).

First, what not to do. If you can help it, don't fly straight through the gate when you're approaching it perpendicularly. The way a gate operates is that the two orange end-caps are the explosive devices. They explode, taking out anything in their blast radius. Again, remember that you're followed by a mob. The closer you can get one of the end-caps to your mob when it explodes, the more 5x points you'll get.

So if you're approaching perpendicularly, fly around to the far side of the gate and run through in reverse. If you're picturing this in your head, you may realize that the mob has followed you around the side, and is more or less positioned directly on top of one end-cap. Mega points!

If you're approaching parallel to the gate, don't just pull a hard left or right turn through it. Snake far to either side and then pull through, which should position more of the mob over and end-cap.

I had a very important realization the other day, that if you have greenies following you, and their turning radius makes them able to intercept you in one of these circling or snaking maneuvers, you can briefly fire shots at them to ward them off of your path. For me at this point, it's a bit of luck, but it seems to help.

There may be some strategy associated with bouncing shots off of the gates, which is supposed to attract a bonus of its own, but I don't know how big the bonus is, and I've never found that I could really be successful while simultaneously evading and aiming at a gate.

Bombs

In all of the videos I've seen for Deadline, the players use at least one bomb, but only to get huge amounts of geoms. Your first two choices for killing a mob ought to be black holes and gates, but barring that, if it's been too long since the game has spawned one of those and your mob is large, it may be worth your while to bomb them and suck up all the geoms. Remember, bombs don't give you points. Also remember, it will take a couple seconds after a bomb to bring up the momentum of spawning again. Precious seconds!

Next time I'll write about some other mode, like Sequence. I finally got the Smile achievement, so I actually have specific advice about some of the levels.

20091211

nathan myhrvold and cooking with science

I watched a ridiculously awesome talk last night. nathan myhrvold, who founded microsoft research and studied with stephen hawking, among other things, is writing a cookbook that explains the scientific processes behind how food cooks. The idea is that by educating cooks about some of the physics and chemistry involved, they are more empowered not only to employ conventional cooking methods more accurately, but also attempt new ways of cooking that can only work with understanding of how things work.

You can find the full talk here. It's an hour long, including questions, and also includes a live demonstration of making ice cream without milk but with liquid nitrogen, and some footage from a ridiculously nice high-speed camera. It's both entertaining and informative, especially if you have any interest at all in physics, biology, chemistry, or computers.

btw, when the cookbook comes out, I am going to purchase it at any cost.

20091208

license pro

just saw a license plate that reads "UNIXPRO". and even better, it had a frame that read, "real programmers use vi". you know i'm all about that.

perhaps ironically, there's a high chance that person works at microsoft.

edit: how embarrassing. I didn't get the URL for the vim site correct

20091202

cowbirds

my wife introduced me to cowbirds in love. it has a stupid name, but > 50% of the comics make me laugh aloud. in a world where most of the comics at most make my mouth twitch with amusement or may elicit a chuckle or chortle, this is awesome

I especially like how this one and the two that follow it are set up so that the third in the series is itself a punchline to the first two.

20091124

gkc

I started reading the hard-bound copy of gunnerkrigg court today that we purchased from the dude who does it. it's pretty rad. I especially like all the mythology and classics references.

my latin is getting rustier by the day, but i think "ora lege relege labora et invenies" means something like "speak, read, study, work, and you will discover"

when this book is done with (already halfway through after less than an hour) i'll probably continue reading on the site, or maybe even order the next one. i think there's a next one...

20091118

delays!

i'm on the bus now. apparently all the lights went out on the busiest street right outside work, so everything ground to a halt. my bus was delayed for over an hour, while it was pretty windy and a bit rainy out there

what surprised me is just how many people were also delayed. there must have been over a hundred people waiting out there with me, and about 10 special buses (for other routes) arrived while i was waiting. i guess i didn't realize just how many different routes run during this time.

20091104

new netbook

i got my wife a new netbook yesterday. it's (another) acer aspire one. this time it's a D250 rather than a A105 or whatever I have that I'm using on the bus to write this post. I just looked on the bottom, and mine's an AOA-150. It's thinner than mine, has a bigger screen than mine, and seems pretty rad overall. I put win7 and office 2010 on it last night, and it seems to be doing fine. a little jealous

20091023

finding the wrong bugs

Has it ever happened to you that when you are trying to investigate the cause of some particular symptom, you find a bunch of other bugs in the process? This sometimes happens to me. Usually I'm totally baffled by what's going on, and I search through all the code relating to it and end up saying things like, "wow, well that's never going to work," and, "oh, no wonder!" and stuff. And I'll fix these various things I found, but it still won't fix the problem.

and then it'll turn out it was just that i had my inputs wrong.or the bug was in totally some different place. and then i'm sometimes left wondering how that ever worked in the first place. usually I'm annoyed enough to figure out the why of that too.

i get the feeling this is one of those universal kinds of behaviors that all programmers run into. just curious if other people do indeed do this.

20091019

Tenuous Path Connectivity

I'm now taking an algorithms class for my masters program. I had to do a kind of neat homework problem this week, for which I think I actually managed to do a proper proof for. Assuming I did the problem right, this is kind of a big deal for me, because I've alwys been pretty awful at proofs. I thought I'd write up my solution, just for fun.

The Problem

You have an undirected graph that represents paths between various places. Intuitively, there is a concept that two nodes far away from each other have a more tenuous connection - that is, they are susceptible from being isolated from each other.

Given a graph of n nodes, and two nodes, s and t that have a distance greater than n/2, show that there is always some node v (different from s and t) that, if deleted, severs all paths between s and t.

My solution

We received a hint to use a breadth-first-search to try to solve this. A BFS basically starts at some node and makes multiple steps, at each point finding all of the nodes one step farther away from the starting point. Visually, it might look like rippling outwards from some point.

More usefully, when you run a BFS on a graph, one representation that is helpful to work with is that it turns the graph into several "layers", L0 through Ln. L0 is the starting node itself, L1 is all of the immediate neighbors of the starting node, and more generally, Ln is all the nodes n hops away from the start.

A direct application of a BFS doesn't seem likely to find any particular special node to delete. We're also given that the node always exists, so there must be some criterion for determining it. My first guesses were all along the lines of finding a node that minimizes or maximizes some aspect of the connectivity. For example, finding a node in a layer that has the most connections to the next layer. If such a node were deleted, perhaps it would knock out too many connections to support a second path to the finish.

None of these attempts worked. Finally, after drawing a bunch of graphs, I noticed an interesting property: they all had one layer in the BFS that had just one node - a single point of failure. I did several example graphs and wasn't able to construct one with both all layers including more than one node and the start and finish being at minimum distance n/2.

If this property were really true, then all we'd have to do is perform a BFS from the start and look for the first layer in which there is only one node.

Proof - by contradiction

n = number of nodes in the graph
d = distance from s to t = number of layers in the BFS from s to t = (n / 2) + 1

For the contradiction, assume there is such a graph that has two independent paths from s to t, so that if any node in one path is severed, the other still remains. In this graph, every layer L1 through Ld-1 (s is in L0, and t is in Ld) contains 2 nodes.

The total number of nodes in this graph is now:
1 +                            (L0)
2 * (d - 1) +                  (L1 through Ld-1)
1                              (Ld)
= 2 + 2d - 2
= 2d
= 2 ((n / 2) + 1)              (d must be (n / 2) + 1)
= n + 2

Contradiction: in the beginning we stated that there are n nodes in the graph, but in this minimal graph that includes two independent paths, there must be n+2 nodes. So this proves that you can't satisfy the minimum distance constraint, the number of nodes in the graph, and have two or more nodes in each level of the BFS. One constraint will always yield.

I hope this is the right answer, because I feel pretty cool for having come up with it.

Update, 11/5: I got a 10/10 on this problem. Woot.

20091014

Test First, Try Later

As I was implementing aliases, I started getting better about testing things. In a previous post, I mentioned how I found testing my code to be kind of tedious work that I was loath to do. Well, I think I've had a change of heart. Part of it is probably that writing tests for old code that's already been working at some level for ages is drudgery. At work, too, nobody particularly likes that. On the other hand, testing shiny new features can be pretty fun.

This time, for pretty much every checkin that I did, some test update went along with it, usually in the form of adding a test to verify the specific behavior that I changed with that checkin.

One particularly bad bug I fixed was a strange case of reentrancy. Every room has a set of exits, each of which points to another room. It's conventional for rooms to have reciprocal exits, that is, a bidirectional passage between two rooms. In some cases, rooms with these reciprocal exits wouldn't fully load each other, so they'd end up with one way passages. The nature of the fix is a little complicated to explain, but when I thought I had it working, I made tests with reciprocal exits and rooms in a unidirectional loop (room 1 -> room 2 -> room 3 -> room 1) to test those kinds of cycles.

Actually, the main difference in my methodology for this feature was writing automated tests before ad-hoc testing a change. In the past, I'd notice a bug by running the MUD itself, log on, do something and notice something wrong due to some symptom. Then I'd fix the bug and test that I'd fixed it by loading the MUD again and trying the same scenario, noticing whether the symptom was gone. This time instead, I'd write a new test that should catch the bug, then check that it fails beforehand and passes after. Afterwards, of course, I test it out in the real MUD to verify my assumption that the new test reflects the same scenario as the problematic symptom.

It's probably worth saying that I don't do this at work. If I find a bug in one of my areas, sometimes it might get translated into a regression test, but not always. I'm not even sure if that happens half of the time. There are a few factors that contribute to that, chief among them that my product is not nearly as testable. I'll show a few examples of how I tested some of my MUD bugs here, and it is self evident that it's easier to write tests like these, compared to writing automated tests that have to click buttons and load web pages in order to make a bug appear.

Alias processing

One set of tests I wrote were for processing the alias strings. That is, given the input command and arguments and the output alias template, I wrote tests to make sure that all the input arguments got filled into the right places. These are pretty close to unit tests in the traditional sense.

def process_test(strCmd, strArgs, strExpected)
    strActual = AliasCommand.processCommandString(strCmd, strArgs)
    assert_equal(strExpected, strActual, "output of processCommandString")
end # function process_test

def test_1arg_trailer()
    process_test("abc &1 d", "a b", "abc a d b")
end

With most of my tests I use this kind of pattern of having a helper function take care of the common work. In every one of my alias processing tests, the test merely consisted of some setup (strCmd), some input (strArgs), and an expected result.

AliasModCommand

AliasModCommand is the command used by a player to view and modify his aliases in-game. I decided to test these with a series of mock objects that simulate quite a bit of a character actually playing the MUD.

def createManager()
    char = XmlCharacterIO.loadCharacter($td.cd1.name)
    manager = ConnectionManager.new(MockConnection.new())
    manager.enterCharacter(char)
    manager.mainInteractionMode.aliases = []
    return manager
end # function createManager

I load a character from a well-known set of test data, since there must always be a character when dealing with these types of simulations. I create a ConnectionManager, because that gives controls to inject commands into the MUD. The MockConnection is a stubbed out connection object. Normally this has the functionality of performing the network I/O, but in my case all the methods do nothing, so it's basically just a dummy.

def showAliases_test(aliases, expectedText)
    manager = createManager()

    aliases.each() { |pr|
        manager.process("alias #{pr[0]} #{pr[1]}")
        manager.pulse()
    }

    filt = LastLineOutputFilter.new()

    manager.addOutputFilter(filt)

    manager.process("alias")
    manager.pulse()

    assert_equal("Aliases:" + expectedText + "\r\n", filt.prev(), "output of alias command")
end # function showAliases_test

There's a lot going on here in this helper function, but I think it's kind of interesting. After creating the manager, we actually instruct it to process lines of text that add the aliases the test requested. The pulse call causes the manager basically to process the next command.

Now it gets trickier. In an old post, I talked about filters and how they're used to get all of the text that some player sees. Well, I'm using one here, too. The LastLineOutputFilter sees all of the text that a given manager outputs, and stores the last output call - basically a short history of the last things the player would have seen on his screen. I enter a command (in this case "alias") and catch the output to see what it was. Note that here it's the job of the test to pass in the correct expected output of the alias command to verify against. Still, with the amount of work taken care of by the helper function, the tests themselves are pretty concise.

def test_showAliases_simple()
    aliases = [["a", "b"]]
    showAliases_test(aliases, "\r\n  a => b")
end

TC_XmlArray

Implementing XmlArray was a big part of getting aliases done, so of course I wrote some tests to make sure they were working themselves. I could have written these tests using actual XML files, but I thought it would be easier just to embed the XML right into the tests.

def test_load_XmlReadable()
    strXml =
<<HERE
<root>
<arr>
    <e>1</e>
    <e>2</e>
    <e>3</e>
    <e>4</e>
    <e>5</e>
</arr>
</root>
HERE

    doc = REXML::Document.new(strXml)

    classTextArray = XmlArray.newClassWithType('e', Multiplier_XmlReadable)

    things = XmlReader.loadThings([doc.elements['root']], "arr", classTextArray, true)

    assert_equal(1, things.length(), "number of arrays loaded")

    arr = things[0]
    assert_equal(5, arr.length(), "number of elements in array")

    arr.each_index() { |i|
        assert_equal(((i+1) * 3), arr[i].num(), "index #{i} in the array")
    }
end

This illustrates something more useful than just embedding XML in the test. Any old schmuck can do that. Notice that the array is of type Multiplier_XmlReadable. This is a mock class I created that implements XmlReadable about as simply as possible. Its XML representation is simply a number. When it is loaded into an object, it simply multiplies the number that was read by some constant. To the loading code, this looks similar to a bunch of other objects throughout the MUD that are persisted.

Complicated loading tests - delayed load

I used these test objects pretty heavily for some other tests. For example, there is code in loading that makes repeated passes over all of the things being loaded, trying to load them and all their properties over and over until all dependencies have been resolved. If all the things being loaded are simple (no outward dependencies) then none of that iterative code ever gets exercised. In order to write tests targeting those codepaths, I need control over the objects being loaded.

Similar to Multiplier_XmlReadable, I created Multiplier_PartiallyLoadable that gives me some hooks to help here. For the example above, I would like to make certain elements not fully load on the first try. I could do this by really recreating this network of dependencies, but at some point that becomes complicated itself, and I want to do as little debugging of tests as possible.

# in the test
    strXml =
<<HERE
<root>
    <c sets='1'><e>1</e></c>
    <c sets='2'><e>2</e></c>
</root>
HERE

# in Multiplier_PartiallyLoadable
def num=(other)
    if (other.kind_of?(Integer))
        if @setCounter < setThreshold()
            @num = PartiallyLoadable::NotLoaded
            @setCounter = @setCounter + 1
        else
            @num = other * Coefficient
        end
    elsif other == PartiallyLoadable::NotLoaded
        @num = other
    else
        raise "setting num to #{other} of wrong type"
    end
end # function num=

So instead I simulate it with a trick. Multiplier_PartiallyLoadable has a single property that points to num, so the loading system knows to call num= when setting the property to some value. What I do here is fail to actually set the property for the first n times, where n is given by the "sets" attribute in XML.

So in the example above, in the first iteration in loading, the first element would get loaded completely, but the second would need one more pass.

Complicated loading tests - linked load

There are two times at which a thing can be pronounced fully loaded. In the top-level loop, if a thing has all of its properties loaded, then its status is changed to loaded. Or, if loading is in the middle of loading the properties of something, each of those properties that becomes loaded has its status likewise changed -- not in the top-level loop but down in the recursive property loading code.

In and of themselves, neither of these are too difficult, and are already covered above. What is a little harder is the case where one of the properties of some thing (which would be loaded in the property loading code, not the top-level iterations) is itself also in the top-level list of things. This requires some property relationship. For example, when loading a list of rooms, this would arise: all the rooms are in the top-level list and are also in properties of other rooms as exits. I alluded to a bug in this scenario earlier in this post.

# in the test
    strXml =
<<HERE
<root>
    <c sets='1' linked_load='2'><e>1</e></c>
    <c sets='1'><e>2</e></c>
</root>
HERE

# in Multiplier_PartiallyLoadable
def loadingComplete()
    if linkedLoad() != nil
        # ll is set to the actual object associated with the linked_load
        # attribute
        if ll
            ll.loadingComplete()
            ll.isLoadingComplete = true
        end
    end
end # function loadingComplete

This snippet of the code is hopefully fairly concise. When one of these objects becomes loaded (loadingComplete is called exactly once per object), if its XML has indicated the number of another object "linked" to this one, also load that guy.

Error condition tests

I throw (sorry, "raise") exceptions from a few places, so I need tests for those places. Although Test::Unit provides some assertions for raising exceptions, their focus is more on the exception being raised at the expense of the result in the case where an exception (incorrectly) isn't raised. Whenever an exception test fails, the actual outcome involves some data, and I want that as part of the failure message. So I wrote a helper.

def loadtest_bad(el)
    gotException = false

    text = nil
    begin
        al = XmlReader.constructFromElement(el, AliasCommand, true)
    rescue
        gotException = true
    end

    assert(gotException, "should have gotten exception for bad input, but got #{al}")
end # function loadtest_bad

I assert that the test got an exception, and if it doesn't, the data it did get is printed out.

Unit Testing doesn't scale

All this testing is fine, but there's a problem with it, and it's the reason why we don't write that many tests like this at work. Testing individual behaviors like this isn't the most efficient way in many cases, and time is always scarce.

If I were testing something like this at work, I would probably model the area. I'd break each aspect up into its separate pieces and create a matrix of possibilities for each scenario. For instance, when testing the command for manipulating aliases in-game, there are several parameters. Each of these parameters is either present, empty, or has some specific characteristics. Understanding the characteristics that are important is the essence of modeling.

The level of test coverage that I have so far is still in its infancy, so I'm not yet at the point where the remaining bugs to find are going to be found with larger models. I'm still covering the basics.

20091011

ramen riot 1: Unif Tung-I Chah Chiang

It's not really a secret that I like ramen. My usual brand of choice from regular stores is Maruchan, which I find to be noticably superior to top ramen. However, we have a few asian groceries around here, like the 99 Ranch Market (super weird name in my opinion). There we can find huge aisles full of weird types of ramen. I have gotten at least five different varieties before, and they're all a little weirder than the regular stuff from Safeway. I thought it would be amusing to write about them when I experience new ones.

Today's weird ramen is called Unif Tung-I Chah Chiang Flavor. What part of that is the brand? I dunno. Maybe "unif". I'm not sure what a Tung-I is either. Maybe it's korean or japanese for "weird ramen." Supposedly chah chiang is a flavor, but I'm not sure (even after eating it) what flavor it represents. Ah, I just tried to look it up, and it seems this is a chinese brand of ramen.

ramen_tung-i-chah-chiang

The main differentiating factor between normal ramen (e.g. maruchan) and weird ramen is the packets that come with it. While normal ramen usually only has a single flavor packet, the weird ones usually have at least two, and at least one of those is some kind of liquid.

Sure enough, this tung-i ramen came with two packets, one that looked like a traditional flavor packet and one containing an oily liquid that seemed to be the combination of something black and something orange that was viscous and might have had some kind of jellyish pellet thing going.

Another potentially interesting thing to note is that the noodles appeared to be pre-seasoned right out of the packet. I noticed red powder on them.

The directions kind of annoyed me on this one. They went a bit like this:

  1. cook noodles like usual (okay, I can handle that)
  2. be sure not to overcook them, stupid! (er, okay, i can handle that too. more on this later)
  3. Pour out 4/5 of the water into a bowl (sure, that's what I norma--hey what? into a bowl? I usually get rid of it)
  4. mix in the regular looking packet into the bowl (but... okay, whatever you say)
  5. now mix in the weird liquid packet into the noodles still in the pot
  6. hooray! now you have chah chiang noodles and a separate "tasty soup"!

I don't want a tasty soup. I want flavored noodles! Whatever. I tried it their way, found that the "tasty soup" was weak broth since either I had cooked the noodles in my usual amount of water or because they gave me too little flavoring, and dumped out the water.

I did mix the chah chiang stuff in with the noodles, and that yielded roughly what I was used to. Ah, but something else annoying was that I think the noodles were a bit weak. With the normal amount of cooking that I usually do, which is also monitored by me, they got wimpy and overcooked. Go figure.

Verdict

I'd eat them again in an pinch, but I think next time I will use less water, cook the noodles in the broth mix, and be careful to cook the noodles less. I've had a few other kinds of weird ramen, and on a scale of "yuck" to "woot", I give this a "meh".

20091009

XmlArray and Bags

In the previous post, I was talking about the aliases feature, which lets you use short commands to expand to longer ones. One useful feature here is that aliases that you enter are saved along with your preferences, so essentially they're remembered. It turns out that I probably spent about half of the time from this feature on getting loading and saving correctly. It's not that loading and saving things like aliases (basically pairs of strings with no outward dependencies) is difficult. It's more like I am vulnerable to feature creep.

The object that is directly responsible for loading the aliases is called CharacterPreferences. The ConnectionManager, which has the primary responsibility of entering and exiting the character from the world, holds the prefs object and tells it to start loading. So the preferences are stored alongside the character data in the file, but are managed separately since they're the player's settings, not the character's.

Just for example, the data in the file looks like this.

<preferences>
  <prompt>jointface&gt;</prompt>
  <aliases>
    <alias str='greet' cmd='t &1 hello sir, how are you doing?' order='0'/>
    <alias str='gre' cmd='greet' order='1'/>
  </aliases>
</preferences>

You can imagine other types of preferences being stored here eventually, such as color preferences and settings related to verbosity in messages.

The loading system on the MUD is nice at times and maybe too complicated at other times. I wanted alias loading to hook into the loading system in a fairly hands-off way, since these particular objects are pretty simple. Let me review loading briefly to frame the content. This is review from a more complete post from ages ago.

Things on the MUD can be XmlReadable and optionally also PartiallyLoadable. If something is XmlReadable, it means it implements fromXmlTree, whose contract is to take an XML element as input and produce an instance of the object as output. If the object is also PartiallyLoadable, then at this point it may not be fully loaded; certain members might be stubbed out due to unresolved dependencies.

In this partially loaded state, the loading system enumerates a set of "properties" in the member that are added in the ctor. These properties say the XPath needed to get to this property, given the element representing the parent, information about how to construct an instance of this object (in most cases, just its class name, upon which the ctor will be invoked), and instructions on what to do if the sub-element isn't found at all (usually just create a blank new instance of the object).

This partially loaded state is absolutely essential to the process of resolving circular dependencies. While it doesn't support true cycles, it will handle cycles that terminate at some depth by allowing repeated passes over all of the properties in all of the objects that aren't fully loaded.

The ideal case is to have a property on the CharacterPreferences object that points to the "aliases" element within the preferences element, and delegate the loading of multiple aliases to some other object, since there are plenty of situations where you need to load a bunch of objects with the same name and type. Additionally, the specifics of the type of the objects that should be loaded multiple times should be abstracted from the code that does the repeated loading.

Introducing XmlArray

This is where XmlArray comes in. Exactly what it sounds like, this represents an array of things in XML, knows how to load them into a real Ruby array, and doesn't know too much about the objects themselves. Using this, the preferences object can fully delegate the loading of aliases to an XmlArray by creating one that knows to enumerate all the "alias" elements and create an AliasCommand object for each one.

Need some class properties

One interesting limitation I came upon as I started to design this is that the property system is fairly heavily based on the class of the object that a property is reponsible for. For example, check out the property responsible for loading the prompt text from above.

addProp(PersistedProperty.new('prompt', ref(:promptText), String))

I declare that the name of the element needed is "prompt", that it is of type String, and in order to read or write its current value, I access the methods promptText() and promptText=(). String isn't a great example here, but it gives you a feel for the tie to the class.

Likewise, for the XmlArray, when the loading system creates it automatically in response to encountering the property, the class it creates needs to know some information. The information it needs to know is the class of the object it will create for each element and the element name in the array to look for.

The way I solve this is a little bit similar to C++ templates or C# generics. I put in functionality to make XmlArray not really a class itself, but a class generator, where the classes it generates have this extra information embedded.

    def self.newClassWithType(element_name, type)
        return newClassWithCreator(element_name) { |el|
            XmlReader.constructFromElement(el, type, true)
        }
    end # function newClassWithType

    def self.newClassWithCreator(element_name, &creator)
        tmp = Class.new(self)
        tmp.procCreator = creator
        tmp.element_name = element_name
        return tmp
    end # function newClassWithCreator

Notice that these functions aren't member functions; they're class methods (since they're declared on the class - see the self). The magic is the use of Class.new(self), which creates a new subclass of the current class, upon which I then set some members that apply for all objects created from that class. The other nice thing is that these "subclasses" are exactly that - new instances of them are still true for thing.kind_of?(XmlArray).

When I was trying to figure out how to get this class nonsense to work, I thought the way to do it was using metaclasses. I stll don't understand metaclasses well, but I get the feeling they aren't the right tool for the job.

And then here's how someone might consume the XmlArray. This creates a property that is an array that will load up all of the elements named alias under the element aliases, and each element will be loaded as type AliasCommand.

@xarrClass = XmlArray.newClassWithType('alias', AliasCommand)
addProp(PersistedProperty.new('aliases', ref(:aliasesForLoading), @xarrClass, PersistedProperty::UseNewTypeFallback))

Array properties

Not only is the XmlArray typically consumed as a property, it implements its own loading functionality using properties. It has to, in fact, in order to get automatic resolution of dependencies. It uses a cool trick to do this.

Recall from earlier in the post that the loading code first calls fromXmlTree to let the object instantiate itself from the element, then enumerates all the properties and loads them one by one. XmlArray takes advantage of this by using the fromXmlTree call to make an early pass over all the elements and add a property in place for each one. Once the placeholders are there, the loading system will automatically take care of loading them, since they're in the property list.

Put it all in a Bag

Midway through designing and coding this up, I noticed that another component of the MUD was doing almost the same thing: bags. Bags are containers represented in the MUD world that hold real MUD objects inside (like dealies, mobs, etc.). Functionally, they're basically sets. Bags have to load from XML (e.g. the nipics that spawn in a room) and save to XML (the contents of your inventory). The requirements are roughly the same, so it would be great to abstract away the loading and saving code into an XmlArray.

A lot of the work I went through was getting these to work with Bags. The idea and design is pretty straightforward, but like all things loading, there are gotchas.

@xarrClass = XmlArray.newClassWithCreator('item') { |el|
    XmlReader.constructFromElement(el, (@restrictClass || Object), false)
}

addProp(PersistedProperty.new('.', ref(:contentForLoading), @xarrClass, PersistedProperty::UseNewTypeFallback))

The somewhat clever part here is that the array loads from ".", i.e. the current node in XML, rather than some sub-element. I ended up having to fix some silly little bugs like not being able to load the XmlArray if the bag containing it isn't specified in the XML (duh! if the bag isn't there, of course any sub-elements aren't there too).

And since I didn't actually use the array as the backing store for the elements in the bag (since bags use sets as their underlying storage, not arrays), I had to make sure to sync content between the bag proper and the "loading/saving contents" at the right times. In contrast, the CharacterPreferences does use the loading array as the actual storage, so it is always up to date.

The other reason this work took so long is that I kept taking these detours to fix other loading bugs or add little enhancements here and there. For instance, I fixed the code that gives every object that is loaded one and only one call back to loadingComplete, which they can optionally implement. And it turns out to be a convenient way to clean up or finalize structures after loading is all done.

For next time I plan to talk about the testing I did to make sure things kept working.

My Fake Name (Alias)

Over the last several weeks (actually, over a month - I just checked svn log) I've been working on implementing aliases. Yes, yet another amenity rather than an actual MUD feature. Implementing actual MUD features are really hard, because they have all kinds of pesky considerations like game design and entertainment value. If I just stick to adding little dinkies like this, I won't have to bother with all that heavy stuff.

My other rationalization is that every feature I implement expands the flexibility of the codebase as a whole. As you'll see in either this post or another (depending on length), I added quite a bit of other functionality to make aliases work well. I tell myself that this extra flexbility will come in handy someday when I get to implementing the real MUD features.

What thems are

Aliases are a way to type a (usually short) command and have it turn into a different (usually longer) command. They're actually a pretty fundamental part of MUDding, and can be used for a lot of useful things. For instance, when I used to PK (nowadays called PvP in all the cool kids games), when I knew whom I was fighting, I'd make an alias mapping the single letter "j" to the command to initiate an attack on him. In this way, I wouldn't even have to move my hand off of the home row of the keyboard to start attacking. Aliases made that possible.

Client vs. server side aliases

Actually, with a sufficiently advanced client program, aliases are more or less obsolete on the MUD. I used to use MUSHclient, a very fully featured MUD client. It had enough customization that anything I could do alias-wise with the help of the MUD (on the server side) I could do at least as well on the client side. Still, sometimes I was away from home and had to use old fashioned telnet, so it's good that server side aliases existed anyway.

As a quick aside, MUSHclient's killer feature in my eyes was the ability to wire it up directly to a perl script that could essentially automate it for you. Sure, you could write perl to be a full telnet client for you, but MUSHclient let you relinquish control temporarily to the script by typing special commands. I'll admit, I wrote at least one script that after I'd left went on to be banned by that particular MUD's administration... but that's a story for another time.

Back to what thems are

The fundamental components of an alias are the command you type in and the string that it gets expanded to. I think many MUDs (stock ROM, for example) leave it at that. I went a little further, for fun. In my MUD, the expanded command can take arguments, too. Since non-programmers don't know or care what arguments are, I'll illustrate what this means with some MUD output.

> alias donate tell &2 Here you go, my good man, have &1 dollars.
Added alias donate => tell &2 Here you go, my good man, have &1 dollars..

> donate 5 man
You tell a man, 'Here you go, my good man, have 5 dollars.'

Here I've added an alias where I can tell someone about some money I'm giving them. The &1 and &2 are placeholders that correspond to the first and second things I type after donate, respectively. Those are called "arguments to donate". I have no idea how that word came about there. The &1 and &2 are inserted into the resultant text in the places I indicated.

Along with the ability to add aliases, it's expected to be able to overwrite them, remove them, and list them. Using the alias command like before, you can do exactly this.

> alias
Aliases:
  donate => tell &2 Here you go, my good man, have &1 dollars.

> alias donate
donate => tell &2 Here you go, my good man, have &1 dollars.

> alias donate ''
Removed alias donate.

And since they are often useful over the long term, they save to your playerfile in your preferences, so they're there automatically whenever you load up.

The rest of this post is more about how they were implemented and what happened along the way. As maybe you can tell, there are a few distinct parts involved in this work: the alias processing, the alias management command, and persistence.

Interpreter changes - lockout

The alias processing had a few interesting parts. For one, it required hooks in the interpreter to make it work. Normally, the interpreter has a single list of commands that it looks up in when you type in a command. Now, for a few reasons, I wanted to make it store aliases separately in another list.

  • I wanted alias lookup to follow different rules than normal command lookup
  • I wanted to be able to show a list of all the built-in commands separately, with the commands command
  • I wanted to be able to display the list of only aliases in the alias command
  • I wanted to be able to add and remove to the alias list without having to traverse or manipulate the list of built-in commands.

Most of those reasons are relatively self-explanatory, but the first one bears more detail. The fundamental difference between how aliases are looked up and how built-in commands are looked up is that aliases use an exact match and builtins use a prefix match. So you can type "al" and get the alias command above, but you can't type "don" and expect to get the donate alias from above.

The prime example I used in my rationale here is making aliases for the alias command itself. It's of utmost importance that users not be able to lock themselves out of getting to the alias command, because then they've have no way of removing a faulty alias.

Consider what would happen if I did prefix matching for aliases. What if the user made an alias named "alias"? Well, maybe the lookup could prefer the builtin there. What about one named "alia"? And then what if they type in "al"? The prefix matches both the alias "alia" and the built-in alias command. It's not obvious in the general case (for other built-in commands) which precedence should take place.

So the interpreter first tries an exact match against the alias list, then a prefix match of the builtins list. In the end I think this will yield the least confusing behavior.

Infinite recursion in the interpreter

Whenever you talk about aliases, the possibility of infinite recursion becomes very real. What happens when I map an alias back to itself?

> al
Aliases:
  abc => def
  def => abc

Oh noes! Danger lurks! Unchecked, this will keep expanding forever. Why would I even want to allow this? Well, it could be convenient. For instance, in the previous section, I talked about how we don't prefix match aliases. Well, with recursive alias mapping the user can do this.

> alias
Aliases:
  donate => tell &2 Here you go, my good man, have &1 dollars.
  don => donate

Now the player can type "don" to get the donate alias. I'd say that's useful enough to keep around. So how to combat against the recursion problem? My solution isn't terribly elegant, unfortunately. The interpreter just keeps track of how deep it is in lookup and bails out if it gets too deep.

def interpret(line)
    if interpretDepth() > MaxInterpretDepth
        DebugOutput.debugOut("possibly recursive alias or command", LWarning, LCommand)
        return nil
    end

    self.interpretDepth = interpretDepth() + 1

    # do lookup and recursive resolution of alias...

    self.interpretDepth = interpretDepth() - 1
    return ret
end # function interpret

Since the depth is stored as a member variable of the interpreter, there are obvious problems if the interpreter ever needs to be accessed, say, on two separate threads. I'll cross that bridge when I come to it.

Alias management

This is actually pretty straightforward. This class of commands is in line with other "management" commands like who, and so already has access to all the objects it needs to manage the list of aliases. Boooooring.

As I start to write the section about persistence, I'm getting the feeling it could use a post all to itself, so that's what I'm going to do. And I feel the urge to talk at least a bit about the way I did testing during this work, because I feel I did some neat things. So maybe that will get a post too.

I have a strange relationship with blogging. I like coding, but I also like blogging about what I did. Sometimes when I near the completion of a feature, I look forward to the blog post I'll write about it soon as a kind of reward for the work I did over the last weeks. One might ask why I don't just blog whenever I want. Well, for this sort of blog post I only feel like I want to when I have something concrete and complete to talk about. I don't really like presenting something that's obviously (to me) half baked or incomplete. As we call it at work, I like to be "code complete" before talking much about it.

20091008

standards mode

Huh, I just noticed that my blog wasn't being rendered in IE8 standards mode, but rather in IE7 compatibility mode. I pretty much wrote this blog to work in IE8 mode, so it's silly that it doesn't. Since the compatibility view button doesn't show up, it means that something has decided to put that site into compatibility mode automatically for me.

Since my user-controlled compat list is empty, the other likely culprit is the automatic list of domains pushed down by microsoft periodically. I bet that blogspot.com is in the compat list. I guess that makes sense, since there are probably a lot of blogs on here that were written for IE7.

Anyway, with the addition of the X-UA-Compatible meta tag, now it's actually in "edge" mode, since I will probably keep it up to date with IE.

potentially little known fact: you can actually specify multiple meta tags in your document. The correct usage is basically to declare what versions of IE you have tested your page with. IE will automatically use the latest mode that it supports. This is kind of an academic point until the day when there are multiple versions of IE out that support the x-ua-compatible meta tag.

links came from the compat view IE blog post.

20090927

windows 7 virtualization

I just took the plunge and upgraded my main machine at home from vista to win7. actually, I didn’t really upgrade. I clean installed because I had some old cruft I wanted to clean out. For example, now I gave 100 gb to my system drive, to account for updates and stuff that inevitably end up on there.

one of the things I’d heard about in win7 was the so-called “xp mode” where you can run an XP virtual machine without having to really install any extra virtualization software. sure enough, you can get Windows Virtual PC as an update to win7. It’s basically like running vpc 2007, but it feels snappier for some reason.

i was easily able to get my linux vpc set up and running. i had to edit the vmc file to get the ethernet address on the vm set properly, but other than that, it seems to work great.

yet another reason why windows 7 is cool

20090903

frustration

I play a fair number of games. I guess a lot, by most people's standards. Like many people (and unlike many others, such as my wife) I don't mind getting frustrated by games. Sometimes I play games that are pretty ridiculously hard, and spend quite a lot of time trying to beat various parts against absurd odds.

The absurdity is often the source of frustration. No matter how good a game is by most measures, sometimes the things you have to do is unequivocally unreasonable. This can be some large number of iterations of some task, or it might be a very small timing window, or it might be a sequence of events that is too exact in its requirements.

For example, I've been playing Odin's Sphere lately. It's a good game. In fact, it's an incredibly good game. But I've died probably fifty times trying to beat the boss I'm stuck at (oswald, the shadow knight, and I'm mercedes, in case you're curious). i don't think it's even possible for me to beat him no matter how skilled I am. I'd have to go back and grind some levels to become powerful enough to do so. The failures and this realization are both kind of frustrating.

So it's nice to see games that aren't frustrating. Well, to be more specific, games that are hard but not frustrating. I've been playing Trials HD lately. The beginner and easy tracks are hard. The medium tracks are passable, but it's hard to get the "gold medal" in them. The hard and extreme tracks are utterly insane.

But I'm not frustrated by the difficulty. Even though I've died almost 200 times on a single track before managing to eke by, and even though I spent a full 2 hours straight trying to get a gold medal on a particular medium level track only to be foiled every time at various spots, I'm not frustrated. The game gives me the level of control and precision needed to lay the blame for the failures squarely on myself. Put a different way, everything that happens "makes sense." This tends to be true of most "physics based" games, where the physics are simulated accurately. This is just a very polished example of this class of games.

20090825

Testing 1 2 3

I have actually been doing stuff since my last post. The life I alluded to last time got in the way again at least for a while, but now I'm safely back to not having one, so it's cool.

Meanwhile I've been doing something thoroughly inane: testing my code. I shouldn't be calling it inane like that, seeing as how I test software for a living. Or rather, as I'm usually careful to point out, I write software that tests other software for a living. I like to think there's a difference there.

But whether or not I should call it inane, it kind of is. I think I've stumbled upon a maxim of some kind, that a developer is less inclined to test his own code than someone else. At least in my case, I don't feel interested to test my code because I already have knowledge of it, so in a way it's like working on the same problem a second time without doing anything new and cool.

On the other hand, testing someone else's code is a challenge - to find what's wrong with it. As I was telling our summer intern at work the other day, when he asked me about code reviews, I review under the assumptions that the code I'm reviewing is crap and that anything the author has done is either unnecessary or can be easily achieved by some better means.

Nevertheless, I think this is a great differentiator among developers. Ones who are good at testing their own code are valuable. It's one of the steps towards being a consistent, thorough coder, as opposed to a rockstar, cowboy, or hacker (as they are sometimes called at work).

So what did I do?

The rest of the post is going to be the usual codey garbage that permeates my other posts. I've written a fair bit of code at this point (about 10 kloc), and that is a fairly ridiculous amount for one person to test. And there are different classes of tests needed, ranging from unit tests to scenario-type tests to other more difficult types.

Before I could really even consider testing, I had to see whether my code was even testable. At work, much of our product is a black box, so testing it has to be done purely by inputs and outputs. This is fine in many cases, but I was hoping to do better here. If the MUD is a black box, then every test consists of hooking up some kind of input generator that drives telnet to connect to a running instance of the MUD, send text in, and verify the text coming back. This is certainly doable and in fact should be one class of tests that I write, but it's really very tedious to do, and is susceptible to unimportant changes like the wording in some command or some minor timing issue.

Turns out the code is structured in a way that I can write most of my tests at a much lower level, which allows instantiating objects on the MUD directly. I decided to start off with unit tests for some simple components, to work out the kinks with the test harness.

The test harness

Speaking of test harness, I chose Test::Unit, which seems well established and offers the right kind of functionality I'm looking for. I guess the functionality I want is to be able to add tests easily, group or categorize tests, and for the harness to largely stay out of my way.

Test::Unit, despite the name, seems like it works well enough for tests other than unit tests. It provides a way to declare startup, cleanup, and test code, and assert conditions. That's pretty much all of the basic needs of any test, far as I know. Everything else can be built on that. Really, the coolest thing I think Test::Unit brings is figuring out at runtime all of the tests that you've written and running them. All you have to do is require 'test/unit' and subclass all of your test classes from Test::Unit::TestCase, and it figures it out.

The tests

I started out writing unit tests for functions that are self-contained. For instance, I wrote tests for argify and flattenHash (which takes a nested hash of hashes (of hashes, etc.) and flattens it into just one level). This was really easy, since it is straight verification of an input string turning into an output array.

I wrote some tests for the base evento system. I used mock objects to represent the source, target, and bystander of an evento and stored state in the mock objects to indicate what evento was received by each. Then I could verify that the eventos received by each were the right ones.

Then I wrote tests for dext. This was a bit more involved, because there is a bit of context required. There is a source and there is a target or bystander, and depending on the various directives in the dext or edext, different things would show up to different actors. I ended up doing this by loading an actual MUD instance and using real nipics to fulfill their roles in the dext and edext.

This consequently meant I needed to be able to define a "test world" populated by these test nipics. There was some refactoring required to make these piecesfall in place, but now I have the ability to test scenarios in an actual MUD instance, for instance testing that player characters can be loaded and move around.

Test Data

When I finished the character tests, I now had two sets of tests that both require a MUD instance to be running. I had created a "test data" class to wrap various features of a MUD world into variables for convenient consumption. For instance, when the test data is created, it finds specific rooms, nipics, dealies, etc. in the world and stores them in variables. Later, tests can do something like place a character directly into some room without having to search the MUD for the room.

The possibility exists that I might want a few different worlds, and tests may use one or another of them, depending on their requirements. However, the MUD (for better or for worse) has a global presence in the form of a few variables, namely $mud. So to swap between different test data, I'd need to tear down the MUD and bring up the new one.

I'd rather not do this all the time, so I built a TDFactory class that manages instances of test data. At test startup, a test can request a test data by name. If that named test data has already been initialized, then no work needs to be done. Otherwise, the old one is brought down and the new one is created.

Next?

I'm about done writing tests for now. Though I don't really feel like writing them, I cannot deny that they afford me peace of mind when making changes. I'm confident now that if I do someting wrong in dext or argify, my tests will catch it. I have a few ideas for what to work on next. We'll see what hapens.

20090818

the special bus

I'm writing this as I ride the special bus to work today. That's right, my work has a special bus just for full time employees. The bus routes travel all over the place, and there's one that runs pretty much directly to a couple miles from my house (my new house - the one I just bought).

Today is my first day riding it instead of the regular metro bus, and I have to say this is a huge step up. The bus has not one but 3 free wifi access points inside (it's one of those long charter buses). It has nice, comfortable seats. It's basically empty, so I get a pair of seats to myself. I reserve a seat online, so I am guaranteed to have room. It has a total of 4 stops along its route (versus about 7 or 8 for the regular bus), so the ride is quicker. Since everyone on here is a working professional, there isn't the loud dude pontificating about his views on life and politics to everyone around or the thug kids talking about their late night escapades.

The stop for this bus wasn't close enough before that it was worth it for me to drive all the way over to catch it, but now, special bus, I'm pretty sure I'll be riding you two times a day, up and back.

Addendum: I've found two other things that make this bus awesome. Firstly, consistency. This bus leaves on time, and by "on time" I mean not early. So if I plan to get to the stop by 8:25, I know that the bus, which is scheduled to leave at 8:27, won't have left yet.

Secondly, I noticed today there are freaking power outlets along the walls. As I write this, I am charging my laptop and using it. That's insanely cool.

20090723

Branching

If you've used revision control much before, and/or worked on a project with a lot of people simultaneously, you have probably been exposed to branching. I reflected a little on how we use it at work and how I use it, and thought I'd write it down.

Revision control is when you get some sort of system to keep track of the entire history of a set of files. For example, all these blog entries are stored in an RCS on my computer at home. I don't feel pressure to make changes to them, because I know the system is remembering everything they ever said.

A "branch" is just a copy of the files, but it's a copy which the system knows originated from some earlier version, so that you can pinpoint a time where the branch split off and diverged from the "trunk", as it is sometimes called. Branches are useful for several things.

The two main uses I see branching used for at work are freezing a version of the code and separating risky features. When we ship a version of our product, as we near the release, we fork a branch off of the trunk where the code is meant to sit with relatively little disturbance until release. Any high-priority changes that absolutely must happen will take place in that branch, while the trunk of the code may continue with the normal rate of change for work beyond the upcoming release. When we ship, we freeze the branch, and that's that.

We also use branches for keeping risky features or large chunks of work a little removed from the trunk. The reason we do this is that we build the trunk nightly, so that every day there's a new installable product for the entire team to test out and use. If large, complicated changes are taking place in the code, the probability that a serious bug arises in the product is high. If this happens, then the bulk of the team suddenly doesn't have a new build that's usable that day. It can halt progress for a day, so it's worth taking precautionary measures.

What happens is that we create a branch of the code that is to be used for the development of one of these "risky features." Changes are added to that version and not to the trunk version, so while the major problems are being ironed out, only the few people working on that feature are affected, not the whole team. Approximately nightly, all the changes in the trunk are merged down into the feature branch, so that the code is still mainly in sync. Periodically (every several weeks), the code in the feature branches is merged up into the trunk if it is of high enough quality, i.e. it passes a bunch of tests. At this point, the trunk and the feature branch contain exactly the same contents.

An interesting idea that came up in our last development cycle was the namingof our team's feature branches. In the product cycle before that, feature branches were named after the features developed within. But in this one, the feature branches were named after colors. Red, green, yellow, etc.. I asked one of our development managers about it when the announcement was made, and one of the answers he gave me is so that we wouldn't have any problems putting any feature in any branch.

This didn't make much sense to me at the time. It felt kind of arbitrary, but now, over a year later, I think I kind of get it. As a programmer, naming is important to me. Sometimes I spend an inordinate amount of time agonizing over how to name something, because if I use a name I don't like, it continually irks me afterwards. Likewise, having neutral names for feature branches eliminates the part of the brain that says, "we can't do this work in that branch, because it's intended [due to its name] for this other kind of work."

I adopted this for my MUD. Even though I'm just a team of one, I have larger features and smaller changes. I like to keep a stable trunk build all the time, so I use a "feature branch" called "blue" for my bigger items. For instance, all the work on socials and pmotes went into blue first, then merged into the trunk. Meanwhile, I made bug fixes for things unrelated to that feature in the trunk and merged them down.

I almost think of this usage as a third way of using branches. If you consider that the trunk is all that really matters, and if you like the idea that the trunk is always stable, and if you like the thought that features are added to the trunk as a single revision, this use of branching kind of satisfies that. Even though the feature really is too big to add all at once, all of the little iterative pieces are added in this branch, kept out of the way of the trunk. Eventually, when it's all ready to go, it's added as a single big change to the trunk. You kind of get the best of both worlds: granular additions, and a nice clean view of the trunk. Plus, if you need to revert an entire feature, you have a single change (the merge) to undo, not a ton of little ones.

I use revision control for everything. I can't imagine getting along without it anymore.

20090720

Socials and Pmotes

I've been busy lately... with life. And with a new feature (or couple features, I guess). Most MUDs have a few different kinds of commands. Most commands are used for doing something useful, like moving around, attacking stuff, examining your stats, and so on. One class of commands is used mainly for fun. These commands are called "socials," because they're used for socializing, I guess. Before now I had one social hard-coded in my MUD, but now I have a few. More importantly, I now have a way of adding more without making code changes.

Here's an example of a social in use. This is one of the new ones I put in.

<type commands for a list> blarg man
You blarg a man about.

<type commands for a list> blarg self
You really blarg yourself good.

<type commands for a list> order man blarg joint
A man blargs you good.

<type commands for a list> order man blarg guy
A man blargs a guy so much that he hurts himself.

<type commands for a list> order man blarg self
A man takes a good long blarg at himself.

Take note at the number of different pieces of text here (and these aren't all of the possible ones, as you'll see in a bit). For any given action, there are a number of different points of view. There is the possibility that the command takes a target to act upon. There's an option to see something different when the target is oneself.

Social Parameters

When writing socials, I tend to follow the model set forth already by Eventos. There are three points of view that matter: you are the originator of the action; you are the target of an action; or you are a bystander observing the action. Accordingly, when authoring a social you have to fill out text for these three points of view.

And I like to think of the status of the target in a few ways: the action has no target (is impersonal); the action has a target, and that target is yourself; the action has a target, and that target is someone else.

This represents the complete set of pieces of text that need to be defined. But we're not done. Notice in the output above that there is some other interpolation going on. "A man blargs a guy so much that he hurts himself." What happens if the actor here isn't "a man" but instead a female? It should read, "...she hurts herself," but I'd really rather avoid having separate canned text for all possible combinations of genders that can be imagined. So there's some interpolation taking place.

Dext

I'm trying to build up suspense for the punchline, but I just can't anymore. The text that you put in the social definition file isn't straight text, but instead text with embedded directives that are substituted at runtime. Text with directives - I call it "dext." If you think that's a crap name, you can tell Kirill about it, but you probably don't want to know what it was called before he helped me out with the naming.

At this point I'm ready to show the full definition of the "blarg" social.

<social kind="social" order="0">
    <string>blarg</string>
    <dextSource>You blarg about.</dextSource>
    <dextBystander>$1c blargs a lot.</dextBystander>
    <withTarget>
        <selfTarget>
            <dextSource>You really blarg yourself good.</dextSource>
            <dextBystander>$1c takes a good long blarg at $1r.</dextBystander>
        </selfTarget>
        <dextSource>You blarg $2 about.</dextSource>
        <dextTarget>$1c blargs you good.</dextTarget>
        <dextBystander>$1c blargs $2 so much that $1s hurts $1r.</dextBystander>
    </withTarget>
</social>

The astute reader will immediately see where $1 and $2 are being substituted for the actor and the target in some of the above examples. But even the astute reader can't possibly imagine what the trailing text on the directives stands for. If you can guess, then you're awesome in my book.

Another interesting observation you can tell from looking at this spectrum is that "I" is conspicuously absent. That's because the MUD is a second- and third-person only game. The system/world tells you things, so addresses you as "you." Sometimes it tells you things about other people, so refers to them as "he" and so on.

A little documentation

Here are all the flags currently supported, which you couldn't possibly have guessed.

FlagMeaning
cCapitalize - used for beginnings of sentences.
sSubjective case - i.e. subject of the sentence. E.g. "he/she/it"
oObjective case - i.e. target of an action. E.g. "him/her/it"
rReflective objective - third person target when the target is oneself. E.g. "himself/herself/itself"
pPossessive case, e.g. "his/her/its"
fShow Full Name - an override to force the system to show the full short description in this place. I'll try to show why this is useful later. Clearly, it's not being used here.

You're lucky I didn't use the Latin cases here (nominative, accusative, acc. again, genetive, respectively). At a glance, the reflective objective may seem out of place, but it's not. I may get disagreement from the hardcore grammar crowd, but I think the reflexive modifier only exists for the objective case in English. In the subjective and possessive cases, the closest you can get is usually termed "intensive": "he himself" and "his own", respectively. These serve functionally different purposes and are more adornments, so I haven't included them now. Also because it's a little hard to tell how to fit them into a simple system that works merely by substitution.

Some of these flags can be combined together, such as a case and capitalization. Others obviously can't, like two different cases.

The way the dext directives are used is that when a social command executes, it constructs an evento that is sent to everyone in the room. The various fields of the evento (source text, target text, and bystander text) are generated from the dext by passing in the source and (optionally) the target. The MUD makes a guarantee to social authors that $1 is always the originator and $2 is always the first target in the target-related text.

Finally, socials don't have to work with a target, and don't have to have separate text for a target when the target is oneself. It's up to the social author. This is one of those reminders that there are multiple audiences for the MUD. I'd say it's primarily about the player, but much thinking goes into making things painless for the various creative people that are inevitably involved, such as builders who create the areas, dealies, and nipics. From what I hear, real game studios make similar investments for the sake of their artists, level designers, etc..

Hey, what about this "pmote" thing?

Silly titles... right, you caught me. I alluded to something called a "pmote" in the title, so I'd better talk about it. Everyone knows what an emote is, right? Well, maybe not. It's a command that sends some output that is your name followed by arbitrary text. In case that sounds too vague, here's a concrete example.

<type commands for a list> emote does a little dance.
jointface does a little dance.

There you have it, instant socials. In IRC I think the format is "/me string". In IMs, people do *string*. It's pretty useful for ad-hoc acting, and if you enjoy role-playing, you might use emote even more than any other communication command. But it has shortcomings, like this.

<type commands for a list> order man emote hits jointface in the face.
a man hits jointface in the face.

He hit him in the face?! I bet that hurt! Hey wait, that's me! You get the point. It would be nice if by doing that action, instead of seeing your name as if you were in the third person, it said "you" to let you know he actually targeted you. This is exactly where pmote comes in.

I don't know what pmote stands for. I reckon it's probably "player emote" or something. It allows for the insertion of directives (hey, weren't we just talking about those before?) into the emote string for more effective acting. It works a little differently than regular socials, though.

As an aside, in looking up what pmote stands for, I found some documentation on other MUDs' pmote systems. I am compelled to admit that this one is more advanced than mine, based purely on the number of different directives it supports.

Directives and Pmotes

There are two criteria for the minimum effective use of pmotes: you can refer to things around you by name and you can refer later in the sentence to those things again. In my system currently, you can refer to a thing in the room by prefixing it with an @ symbol. Once you've done this, it assumes a position at the end of the list of known things and can be referred to using the usual $ syntax. I call this variation on dext "edext", as in dext with embedded directives. It's not a great name, but whatever.

<type commands for a list> pmote grins at @man.
jointface grins at a man.

<type commands for a list> order man pmote grins at @joint.
a man grins at you.

<type commands for a list> order man pmote grins at @guy.
a man grins at a guy.

Remember that "show full text" flag from above? You can see it at work here. I include that flag for @ directives to keep "pmote grins at @man" from becoming "jointface grins at him". Some more interesting examples:

<type commands for a list> pmote is really mad at @man because $1s is dumb.
jointface is really mad at a man because he is dumb.

<type commands for a list> order man pmote hates @guy and $1p dog.
a man hates a guy and his dog.

Notice in both these examples how I referred back to the assimilated target later on. The first time you point to a target, it always inserts their full short description (again, that "show full text" flag). This limits the sentence structure a little, but in practice I haven't noticed it as a big deficiency in the past. Here's another neat use of the possessive:

<type commands for a list> pmote pets @man 's cat.
jointface pets a man's cat.

<type commands for a list> order man pmote is jealous of @joint 's dog.
a man is jealous of your dog.

I opted for a natural syntax for the possessive here, where having the orphaned "'s" retroactively indicated to the parser that the previous directive was a possessive form. The reason the space is required is that the text immediately following the @ is a full target string, conferring the extent of its expressiveness. Check out this example, showing off a target string and why the apostrophe would be ambiguous if not separated.

<type commands for a list> pmote thinks @1.'man dude' 's bagel is cool.
jointface thinks a man's bagel is cool.

The @ syntax was pretty arbitrary. I was looking across the top of my keyboard for a piece of punctuation to use, and I just happened to feel like using it. Only later did I realize it happens to match the Twitter syntax for directing messages to specific people.

How it works in code

Dext and edext are handled by different functions. Here are their signatures.

def DextTools.textFromDext(dext, hearer, targets)

def DextTools.textFromEdext(edext, hearer, procLookupTarget)

In the case of dext, there is no assimilating of embedded directives. The set of known things (termed "targets" here) is known already, so the caller just passes in an array of them, where targets[0] is $1 and so on.

For edext, there is no predetermined list of targets; it's built up as the string is scanned. So instead, the caller passes in a function that maps target strings to targets. I like this design because it doesn't restrict the caller at all with how the lookups are done. They could be done in a static array, generated randomly, queried from some other source, etc.. Here's the fairly sensible code for pmote, which currently lets you target mobs in your current room. Check out them closures!

procLookupInRoom = lambda { |targetString|
    return room().bagMobsInRoom().searchByTargetString(targetString, self)
}

Not everything is perfect

There are some inevitable problems with trying to make everything work.

<type commands for a list> order man pmote thinks @joint is crazy.
a man thinks you is crazy.

Woops! This looks fine to everyone else, at the expense of you. Without making the system aware of the various verb forms of "to be" and making it dynamically rewrite that on the fly, I don't really see a good way to make this look correct to both the second and third person view.

This brings up an interesting point: the previous example might be considered an invalid input. Step back from the nitty gritty of the substitution and consider the overall goal. The pmote command lets you act out things with your character, potentially including others nearby in the text. In the previous example, the originator conveyed far more in an action than is really possible.

And here's the crux of the point. I think any such pmote that includes some other person being used in a subjective case (like the example above) is probably doing an invalid pmote. In contrast, objective is fine, since you can do actions to people, and possessive is fine, since it's essentially adjectival.

In the end, there are infinite ways that players can abuse emotes and pmotes, but precedent across many MUDs has given these particular powers to players despite the risk. As a formerly avid role-player, I like giving players good tools to express themselves, so I agree here. I had fun coding up this feature because I like coding, and I like grammar.

20090630

Target Strings

I haven't posted substantially in a while. I got a bit busy with school and house stuff and messing around with the Ruby bugs I ran into before. Oh yeah, and I did some random bugfixes and cleanup. I don't have a lot to report, but I did make some updates to target string processing.

Target strings are a way of identifying a thing in the MUD world. You need to do this all the time. To look at someone, pick up an object, put an object in some container, attack someone all use target strings to identify the thing in question. I've somewhat copied the style of target strings that I'm familiar with from ROM.

A [key]word about keywords

I've talked about keywords before, but it's been a while, and this post involves them a lot, so I'll say a bit more.

Each thing in the game world has a set of keywords that are used to identify it. The player never sees these words, but the implicit contract is that at least some of the keywords can be gleaned from the short description or long description.

KeywordsShort DescriptionLong Description
sandwich ham cheesea ham and cheese sandwichA ham and cheese sandwich sits here, tempting you.
businessman man suita well dressed, important looking fellowA man wearing an expensive suit and wielding a briefcase stands here looking important.
amethysta beautiful purple crystalGlinting in the light, a light-purple gem is lying here.

Notice that there is no requirement that the keywords are easily guessed given the short and long description. Sometimes level builders like to play these kinds of games, though as often as not it can be seen as bad form. As an aside, keywords are unordered; in fact, internally they are stored as a Set.

So why all this nonsense about keywords? Well, for fun, but also for notation. In the following section I will represent a set of keywords as {keyword1, keyword2, ...}.

Target Strings

As I mentioned before, target strings are a way of identifying something when you enter a command. Target strings operate on keywords only, never long or short description. When I say "matches" below, I mean "prefix matches." So "man" matches {man} but also {manticore}.

Examples:

man
a thing in this room where one of its keywords matches "man"
2.man
the second thing in this room with a keyword matching "man". If there is only one such thing in this room, then this is not found.
'man guy'
the first thing in this room that has a keyword that matches "man" and a keyword that matches "guy". E.g. matches {cool, guy, man} but not {cool, man}.
'man guy
same thing. NB: no closing quote
all
a special identifier that indicates the group of all things in this room. This will actually have a different set of things matched for each command that operates on it. For instance, "get all" means to pick up all dealies, but "kill all" means start attacking all mobs.
all.
same as "all"
all.knife
all the things in this room that have a keyword matching "knife"
all.'sharp knife'
matches all the things in the room with a keyword matching "sharp" and a keyword matching "knife". E.g. matches {sharp, blade, knife} but not {dull, knife}.
self
a special identifier that always refers to my own mob
1.
the first thing of any kind in this room
'
same as 1.

Once you have the target string and the set of keywords, doing the prefix matching against the set of keywords uses the obvious approach.

argify

Each command takes zero or more arguments. For example, "look" can take zero arguments, which means to look around in the current room, or it can take one argument, a target string indicating the thing to look at. "Get" takes one or two arguments: the item to get, and optionally the container to get it out of (both target strings). "Tell" takes two arguments: the person to tell something to, and the string to send to them.

The process of turning an input string into an array of arguments to pass to the command is called (by me and the code) "argification," because the function that does it is called argify. The caller passes in the string to parse and the maximum number of arguments it wants. argify gives back an array of at most that many elements.

So how would you go about parsing input into arguments? Here are some of the rules:

  1. Arguments are space-delimited ("one two" => ["one", "two"])
  2. Quoted strings become a single argument ("'one two'" => ["one two"])
  3. Quoted strings in the middle of an argument are assimilated into that argument ("1.'one two'" => ["1.'one two'"])
  4. If the string is too long for the max requested items, everything at the end is bundled into the last argument ("a b c d" with max 3 args => ["a", "b", "c d"])
  5. A non-terminated quote means to take all of the rest of the string as part of that argument ("'hello goodbye hello" => ["hello goodbye hello"])
  6. Anything else is probably undefined.

Do you scan through the string, keeping track of whether you've seen a quote and are waiting for an end quote? That solution is tricky for inputs like "a'b'c'" (I mean, what should that even do? Probably just give back the entire string in the first argument). So then you need to track quotes only at certain points. Which points? Word boundaries? When preceded by whitespace? What if the string starts with a quote? I'm asking all these questions because these are all the bugs I found in my original implementation of argify, which tried to use a StringScanner and a single pass through the input.

I eventually abandoned that plan in favor of splitting the string by whitespace and combining arguments together that are part of the same quoted string or are at the tail end of the string.

Even this has a few caveats, though. Consider this naive way of splitting (warning: assumes familiarity with regular expressions):

tsp = text.split(/\s+/)
#...
# join arguments back together that belong together

This doesn't work when an argument needs its whitespace preserved, like this command: "tell man hello       mister". By the end, you will get ["man", "hello mister"] instead of ["man", "hello       mister"]. The trick is to use some special regex magic:

tsp = text.split(/(?=\s)/)

This is called a "zero-width lookahead." It matches the thing in bold without actually consuming it, so the items in the array after the split may have leading or trailing whitespace, which I can then trim off.

Who cares?

This may seem uninteresting to you, but there's one aspect of it I find interesting. When writing progams that accept input of any kind, if possible it is usually desirable to accept that input in a very strict way. In fact, that was a key design goal of XML. Unlike HTML, if your XML document is malformed, a conformant XML parser will reject it outright. HTML parsers, on the other hand, sometimes go to great lengths to guess what you meant when you fed it something that makes no logical sense (e.g. <b><i>overlapping end tags</b></i>).

With target strings, a key design goal is that they are relatively lax. They serve a dual purpose: firstly, to unambiguously identify a thing; and secondly, to give a shorthand for identifying a thing. I was faced with decisions around questionable inputs. For example, I decided to allow an unterminated quoted string to grab all input until the end of the line. I had to make sure that an opening quote running till a proper closing quote is captured properly into a single argument, but pathological cases like "a'b'c d'" are captured as two arguments. Actually, that last one probably falls into the undefined category.

At this point, I think I've implemented most or all of anything I ever used when I was actively MUDding. I wonder if I will get requirements for something more elaborate eventually.