第9章 対話プログラム #3

ライフゲーム

さて、9.7はライフゲームの実装。 ひたすら写経。9.5で実装した関数群を使っている。

module NineSeven where

import NineFive

width :: Int
width =  5

height :: Int
height =  5

type Board = [Pos]

glider :: Board
glider =  [(4,2), (2,3), (4,3), (3,4), (4,4)]

showcells   :: Board -> IO ()
showcells b =  seqn [writeat p "O" | p <- b]

isAlive     :: Board -> Pos -> Bool
isAlive b p =  elem p b

isEmpty     :: Board -> Pos -> Bool
isEmpty b p =  not (isAlive b p)

neighbs       :: Pos -> [Pos]
neighbs (x,y) = map wrap [(x-1,y-1), (x,y-1), (x+1,y-1),
                          (x-1,y),            (x+1,y),
                          (x-1,y+1), (x,y+1), (x+1,y+1)]

wrap       :: Pos -> Pos
wrap (x,y) =  (((x-1) `mod` width) + 1, ((y-1) `mod` height) + 1)

liveneighbs   :: Board -> Pos -> Int
liveneighbs b =  length . filter (isAlive b) . neighbs

survivors   :: Board -> [Pos]
survivors b =  [p | p <- b, elem (liveneighbs b p) [2,3]]

births   :: Board -> [Pos]
births b =  [p | p <- rmdups (concat (map neighbs b)),
                 isEmpty b p,
                 liveneighbs b p == 3]

rmdups        :: Eq a => [a] -> [a]
rmdups []     =  []
rmdups (x:xs) =  x:rmdups (filter ((/=) x) xs)

nextgen   :: Board -> Board
nextgen b =  survivors b ++ births b

life   :: Board -> IO ()
life b =  do cls
             showcells b
             wait 5000
             life (nextgen b)

wait   :: Int -> IO ()
wait n =  seqn [return () | _ <- [1..n]]

life gliderって実行すると、たしかにグライダーが! ただし無限ループするので、Ctrl-cで止めないといけないけど。

waitがビジーループなのがおもしろい。IOモナドにもだいぶん馴染んできたかな。