Haskell
Not a very pretty solution today, I'm afraid.
import Control.Arrow
import Control.Monad
import Data.Biapplicative
import Data.Ix
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Set qualified as Set
type Coords = (Int, Int)
readInput :: String -> Map Coords Char
readInput s =
Map.fromAscList
[ ((i, j), c)
| (i, l) <- zip [0 ..] (lines s),
(j, c) <- zip [0 ..] l
]
(.+.), (.-.) :: Coords -> Coords -> Coords
(.+.) = join biliftA2 (+)
(.-.) = join biliftA2 (-)
part1, part2 :: (Coords -> Bool) -> (Coords, Coords) -> [Coords]
part1 valid (p1, p2) =
let s = p2 .-. p1
in filter valid [p1 .-. s, p2 .+. s]
part2 valid (p1, p2) =
let (si, sj) = p2 .-. p1
d = gcd si sj
s = (si `div` d, sj `div` d)
in takeWhile valid (iterate (.+. s) p1)
++ takeWhile valid (drop 1 $ iterate (.-. s) p2)
pairs (x : xs) = map (x,) xs ++ pairs xs
pairs _ = []
main = do
input <- readInput <$> readFile "input08"
let antennas = Map.filter (/= '.') input
antennaGroups =
Map.foldrWithKey
(\p c m -> Map.insertWith (++) c [p] m)
Map.empty
antennas
valid =
inRange
. (Set.findMin &&& Set.findMax)
$ Map.keysSet input
antiNodes model =
Set.fromList
. concatMap (concatMap (model valid) . pairs)
$ antennaGroups
print . Set.size $ antiNodes part1
print . Set.size $ antiNodes part2