April 29th, 2006

  • evan

diplomacy xul

I went out to dinner with Darin Fisher (and others) last night, following a talk he gave on Firefox. This finally prompted me to get around to starting work on my Diplomacy viewer.

If you don't know it, Diplomacy is a great game that's decades old. (That site is full of good resources, including the rules...) It's like Risk but with most of the chance removed -- I resolved never to play Risk again when I attacked someone with 80 armies and still lost to their ~30 -- and bit more abstract strategy with a whole lot of schmoozing added. And it plays by mail in a very relaxed sort of way, which is nice for me 'cause I don't have a lot of free time to dedicate to today's games. (I'm sure WoW is fun, but I think I'd be too depressed to enjoy it realizing I was dedicating all these hours to incrementing numbers.)

Anyway, Diplomacy the game is fun, but the site we play it with is sorta lacking. It definitely gets the job done, but it has a bunch of misfeatures, like pretty much everything being done by form POSTs (so you can't bookmark any URLs), being generally slow, and producing nearly unreadable maps (you can see my current game here), which are made even worse by my dpi problem.

So I had been thinking about making a DHTML Diplomacy viewer/planner that would let you shuffle the pieces around. But the tricky part is that showing the moves in a turn involve drawing arrows on a map, which are hard to do with HTML*. I had thought this would be a fun opportunity to check out the Firefox canvas implementation. (I eventually switched to the SVG+DOM approach.)

Partway through implementing this, and looking up something on quirksmode for the n millionth time, I realized that I'm Firefox-specific already (using SVG, and I don't test on IE anyway), and, thinking of talking with Darin, I may as well use the more pleasant API and widgets of XUL. It's actually pretty straightforward. Of course, except, when you want to do something slightly outside of the box (which apparently includes "putting something in a scrollable region but still being able to compute mouse coordinates on the underlying surface") involved some kinda-gross hacking, but I figured it out after a few hours.

Anyway, I've got general board layout, some status file parsing, and some UI wrapped around it working. You can run it directly out of the repository, too, though of course it only works if you're using Firefox: dip.xul. But it's still mostly vaporware.

* With maybe 30 vertices on the board graph, 6 edges per vertex, and probably averaging around 3 moves per vertex+edge, I suppose I might be able to pregenerate all the possible move graphics offline...
  • evan

skype brokenness

I talk with Meena via Skype* on the weekends. It usually works for 10-20 minutes and then suddenly the audio drops out. She can still hear me, but I hear nothing, not even static. Usually it seems to be fixed by her unplugging/replugging her headphones (USB, ick, I know) and sometimes restarting Skype.

Most of the indicators (packet loss, etc.) seem reasonable. It sounds like her computer may be getting really loaded while running Skype -- it's slower than mine, and even on mine (1.7ghz Pentium M) the audio gets choppy if I run other programs at the same time. The other weird thing is that I never hear it ring, even when the call is first connected and the audio is otherwise fine -- it'll say "ringing" on the UI but there's no sound.

The other strange thing is that initially we'll talk for 30 minutes before it breaks, but then each subsequent time we connect it only will work for five.

Do any of you have any suggestions on how to make this work? I'm happy to switch software as well if that makes a difference.

* Why not Google Talk? (1) Brand loyalty like that is just silly; (2) I actually would've used it since we both already have gmail accounts, but they don't support Linux.
  • evan

hidden state pattern in javascript

A common pattern in functional languages is attaching some mutable state to a function that needs to exist across calls. (Like static local variables in C.) You could just make the state a global variable alongside the function, but you can hide it in a cute way.

The idea is you write a function that has the state as a local variable, then return a closure holding that variable. It's harder to explain in text than it is in code:
# let at_most n f =
    let count = ref 0 in  (* "ref" is ml for "mutable" *)
    fun x ->
      if !count < n then (incr count; f x)
                    else ()
val at_most : int -> ('a -> unit) -> 'a -> unit = <fun>

(* and demonstration: *)
# let say_hi () = print_endline "hi";;
val say_hi : unit -> unit = <fun>
# let limited_say_hi = at_most 2 say_hi;;
val limited_say_hi : unit -> unit = <fun>
# limited_say_hi ();;
- : unit = ()
# limited_say_hi ();;
- : unit = ()
# limited_say_hi ();;
- : unit = ()
(* ... it ran twice, so it will no longer
   produce any output. *)

It occurred to me that this would be pretty useful in JavaScript, especially when you're doing alert()-based debugging. The problem you often run into is that you want to alert() inside of a loop to see a value but if you let the loop go you get an endless cascade of popups.

function at_most(n, f) {
  var i = 0;
  return function(x) {
    if (i++ < n) f(x);
var aler = at_most(3, alert);

(I can't promise I got the JS-closure-fu right, but it seems to work.) I can now sprinkle calls to aler(...) throughout my code and it'll never bring up more than three popups.