1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
5 import Control.Applicative
6 import Control.Monad.Random
7 import Control.Monad(replicateM, liftM, foldM)
8 import Data.List(sortBy)
10 ------------------------------------------------------------
13 newtype DieValue = DV { unDV :: Int }
14 deriving (Eq, Ord, Show, Num)
16 first :: (a -> b) -> (a, c) -> (b, c)
17 first f (a, c) = (f a, c)
19 instance Random DieValue where
20 random = first DV . randomR (1,6)
21 randomR (low,hi) = first DV . randomR (max 1 (unDV low), min 6 (unDV hi))
23 die :: Rand StdGen DieValue
26 ------------------------------------------------------------
31 data Battlefield = Battlefield { attackers :: Army, defenders :: Army }
33 newBattlefield :: Battlefield -> [DieValue] -> [DieValue] -> Battlefield
34 newBattlefield battlefield rollA rollB =
35 let pairList = zip (sortBy (flip compare) rollA) (sortBy (flip compare) rollB)
37 foldr (\(a, d) acc -> if a > d then Battlefield (attackers acc) ((defenders acc) - 1)
38 else Battlefield ((attackers acc) - 1) (defenders acc)) battlefield pairList
41 battle :: Battlefield -> Rand StdGen Battlefield
42 battle (Battlefield {attackers = a, defenders = d}) =
43 let rolla = replicateM (min (a - 1) 3) die
44 rolld = replicateM (min d 2) die
46 liftA2 (newBattlefield (Battlefield a d)) rolla rolld
49 endgame :: Battlefield -> Bool
50 endgame (Battlefield {attackers=a, defenders=d}) = d == 0 || a < 2
52 enemyDestroyed :: Battlefield -> Bool
53 enemyDestroyed bf = endgame bf && (defenders bf) == 1
55 invade :: Battlefield -> Rand StdGen Battlefield
56 -- invade battlefield = foldM (\acc f -> f $ acc) battlefield (repeat battle)
57 invade battlefield = battle battlefield >>= \bf ->
58 if (endgame bf) then return bf
61 successProb :: Battlefield -> Rand StdGen Double
62 successProb battlefield = (pure (filter enemyDestroyed) <*> replicateM 1000 (invade battlefield))
63 >>= \xs -> return (fromIntegral (length xs) / 1000)