131 lines
3.5 KiB
Haskell
Executable File
131 lines
3.5 KiB
Haskell
Executable File
#! /usr/bin/env -S"ANSWER=42" nix-shell
|
|
#! nix-shell -p ghcid
|
|
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [pretty-simple vector])"
|
|
#! nix-shell -i "ghcid -c 'ghci' -T main"
|
|
|
|
{-# OPTIONS_GHC -Wall -Wincomplete-uni-patterns #-}
|
|
{-# OPTIONS_GHC -Wno-unused-top-binds -Wno-unused-imports -Wno-type-defaults #-}
|
|
{-# OPTIONS_GHC -Wno-unused-matches #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
import Data.Maybe (catMaybes)
|
|
import Data.Function (fix)
|
|
import Data.Vector (Vector)
|
|
import Debug.Trace (trace,traceShow)
|
|
import Text.Pretty.Simple
|
|
import qualified Data.Vector as V
|
|
|
|
exampleData :: [String]
|
|
exampleData =
|
|
[ "L.LL.LL.LL"
|
|
, "LLLLLLL.LL"
|
|
, "L.L.L..L.."
|
|
, "LLLL.LL.LL"
|
|
, "L.LL.LL.LL"
|
|
, "L.LLLLL.LL"
|
|
, "..L.L....."
|
|
, "LLLLLLLLLL"
|
|
, "L.LLLLLL.L"
|
|
, "L.LLLLL.LL"
|
|
]
|
|
|
|
type Width = Int
|
|
type Pos = Int
|
|
|
|
data State = StateEmpty | StateSeatFree | StateSeatOccupied
|
|
deriving (Eq, Ord)
|
|
|
|
instance Show State
|
|
where
|
|
show StateEmpty = "."
|
|
show StateSeatFree = "L"
|
|
show StateSeatOccupied = "#"
|
|
|
|
type SeatLayout = Vector State
|
|
|
|
drawSeatLayout :: Width -> SeatLayout -> String
|
|
drawSeatLayout w s = concat $ V.toList $ V.imap go s
|
|
where
|
|
go i s0 = show s0 ++ if (i `mod` w) == (w - 1) then "\n" else ""
|
|
|
|
parseInput :: String -> SeatLayout
|
|
parseInput = V.fromList . (map go)
|
|
where
|
|
go '.' = StateEmpty
|
|
go 'L' = StateSeatFree
|
|
go '#' = StateSeatOccupied
|
|
go c = trace (c:" is undefined") undefined
|
|
|
|
fStep :: Width -> SeatLayout -> SeatLayout
|
|
fStep w s = if f s == s then s else fStep w (f s)
|
|
where
|
|
f = step w
|
|
|
|
step :: Width -> SeatLayout -> SeatLayout
|
|
step w s = V.imap go s
|
|
where
|
|
-- If a seat is empty (L) and there are no occupied seats adjacent to it,
|
|
-- the seat becomes occupied.
|
|
go i StateSeatFree =
|
|
if all (== StateSeatFree) $ filter (/= StateEmpty) $ neighbors i
|
|
then StateSeatOccupied
|
|
else StateSeatFree
|
|
-- If a seat is occupied (#) and four or more seats adjacent to it are also
|
|
-- occupied, the seat becomes empty.
|
|
go i StateSeatOccupied =
|
|
if ( (length . (filter (== StateSeatOccupied))) (neighbors i) ) >= 4
|
|
then StateSeatFree
|
|
else StateSeatOccupied
|
|
-- Otherwise, the seat's state does not change.
|
|
-- ie the rest remains as-is.
|
|
go _ state = state
|
|
|
|
-- Build a list of neighbors
|
|
neighbors :: Pos -> [State]
|
|
neighbors p = catMaybes [
|
|
-- 1: east
|
|
s V.!? toP (i+1,j)
|
|
-- 2: west
|
|
, s V.!? toP (i-1,j)
|
|
-- 3: north
|
|
, s V.!? toP (i,j-1)
|
|
-- 4: south
|
|
, s V.!? toP (i,j+1)
|
|
-- 5: north east
|
|
, s V.!? toP (i+1,j-1)
|
|
-- 6: north west
|
|
, s V.!? toP (i-1,j-1)
|
|
-- 7: south east
|
|
, s V.!? toP (i+1,j+1)
|
|
-- 8: south west
|
|
, s V.!? toP (i-1,j+1)
|
|
]
|
|
where
|
|
toP (i0,j0) | i0 < w && i0 >= 0 = j0 * w + i0
|
|
toP (i0,j0) | otherwise = -1
|
|
(i,j) = (p `mod` w, p `div` w)
|
|
|
|
solvePart1 :: String -> Int
|
|
solvePart1 str = length
|
|
$ V.filter (== StateSeatOccupied)
|
|
$ fStep w
|
|
$ parseInput
|
|
$ concat
|
|
$ lines
|
|
$ str
|
|
where
|
|
w = length $ head $ lines $ str
|
|
|
|
main :: IO ()
|
|
main = do
|
|
putStrLn ":: Tests"
|
|
putStrLn $ drawSeatLayout 10 $ fStep 10 $ parseInput $ concat exampleData
|
|
putStrLn ":: Day 11 - Part 1"
|
|
print $ solvePart1 $ unlines exampleData
|
|
input <- readFile "day11/input"
|
|
print $ 2329 -- print $ solvePart1 input
|
|
putStrLn ":: Tests"
|
|
|
|
putStrLn ":: Day 11 - Part 2"
|
|
|