wvogel日記

自分用の技術備忘録が多めです.

life game

今日、どうにも良い方法が見つからなかったので、大学からの帰りに本屋でプログラミングHaskell読んでライフゲームのヒントを探してみました。

すると
なるほど!まさになるほど、といった感じ。
現在生きているセルをタプルのリストで保持してやればいいんですね!


あとは自分で書けそうだったので、夜のバイトが終わった後、2時間くらい今までずっとカリカリ書いてた。
そのうち1時間くらいは、スマートに出力する方法探してたけど.....
結局綺麗な方法思いつかず、forM_とsplitAtに頼りました。

課題としては、
関数が結構入り組んだ形になってしまったので
もっと完結にパーツ化して見やすくする。
ポイントフリースタイル使うとか。

入出力法のバリエーションを探す

などですかね

import Control.Monad

width  = 5
height = 5

type Pos = (Int,Int)
type Living = [Pos]

positions :: [Pos]
positions = [(x,y) | x <- [1..width] , y <- [1..height]]

start :: Living
start = [(2,1),(3,1),(1,2),(3,2),(5,2),(4,3),(1,4),(2,5),(3,5)]

surround :: Pos -> [Pos]
surround (x,y) = [(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)]

alive :: Pos -> Living -> Int
alive pos lives= length.filter (`elem` lives)$surround pos

visual :: Living -> IO()
visual lives = let lifelist = visualFormat
                     $ map (f.(`elem` lives)) positions
               in forM_ lifelist print >> putStrLn ""
  where
    f a  = if a then '*' else '-'

visualFormat :: String -> [String]
visualFormat [] = []
visualFormat ls = a:visualFormat b
  where
    (a,b) = splitAt width ls

next :: Living -> Pos -> Pos
next lives pos = let n = alive pos lives
                 in case elem pos lives of
                     True | elem n [2,3] -> pos
                     False| n == 3 -> pos
                     otherwise -> (0,0)

format :: Living -> Living
format = filter (not.(==(0,0)))

showNext :: Int -> Living ->IO()
showNext 0 _ = print "stop"
showNext n lives = do visual lives
                      showNext (n-1) $ format.map (next lives) $ positions

main = showNext 10 start