Life Gameでハマる(パート2)
はぁ、だいぶ間が空いてしまた。
今年の夏はいろいろあったなぁ。
…まだ終わってないけど、自分の中では今年の夏は終わり気分。
つーことで、挫折したまんまじゃ悔しいので続き。
モナドの罠
って、勝手にハマってるだけなんだけれども。
誰かが”モナドは深く理解しなくとも使えりゃそれでよし”とか言ってたような気がするけど、使うことに四苦八苦。
どうすれば慣れるんだろう。いつか悟りが開けるときがくるのか?
とりあえず前回課題の次世代のセルを求めることはできた。
乱数
Haskellで乱数を発生させるのにRandomモジュールを使う。
http://www.sampou.org/haskell/report-revised-j/random.html
ほんとは、
randRs i = do g <- newStdGen return $ take i $ randomRs (0,1) g randField = zip [(x, y)| x <- [0..99], y <- [0..99]] (randRs 10000)
みたいに一気に求めたかったのに、悲しいかなIOモナドなんだな。
randRs :: Int -> (Int, Int) -> IO [Int]
IOモナドのバカ…(いやバカは俺orz)
プロトタイプ2
やっとこさ
・ランダムに生成
・次世代を求める
とこまで実装。
module Main where import System.Random import Data.Array.IO import Data.Array.Base import Graphics.UI.WXCore import Graphics.UI.WX --1か0の乱数 randInt :: IO (Int) randInt = getStdRandom (randomR (0,1)) --ランダムにセルの生死を決める randField :: Int -> Int -> IOUArray (Int, Int) Bool -> IO () randField x y field = randInt >>= (\r -> writeArray field (x,y) (r == 1)) --あるセルの周辺セルの相対位置 around = [(-1,-1),(0,-1),(1,-1),(-1,0),(1,0),(-1,1),(0,1),(1,1)] --セルの次世代の生死を決める --誕生: 死んでいるセルの周囲に3つの生きているセルがあれば次の世代では生きる --維持: 生きているセルの周囲に2つか3つの生きているセルがあれば次の世代でも生き残る --死亡: 上以外の場合には次の世代では死ぬ nextField x0 y0 now next = do alive <- readArray now (x0,y0) s <- mapM (\(x,y) -> readArray now (x0+x,y0+y)) around case (count s) of 3 -> writeArray next (x0,y0) True 2 -> writeArray next (x0,y0) alive _ -> writeArray next (x0,y0) False where count [] = 0 count (x:xs) = if x then 1 + count xs else count xs --セルを描画する drawCell :: Int -> Int -> IOUArray (Int, Int) Bool -> DC a -> IO () drawCell x y field dc = do alive <- readArray field (x,y) when alive (drawPoint dc (pt x y) [color := black]) --全てのx,y座標 points = [(x, y)| x <- [0..99], y <- [0..99]] gui :: IO () gui = do f <- frame [text := "Life game Prototype2", fullRepaintOnResize := False, clientSize := sz 100 100, on paint := onpaint] return () where onpaint dc viewArea = do --現世代、次世代のフィールドを用意 f1 <- newArray ((0,0),(99,99)) False :: IO (IOUArray (Int, Int) Bool) f2 <- newArray ((0,0),(99,99)) False :: IO (IOUArray (Int, Int) Bool) --まずはランダムにセルを生成して描画 mapM_ (\(x,y) -> randField x y f1) points mapM_ (\(x,y) -> drawCell x y f1 dc) points --次世代を求めて描画 mapM_ (\(x,y) -> nextField x y f1 f2) [(x,y)| x <- [1..98],y <- [1..98]] mapM_ (\(x,y) -> drawCell x y f2 dc) points main :: IO () main = start gui
課題
残るは、
・sleepしては次世代を描画するループ
だけなんだけど、これまたシステムコールを使うのにクセがあるとか無いとか…。
だんだん夏休みの宿題みたくなってきた。