Chip's Tips for Developers

Contains coding, but not narcotic.

Rattus Reinhardtius: The Rat Race on Django

March 29th, 2010 5:34:50 pm pst by Sterling Camden

image It’s been almost four years since I released the rats upon the unsuspecting world of Ruby on Rails.  Since then, I’ve learned a lot about writing web applications – so I’ve been meaning to update this fun little game with some of the tricks I’ve learned.  But, you know, it hasn’t been exactly a priority.

Then a few weeks ago a prospective client asked me about Django.  I had never used it before, so prior to a conference call with them I downloaded it and worked through a couple of tutorials.

Python is not my favorite language, though it’s far from my least favorite.  A lot about Python is really clean, but some of it just seems anal-retentive – especially the indentation rules.  Oh, it’s consistent all right, but it just strikes me as a Simon Says Rule.  I mean, c’mon – you can’t even use tabs instead of spaces?  Between that and always forgetting the colons I end up wasting a lot of time on simple syntax errors.  At least the interpreter gives helpful error messages.

The web development framework Django seems to be a study in powerful simplicity.  It makes the usual things dead easy, and the less usual things still easy — just a little less so.  A web project requires a lot less scaffolding to sort through than in Rails.  The project/application generator writes a lot less code for you (almost none, in fact), not only because the design of the framework is simpler, but also because it relies more on inheritance and less on generated code.  I have never lost track of where code lives in a Django project the way I used to forget where I put my Rails code.

As an exercise, I decided to rewrite the Rat Race in Django, which you can download below.  This version also makes liberal use of JavaScript, especially the jQuery library (which I’ve included in the directory where the app expects to find it).  If you try to run the race with JavaScript disabled in your browser, you’ll get a nice red and yellow banner telling you that you need it.

To try this app out, unzip the download wherever you like.  Make sure you have Python and Django installed.  I’d recommend using the latest versions (Python 2.6, Django 1.1.1), as I haven’t tested with any others.  Go to the “ratrace” directory and start the included server:

manage.py runserver

Now you can bring up a browser (anything except Internet Explorer) and navigate to http://127.0.0.1:8000/ — which should start the next race.  I’ve included a sqlite3 database containing 72 rats.  You can administer this database by navigating to http://127.0.0.1:8000/admin/ – and if you don’t like my rats, you can just delete the database (rats.sqlite), regenerate it:

manage.py syncdb

and enter your own.  You must have at least three rats to run the race.

In this version of the rat race (back story here), I use the Ajax and JSON capabilities of jQuery and Django to drive the race.  jQuery’s animation lets me smooth the rat movements so you don’t get quite the jumpy effect of my earlier versions, and it allows me to ping the server for updates a little less frequently.  You can run multiple browsers pointed at the same server and get essentially the same race except for unimportant rat gestures.  I’ve tested it with the latest versions of Chrome, Firefox, Safari, Opera (some minor display issues) and Internet Explorer (no worky, not sweating it).

As in all earlier versions, each rat’s likelihood of moving forward or backwards in the race is controlled by a random number generator that’s biased according to their track record.  So a rat that has done well in the past is more likely to do well in the future.  But that isn’t set in stone, and I’ve introduced another random factor that can’t be seen by the user.  Instead of basing the odds strictly on that bias, I now simulate between $200 and $10,000 in bets of $2, $5, or $10 each.  Each bet is randomly chosen, but skewed according to the rat’s track record and an additional random factor.  This is intended to simulate a hunch, a whim, or individual knowledge that may or may not be in line with the additional random factor controlling the rat’s performance in that particular race.  The result is, I think, fairly close to true life experience – in which the best rat usually does well but doesn’t always win, while the poorer rats hardly ever place but sometimes surprise everyone.

Some interesting coincidences with real life fell out of algorithmic decisions I made.  In order to avoid divide-by-zero errors, I added one to the number of races run and then offset that by a random number.  This has the result of making the track record count more for more experienced rats, and conversely newer rats are less predictable.

I placed a chunk of cheese at the end of the race as an incentive for the rats.  When they reach it, they appear to eat it by overlapping its image.  At that point, I change the motion algorithm to avoid backing up (and thus regurgitating the cheese), and also slow it down a little.  This increases the excitement at the end of the race, because even if a rat reaches the cheese first, another rat may still be able to come up and polish off his/her chunk of cheddar to win.  The transition from the normal speed down to the eating phase also improves the favoring of rats with a better bias, so the rats who got near the end on sheer luck may not be able to hold out all the way to reaching the cheese.

The only thing I might add in the future is the ability to track bets from the user.

Enjoy the races.  For background music, may I recommend the works of Django’s namesake?

download

Posted in AJAX, Django, JSON, JavaScript, Python, Web, jQuery | 1 Comment » RSS 2.0 | Sphere it!

Swapping two variables in Synergy/DE

