lifegame

『プログラミング Haskell』を半分程度読み終えたのにコードを一行も書いたことが無いというのは流石にまずいだろうと思ってライフゲームを実装してみた.というかこのライフゲーム自体もプログラムとして書いた経験が無いという事が判明したのですが,大体の場合よく知らない言語で初めて書くプログラムは一度書いたことのあるアルゴリズムだと思うので,なんか得したなーという気分になった.
あとこのプログラム書いてて思ったのが,GHC の吐くエラーメッセージ分かりにくいという事.まあたぶん慣れの問題だと思うけどどうなんだろう….

data Flag = Live | Dead

type OrderedBoard = [Flag]


gen :: Int -> [(Int, Int)] -> OrderedBoard
gen sidelen pos = map (\(n, _) -> bool_to_flag (elem (n `div` sidelen, n `rem` sidelen) pos)) (zip [0,1..(sidelen^2-1)] (replicate (sidelen^2) Dead))

bool_to_flag :: Bool -> Flag
bool_to_flag True  = Live
bool_to_flag False = Dead


encode :: OrderedBoard -> [Int]
encode board = map encode_1 board

encode_1 :: Flag -> Int
encode_1 Live  = 1
encode_1 Dead  = 0


next_state :: OrderedBoard -> OrderedBoard
next_state board = map (live_or_dead board) (zip [0,1..length board] board)

live_or_dead :: OrderedBoard -> (Int, Flag) -> Flag
live_or_dead board (i, flag) = judge flag (foldl count_live_cell 0 (map (\n -> board !! n) (neighbors (fromEnum (sqrt (fromIntegral (length board)))) i)))

judge :: Flag -> Int -> Flag
judge _    3 = Live
judge Live 2 = Live
judge _    _ = Dead

count_live_cell :: Int -> Flag -> Int
count_live_cell n Live = n + 1
count_live_cell n Dead = n

neighbors :: Int -> Int -> [Int]
neighbors sidelen n = filter (\index -> index >= 0 && index < sidelen^2 && index /= n) (neighbors_1 sidelen n)

neighbors_1 :: Int -> Int -> [Int]
neighbors_1 sidelen n | n < 0                          = []
                      | n > sidelen^2                  = []
                      | n `rem` sidelen == 0           = [n - sidelen, n - sidelen + 1, n, n + 1, n + sidelen, n + sidelen + 1]
                      | n `rem` sidelen == sidelen - 1 = [n - sidelen - 1, n - sidelen, n - 1, n, n + sidelen - 1, n + sidelen]
                      | otherwise                      = [n - sidelen - 1, n - sidelen, n - sidelen + 1, n - 1, n, n + 1, n + sidelen - 1, n + sidelen, n + sidelen + 1]