Haskell로 만드는 콘솔 Life Game
지금도 하스켈 공부 중이지만, 결국 모나드는 함수와 값을 감싸고
<<=
같은 연산자나 특정 함수로 조작을 하는 것을 명시하는 OOP에서 말하는 캡슐화의 개념에 가까운 것 그럴까?어쨌든 프로그래밍을 배우는 것보다는 쓰기가 가장 좋기 때문에 콘솔에서 작동하는 간단한 LifeGame을 작성해 보았습니다.
게임 상태 정의
필드의 상태는 셀을 좌표
Pos
로 표시하고 전체적으로 Board
로 관리합니다.Board
는 살아있는 셀을 요소로하는 Pos
형식의 목록입니다. 필요한 라이브러리도 가져옵니다.import Control.Monad
import System.Random
type Pos = (Int, Int)
type Board = [Pos]
필드 크기
쉽게 변경할 수 있도록 가로 너비
width
와 세로 너비 height
도 함수에 정의되어 있습니다.width :: Int
width = 50
height :: Int
height = 50
각 셀의 상태 확인
그런 다음
Pos
형식의 좌표에서 Board
내에 해당 셀이 있는지 확인하고 해당 셀이 살아 있는지 여부를 Bool
형식으로 반환하는 함수 isAlive
를 준비합니다. ) 함수는 셀에 생명이 있는지 여부를 반환합니다.isAlive :: Board -> Pos -> Bool
isAlive b p = p `elem` b
isEmpty :: Board -> Pos -> Bool
isEmpty b p = not (isAlive b p)
주위의 셀 취득
isEmpty
함수는 셀 좌표 neighbers
에서 그 주위 8 방향의 셀을 가져옵니다. 주변의 셀 획득에 해당합니다.wrap :: Pos -> Pos
wrap (x, y) = (((x-1) `mod` width) + 1,
((y-1) `mod` height)+ 1)
neighbers :: Pos -> [Pos]
neighbers (x, y) = map wrap [(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)]
차세대 생활
Pos
함수는 주변의 살아있는 셀 수를 "wrap
유형의 인수에서 반환하는 함수"를 반환하는 함수입니다.그리고
liveneighbers
는 현세대에서 차세대에서 살아남는 셀 목록을 반환합니다.반면에
Pos
는 차세대에서 새롭게 생명이 생성되는 셀 목록을 반환합니다.survivors
에서 사용되는 births
는 목록에서 중복 요소를 제외하고 하나로 만듭니다.그리고 마지막으로
births
와 rmdups
에서 survivors
로 차세대 생활을 얻습니다.liveneighbers :: Board -> Pos -> Int
liveneighbers b = length . filter (isAlive b) . neighbers
survivors :: Board -> [Pos]
survivors b = [p | p <- b, liveneighbers b p `elem` [2, 3]]
rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : rmdups (filter (/= x) xs)
births :: Board -> [Pos]
births b = [p | p <- rmdups (concatMap neighbers b),
isEmpty b p,
liveneighbers b p == 3]
nextgen :: Board -> Board
nextgen b = survivors b ++ births b
그리기 함수
그런 다음 콘솔에 표시하는 기능을 제공합니다.
births
콘솔 화면을 지우고. 요소 작업을 순차적으로 수행하는 nextgen
함수를 사용하여 그립니다.cls :: IO ()
cls = putStr "\ESC[2J"
goto :: Pos -> IO ()
goto (x, y) = putStr ("\ESC[" ++ show y ++ ";" ++ show x ++ "H")
writeat :: Pos -> String -> IO ()
writeat p xs = do goto p
putStr xs
seqn :: [IO a] -> IO ()
seqn [] = return ()
seqn (a:as) = do a
seqn as
showcells :: Board -> IO ()
showcells b = seqn [writeat p "O" | p <- b]
게임 사이클
마지막으로 게임 본문을 정의합니다.
cls
는 세대간에 그리기 사이를 취하는 기능입니다.lifegame :: Board -> IO ()
lifegame b = do cls
showcells b
wait 5000
lifegame (nextgen b)
초기 상태
goto
함수를 호출하려면 초기 상태 writeat
를 제공하지 않으면 갈 수 없으므로 반환하는 함수를 준비하십시오.showcells
에서 난수를 사용하여 임의의 위치의 셀 좌표를 반환합니다.그런 다음
seqn
에 지정된 수의 수명을 가진 wait
를 가져옵니다.makeLife :: IO Pos
makeLife = do
x <- getStdRandom $ randomR(1, width) :: IO Int
y <- getStdRandom $ randomR(1, height) :: IO Int
return (x, y)
initBoard :: IO Board
initBoard = replicateM (width * height `div` 5) makeLife
완성!!!
main :: IO ()
main = lifegame =<< initBoard
자 실행해보자!!!!!
...........
네! ! ! 훌륭합니다! ! ! 훌륭합니다! ! !
..........
한화휴제
함수형으로 제대로 코드 쓴 것은 처음입니다만, haskell의 형태->형의 형태를 강하게 의식시키는 사양은 함수의 세분화를 촉진해, 그 때문에 함수 각각의 독립성이 강하게 느꼈습니다. 성・안전성이 높다고 하는 것도 어쩐지 알 수 있다고 생각합니다. 무엇보다 단순한 함수의 편성으로 복잡한 처리를 간결하게 쓸 수 있는 것은 기분 좋네요. 내가 생각했던 Project Euler를 Haskell로 써 보자.
Reference
이 문제에 관하여(Haskell로 만드는 콘솔 Life Game), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/S_A_Y_A/items/1a2b1b8d944aefb2dd6d텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)