Classic Games : The Fool’s Errand

Posted on August 19th, 2006 by amd.

The Fool’s Errand was a Macintosh game released in 1988 by Cliff Johnson, who went on to create several other moderately successful puzzle games. None of his subsequent efforts could top the brilliance of The Fool’s Errand.

The Fool’s Errand is the story of a wandering fool tasked with saving the land. The game consists of a set of linked puzzles, tightly coiled together with a Tarot theme. Along the way you encounter each character from the Taro deck, and you must help The Fool unlock their secrets before you can proceed. Portions of the map can’t be reached until certain puzzles have been completed, and the final puzzle cannot be attempted until all others are finished. Strangely, none of the puzzles include instructions. Figuring out the rules is part of the fun.

As a thirteen year old I found the game exceptionally challenging. Now that I’m partially grown, I thought I’d give it another go to see how my skills have improved. It was still rather strenuous. Luckily I was able to vaguely recall some of the puzzles - otherwise I might have been stuck on the The Humbug indefinitely.

Here are a few screen shots:





If you want to play The Fool’s Errand on Windows, try the following:

  1. Download Mini vMac.
  2. Rip the ROM from an existing Mac Classic, or try googling file:vmac.rom. Drop the ROM file into the same directory as vMac.
  3. Laboriously create a bootable Macintosh disk image using HFVExplorer.
  4. With a last Herculean effort, copy The Fool’s Errand files onto your new boot disk. Without losing the resource forks.

Or, you can skip all that hard work and just download a playable copy of the above. Drag fool.dsk onto vMac to start. Have fun!

An Aside : Cliff Johnson

I’m grateful to Cliff for creating The Fool’s Errand and helping me fill those endless days while I awaited puberty. Still, I find his home page somewhat disquieting and I can’t help but feel that Cliff is teetering on the edge of madness. Cliff has been working hard on The Fool and his Money, a sequel to The Fool’s Errand. I pre-ordered the game a year or two ago, and despite numerous delays I’m eagerly anticipating its imminent release.

Laziness Part Two : The 6,000 Line Hashtable

Posted on August 15th, 2006 by amd.

The Problem

Recently, I was called in to review the design for a major piece of functionality in a soon-to-be-shipping product. What began as an inspection eventually grew into a complete rewrite. The most glaringly awful code dealt with storing key/value properties on disk. The code was so overgrown that we couldn’t even begin to consider replacing it for this release. The property code was on the periphery of my original assignment, but it’s sheer size compelled me to take a look.

The property code was amazing! It allowed for categories, so that different sets of properties were guaranteed not to collide. Property bags could be intelligently merged and combined. Each property was typed, with massive amounts of type checking and assertions galore. The properties themselves were persisted on disk using multiple file formats simultaneously. The writable version was stored using sqlite while the readonly version used a custom format complete with a lex/yacc parser.

The implementation was 6,000 lines of code scattered across a dozen files. Nobody wanted to change the code because it was so inscrutable. Most of the features had never been tested. The property code was a ticking time bomb, but it was too late to make any changes for the current release.

It’s Just a Hashtable

This mess could easily be replaced with a string to string hashtable. Hashtables are simple to implement, use, and test, even in a washed up language like C. Hashtables can easily be persisted to text, XML, URL query strings, RPC calls, and databases. Just to be clear:

  • Instead of schema checking, use plain old strings. Convert the values to the appropriate type as necessary.
  • To merge hashtables, add a putAll() method that adds one hashtable to another.
  • To avoid collisions, prepend a category string to each key. You can put the prefix functionality into a subclass.
  • Add read/write methods to persist your hashtable.

This situation would be comical if I hadn’t seen it so often at so many different companies. People inevitably seem to think that hashtables aren’t efficient or type safe enough for their specific needs. I’ve implemented nearly identical hashtable wrappers in C, C++, Java, Ruby, Perl, and Python. I’ve run it on Linux and Windows, on ARM, MIPS, and x86. I feel a warm sense of security each time I write those familiar lines because IT’S JUST A HASHTABLE.

