import Control.Monad (forM_) import Data.Function (on) import Data.List (sortBy, unfoldr) import System.Random (RandomGen, randoms, getStdGen, split) import Data.HashSet (HashSet, fromList, member) data Player = Player { name :: String , rank :: Int , matchLimit :: Int } deriving (Show, Eq) players :: [Player] players = [ Player "orbekk" (-1) 4 , Player "ltsang" (-4) 4 , Player "sebh" 2 4 , Player "fringd" (-1) 4 , Player "sheyrax" (-2) 2 ] shuffle :: (RandomGen g) => g -> [a] -> [a] shuffle g xs = map snd $ sortBy (compare `on` fst) (zip rs xs) where rs = randoms g :: [Int] matchOnce :: [String] -> [String] -> [(String, String)] matchOnce (p:ps) (q:qs) | p == q = matchOnce (p:ps) qs | otherwise = (p, q) : matchOnce (filter (/=q) ps) (filter (/=p) qs) matchOnce _ _ = [] subtractMatches :: [(String, String)] -> [Player] -> [Player] subtractMatches matches players = fmap subtractMatch players where matchPlayers :: HashSet String matchPlayers = fromList (map fst matches ++ map snd matches) subtractMatch p | (name p) `member` matchPlayers = p { matchLimit = matchLimit p - 1 } | otherwise = p matchPlayers :: (RandomGen g) => g -> [Player] -> [(String, String)] matchPlayers _ [] = [] matchPlayers _ [p] = [] matchPlayers g players = matches ++ matchPlayers g3 nextPlayers where matches = matchOnce (shuffle g1 playerNames) (shuffle g2 playerNames) playerNames = fmap name players (g', g1) = split g (g2, g3) = split g' nextPlayers = filter ((>0) . matchLimit) (subtractMatches matches players) iterativeMatchPlayers :: (RandomGen g) => g -> [Player] -> [[(String, String)]] iterativeMatchPlayers g players = unfoldr f (g, players) where f (g, players) = Just (matchPlayers g1 players, (g2, players)) (g1, g2) = split g main = do g <- getStdGen let matches = iterativeMatchPlayers g players !! 1010 forM_ matches $ \(p, q) -> putStrLn $ p ++ " vs. " ++ q