-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLife.hs
More file actions
132 lines (109 loc) · 4.45 KB
/
Life.hs
File metadata and controls
132 lines (109 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
-- file: Life.hs
-- based on https://speakerdeck.com/dmoverton/comonads-in-haskell
import Control.Applicative ((<$>), (<*>))
import Control.Comonad (Comonad, extract, duplicate, extend)
import Data.Foldable (Foldable, foldMap)
import Data.List (unfoldr, intercalate)
import Data.Maybe (catMaybes)
import Data.Monoid ((<>))
import Data.Traversable (Traversable, traverse, sequenceA)
import System.Environment (getArgs)
import System.Random (RandomGen, getStdGen, randoms)
import Codec.Picture (Image, Pixel, GifLooping(LoopingNever))
import Codec.Picture (generateFoldImage, writeGifImages)
import Codec.Picture.Gif (greyPalette)
-- one-dimensional zipper
data Z a = Z [a] a [a]
-- two-dimensional zipper
newtype Z2 a = Z2 (Z (Z a))
type Board = Z2 Bool
-- shift one-dimensional focus back/forth
back, forth :: Z a -> Maybe (Z a)
back (Z (l:ls) c rs) = Just $ Z ls l (c:rs)
back _ = Nothing
forth (Z ls c (r:rs)) = Just $ Z (c:ls) r rs
forth _ = Nothing
-- shift two-dimensiona focus left/right/up/down
left, right, up, down :: Z2 a -> Maybe (Z2 a)
left (Z2 z) = Z2 <$> back z
right (Z2 z) = Z2 <$> forth z
up (Z2 z) = (Z2 <$>) . sequenceA $ fmap back z
down (Z2 z) = (Z2 <$>) . sequenceA $ fmap forth z
toZ :: [a] -> Z a
toZ (c:rs) = Z [] c rs
toZ _ = undefined
fromZ :: Z a -> [a]
fromZ (Z ls c rs) = (reverse ls) ++ c : rs
instance Functor Z where
fmap f (Z ls c rs) = Z (map f ls) (f c) (map f rs)
instance Comonad Z where
extract (Z _ c _) = c
duplicate z = Z (unfoldr ((split <$>) . back ) z)
z
(unfoldr ((split <$>) . forth) z)
split :: a -> (a,a)
split a = (a,a)
instance Foldable Z where
foldMap f (Z ls c rs) = foldMap f (reverse ls) <> f c <> foldMap f rs
instance Traversable Z where
traverse f (Z ls c rs) = Z <$> traverse f ls <*> f c <*> traverse f rs
instance Functor Z2 where
fmap f (Z2 z) = Z2 $ fmap (fmap f) z
instance Comonad Z2 where
extract (Z2 z) = extract $ extract z
duplicate (Z2 z) = fmap Z2 $ Z2 $ roll $ roll z where
roll a = Z (unfoldr ((split <$>) . traverse back) a)
a
(unfoldr ((split <$>) . traverse forth) a)
instance Foldable Z2 where
foldMap f (Z2 z) = foldMap (foldMap f) z
instance Traversable Z2 where
traverse f (Z2 z) = Z2 <$> traverse (traverse f) z
main :: IO ()
main = do
(x:y:gens:file:[]) <- getArgs
board <- mkBoard (read x, read y) <$> getStdGen
let generations = take (read gens) $ iterate (extend live) board
let images = map (boardToImage (read x,read y)) generations
let triples = zip3 (repeat greyPalette) (repeat 10) images
let result = writeGifImages file LoopingNever triples
case result of
Right r -> r >> putStrLn "Success"
Left s -> putStrLn s
mkBoard :: RandomGen g => (Int,Int) -> g -> Board
mkBoard (x,y) = Z2 . toZ . take x
. unfoldr ((\(a,b) -> return (toZ a,b)) . splitAt y)
. randoms
showBoard :: Board -> String
showBoard (Z2 z) = intercalate "\n"
$ map (intercalate " " . map (show . btoi) . fromZ)
$ fromZ z
boardToImage :: (Bounded p, Pixel p) => (Int,Int) -> Board -> Image p
boardToImage (x,y) (Z2 z) = snd
$ generateFoldImage (\l _ _ -> (tail l, btop (head l)))
(concat listBoard)
x
y
where listBoard = map fromZ $ fromZ z
btop :: (Bounded p, Pixel p) => Bool -> p
btop True = maxBound -- white
btop False = minBound -- black
btoi :: Bool -> Int
btoi False = 0
btoi True = 1
live :: Board -> Bool
live b = case (extract b, sn < 2, sn < 4, sn == 3) of
(True, True, _, _ ) -> False
(True, False, True, _ ) -> True
(False, _, _, True) -> True
_ -> False
where sn = sum $ catMaybes $ map get [ right b
, left b
, up b
, down b
, right b >>= up
, right b >>= down
, left b >>= up
, left b >>= down
]
get = (btoi . extract <$>)