aboutsummaryrefslogtreecommitdiffstats
path: root/day20/main.hs
blob: 450d24b87ad310300ed31971f25577ed750898a1 (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
{-# LANGUAGE LambdaCase #-}

module Main where

import Data.Bifunctor (bimap, first, second)
import Data.Function (on)
import Data.List (intercalate, minimumBy)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Debug.Trace (trace)
import Utils

type Binary = [Char]

type Point = (Int, Int)

type Image = Map Point Char

printImage image =
  let ps = M.keys image
      minX = ps $> map fst .> minimum
      minY = ps $> map snd .> minimum
      maxX = ps $> map fst .> maximum
      maxY = ps $> map snd .> maximum
      coords = [(x, y) | y <- [minY .. maxY], x <- [minX .. maxX]]
      charAtPoint p =
        M.findWithDefault '0' p image
          $> \case '1' -> '#'; '0' -> '.'; other -> other
      withSepLine lst = lst ++ [putStrLn "---"]
   in [minY .. maxY] $> map (\y -> putStrLn ([minX .. maxX] $> map (\x -> charAtPoint (x, y))))
        .> withSepLine
        .> sequence_

neighbors :: Image -> Point -> Binary
neighbors image p@(x, y) =
  let diffs = [(dx, dy) | dy <- [-1, 0, 1], dx <- [-1, 0, 1]]
      lookup p = M.findWithDefault '0' p image
      indices = diffs $> map (bimap (x +) (y +))
   in indices $> map lookup

computeNewValue algo image p =
  let bin = neighbors image p
      index = bin $> binaryToInt
   in algo !! index

process :: [Char] -> Image -> Image
process algo image =
  M.foldrWithKey (\p _ acc -> M.insert p (computeNewValue algo image p) acc) M.empty image

enlargeBoundsOnceWith c image =
  let ps = M.keys image
      minX = ps $> map fst .> minimum
      minY = ps $> map snd .> minimum
      maxX = ps $> map fst .> maximum
      maxY = ps $> map snd .> maximum
      newPs =
        [(x, y) | x <- [minX - 1], y <- [minY - 1 .. maxY + 1]]
          ++ [(x, y) | x <- [maxX + 1], y <- [minY - 1 .. maxY + 1]]
          ++ [(x, y) | x <- [minX - 1 .. maxX + 1], y <- [minY - 1]]
          ++ [(x, y) | x <- [minX - 1 .. maxX + 1], y <- [maxY + 1]]
   in foldr (`M.insert` c) image newPs

enlargeBoundsWith c n image =
  iterate (enlargeBoundsOnceWith c) image !! n

reduceBounds n image =
  let ps = M.keys image
      minX = ps $> map fst .> minimum
      minY = ps $> map snd .> minimum
      maxX = ps $> map fst .> maximum
      maxY = ps $> map snd .> maximum
   in M.filterWithKey (\(x, y) _ -> x - minX >= n && maxX - x >= n && y - minY >= n && maxY - y >= n) image

main :: IO ()
main = do
  -- config constants
  let iters = 50
  let reduceN = iters
  let enlargeN = 2 * iters + 10

  contents <- getContents
  let [algoArr, imageArr] = contents $> map (\case '#' -> '1'; '.' -> '0'; other -> other) .> lines .> splitOn null
  let algo = concat algoArr
  let (xSize, ySize) = (length $ head imageArr, length imageArr)
  let coords = [(x, y) | y <- [0 .. ySize -1], x <- [0 .. xSize - 1]]
  let image = zip coords (concat imageArr) $> M.fromList .> enlargeBoundsWith '0' enlargeN
  let computation = iterate (process algo) image

  let results = take (iters + 1) computation

  let lastImageReduced = reduceBounds reduceN (last results)
  printImage lastImageReduced

  let e1Result = lastImageReduced $> M.toList .> map snd .> filter (== '1') .> length
  print e1Result