ライフゲーム
Posted feedbacks - Haskell
とりあえずWikiPediaでルールを読んで23/3を組んでみました。末端のループもやっています。 ライフゲームは昔から動いてるところは見たことあっても、実際に書いてみたことはなかったので楽しめました...間引きはこれから勉強します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | module Main
where
import Data.Array
type Point = (Int, Int)
type Size = (Int, Int)
type TwoDArray = (Size, Array Int Char)
glider = ".........." ++
"..X......." ++
".X........" ++
".XXX......" ++
".........." ++
".........." ++
".........." ++
".........." ++
".........." ++
".........."
makeArray :: Size -> [Char] -> TwoDArray
makeArray sz@(cx, cy) str = (sz, listArray (0, cx * cy - 1) str)
ptToIdx :: Size -> Point -> Int
ptToIdx (cx, cy) (x, y) = (y * cx) + x
fetch :: TwoDArray -> Point -> Char
fetch (sz, rg) pt = rg!(ptToIdx sz pt)
surrounding = [(x, y) | x <- [-1..1], y <-[-1..1], x /= 0 || y /= 0]
addPt :: Point -> Size -> Point -> Point
addPt (x1, y1) (cx, cy) (x2, y2) = ((x1 + x2) `mod` cx, (y1 + y2) `mod` cy)
getSurroundingPts :: Point -> Size -> [Point]
getSurroundingPts pt sz = map (addPt pt sz) surrounding
countSurroundingLife :: TwoDArray -> Point -> Int
countSurroundingLife td@(sz, rg) pt =
length $ filter (/='.') $ map (fetch td) $ getSurroundingPts pt sz
allPoints :: Size -> [Point]
allPoints (cx, cy) = [(x, y) | y <- [0..(cx - 1)], x <- [0..(cy - 1)]]
birthDeath :: Char -> Int -> Char
birthDeath ch c
| c == 3 && ch == '.' = 'X'
| ch == 'X' && (c == 2 || c == 3) = 'X'
| otherwise = '.'
doGen :: TwoDArray -> Point -> Char
doGen rg pt = birthDeath (fetch rg pt) (countSurroundingLife rg pt)
nextGen :: TwoDArray -> TwoDArray
nextGen td@(sz, rg) = makeArray sz $ map (doGen td) (allPoints sz)
lifes :: TwoDArray -> [TwoDArray]
lifes td = [td] ++ (lifes (nextGen td))
dumpRow :: TwoDArray -> Int -> IO()
dumpRow ((cx, cy), rg) iy = putStrLn $ map (rg!) $ [(iy * cx)..((iy * cx) + cx - 1)]
dumpMap :: TwoDArray -> Int -> IO()
dumpMap td@(size@(cx, cy), rg) iGen = do
putStrLn $ "Generation " ++ (show iGen)
mapM (dumpRow td) [0..(cy-1)]
putStrLn ""
doLife :: [(Int, TwoDArray)] -> IO()
doLife [] = return ()
doLife (x@(iGen, rg):xs) = do
dumpMap rg iGen
doLife xs
main :: IO()
main = do
doLife $ zip [1..20] (lifes start)
where
start = makeArray sz glider
sz = (10, 10)
|
Haskell の勉強中です. 間引きはありません. 初期状態を表す, "0001000:0010010:001000..."のような文字列を,引数として与えます(':' は行の区切り).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | import List
import System
import System.Posix.Unistd
main = do args <- getArgs
let initState = [[read [c] :: Int| c <- line]| line <- initStateStr]
initStateStr = split ':' $ head args
where
split :: (Eq a) => a -> [a] -> [[a]]
split d xs
| null xs = []
| otherwise = case break (== d) xs of
(fxs,[]) -> [fxs]
(fxs,sxs) -> fxs : split d (tail sxs)
(putEachStrWithSleep 400000) . (map stateToStr) $ lifeGame initState
putEachStrWithSleep :: Int -> [String] -> IO ()
putEachStrWithSleep _ [] = putStr ""
putEachStrWithSleep usec (s:ss) = (putStrLn . replace0 . replace1) s >>
usleep usec >>
putEachStrWithSleep usec ss
where
replace0 = replaceStr '0' '.'
replace1 = replaceStr '1' '@'
replaceStr :: Char -> Char -> String -> String
replaceStr _ _ "" = []
replaceStr c c' (s:ss) = (if (c == s) then c' else s) : replaceStr c c' ss
infixl 9 !!>
(!!>) :: [a] -> Int -> a
(!!>) xs n
| null xs = error "Main.(!!>): empty list\n."
| otherwise = xs !! (n `mod` length xs)
type State = [[Int]]
stateToStr :: State -> String
stateToStr [] = ""
stateToStr (s:ss) = concatMap show s ++ "\n" ++ stateToStr ss
lifeGame :: State -> [State]
lifeGame state = state : (lifeGame . nextState) state
nextState :: State -> State
nextState state = [[nextCell state (x,y)| x <- [0..maxX]]| y <- [0..maxY]]
where
maxX = ((length . head) state) - 1
maxY = (length state) - 1
nextCell :: State -> (Int,Int) -> Int
nextCell state (x,y)
| numNeighborLives == 2 = targetCell
| numNeighborLives == 3 = if (targetCell == 0) then 1 else targetCell
| otherwise = 0
where
targetCell :: Int
targetCell = cell (x,y)
numNeighborLives :: Int
numNeighborLives = (length . filter (== 1)) neighborCells
neighborCells :: [Int]
neighborCells
= [cell (x-1,y-1), cell (x,y-1), cell (x+1,y-1),
cell (x-1,y) , cell (x+1,y) ,
cell (x-1,y+1), cell (x,y+1), cell (x+1,y+1)]
cell :: (Int,Int) -> Int
cell (x,y) = state !!> y !!> x
|
ランダムな初期環境を作り、50世代分を表示します。
(ただし、乱数処理が手抜きです)
間引きとかはやっていません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | module Main (main) where
import Data.Array
import Data.List (iterate)
import System.Random (randomRs, newStdGen)
newtype World = World (Array (Int, Int) Bool)
instance Show World where
show (World cells) = concat $ map f $ assocs cells
where showCell c = if c then "[*]" else "[ ]"
(_, (lastx, _)) = bounds cells
f ((_, x), c) | x == lastx = showCell c ++ "\n"
| otherwise = showCell c
(!@) :: World -> (Int, Int) -> Bool
(World cells) !@ (x, y) = cells ! (wrap x, wrap y)
where (_, (size, _)) = bounds cells
wrap n | n < 0 = size
| n > size = 0
| otherwise = n
next :: World -> World
next w@(World cells) = rebuild $ map step $ assocs cells
where rebuild = World . array (bounds cells)
around (px, py) = length $ filter id [w !@ (x, y) | x <- [px - 1 .. px + 1],
y <- [py - 1 .. py + 1],
x /= px || y /= py]
step (p, True) = let a = around p in (p, a == 2 || a == 3)
step (p, False) = (p, around p == 3)
mkWorld :: Int -> IO World
mkWorld size = do g1 <- newStdGen
g2 <- newStdGen
return $ w g1 g2
where ix = ((0, 0), (size - 1, size - 1))
init = [((x, y), False) | x <- [0 .. size - 1], y <- [0 .. size - 1]]
w g1 g2 = World $ array ix init // ps g1 g2
ps g1 g2 = take (size * size `div` 3) $
flip zip (repeat True) $
zipWith (,) (randomRs (0, size - 1) g1) (randomRs (0, size - 1) g2)
generations :: World -> [World]
generations = iterate next
main :: IO ()
main = mkWorld 10 >>= mapM_ print . take 50 . generations
|
間引きはやってません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import List
import System.Environment
g0 = [[0,1,0,0,0,0,1,1,1,0],
[0,0,0,0,1,0,0,1,1,0],
[0,0,0,1,0,0,1,0,1,0],
[1,0,1,1,0,0,1,0,0,0],
[0,1,0,0,0,0,0,0,1,0],
[1,0,0,0,1,0,1,1,0,1],
[0,1,0,0,0,0,1,0,0,0],
[0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,1,0,0,1],
[0,0,0,0,1,1,0,0,1,0]]
revFun f = reverse.f.reverse
tspFun f = transpose.f.transpose
l t = map rotateL t
r t = map rotateR t
u t = (tspFun l) t
d t = (tspFun r) t
rotateL (x:xs) = xs++[x]
rotateR = revFun rotateL
showGame table = unlines $ map concat $ (map.map) (\x->if x==0 then "_" else "X") table
zipped xs
|any (==[]) xs = []
|otherwise = map head xs:zipped (map tail xs)
next t = every 10 $ map nextCell $ zipped $ map concat [t,u t,d t,l t,r t,u$l$t,u$r$t,d$l$t,d$r$t]
where nextCell (0:xs) = let live = length (filter (==1) xs)
in if live == 3 then 1 else 0
nextCell (1:xs) = let live = length (filter (==1) xs)
-- in if live == 3 || live == 2 then 1 else 0
in if live == 2 then 1 else 0
every n xs = unfoldr f xs
where f [] = Nothing
f cs = Just (splitAt n cs)
main = getArgs >>= (\args-> mapM_ putStr $ take (getLim args) $
zipWith (\t game->"t="++show t++"\n"++showGame game) [1..] (iterate next g0))
where getLim [] = 10 --default
getLim (x:xs) = read x
{-
*Main> :main 3
t=1
_X____XXX_
____X__XX_
___X__X_X_
X_XX__X___
_X______X_
X___X_XX_X
_X____X___
_________X
X_____X__X
____XX__X_
t=2
____X____X
_____X___X
__X_XXX_XX
_X_X_____X
__XX_XX_X_
_X___XX_XX
_____XXXXX
_________X
X____X__X_
X_________
t=3
X________X
X__X__X___
__XX__X___
XX________
_X_X______
X_X_______
__________
X____X____
X_________
X_________
-}
|
今更ですが、短さ重視で。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import Data.List
-- 生きているセル:1 、死んでいるセル: 0
initialState = [[1,0,1,0,1,0,1,0,1,0],
[1,1,1,0,1,0,1,0,1,0],
[1,1,1,0,1,0,1,0,1,0],
[1,0,1,0,1,0,1,0,1,0],
[1,1,1,0,1,1,1,0,1,0],
[1,1,1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,1,0,1,0],
[1,1,1,0,0,0,1,0,1,0],
[1,1,1,0,1,0,1,0,1,0],
[1,0,1,0,1,1,1,0,1,0]]
lifeGame = -- 状態遷移の無限リスト
unfoldr (\g -> Just (unfoldr f g, nextGeneration g)) $ foldl1 (++) initialState
where w = length $ head initialState
f g = if length g == 0 then Nothing else Just (take w g, drop w g)
nextGeneration g = snd $ mapAccumL (\ i _ -> (i + 1, newState i)) 0 g
where getState i = (cycle g) !! (if i >= 0 then i else length g + i)
livingCells i = sum [getState (x + y) | x <- [i - 1 .. i + 1], y <- [-w, 0, w]] - (getState i)
newState i = case getState i of
1 -> if livingCells i == 2 || livingCells i == 3 then 1 else 0
0 -> if livingCells i == 3 then 1 else 0
main = let n = 25 in -- 最初の25世代を表示
let t = take (n + 1) lifeGame in
flip mapM_ [0 .. n] $ \n -> do
putStrLn $ "generation: " ++ (show n)
mapM_ (\l -> putStrLn $ show l) $ t !! n
|


saws
#5330()
Rating7/13=0.54
see: Wikipedia:ライフゲーム
[ reply ]