136 lines
4.2 KiB
Haskell
136 lines
4.2 KiB
Haskell
import Data.List (nub, tails)
|
|
import Parsing (splitByString)
|
|
|
|
type Line = ((Int, Int), (Int, Int))
|
|
|
|
main :: IO ()
|
|
main = do
|
|
input <- getContents
|
|
let
|
|
vents = parseVents input
|
|
putStrLn (show (solution1 vents))
|
|
|
|
solution1 :: [Line] -> Int
|
|
solution1 = length . nub . intersectAll . filterDiagonal
|
|
|
|
intersectAll :: [Line] -> [(Int, Int)]
|
|
intersectAll =
|
|
flatten . (map intersectOne) . (combinations 2)
|
|
where
|
|
intersectOne :: [Line] -> [(Int, Int)]
|
|
intersectOne [] = []
|
|
intersectOne (vent:others) = flatten (map (intersect vent) others)
|
|
|
|
intersect :: Line -> Line -> [(Int, Int)]
|
|
intersect ((x1, y1), (x1', y1')) ((x2, y2), (x2', y2'))
|
|
-- Colinear - In these cases, we may have multiple intersections
|
|
| all (== x1) [x1', x2, x2'] =
|
|
(map (\y -> (x1,y)) (intersect1D (sortedTuple (y1, y1')) (sortedTuple (y2, y2'))))
|
|
| all (== y1) [y1', y2, y2'] =
|
|
(map (\x -> (x,y1)) (intersect1D (sortedTuple (x1, x1')) (sortedTuple (x2, x2'))))
|
|
-- Not colinear - We can find out if these intersect by simply
|
|
-- checking whether the second line goes through the
|
|
-- horizontal/vertical range of the first (whichever line the first
|
|
-- segment lives on)
|
|
| (x1 == x1' && y2 == y2') =
|
|
if (min x2 x2') <= x1 && (max x2 x2') >= x1 && (min y1 y1') <= y2 && (max y1 y1') >= y2
|
|
then
|
|
[(x1,y2)]
|
|
else
|
|
[]
|
|
| (y1 == y1' && x2 == x2') =
|
|
if (min y2 y2') <= y1 && (max y2 y2') >= y1 && (min x1 x1') <= x2 && (max x1 x1') >= x2
|
|
then
|
|
[(x2,y1)]
|
|
else
|
|
[]
|
|
-- In all other cases, we consider the lines not to intersect, since
|
|
-- diagonal lines are out of scope
|
|
| otherwise = []
|
|
where
|
|
sortedTuple :: (Int, Int) -> (Int, Int)
|
|
sortedTuple (a, b)
|
|
| a > b = (b, a)
|
|
| otherwise = (a, b)
|
|
intersect1D :: (Int, Int) -> (Int, Int) -> [Int]
|
|
intersect1D (a, b) (c, d) =
|
|
let
|
|
start = max a c
|
|
end = min b d
|
|
in
|
|
[start..end]
|
|
|
|
filterDiagonal :: [Line] -> [Line]
|
|
filterDiagonal = filter (not . isDiagonal)
|
|
where
|
|
isDiagonal :: Line -> Bool
|
|
isDiagonal ((x, y), (x', y')) = x /= x' && y /= y'
|
|
|
|
parseVents :: String -> [Line]
|
|
parseVents = tupelize . splitUp
|
|
where
|
|
tupelize :: [[[Int]]] -> [Line]
|
|
tupelize = map subTupelize
|
|
where
|
|
subTupelize :: [[Int]] -> Line
|
|
subTupelize [[a, b], [c, d]] = ((a, b), (c, d))
|
|
-- For our parsing purposes, we don't need to cover error
|
|
-- cases
|
|
subTupelize _ = undefined
|
|
splitUp :: String -> [[[Int]]]
|
|
splitUp = (map (map (map read))) . (map (map (splitByString ","))) . (map (splitByString " -> ")) . lines
|
|
|
|
-- Missing stdlib functions
|
|
|
|
combinations :: Int -> [a] -> [[a]]
|
|
combinations 0 _ = [[]]
|
|
combinations n xs = [y:ys | y:xs' <- tails xs
|
|
, ys <- combinations (n-1) xs']
|
|
|
|
flatten :: [[a]] -> [a]
|
|
flatten = foldr1 (++)
|
|
|
|
-- Tests
|
|
|
|
testInput1 :: String
|
|
testInput1 = unlines [
|
|
"0,9 -> 5,9",
|
|
"8,0 -> 0,8",
|
|
"9,4 -> 3,4",
|
|
"2,2 -> 2,1",
|
|
"7,0 -> 7,4",
|
|
"6,4 -> 2,0",
|
|
"0,9 -> 2,9",
|
|
"3,4 -> 1,4",
|
|
"0,0 -> 8,8",
|
|
"5,5 -> 8,2"
|
|
]
|
|
|
|
test1 = solution1 (parseVents testInput1)
|
|
|
|
testIntersect1 = intersect ((0, 0), (0, 5)) ((0, 2), (0, 4)) == [(0, 2), (0, 3), (0, 4)]
|
|
testIntersect2 = intersect ((0, 0), (0, 5)) ((0, 5), (0, 6)) == [(0, 5)]
|
|
testIntersect3 = intersect ((0, 0), (0, 5)) ((0, 6), (0, 7)) == []
|
|
testIntersect4 = intersect ((0, 0), (5, 0)) ((2, 0), (4, 0)) == [(2, 0), (3, 0), (4, 0)]
|
|
testIntersect5 = intersect ((0, 0), (0, 5)) ((0, 2), (4, 2)) == [(0, 2)]
|
|
testIntersect6 = intersect ((0, 0), (0, 5)) ((0, 3), (4, 3)) == [(0, 3)]
|
|
testIntersect7 = intersect ((1, 0), (1, 5)) ((0, 2), (4, 2)) == [(1, 2)]
|
|
testIntersect8 = intersect ((4, 0), (4, 5)) ((0, 2), (4, 2)) == [(4, 2)]
|
|
testIntersect9 = intersect ((1, 0), (1, 5)) ((2, 2), (4, 2)) == []
|
|
testIntersect10 = intersect ((9, 4), (3, 4)) ((7, 0), (7, 4)) == [(7, 4)]
|
|
testIntersect11 = intersect ((2, 2), (2, 1)) ((3, 4), (1, 4)) == []
|
|
testIntersect12 = intersect ((0, 9), (5, 9)) ((7, 0), (7, 4)) == []
|
|
|
|
testIntersect =
|
|
all (== True) [
|
|
testIntersect1,
|
|
testIntersect2,
|
|
testIntersect3,
|
|
testIntersect4,
|
|
testIntersect5,
|
|
testIntersect6,
|
|
testIntersect7,
|
|
testIntersect8,
|
|
testIntersect9
|
|
]
|