Be Lazy

The lazy programmer uses hashtables wherever possible. If you think you can do better than a hashtable, think twice. If you think you need type safety for property files, please reconsider. If you think hashtables take too much memory or are too slow, you’re probably wrong.

Be lazy and use hashtables. You can always churn out those 6,000 lines later if the need arises.

emacs dotfiles

Posted on August 8th, 2006 by amd.

Today I am releasing my emacs dotfiles for public consumption. Download them here : Adam’s Emacs Dotfiles

History

At Strangeberry we faced a dilemma. Arthur, Jonathan and I had deep experience with emacs, but our dotfiles were wildly divergent. We decided to combine our skills and create a rich set of dotfiles that was also easy to customize. Over time, these dotfiles grew in popularity and are now used by many people across several different companies. Thanks to all the people who contributed to this effort - Arthur van Hoff, Jonathan Payne, Adam Feder, Greg Spencer, Brigham Stevens, and Jeff Jolma.

Features

Here is a brief feature list:

  • cross platform - works on Windows and Linux, windowed or in a terminal
  • lots of modes - including some simple ones I created (valgrind, svn, mbox, NSIS, …)
  • customizable - these dotfiles are probably more customizable then others you’ve seen, because they’ve been used by so many people over the years
  • speedy - I use autoloads everywhere so emacs starts up quickly
  • make.el - helpers for compiling (try M-s)
  • abtags.el - quickly jump around large numbers of files. Build a TAGS file with ctags, then use abtags-find-file (bound to F7 in my .emacs).
  • TAGS helpers - M-. to lookup a tag, M-, to pop back

If there is enough public interest I will release subsequent versions as my own dotfiles evolve. Enjoy!

Quick and Dirty Dynamic Arrays in C

Posted on August 1st, 2006 by amd.

C is an obsolete language and there’s really no good reason to use it. For all but the most esoteric projects, C++ should be used instead of C. I may elaborate on this theme in a future post.

That being said, sometimes you’re stuck with C code. Maybe you’re working on legacy code, or you don’t have control over the engineering requirements. Maybe management mistakenly thought C was “faster” or “more portable”. Maybe there’s no C++ compiler for your platform.

Recently, I was asked to reduce the memory footprint for a C application which had somehow ballooned to gargantuan proportions. The application was targeted at an embedded device with 64MB of RAM and no virtual memory. A bit of sleuthing revealed that a geocoding API was allocating a 20MB data structure. That’s a pretty hefty chunk of change for our little device. The API allocated space for up to 50 matches (each of which was 400kb), instead of using a dynamic array to return the results. The code was written by a junior engineer.

When using C, you tend to spend inordinate amounts of time managing arrays, strings, and hashtables. Think of the thousands of hours wasted on such minutae. Isn’t our collective time more valuable? I feel pity for the poor engineers (like me) forced to labor under such cruel conditions.

Here is a recipe for quick and dirty dynamic arrays in C. I have used this pattern many times with excellent results.

Introduction

This example is targeted at a specific use case. It will serve you well if you have to accumulate an unknown amount of data. You can roll this into an existing struct or grow it into a generic void* array class. I tend to use it inline in other data structures because it’s so simple. You can also use this pattern in Java if you want to avoid the overhead of the java.util.* classes.

Struct

First, create a struct or add the members to an existing struct. In this case I’m creating a new struct and my array will store char*’s. Note that pos is the number of elements in the array, and capacity is the number of elements that the array is capable of storing. Feel free to use other variable names if they make more sense to you.

Gub is simply a word I use a lot in test code, similar to foo or bar.

typedef struct {
    int pos;
    int capacity;
    char **array;
} Gub;

Init the struct

calloc is a handy allocation function that fills the memory with zeroes before returning it. This is an easy way to avoid uninitialized memory errors. In this case, I’m also counting on that behavior because I want pos to be 0 and I want array to be NULL before proceeding.

