|
| 1 | +module Main where |
| 2 | + |
| 3 | +import Data.List |
| 4 | +import Data.List.Unique |
| 5 | +import System.Environment |
| 6 | + |
| 7 | +type Input = ((Int, Int), [(Char, [(Int, Int)])]) |
| 8 | +type Output = Int |
| 9 | + |
| 10 | +parseInput :: String -> Input |
| 11 | +parseInput s = ((width, height), freqs) |
| 12 | + where |
| 13 | + ls = lines s |
| 14 | + height = length $ ls |
| 15 | + width = length . head $ ls |
| 16 | + |
| 17 | + freqs = map (\f -> (f, locations f)) frequencies |
| 18 | + frequencies = uniq . sort . filter (/= '.') $ concat ls |
| 19 | + locations c = [(x, y) | x<-[0..width-1], y<-[0..height-1], (ls !! y) !! x == c] |
| 20 | + |
| 21 | +isOut :: (Int, Int) -> (Int, Int) -> Bool |
| 22 | +isOut (width, height) (x, y) = x < 0 || y < 0 || x >= width || y >= height |
| 23 | + |
| 24 | +diffVect :: (Int, Int) -> (Int, Int) -> (Int, Int) |
| 25 | +diffVect (x, y) (x', y') = (x-x', y-y') |
| 26 | + |
| 27 | +addVect :: (Int, Int) -> (Int, Int) -> (Int, Int) |
| 28 | +addVect (x, y) (x', y') = (x+x', y+y') |
| 29 | + |
| 30 | +addVectn :: (Int, Int) -> Int -> (Int, Int) -> (Int, Int) |
| 31 | +addVectn (x, y) n (x', y') = (x+n*x', y+n*y') |
| 32 | + |
| 33 | +pairs :: [a] -> [(a, a)] |
| 34 | +pairs l = [(x,y) | (x:ys) <- tails l, y <- ys] |
| 35 | + |
| 36 | +algo :: [Int] -> Bool -> Input -> [(Int, Int)] |
| 37 | +algo range authorizeAntenna (dim, antennaGrid) = nub . concat $ map getAntinodes antennaGrid -- |
| 38 | + where |
| 39 | + getAntinode :: (Int, Int) -> (Int, Int) -> [(Int, Int)] |
| 40 | + getAntinode ant1 ant2 = concat [side ant1 $ diffVect ant1 ant2, side ant2 $ diffVect ant2 ant1] |
| 41 | + where |
| 42 | + side :: (Int, Int) -> (Int, Int) -> [(Int, Int)] |
| 43 | + side ant dist = takeWhile (not . isOut dim) $ map (\n -> addVectn ant n dist) range |
| 44 | + |
| 45 | + getAntinodes (_, antennas) = filter ((||) authorizeAntenna . (`notElem` antennas)) . concat . map (uncurry getAntinode) $ pairs antennas |
| 46 | + |
| 47 | +part1 :: Input -> Output |
| 48 | +part1 = length . algo [1] False |
| 49 | + |
| 50 | +part2 :: Input -> Output |
| 51 | +part2 = length . algo [0..] True |
| 52 | + |
| 53 | +insertAt :: Char -> [String] -> (Int, Int) -> [String] |
| 54 | +insertAt c grid (x, y) = linesBefore ++ [charsBefore ++ [c] ++ charsAfter] ++ linesAfter |
| 55 | + where |
| 56 | + (linesBefore, currLine:linesAfter) = splitAt y grid |
| 57 | + (charsBefore, _:charsAfter) = splitAt x currLine |
| 58 | + |
| 59 | +getGrid :: Input -> [(Int, Int)] -> [String] |
| 60 | +getGrid ((w, h), freqs) antinodes = antennasGrid |
| 61 | + where |
| 62 | + blankGrid = [['.' | _<-[1..w]] | _<-[1..h]] |
| 63 | + antinodesGrid = foldl (insertAt '#') blankGrid antinodes |
| 64 | + antennasGrid = foldl (\g (c, as) -> foldl (insertAt c) g as) antinodesGrid freqs |
| 65 | + |
| 66 | +getGrid1 :: Input -> [String] |
| 67 | +getGrid1 input = getGrid input $ algo [1] False input |
| 68 | + |
| 69 | +getGrid2 :: Input -> [String] |
| 70 | +getGrid2 input = getGrid input $ algo [0..] True input |
| 71 | + |
| 72 | +main :: IO () |
| 73 | +main = do |
| 74 | + args <- getArgs |
| 75 | + content <- readFile (last args) |
| 76 | + let input = parseInput content |
| 77 | + |
| 78 | + print $ part1 input |
| 79 | + print $ part2 input |
| 80 | + |
| 81 | + -- putStrLn $ unlines $ getGrid1 input |
| 82 | + -- putStrLn $ unlines $ getGrid2 input |
0 commit comments