summaryrefslogtreecommitdiff
path: root/2019/12.hs
blob: 81d20ae55a503e73b3fbc8fdd93ec5d016d62843 (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
module Main where

import Data.Char
import Data.List
import Data.Maybe

import Input


parseVec :: String -> [Int]
parseVec = map read . tokens (3 :: Int)
  where
    tokenf c = isDigit c || c == '-'
    token str = span tokenf (snd (break tokenf str))
    tokens 0 _ = []
    tokens n str = uncurry (:) (fmap (tokens (n - 1)) (token str))

data Moon1D = Moon1D { mPos :: {-# UNPACK #-} !Int, mVel :: {-# UNPACK #-} !Int } deriving (Show, Eq)
newtype System1D = System1D [Moon1D] deriving (Show, Eq)

gravity1D :: [Int] -> [Int]
gravity1D coords = [length (filter (> x) coords) - length (filter (< x) coords) | x <- coords]

-- -- If there are MANY moons, the below will be faster (n log n instead of n^2)
-- gravity1D :: [Int] -> [Int]
-- gravity1D inCoords =
--     let (coords, backIndices) = unzip (sortOn fst (zip inCoords [0::Int ..]))
--         cumBeforeOf cds = map fst (scanl (\(nb, ct) (a, b) ->
--                                               if a < b then (nb + ct, 1) else (nb, ct + 1))
--                                          (0, 1) (zip cds (tail cds)))
--         cumBefore = cumBeforeOf coords
--         cumAfter = reverse (cumBeforeOf (map negate (reverse coords)))
--         grav = zipWith (-) cumAfter cumBefore
--     in map snd (sortOn fst (zip backIndices grav))

physics1D :: System1D -> System1D
physics1D (System1D moons) =
    let gravity = gravity1D (map mPos moons)
        moons1 = zipWith (\(Moon1D p v) g -> Moon1D p (v + g)) moons gravity
        moons2 = [Moon1D (p + v) v | Moon1D p v <- moons1]
    in System1D moons2

energy :: [System1D] -> Int
energy systems =
    let moons = transpose [moons1d | System1D moons1d <- systems]
        moonEnergy moon1ds = sum (map (abs . mPos) moon1ds) * sum (map (abs . mVel) moon1ds)
    in sum (map moonEnergy moons)

main :: IO ()
main = do
    initSystems <- map System1D . transpose . map (map (\p -> Moon1D p 0) . parseVec) <$> getInput 12

    print $ energy [iterate physics1D s !! 1000 | s <- initSystems]

    -- Note: any cycle must contain the starting position, because the
    -- simulation step is uniquely reversible. -- Credits Bert
    let reps = [1 + fromJust (findIndex (== initSystem) (tail (iterate physics1D initSystem)))
               | initSystem <- initSystems]
    print $ foldl1 lcm reps