Sunday, October 6, 2013

Rock-paper-scissors

The ever-popular game of rock-paper-scissors is a fun game to play with friends and, as it turns out, a fun game to implement in Factor.

We have an open issue to finish some planned changes to multi-methods. While largely a syntax improvement, one of the challenges will be keeping the simple case fast while providing for enhanced dispatch ability. As a way of showing how the current syntax works, I thought I would implement the "rock-paper-scissors" game.

First, we define each type of object in our game:

SINGLETONS: rock paper scissors ;
Note: we are not playing rock-paper-scissors-lizard-spock, which is a bit more complicated and possibly a lot more fun.

Next, we need to determine a winner using multi-methods. You'll notice that our generic method dispatches off the types of two objects. We can use any type here, including predicate classes, but can simply dispatch off the singletons we defined above:

FROM: multi-methods => GENERIC: METHOD: ;

GENERIC: beats? ( obj1 obj2 -- ? )

METHOD: beats? { scissors paper } 2drop t ;
METHOD: beats? { rock scissors } 2drop t ;
METHOD: beats? { paper rock } 2drop t ;
METHOD: beats? { object object } 2drop f ;

Each play of the game will determine the outcome (win, lose, or tie) and print a summary:

: play. ( obj1 obj2 -- )
    {
        { [ 2dup beats? ] [ "WIN" ] }
        { [ 2dup = ] [ "TIE" ] }
        [ "LOSE" ]
    } cond "%s vs. %s: %s\n" printf ;

The computer will choose randomly amongst the possibilities:

: computer ( -- obj )
    { rock paper scissors } random ;

And for fun, we will define words to allow the user to play the computer at the listener:

: rock ( -- ) \ rock computer play. ;

: paper ( -- ) \ paper computer play. ;

: scissors ( -- ) \ scissors computer play. ;

A sample output from a few games:

The code for this is available on my GitHub.

No comments: