summaryrefslogtreecommitdiff
path: root/2017/22x.hs
blob: c2dd9dbba7fd0cbf2f94f6c9e53c153e6d81e001 (plain)
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
{-# LANGUAGE MultiWayIf, BangPatterns #-}
import Prelude hiding (Right, Left)
import Control.Monad
import Data.Array.IO


type Idx = (Int, Int)
data Dir = Up | Right | Down | Left
  deriving (Show, Eq, Enum)

type Chart = IOUArray Idx Int
data State = State !Chart !Idx !Dir

concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b]
concatMapM f l = liftM concat (mapM f l)

printstate :: State -> IO ()
printstate (State chart idx dir) = do
    ((top, left), (bottom, right)) <- getBounds chart
    rows <- mapM (row (left, right)) [top..bottom]
    mapM_ putStrLn rows
  where
    pcell :: Idx -> Int -> String
    pcell idx' c =
        if | idx' == idx -> ['[', c']
           | idx' == add idx Right -> [']', c']
           | otherwise -> [' ', c']
      where c' = ".W#F" !! c

    row :: (Int, Int) -> Int -> IO String
    row (left, right) y = concatMapM (\i -> liftM (pcell i) (readArray chart i)) (range ((y, left), (y, right)))

add :: Idx -> Dir -> Idx
add (y, x) dir = case dir of
    Up -> (y - 1, x)
    Right -> (y, x + 1)
    Down -> (y + 1, x)
    Left -> (y, x - 1)

rotright :: Dir -> Dir
rotright Up = Right
rotright Right = Down
rotright Down = Left
rotright Left = Up

rotleft :: Dir -> Dir
rotleft Up = Left
rotleft Left = Down
rotleft Down = Right
rotleft Right = Up

rot180 :: Dir -> Dir
rot180 = rotright . rotright

type Virus = Int -> Dir -> (Int, Dir)

simulate :: Virus -> Int -> State -> IO (State, Int)
simulate virus numsteps initstate = do
    let foldfunc (st, n) _ = go st >>= \(st', b) -> return (st', n + fromEnum b)
    foldM foldfunc (initstate, 0) [1..numsteps]
  where
    go :: State -> IO (State, Bool)
    go !(State chart idx dir) = do
        bnds <- getBounds chart
        if inRange bnds idx
            then do
                c <- readArray chart idx
                let (c', dir') = virus c dir
                writeArray chart idx c'
                return (State chart (add idx dir') dir', c' == 2)
            else do
                let ((top, left), (bottom, right)) = bnds
                    bnds' = ((2 * top, 2 * left), (2 * bottom, 2 * right))
                chart' <- newArray bnds' 0
                getAssocs chart >>= mapM (uncurry (writeArray chart'))
                go (State chart' idx dir)

virus1 :: Virus
virus1 0 dir = (2, rotleft dir)
virus1 2 dir = (0, rotright dir)

virus2 :: Virus
virus2 0 dir = (1, rotleft dir)
virus2 1 dir = (2, dir)
virus2 2 dir = (3, rotright dir)
virus2 3 dir = (0, rot180 dir)

main :: IO ()
main = do
    input <- (readFile "22.in")
    let w = length (head (lines input))
        h = length (lines input)
        (halfw, halfh) = (w `quot` 2, h `quot` 2)

    chart <- newListArray ((-halfh, -halfw), (halfh, halfw))
                (map (\c -> fromEnum $ if c == '#' then 2 else 0) $ filter (/= '\n') input)

    (state1, numinfs1) <- simulate virus1 10000 (State chart (0, 0) Up)
    -- printstate state1
    print numinfs1

    (state2, numinfs2) <- simulate virus1 1000000 (State chart (0, 0) Up)
    -- printstate state2
    getBounds ((\(State ch _ _) -> ch) state2) >>= print
    print numinfs2