Fork channel

Create a new channel as a copy of main.

Rename channel

Rename main to:

Delete channel

Delete main? This cannot be undone.

life.hs
import Control.Arrow
import Control.Monad
import Data.Maybe
import qualified Data.Set as S

type Point = (Int, Int, Int)

type Board = S.Set Point

member :: Ord a => a -> S.Set a -> Bool
member = S.member

parse :: String -> Board
parse s =
  let grid = lines s
      annotated =
        map (\(y, (x, v)) -> ((x, y), v)) $
          join $
            zipWith (map . (,)) [0 ..] $ map (zip [0 ..]) grid
      positions =
        foldr
          (\((x, y), v) -> if v == '#' then S.insert (x, y, 0) else id)
          S.empty
          annotated
   in positions

adjacents :: [(Int, Int, Int)]
adjacents =
  [ (x, y, z)
    | x <- [-1, 0, 1],
      y <- [-1, 0, 1],
      z <- [-1, 0, 1],
      (x, y, z) /= (0, 0, 0)
  ]

applyDir :: Point -> (Int, Int, Int) -> Point
applyDir (x, y, z) (i, j, k) = (x + i, y + j, z + k)

makeView :: Point -> [Point]
makeView p = map (applyDir p) adjacents

step :: Board -> Board
step = uncurry (flip foldr S.empty) . (adjustCells &&& id)
  where
    adjustCells :: Board -> Point -> S.Set Point -> S.Set Point
    adjustCells b p s =
      let liveInView = S.size . S.intersection b
          pointsViewed = makeView p
          s' =
            if (liveInView $ S.fromList pointsViewed) `elem` [2, 3]
              then S.insert p s
              else s

          alsoAlive p' =
            -- don't need to handle this case here, it will be checked anyway
            not (p' `member` b)
              && (liveInView $ S.fromList $ makeView p') == 3

          newLiveAdj = S.fromList $ filter alsoAlive pointsViewed
       in S.union s' newLiveAdj

solveOne :: String -> Int
solveOne = S.size . step . step . step . step . step . step . parse

type Point2 = (Int, Int, Int, Int)

type Board2 = S.Set Point2

parseTwo :: String -> Board2
parseTwo = S.map (\(x, y, z) -> (x, y, z, 0)) . parse

-- properly we should be able to reuse the same code via typeclasses, but I'm
-- lazy
adjacents2 :: [(Int, Int, Int, Int)]
adjacents2 =
  [ (x, y, z, w)
    | x <- [-1, 0, 1],
      y <- [-1, 0, 1],
      z <- [-1, 0, 1],
      w <- [-1, 0, 1],
      (x, y, z, w) /= (0, 0, 0, 0)
  ]

applyDir2 :: Point2 -> (Int, Int, Int, Int) -> Point2
applyDir2 (x, y, z, w) (i, j, k, l) = (x + i, y + j, z + k, w + l)

makeView2 :: Point2 -> [Point2]
makeView2 p = map (applyDir2 p) adjacents2

step2 :: Board2 -> Board2
step2 = uncurry (flip foldr S.empty) . (adjustCells &&& id)
  where
    adjustCells :: Board2 -> Point2 -> S.Set Point2 -> S.Set Point2
    adjustCells b p s =
      let liveInView = S.size . S.intersection b
          pointsViewed = makeView2 p
          s' =
            if (liveInView $ S.fromList pointsViewed) `elem` [2, 3]
              then S.insert p s
              else s

          alsoAlive p' =
            -- don't need to handle this case here, it will be checked anyway
            not (p' `member` b)
              && (liveInView $ S.fromList $ makeView2 p') == 3

          newLiveAdj = S.fromList $ filter alsoAlive pointsViewed
       in S.union s' newLiveAdj

solveTwo :: String -> Int
solveTwo = S.size . step2 . step2 . step2 . step2 . step2 . step2 . parseTwo