aboutsummaryrefslogtreecommitdiffstats
path: root/day19/main.hs
blob: cf01624ca87e1b0ab05bf22f90936fd38838b129 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
{-# LANGUAGE TupleSections #-}

module Main where

import Control.Monad (join)
import Data.Foldable (find)
import Data.List (deleteBy, nub)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (isJust)
import Debug.Trace (trace, traceShow)
import Utils

type Point = (Int, Int, Int)

type Scanner = (Int, [Point])

cmap :: t -> [t -> b] -> [b]
cmap x = map ($ x)

dot (a, b, c) (d, e, f) = a * d + b * e + c * f

mmulti (a, b, c) v = (dot a v, dot b v, dot c v)

transforms v =
  let x = mmulti ((1, 0, 0), (0, 0, -1), (0, 1, 0))
      y = mmulti ((0, 0, 1), (0, 1, 0), (-1, 0, 0))
      z = mmulti ((0, -1, 0), (1, 0, 0), (0, 0, 1))
   in cmap
        v
        [ id,
          x,
          y,
          z,
          x . x,
          x . y,
          x . z,
          y . x,
          y . y,
          z . y,
          z . z,
          x . x . x,
          x . x . y,
          x . x . z,
          x . y . x,
          x . y . y,
          x . z . z,
          y . x . x,
          y . y . y,
          z . z . z,
          x . x . x . y,
          x . x . y . x,
          x . y . x . x,
          x . y . y . y
        ]

transformBeacons :: [Point] -> [[Point]]
transformBeacons beacons =
  beacons $> map transforms .> transpose

checkAssumedMatch beacons1 beacons2 (b1, b2) =
  let delta = calculateDelta b1 b2
      translatedBeacons2 = translateBeacons delta beacons2
      matchingCommonBeacons = translatedBeacons2 $> filter (`elem` beacons1)
   in if length matchingCommonBeacons >= 12
        then Just (translatedBeacons2, delta)
        else Nothing

checkCompositeAndScannerTf :: Scanner -> Scanner -> Maybe (Int, [Point], Point)
checkCompositeAndScannerTf (s1, beacons1) (s2, beacons2) =
  let pairings = [(b1, b2) | b1 <- beacons1, b2 <- beacons2]
      found = map (checkAssumedMatch beacons1 beacons2) pairings $> find isJust .> join
      -- if found
      composite = concat .> nub
   in case found of
        Just (newBeacons2, newScannerPos) -> Just (s2, composite [beacons1, newBeacons2], newScannerPos)
        Nothing -> Nothing

manhattanD (x, y, z) (x', y', z') = abs (x - x') + abs (y - y') + abs (z - z')

calculateDelta :: Point -> Point -> Point
calculateDelta (x, y, z) (x', y', z') = (x - x', y - y', z - z')

translateBeacons :: Point -> [Point] -> [Point]
translateBeacons (dx, dy, dz) =
  map (\(x, y, z) -> (x + dx, y + dy, z + dz))

check2CompositeAndScanner :: Scanner -> Scanner -> Maybe (Int, [Point], Point)
check2CompositeAndScanner scanner1@(s1, beacons1) scanner2@(s2, beacons2) =
  let beacons2Tfs = transformBeacons beacons2
      found = beacons2Tfs $> map (\bs -> checkCompositeAndScannerTf scanner1 (s2, bs)) .> find isJust .> join
   in found

compute _ [] = error "empty scanners list"
compute scannerPositions [composite] = Just (composite, scannerPositions)
compute scannerPositions (scanner@(s, _) : others) =
  let scannerPairs = others $> map (scanner,)
      found = scannerPairs $> map (uncurry check2CompositeAndScanner) .> find isJust .> join
   in case found of
        Just (droppedS, compositeBeacons, newScannerPos) ->
          let newOthers = filter (fst .> (/=) droppedS) others
              newComposite = (s, compositeBeacons)
              newScannerPositions = newScannerPos : scannerPositions
           in compute newScannerPositions (newComposite : newOthers)
        Nothing -> compute scannerPositions others

main :: IO ()
main = do
  contents <- getContents
  let scanners =
        contents
          $> lines
          .> splitOn null
          .> map (drop 1)
          .> map (map (splitOn (== ',') .> map read .> \[x, y, z] -> (x, y, z)))
          .> zip [0 ..]

  let Just ((_, resultBeacons), resultScannerPositions) = compute [] scanners
  length resultBeacons $> print
  let scannerPairs = [(s1, s2) | s1 <- resultScannerPositions, s2 <- resultScannerPositions, s1 /= s2]
  scannerPairs $> map (uncurry manhattanD) .> maximum .> print