Gub *g = (Gub*)calloc(sizeof(Gub), 1);

Create an empty array

When creating the empty array, it’s necessary to select an initial capacity. In this case the array can hold four elements before it needs to be resized. Note that we init capacity inline here. Personally I think this makes the code more readable.

This could be written just as easily with malloc (or calloc), but I prefer to use realloc to matche the code used when appending elements (see below). Realloc is a magical allocation function that resizes an existing block of allocated memory to a new size. It’s tremendously useful for resizable data structures. If you pass NULL to realloc (as we’re doing here with the g->array), realloc will simply allocate new memory for you.

g->array = (char**)realloc(g->array, (g->capacity = 4) *
                                     sizeof(char*));

Append some data

This is a silly loop that appends argv to the array. Before adding an element to the array, we check to see if there’s room for more elements. If pos == capacity, the array is full and must be resized. Realloc resizes the block of memory and then we’re good to go. Again, note that capacity is modified inline during the realloc. I think this makes the code more readable. In this case I’m simply doubling the size of the array whenever we run out of space.

Also, note that g->pos is incremented inline when we actually add the element.

for (int i = 0; i < argc; ++i) {
    if (g->pos == g->capacity) {
        g->array = (char**)realloc(g->array, (g->capacity *= 2) *
                                             sizeof(char*));
    }
    g->array[g->pos++] = argv[i];
}

Free

When we’re done with the array, we free it and then the containing struct. Don’t forget to free the elements themselves if necessary.

free(g->array);
free(g);

Laziness

Posted on August 1st, 2006 by amd.

I am an exceptionally lazy developer. If you look at my code, you’ll find that I mercilessly cut corners, fail to implement obvious features, and my big O sometimes careens wildly out of control. Why am I so lazy?

When I began my career I was the opposite of lazy. I tried to future-proof my code by designing complicated systems that anticipated our undefined needs. I carefully scrutinized every loop and allocation for stray operations that would impact performance. Like a freshman vying for extra credit, I piled on features like there was no tomorrow. Gradually, I came to understand that my efforts were misguided:

  • future-proofing rarely works, because the product inevitably evolves in unforseen ways. You’re likely to end up with a bridge to nowhere. Also, the wacky futuristic code that you write will be untestable and buggy. Future-proofing is an example of over engineering, where well-meaning engineers mess things up by trying too hard.
  • premature optimization is another no-no. Optimized code is more difficult to write, debug, and test, and always ends up buggier than unoptimized code. Just turning on optimization in the compiler can sometimes lead to horrible bugs. Instead of optimizing every line, find and fix the bottlenecks. It’s more satisfying that way anyhow.
  • extra features probably won’t earn you any bonus points. Your features might not get used, and you’re definitely creating more bugs for your poor QA team to squash. This is another example of over engineering.

Writing software is a constant battle against complexity. Complexity kills. Complexity kills testing, deadlines, products, careers, and companies. Each additional line of code increases complexity. In the pursuit of excellence, senior developers must constantly strive to eliminate features that aren’t absolutely necessary.

Cultivating laziness is the easiest way to combat complexity. The lazy developer doesn’t need to show off by tossing more features onto the pile. The lazy developer optimizes intelligently instead of trying to perfect each line. The lazy developer hesitates before deploying complex and indecipherable algorithms.

By now most of you are probably nodding your heads (or nodding off). But clearly there are people who don’t get it, because I’m constantly running into complicated, over engineered software written by developers who are trying too hard.

Future posts will illustrate with some anecdotes. Names will be omitted to protect the guilty.

Coming Soon : Soon to be Deleted Domains

Posted on July 27th, 2006 by amd.

A few weeks ago I created a data feed in the same vein as Paul Kedrosky’s (now defunct) BubbleWire. The Coming Soon feed lists “interesting” domain names that will soon expire. For my purposes, “interesting” means that the domain name is a common english word. Here’s the FeedBurner link:

