summaryrefslogtreecommitdiff
path: root/2017/20.hs
blob: 497ff58f12ef724135a29f91cf1ce1b4f97e9a62 (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
{-# LANGUAGE TupleSections #-}
import Control.Monad
import Data.Function
import Data.List
import Data.Maybe
import Debug.Trace


uniqOn :: Eq b => (a -> b) -> [a] -> [a]
uniqOn f (a:b:cs) | f a == f b = uniqOn f (b:cs)
                  | otherwise = a : uniqOn f (b:cs)
uniqOn _ l = l

cartprod3 :: [a] -> [b] -> [c] -> [(a, b, c)]
cartprod3 as bs cs = [(a, b, c) | a <- as, b <- bs, c <- cs]

type Vec = (Int, Int, Int)

data Particle = Particle {pP :: Vec, pV :: Vec, pA :: Vec}
  deriving (Show, Eq)

add :: Vec -> Vec -> Vec
add (a, b, c) (d, e, f) = (a + d, b + e, c + f)

sub :: Vec -> Vec -> Vec
sub (a, b, c) (d, e, f) = (a - d, b - e, c - f)

mul :: Int -> Vec -> Vec
mul n (a, b, c) = (n * a, n * b, n * c)

parse :: String -> Particle
parse str =
    let [p, v, a] = map (parseVec . init . init . drop 3) (words (str ++ ","))
    in Particle p v a

parseVec :: String -> Vec
parseVec str =
    let [x, y, z] = map read (words [if c == ',' then ' ' else c | c <- str])
    in (x, y, z)

man :: Vec -> Int
man (x, y, z) = abs x + abs y + abs z

vecmax :: Vec -> Int
vecmax (x, y, z) = maximum [abs x, abs y, abs y]

distance :: Particle -> Int
distance = man . pP

simulate :: Int -> Particle -> Particle
simulate t (Particle pos vel acc) =
    let p = add pos $ add (mul t vel) $ mul (t * (t + 1) `div` 2) acc
        v = add vel (mul t acc)
    in Particle p v acc

collide' :: Int -> Int -> Int -> [Int]
collide' dp' dv' da' =
    let (dp, dv, da) = (fromIntegral dp', fromIntegral dv', fromIntegral da') :: (Double, Double, Double)
        discr = (dv + da/2) ^ 2 - 2 * da * dp
        s = sqrt discr
        t1 = (-dv - da/2 + s) / da
        t2 = (-dv - da/2 - s) / da
        rt1 = round t1
        rt2 = round t2
    in if discr < 0 || s * s /= discr then []
       else (if t1 >= 0 && fromIntegral rt1 == t1 then [rt1] else []) ++
            (if t2 >= 0 && fromIntegral rt2 == t2 then [rt2] else [])

collide :: Particle -> Particle -> Maybe Int
collide part1@(Particle p1 v1 a1) part2@(Particle p2 v2 a2) =
    let (dpx, dpy, dpz) = sub p1 p2
        (dvx, dvy, dvz) = sub v1 v2
        (dax, day, daz) = sub a1 a2
        ts = [a
             | (a, b, c) <- cartprod3 (collide' dpx dvx dax) (collide' dpy dvy day) (collide' dpz dvz daz),
               a == b && b == c]
        [] = [pP (simulate t part1) /= pP (simulate t part2) | t <- ts]
    in if null ts then Nothing else Just (minimum ts)

prune :: [Particle] -> [Particle]
prune parts = case traceShowId $ groupBy ((==) `on` snd) $ sortOn snd $
                       catMaybes [fmap ((pi, qi),) (collide p q)
                                 | (p, pi) <- zip parts [0..], (q, qi) <- zip parts [0..], pi /= qi]
              of
    [] -> parts
    (colls : _) ->
        let indices = concatMap (\((a, b), _) -> [a, b]) colls
            parts' = [p | (p, i) <- zip parts [0..], not (i `elem` indices)]
        in prune parts'

main :: IO ()
main = do
    input <- liftM (map parse . lines) (readFile "20.in")
    let bigt = maximum (map (vecmax . pP) input)
        parts = map (simulate bigt) input
        sorted = sortOn (man.pA.fst) $ sortOn (man.pV.fst) $ sortOn (man.pP.fst) $ zip parts [0..]
    print $ head (map snd sorted)

    print $ length $ iterate (map head . filter ((== 1) . length) . groupBy ((==) `on` pP) . sortOn pP . map (simulate 1)) (sortOn pP input) !! 100
    -- mapM_ print $ groupBy ((==) `on` snd) $ sortOn snd $ catMaybes [fmap ((pi, qi),) (collide p q) | (p, pi) <- zip input [0..], (q, qi) <- zip input [0..]]

    -- print $ length (prune input)