Phillip Trelford's Array

POKE 36879,255

Sixes and Sevens

A few weeks back I built a simple console application with my 9yo using F# for practicing the times tables.

TimesTable

So that he could share the game with his friends at school I followed Attwood’s law and created a JavaScript version.

Times Tables Java Script

However I wasn’t particularly satisfied with this JavaScript version. Among other things, in the F# version I was able to easily do a timed count down before the game starts:

for message in ["Get Ready";"3";"2";"1";"Go"] do 
    printfn "%s" message
    Thread.Sleep 1000

To do the same thing in JavaScript would require nesting calls to Window setTimeout, which is neither readable or maintainable. I decided to skip this, but in doing so lost some of the aesthetic of the original implementation.

With the recent release of the Pit project that compiles F# code to JavaScript, just over a week ago, I thought I’d revisit the game. Using F# computation expressions (a mechanism used to implement F# Async workflows), it was possible to implement the get ready sequence relatively cleanly:

delay {
    root.InnerHTML <- "3"
    do! Delay 1000
    root.InnerHTML <- "2"
    do! Delay 1000
    root.InnerHTML <- "1"
    do! Delay 1000
    root.InnerHTML <- "Go"
    do! Delay 1000
    play total root
}

This generates the following quite heavily nested JavaScript:

SevenSixes.App.countdown = function (total) {
   return function (root) {
      return function (builder) {
         return builder.Delay(function () {
            root.innerHTML = "3";
            return builder.Bind({
               Item1: new SevenSixes.App.Delay.Delay(1000),
               Item2: function (_arg5) {
                  root.innerHTML = "2";
                  return builder.Bind({
                     Item1: new SevenSixes.App.Delay.Delay(1000),
                     Item2: function (_arg4) {
                        root.innerHTML = "1";
                        return builder.Bind({
                           Item1: new SevenSixes.App.Delay.Delay(1000),
                           Item2: function (_arg3) {
                              root.innerHTML = "Go";
                              return builder.Bind({
                                 Item1: new SevenSixes.App.Delay.Delay(1000),
                                 Item2: function (_arg2) {
                                    SevenSixes.App.play(total)(root);
                                    return builder.Zero();
                                 }
                              });
                           }
                        });
                     }
                  });
               }
            });
         });
      } (SevenSixes.App.get_delay);
   };
};

Basically code gets mapped to a Bind method that runs a function after a specified timeout:

type Delay = Delay of int
type DelayBuilder() =
    [<Js>] member x.Bind(Delay t, f:unit->unit) = window.SetTimeout(f, t)
    [<Js>] member x.Return(t)  = fun () -> t
    [<Js>] member x.Delay(f:unit->'a) = f()
    [<Js>] member x.Zero () = ()
let [<Js>] delay = DelayBuilder()

So without further ado, click below to play the game:

12x3

As far as I’ve tested the game seems to run on IE9, Mozilla and Chrome browsers, along with Blackberry, iPhone and Windows Phone 7 (Mango).

P.S. If you have an Android phone, please let me know if the game works for you.

Resources

Comments (2) -

  • Joel

    11/20/2011 9:18:54 AM |

    It works on Android, but not on Opera Mobile, where there's no way to get it to notice that you typed an answer. Perhaps trigger with onchange as well? Also for mobile it would be nice to use the html5 input type that prompts a numeric keyboard instead of an alphabetic one. Perhaps firing the focus event after changing the problem would prevent the user having to tap the input box over and over to get a keyboard...

  • Phil

    11/20/2011 12:14:51 PM |

    Thanks Joel!

    I'll take a look at how I can use the HTML 5 number input type while remaining compatible with older browsers.

    Cheers,
    Phil

Comments are closed