<commit_before>{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Risk where

import Control.Monad.Random
import Control.Monad
import Data.List (sort, foldl')

------------------------------------------------------------
-- Die values

newtype DieValue = DV { unDV :: Int } 
  deriving (Eq, Ord, Show, Num)

first :: (a -> b) -> (a, c) -> (b, c)
first f (a, c) = (f a, c)

instance Random DieValue where
    random           = first DV . randomR (1,6)
    randomR (low,hi) = first DV . randomR (max 1 (unDV low), min 6 (unDV hi))

die :: Rand StdGen DieValue
die = getRandom

------------------------------------------------------------
-- Risk

type Army = Int

data Battlefield = Battlefield { attackers :: Army, defenders :: Army }
    deriving Show

-- Exercise #1:

battle :: Battlefield -> Rand StdGen Battlefield
battle bf = do 
    atkDice <- (roll attackForce)
    defDice <- (roll defenseForce)
    let lineUp = zip (sort atkDice) (sort defDice)
        winners = map (uncurry (>)) lineUp
        defenseLost = length $ filter id  winners
        offenseLost = length $ filter not winners
    return (Battlefield (atk - offenseLost) (def - defenseLost))
    where 
      atk = attackers bf
      def = defenders bf
      attackForce  = if atk > 4 then 3 else min (atk - 1) 3
      defenseForce = if def > 2 then 2 else def
      roll n = replicateM n die


-- Exercise #2:

invade :: Battlefield -> Rand StdGen Battlefield
invade bf = do
    newBf <- battle bf
    if attackers newBf > 1 && defenders newBf > 0  
       then invade newBf
       else return newBf

-- Exeercise #3:

successProb :: Battlefield -> Rand StdGen Double
successProb bf = do
    battlefields <- mapM invade (replicate 1000 bf) 
    let wins = foldl' winFold  0 battlefields
    return ((fromIntegral wins) / (fromIntegral . length) battlefields)
    where 
        winFold acc b = acc + if 0 == (defenders b) then 1 else 0<commit_msg>Do it with a rational<commit_after>{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Risk where

import Control.Monad.Random
import Control.Monad
import Data.List (sort, foldl')
import Data.Ratio

------------------------------------------------------------
-- Die values

newtype DieValue = DV { unDV :: Int } 
  deriving (Eq, Ord, Show, Num)

first :: (a -> b) -> (a, c) -> (b, c)
first f (a, c) = (f a, c)

instance Random DieValue where
    random           = first DV . randomR (1,6)
    randomR (low,hi) = first DV . randomR (max 1 (unDV low), min 6 (unDV hi))

die :: Rand StdGen DieValue
die = getRandom

------------------------------------------------------------
-- Risk

type Army = Int

data Battlefield = Battlefield { attackers :: Army, defenders :: Army }
    deriving Show

-- Exercise #1:

battle :: Battlefield -> Rand StdGen Battlefield
battle bf = do 
    atkDice <- roll attackForce
    defDice <- roll defenseForce
    let lineUp = zip (sort atkDice) (sort defDice)
        winners = map (uncurry (>)) lineUp
        defenseLost = length $ filter id  winners
        offenseLost = length $ filter not winners
    return (Battlefield (atk - offenseLost) (def - defenseLost))
    where 
      atk = attackers bf
      def = defenders bf
      attackForce  = if atk > 3 then 3 else min (atk - 1) 3
      defenseForce = if def > 2 then 2 else def
      roll n = replicateM n die


-- Exercise #2:

invade :: Battlefield -> Rand StdGen Battlefield
invade bf = do
    newBf <- battle bf
    if attackers newBf > 1 && defenders newBf > 0  
       then invade newBf
       else return newBf

-- Exeercise #3:

successProb :: Battlefield -> Rand StdGen Double
successProb bf = do
    battlefields <- replicateM 1000 (invade bf) 
    let wins = foldr winFold 1 battlefields
    return $ fromRational wins

winFold :: (Integral a) => Battlefield -> Ratio a -> Ratio a
winFold bf ratio = if 0 == (defenders bf)
                      then (numerator ratio + 1) % (denominator ratio)
                      else (numerator ratio) % (denominator ratio + 1)
{'fraction_non_alphanumeric': 0.15153035624686403, 'fraction_numerical': 0.01154039136979428, 'mean_word_length': 2.1124121779859486, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6289362', 'n_tokens_mistral': 1313, 'n_tokens_neox': 1212, 'n_words': 459}