module Combinatorics.Mastermind (
Eval(..),
evaluate,
evaluateAll,
formatEvalHistogram,
numberDistinct,
) where
import qualified Combinatorics.Permutation.WithoutSomeFixpoints as PermWOFP
import Combinatorics (binomial)
import Text.Printf (printf)
import qualified Data.Map as Map; import Data.Map (Map)
import qualified Data.Foldable as Fold
import qualified Data.List.HT as ListHT
import Data.Tuple.HT (mapPair)
data Eval = Eval {black, white :: Int}
deriving (Eq, Ord, Show)
evaluate :: (Ord a) => [a] -> [a] -> Eval
evaluate code attempt =
uncurry Eval $
mapPair
(length,
Fold.sum . uncurry (Map.intersectionWith min) .
mapPair (histogram,histogram) . unzip) $
ListHT.partition (uncurry (==)) $
zip code attempt
evaluateAll :: (Ord a) => [[a]] -> [a] -> Map Eval Int
evaluateAll codes attempt = histogram $ map (evaluate attempt) codes
formatEvalHistogram :: Map Eval Int -> String
formatEvalHistogram m =
let n = maximum $ map (\(Eval b w) -> b+w) $ Map.keys m
in unlines $
zipWith
(\b ->
unwords .
map (\w -> printf "%6d" $ Map.findWithDefault 0 (Eval b w) m))
[0..] (reverse $ tail $ ListHT.inits [0..n])
histogram :: (Ord a) => [a] -> Map a Int
histogram = Map.fromListWith (+) . map (\a -> (a,1))
numberDistinct :: Int -> Int -> Int -> Int -> Integer
numberDistinct n k b w =
binomial (toInteger k) (toInteger b)
*
numberDistinctWhite (n-b) (k-b) w
numberDistinctWhite :: Int -> Int -> Int -> Integer
numberDistinctWhite n k w =
let ni = toInteger n
ki = toInteger k
wi = toInteger w
in binomial ki wi * PermWOFP.numbers !! k !! w * binomial (ni-ki) (ki-wi)