import qualified Data.Map as Map import qualified Data.Set as Set type LifeField = Set.Set (Int,Int) lifeStep :: LifeField -> LifeField lifeStep start = next where occupied = Set.toList start expandNeighbour (y,x) = [Map.singleton (j,i) 1 | i <- [x-1 .. x+1] , j <- [y-1 .. y+1] , i /= x || j /= y] expandedNeighbours = concatMap expandNeighbour occupied neighbours = Map.unionsWith (+) expandedNeighbours nbCount xy = Map.findWithDefault 0 xy neighbours die n = n < 2 || n > 3 withoutDead = Set.filter (not . die . nbCount) start newborn = Map.keysSet $ Map.filter (==3) neighbours next = Set.union withoutDead newborn display field | Set.null field = ["."] | otherwise = lines where xs = map snd $ Set.toList field ys = map fst $ Set.toList field xMin = minimum xs yMin = minimum ys xMax = maximum xs yMax = maximum ys risen x y = Set.member (y,x) field row y = [if risen x y then '*' else '.' | x <- [xMin..xMax]] lines = map row [yMin..yMax] fromLines :: [String] -> LifeField fromLines lines = Set.fromList $ map fst $ filter snd $ concat $ zipWith fromLine [0..] lines where fromLine y cs = zipWith (fromChar y) [0..] cs fromChar y x '*' = ((y,x),True) fromChar y x _ = ((y,x),False) lifeSteps field n | n > 0 = do mapM putStrLn $ display field putStrLn (replicate 10 '-') lifeSteps (lifeStep field) (n-1) | otherwise = do putStrLn $ "Field inhabitants count: "++show (Set.size field) return () gliders = fromLines [ "........................" ,"....***.......***......." ,"....*...........*......." ,".....*.........*........" ,".........*.............." ,".........*.............." ,".........*.............." ,".....*.........*........" ,"....*...........*......." ,"....***.......***......." ,"........................" ]
Запуск в ghci 'lifeSteps gliders 100', например.
Всё написано оптимальным образом: память требуется только под живые клетки.
Ради прикола. И ради спора в rsug. ;)