http://feeds.feedburner.com/gurge/coming_soon

For the technically proficient among you, here’s how Coming Soon is generated:

  1. Each night a Ruby script fires up on my linux box.
  2. The script fetches the list of expiring domains from pool.com. You can find the link on their home page.
  3. Each domain is given a quality ranking.
  4. For the time being I’m only interested in domains with QUALITY=1, which means that the domain is in the 3esl list.
  5. The interesting domains are placed in a db.
  6. The RSS feed is generated and uploaded to gurge.com, where it gets picked up by feedburner.

I have several other quality rankings:

  • is the domain an uncommon word?
  • is the domain two common words put together?
  • is the domain two uncommon words put togehter?
  • etc.

I found that the other lists are way too long to be useful for casual domain watchers like me.

RAID is Real Easy

Posted on July 26th, 2006 by amd.

Putting together a PC from scratch is fun and not too difficult. Line things up, put in a few screws and flip the switch. If you hear the happy POST beep, you’re good to go.

Installing the OS is another story. I’ve fought (and won) several epic battles with various linux distros, trying to convince them to accept common PC hardware. Hopefully we’re near the end of the “compile your own kernel and drivers” era. On the other hand, Windows always installs perfectly. Locating and installing drivers is generall pretty easy since hardware companies target Windows as their main platform.

This time, things were different. I’m embarassed to say that I ignored the warnings on the newegg message board. I figured they were out of date, or uninformed. In retrospect I should have paid far more attention to things like this in the newegg reviews:

ASRock 939SLI32-eSATA ATX AMD Motherboard

Pros: …

Cons: Sata/Sata Raid has to have a floppy for f6 (not included), but the drivers CD is bootable and has will make the floppy with easy to follow instructions.”

Let me translate this into something more meaningful for the casual reader.

When you try to install Windows XP onto your brand new SATA RAID array, Windows won’t recognize the drive. Googling indicates that you have to press F6 during an obscure 3-second interval of the initial XP install process. A prompt flashes on screen for a few milliseconds to inform you of this fact.

If you manage to hit F6 quickly enough, Windows will then ask you to insert a floppy with the correct SATA drivers. Not a CD or USB key, a floppy. A floppy?!?! Who the hell has floppy drives anymore, especially in a home built system?

So, you run out to the nearest store (Computer Stop on Aurora) to buy a floppy drive for ~$15. When you get home, you have to install the floppy drive into your old PC in order to copy the SATA drivers to a disk.. Let’s hope you haven’t yet taken apart the old PC. Unfortunately, it’s impossible to find the SATA drivers online. You have to use the magic install CD that came with the motherboard. Let’s also hope you haven’t taken the CDROM out of your old PC!

Don’t forget to detach the floppy from the old PC and drop it into the new PC.
Assuming you’ve done everything correctly, and you have good reflexes, you will now be able to press F6 and tell Windows about the drivers so it can talk to your RAID array. Luckily I played a lot of Egyptian Ratscrew as a child so that part wasn’t too much of a problem. Windows will now install perfectly.

Time spent assembling the new PC : 20 minutes
Time spent getting XP to recognized my RAID array : 6 hours

Blackbox

Posted on July 25th, 2006 by amd.

“Blackbox” is my new Windows PC. I ordered the components from newegg and put it together last week. Here’s the breakdown:

Total cost including tax and shipping ~$850. This is a big upgrade from my old machine, which had a dinky P4 and only 512mb of RAM.

It’s probably not obvious from the above list but this machine is almost totally silent. The Antec Solo case doesn’t have a traditional drive cage. Instead, the drives hang in a nylon mesh to absorb the vibration. The sidewalls of the Solo have a layer of soft acoustic paneling. The Seasonic S12 is an efficient and nearly silent power supply. Diligent readers may recall that my old PC was discarded due to a faulty video card fan. The Gigabyte video card is fanless. Instead of using the stock AMD Athlon cooler, I dropped $12 for the quiet Arctic Alpine. I probably paid a $100 premium to have a quiet PC instead of a noisy one.

