|
module Conway where |
|
|
|
import qualified Data.Set as Set |
|
import Control.Concurrent (threadDelay) |
|
|
|
type Entry = (Integer, Integer) |
|
|
|
newtype Field = Field { getField :: (Set.Set Entry) } deriving Eq |
|
|
|
emptyField :: Field |
|
emptyField = Field Set.empty |
|
|
|
parseField :: String -> Field |
|
parseField raw = Field $ Set.fromList filtered where |
|
parseLine (line, x) = zip (map (=='O') line) [(y,x) | y <- [0..]] |
|
indexed = (zip (lines raw) [0..]) >>= parseLine |
|
filtered = [xy | (live, xy) <- indexed, live] |
|
|
|
instance Show Field where |
|
show (Field set) = let |
|
(xs,ys) = unzip $ Set.elems set |
|
cell x y = if Set.member (x,y) set then 'O' else 'X' |
|
in unlines $ [[cell x y | x <- [minimum xs..maximum xs]] | y <- [minimum ys..maximum ys]] |
|
|
|
isAlive :: Field -> Entry -> Bool |
|
isAlive (Field set) k = Set.member k set |
|
|
|
neighbourIndices :: Entry -> [Entry] |
|
neighbourIndices (x,y) = [(x+dx,y+dy) | dx <- [-1..1], dy <- [-1..1], not (dx == 0 && dy ==0)] |
|
|
|
neighbourCount :: Field -> Entry -> Int |
|
neighbourCount field = length . filter (isAlive field) . neighbourIndices |
|
|
|
willBeAlive :: Field -> Entry -> Bool |
|
willBeAlive field entry = (count == 3) || (alive && (count == 2)) where |
|
count = neighbourCount field entry |
|
alive = isAlive field entry |
|
|
|
step :: Field -> Field |
|
step field = Field (Set.fromList indices) where |
|
candidates = Set.elems (getField field) >>= neighbourIndices |
|
indices = filter (willBeAlive field) candidates |
|
|
|
main :: IO () |
|
main = getContents >>= (loop . parseField) where |
|
loop field = do |
|
print field |
|
threadDelay 500000 |
|
loop $ step field |