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しては次世代を描画するループ
だけなんだけど、これまたシステムコールを使うのにクセがあるとか無いとか…。
だんだん夏休みの宿題みたくなってきた。