Newegg is an amazing store, and I think that they’re quietly blazing a trail toward better online retailing. Their search features are stellar. Once I’d decided to buy an Athlon 64, I could easily perform a search for “socket 939 motherboards < $100″. Most popular items are absolutely stuffed full of customer reviews. For some reason, unlike the uniform “this product is great!” reviews on Amazon, people will actually give a product zero stars if it doesn’t work for them or if they’re unhappy with the service.

I buy a new PC every few years. Whenever I start shopping I obsessively try to get educated about the latest and greatest, but inevitably I don’t have quite enough time or energy to learn everything. I’m somewhat conservative and the products I buy were bleeding edge a year or two earlier. Sometimes it’s hard for me to make an informed decision.

It may sound strange, but at least THREE of the items on my list were picked out almost solely based on newegg customer reviews. I don’t consider myself a sophisticated PC buyer and it’s a relief to know that my G.SKILL ram gets an average of 5 stars across 150 reviews. If you’ve ever had an encounter with cheap ram before you’ll understand why this is important. There’s definitely a cult of newegg out there, and I remember how amazed I was when Joe, one of my Jobster co-workers, showed up to work in a newegg t-shirt.

Those of you who always buy Dell - please stop and go to newegg instead. Putting a PC together is (usually) fun, easy, and educational. That is, unless you have to setup SATA RAID drivers under XP, which can be a complete nightmare. I’ll tell you all about it in my next post.

Updated: added product links for Patrick

Still Windows After All These Years

Posted on July 22nd, 2006 by amd.

My old shoebox Windows PC was emitting a strange whining sound. I cracked open the case and discovered that the cheapo fan on my cheapo video card was dying. Tending toward lazy, I tried buying a new fan. I carefully measured the dimensions and ordered a similar model from newegg. Total cost : $10. I guess in this case I was a little too lazy because when I tried to install my gleaming new fan I discovered that the old fan was mounted directly on the heat sink. There was no way the new fan would fit without major surgery.

Maybe I should bite the bullet and buy a new video card? I could probably get an equivalent model on ebay for around $75. Though my shoebox was only two years old, it’s AGP slot made it a relic from another era. If I bought a new AGP video card, I would probably end up throwing it out along with the shoebox PC.
I decided to “save” the $75 and buy a new PC instead.

Like the assassination of Archduke Ferdinand, the wheezing fan was a catalyst for massive change in my life. A new question arose. Was it time to Switch?

Flashback to 1997, the last time I owned a Mac. Back in college I was like a mini John Sculley. I pushed Monaco and BBEdit on all my disinterested friends. I released amateurish Macintosh freeware. I hacked AfterDark and learned how to crack with MacsBug. But even good things must come to an end. Upon graduation I put away my childish things and moved entirely to the lucrative, disorganized world of Windows and POSIX variants.

Several years passed. Exciting, productive, disorganized years on XP and Linux boxes. Recently, I’ve jealously watched while one friend after another loudly switched to MacOS, with their shiny screens and bizarre motion sensor hacks. They flaunt their beautiful machines at every opportunity. It’s very tempting, especially for an old school Mac fiend like your’s truly.

So why won’t I switch? Let me enumerate a few good reasons:

Half-Life 2 (and Episode 1-N)
Oblivion
Chronicles of Riddick
etc.

Future posts will detail my new rig and provide helpful hints to make Windows development bearable.

hello, world!

Posted on July 22nd, 2006 by amd.

My name is Adam Doppelt. I am a software developer by trade, but I think of myself as a serial entrepreneur. This blog is a repository for random thoughts bouncing around my brain. Areas of interest include software development, startups and games.

See if you can get through my spam filter:

amd@gurge.com