In today’s Programming Praxis exercise, our goal is to simulate the board game Chutes and Ladders. Let’s get started, shall we?
import Control.Monad import System.Random
First, we write a function to handle the chutes and ladders. If a player lands on one these squares, he automatically moves to the corresponding destination.
promote :: Num a => a -> a promote n = maybe n id $ lookup n [(16,6), (47,26), (49,11), (56,53), (62,19), (64,60), (87,24), (93,73), (95,75), (98,78), (1,38), (4,14), (9,31), (21,42), (28,84), (36,44), (51,67), (71,91), (80,100)]
A single player game consists of throwing dice until you land on the goal.
game :: IO [Int] game = fmap (tail . turn 0 . randomRs (1,6)) newStdGen where turn 100 _ =  turn n ~(d:ds) = n : turn (if n+d > 100 then n else promote $ n+d) ds
The length of a multiplayer game can be determined by taking the shortest of k single player games. For the statistics, we look at the shortest game, the longest game and the average length.
stats :: Int -> Int -> IO (Int, Int, Float) stats k n = fmap (\rs -> (minimum rs, maximum rs, average rs)) . replicateM n . fmap minimum . replicateM k $ fmap length game where average xs = fromIntegral (sum xs) / fromIntegral n
We simulate the results for games with one to five players.
main :: IO () main = do print =<< stats 1 100000 print =<< stats 2 100000 print =<< stats 3 100000 print =<< stats 4 100000 print =<< stats 5 100000
One run produced the following results:
(7,232,39.391) (7,127,26.2546) (7,91,21.8529) (7,73,19.3968) (7,57,17.7395)
The results are within tolerance of the ones produced by the Scheme version. As a regular player of board games myself, I must say this seems like a fairly rubbish game. There’s no interaction, no strategy, the winner is determined purely by chance and the game can theoretically last infinitely long. Do yourself a favor and go play a good game instead.