Twitter icon.
Random notes on software, programming and languages.
By Adrian Kuhn

The Rabbit Will Die in Java


Since some days, an army of mad rabbits has been invading my brain. DWEMTHY’S ARRAY, GIEV US DWEMTHY’S ARRAY, MOAR, MOAR SAUCE, MOAR!!!!!1!!1, they cry.

Finally, I have given in and did Dwemthy’s Array … in Java.

“In this game, you are a rabbit who is going to die. A dragon is going to do it. Deep in Dwemthy’s Array.” – why the lucky stiff

new Creature("Dragon") {{
    life = 1340;     // tough scales
    strength = 451;  // bristling veins
    charisma = 1020; // toothy smile
    weapon = 939;    // fire breath
}};

More on this code in a moment.

For those unfamiliar with Dwemthy’s Array, it is a Ruby meta-programming example that is along the lines of a text-based game. The game has been put on Ward’s wiki as a challenge to Smalltalk advocates: “Go ahead and reimplement DwemthysArray!” Darren Hobbes and, recently, Nicholas Chen have taken the challenge. But never has nobody been so insane to met the challenge in the vast quagmires of Java meta-programming—not until, NOW.

*   *   *

You can not enter the Array unprepared, let us face the ScubaArgentine first:

new Creature("ScubaArgentine") {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
}};

Creature r = new Rabbit();
Creature s = new_("ScubaArgentine");

Now use the little boomerang!

r.send( '^', s )

>> [You hit with 2 points of damage!]
>> [Your enemy hit with 28 points of damage!]
>> [Rabbit has died.]

How does it work? We use double brace initialization to define an anonymous subclasses with pre-initialized fields. And, a custom binding mechanism is used to create instances of that class.

Even though most Java programmers are familiar with double brace initialization, I will explain it for the n00bs. The idiom combines two features of Java: anonymous inner classes and instance initializers (think anonymous constructors). Thus, the first brace creates a new anonymous inner class, the second declares an instance initializer block that is run when the anonymous inner class is instantiated.

Being anonymous, the inner class is not bound to any identifier. Therefore we can not simply use new to create new instances. Instead, we must setup a custom binding mechanism. The name “ScubaArgentine” is passed to Creature’s constructor, which binds name and anonymous class in a static lookup table. The same table is consulted by a static new_ method to create custom instances.

This is implemented in Creature as follows:

private static final Map<String, Class<? extends Creature>> INDEX = new TreeMap();
protected String name;

public Creature(String name) {
    INDEX.put(name, this.getClass());
    this.name = name;
}

public static Creature new_(String name) { try {
    return (Creature) INDEX.get(name).getDeclaredConstructors()[0].newInstance(name);
}
//handle
     catch(Exception e) { throw new RuntimeException(e); }}

Once you’re done with the example guy, it’s time to enter The Array.

You have six foes.

new Creature("IndustrialRaverMonkey") {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
}};

new Creature("DwarvenAngel") {{
    life = 540;
    strength = 6;
    charisma = 144;
    weapon = 50;
}};

new Creature("AssistantViceTentacleAndOmbudsman") {{
    life = 320;
    strength = 6;
    charisma = 144;
    weapon = 50;
}};

new Creature("TeethDeer") {{
    life = 655;
    strength = 192;
    charisma = 19;
    weapon = 109;
}};

new Creature("IntrepidDecomposedCyclist") {{
    life = 901;
    strength = 560;
    charisma = 422;
    weapon = 105;
}};

new Creature("Dragon") {{
    life = 1340;     // tough scales
    strength = 451;  // bristling veins
    charisma = 1020; // toothy smile
    weapon = 939;    // fire breath
}};

Creature dwary = new DwemthysArray(new_("IndustrialRaverMonkey"),
                                   new_("DwarvenAngel"),
                                   new_("AssistantViceTentacleAndOmbudsman"),
                                   new_("TeethDeer"),
                                   new_("IntrepidDecomposedCyclist"),
                                   new_("Dragon"));

In Ruby, DwemthysArray is a subclass of Array that delegates unknown methods to its first creature. Whenever the first creature dies, it is removed from the array, and thus, a new enemy emerges.

Alas, the implementation of DwemthysArray in Java is not as elegant. Why? Java has neither method_missing hook, nor does it support duck typing. And even worse, Java arrays are a built-in type which can not be subclasses. Thus, to keep the demons of static type checking at bay, we extend Creature and include the array as private array field only.

In order to let a new creature emerge whenever the current is found to be dead, we must intercept any boolean check that compares the life field against zero. As we can not override a field access, the Java implementation uses alive() wherever live <= 0 is used in Ruby.

Edit: Clinton Begin has refactored my code to be even MOAR META!!!. He implements DwemthysArray using a dynamic proxy, which is much closer to the original Ruby code.

Here, I shall unmask the Array Creature for you:

public class DwemthysArray extends Creature {

    private Creature[] array;

    public DwemthysArray(Creature... array) {
        super("$");
        this.array = array;
    }

    public boolean alive() {
        if (life > 0) return true;
        if (array.length == 0) {
            puts( "[Whoa.  You decimated Dwemthy's Array!]" );
            return false;
        } else {
            life = array[0].life;
            strength = array[0].strength;
            charisma = array[0].charisma;
            weapon = array[0].weapon;
            name = array[0].name;
            array = Arrays.copyOfRange(array, 1, array.length);
            puts( "[Get ready. %s has emerged.]", this.name );
            return true;
        }
    }

}

“Fight the Array and the monsters will appear as you go. Godspeed and may you return with harrowing tales and nary an angel talon piercing through your shoulder.”

Start here:

while (dwary.alive() && r.alive()) {
    puts( r );
    r.send( gets().charAt(0), dwary );
}

The remainder of the implementation is (except for some additional syntactic noise) almost a genuine port of the Ruby code. Reflection is used to dispatch Rabbit’s overloaded operators to methods and to mimic the output of Ruby’s inspect method. And there are a couple of static convenience methods, like puts and gets.

The full source code is available online.

2 Responses to “The Rabbit Will Die in Java”

  1. Frank Davis Says:

    This is actually cool enough to make me want to re-learn Java.

  2. Clinton Begin Says:

    Hi Adrian,

    Inspiring work! I’ve taken your implementation and refactored it to be “MOAR META!!!” ;-)

    http://www.dzone.com/links/moar_rabbits_dwemthys_array_in_java_refactored.html

    Cheers,
    Clinton

For.example is Digg proof thanks to caching by WP Super Cache!