In today’s Programming Praxis exercise our task is to run a simulation of a ballgame to see if the scoring mechanic is fair. The provided Scheme solution clocks in at 25 lines. Let’s see if we can do any better.
First, some imports.
import Control.Applicative import Control.Monad import Data.List import System.Random
After a match, the winner gets a point and the loser is moved to the end of the queue.
match :: Int -> [(a, Int)] -> Int -> [(a, Int)] match ps ~(x:y:r) w = (p,s + if ps > 7 then 2 else 1) : r ++ [c] where ((p,s), c) = if w == 0 then (x,y) else (y,x)
A game ends when one of the teams has 7 or more points.
game :: IO Int game = f 0 (zip [1..8] [0,0..]) . randomRs (0,1) <$> newStdGen where f ps a ~(x:xs) = maybe (f (ps+1) (match ps a x) xs) fst $ find ((>= 7) . snd) a
To simulate the game, we play a number of games and calculate the winning percentages of each team.
simulate :: Int -> IO [Float] simulate n = (\ws -> map (\x -> 100 * (l x - 1) / l ws) . group . sort $ ws ++ [1..8]) <$> replicateM n game where l = fromIntegral . length
All that’s left is to run the simulation.
main :: IO () main = mapM_ print =<< simulate 10000
That leaves us with 7 lines, more than a two thirds reduction compared to the Scheme solution. That’ll do nicely.