Friday, December 30, 2011

Slot Machines

Playing slot machines can be pretty fun, but don't be fooled by claims that the casino has the "loosest slots", odds are probably still against you. I thought it would be fun (and cheaper!) to build a slot machine simulator using Factor.

Our slot machine is going to be a console application that will look something like this:


Even though our slot machine is text-only, we can still make use of some nice unicode characters to be our symbols:


Each spin chooses a different symbol at random (each being equally likely):

: spin ( value -- value' )
    SYMBOLS remove random ;

To reproduce the feel of spinning a slot machine, we will introduce a slight delay so that the wheel spins fast at the beginning and then slower and slower until it stops on a symbol:

: spin-delay ( n -- )
    15 * 25 + milliseconds sleep ;

Spinning the slot machine takes a spin number, delays for a bit, then rotates each wheel (we stop spinning the first column after 10 spins, the second after 15, and the last after 20):

: spin-slots ( a b c n -- a b c )
        [ spin-delay ]
        [ 10 < [ [ spin ] 2dip ] when ]
        [ 15 < [ [ spin ]  dip ] when ]
        [ drop spin ]
    } cleave ;


Each "spin" of the slot machine will be printed out. Using ANSI escape sequences, we move the cursor to the top left ("0,0") of the screen and then issue a clear screen instruction. Then we print out the current display and flush the output to screen:

: print-spin ( a b c -- a b c )
    "\e[0;0H\e[2J" write
    "Welcome to the Factor slot machine!" print nl
    "  +--------+" print
    "  | CASINO |" print
    "  |--------| *" print
    3dup "  |%c |%c |%c | |\n" printf
    "  |--------|/" print
    "  |    [_] |" print
    "  +--------+" print flush ;


The player will have won if, after all the spins, the "pay line" shows three of the same characters:

: winner? ( a b c -- )
    3array all-equal? nl "You WIN!" "You LOSE!" ? print nl ;

Playing the slot machine consists of spinning the wheels 20 times, displaying each spin to the user, then checking if the user has won the game.

: play-slots ( -- )
    f f f 20 iota [ spin-slots print-spin ] each winner? ;

Since our casino wants the user to keep playing, we make it really easy to just hit ENTER to continue:

: continue? ( -- ? )
    "Press ENTER to play again." write flush readln ;

And, to finish it off, we define a "MAIN" entry point that will be run when the script is executed:

: main-slots ( -- )
    [ play-slots continue? ] loop ;

MAIN: main-slots

The code for this is on my Github.


Kevin Reid said...

Depending on the terminal and connection you may get blank flashes due to clearing the screen, even if you flush selectively. Since you're writing the full “image” each time you can just let it overwrite. Remove the screen clearing from print-spin and put it at the beginning of play-slots.

mrjbq7 said...

@Kevin: Good idea, thanks! I've updated the version on my Github.