July 3rd, 2009 9:19:44 am pst by Sterling Camden

Swapping the contents of two variables is a common programming task that should be easy to perform.  In scripting languages of the Perl tradition it’s dead simple, using simultaneous assignment:

Perl:  ($a,$b) = ($b,$a);
Python:  (a,b) = (b,a)
Ruby:  a,b = b,a

Common Lisp has a macro for the purpose:

(rotatef a b)

Most other languages require more than one statement.  For instance, in C (assuming that a and b are integers):

{ int swap = a; a = b; b = swap; }

which isn’t bad – after all, you could define it as a macro (although you’d have to make the type an argument as well if you wanted to support multiple types).

In Synergy/DE, you can’t put it all on one line, so you can’t define it as a macro:


begin
  data swap, int, a
  a = b
  b = swap
end

Well, actually, now you can define it as a macro, by using Let and Progn.  The downloadable code below does just that, in an include file named swap.def.  The following macros are defined therein:

swap(obj1, obj2, type) – swap two objects, casting as type.
swapi(int1, int2) – swap two integers
swapd(dec1, dec2) – swap two decimal values
swapa(a1, a2) – swap two alphanumeric values (or strings)
swapv(var1, var2) – swap two Vars

Note that you can swap different types as long as both the variables and the objects they contain are compatible with the specified type.  The type you specify will be used to cast both operands, so it can either be a common ancestor or one of the objects has to provide an op_Explicit conversion.  In the case of primitives, “type” will specify the type of conversion you want performed, via Var.

The “type” parameter must be enclosed in parentheses, if it is a type.  If parentheses are omitted, type will be treated as a function, method, or macro name, to which will be passed an object parameter (either a Var or some other class).   This gives you a way to insert your own conversion routine if you need it.  Or you can omit type entirely, if both operands are just object variables (@*).

The test routine test_swap.dbl provides some tests, using assertions.  As you can see from the commented out code at the end, the swap macro does not work for properties (including indexer properties).  That’s because Synergy/DE does not return a value from an assignment to a property.  This issue has already been reported as tracker 23837, so hopefully it will be fixed soon.  Because of that limitation, you can’t use these macros to swap elements of an ArrayList.  But I have even bigger plans for extending ArrayList that will include a method for swapping elements, among other things.

UPDATE 2009-09-13: for Synergy/DE 9.3

Posted in SynergyDE | No Comments » RSS 2.0 | Sphere it!

Associative array (hash, dictionary) class for Synergy/DE

March 16th, 2008 1:29:29 pm pst by Sterling Camden

One of the more useful features found in many scripting languages is the concept of an associative array.  It goes by the name of a hash in Perl and Ruby, or a dictionary in Python.  A hash lets you associate key values with data, and later retrieve a datum using its key — often via a syntax similar to an array element reference.  On this lazy Sunday morning it occurred to me that Synergy/DE could benefit from such a facility.

The downloadable code below contains a Hash class that acts as an array of untyped objects with an alphanumeric index.  You can control the case-sensitivity of the key by setting the CaseSensitive property (which defaults to false) before adding the first item.  Because this uses the new Synergy class syntax, it requires at least version 9 of Synergy/DE.

Usage:

myhash = new Hash()
myhash['key'] = anyobject
anyobject = myhash['key']

If a key is not found, then ^null is returned.  Thus, to clear an item, just set its entry to ^null.  This hash does not reclaim any array entries, so it will eventually grow to the size of all keys that have ever been used to store a value.

As you can see in the code, the class uses an internal ArrayList to store the object references, and associates the keys with array indices using the built-in Symbol Table functions in Synergy/DE.  If the Symbol Table API could take objects as associated data then we wouldn’t need this class at all — but it doesn’t.  The Symbol Table API can only take primitive types for data, so we have to introduce this extra layer of reference in order to support objects.

Synergy/DE’s static typing gets in our way in usage as well.  Because the elements are typed as System.Object, client code must cast any returned element to the type expected.  If a primitive type is to be stored in the hash, then it must be cast as an object in order to box it.  Even though you can store a hash as an element of a hash to created nested arrays, you cannot dereference a nested element using a single statement, because you must first store the nested hash element into a variable of type Hash.  And we don’t get any benefit at all from static typing here.  Of course, you could create a derived class of Hash that expects and returns a specific object type.  That would better be a job for generics, which have not yet been implemented in Synergy/DE.  Personally, I’d like to suspend static typing altogether for a class like this, but that’s just the Ruby talking.

UPDATED 3/22/08: Added an iterator and Count property.

UPDATED 7/6/09: Changed the namespace to “ChipsTips”.

UPDATED 9/9/09: Corrected iterator to use base-zero indexing.

Posted in SynergyDE, Wildly popular | 12 Comments » RSS 2.0 | Sphere it!

Better Tag Cloud