Advertisement
Guest User

Daily Programmer Challenge #163 [Hard] by /u/Regimardyl

a guest
May 24th, 2014
263
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import Data.Function(on)
  2. import Data.List(nub, (\\))
  3. import Data.Maybe(mapMaybe)
  4. import System.Environment(getArgs)
  5. import System.IO(readFile, putStrLn)
  6.  
  7. -- A simple line
  8. data Line =
  9.     NLine { gradient :: Rational, yIntercept :: Rational } -- y = gradient * x + yIntercept
  10.     | VLine { xIntercept :: Rational } -- Vertical line: x = xIntercept
  11.     deriving (Eq, Show)
  12.  
  13. type Point = (Rational, Rational)
  14.  
  15. -- Allows to easily read and process the given input
  16. type NamedLine = (String, Point, Point)
  17.  
  18. -- (Line name, Line name, Comments about the intersection)
  19. type Intersection = (String, String, String)
  20.  
  21. -- Make a line going through two points
  22. fromPoints :: Point -> Point -> Line
  23. fromPoints (x1,y1) (x2,y2)
  24.     | x2 == x1  = VLine x1
  25.     | otherwise = NLine gradient yIntercept
  26.     where
  27.         gradient    = (y2-y1) / (x2-x1)
  28.         yIntercept  = y1 - x1 * gradient
  29.  
  30. -- Same for NamedLine
  31. fromNamedLine :: NamedLine -> Line
  32. fromNamedLine (_,p1,p2) = fromPoints p1 p2
  33.  
  34. -- Checks whether a number is between two others
  35. inRange :: Ord a => a -> a -> a -> Bool
  36. inRange n r1 r2 = (n>=from) && (n<=to)
  37.     where
  38.         (from, to) = if r1<r2 then (r1,r2) else (r2,r1)
  39.  
  40. -- Nice String representation for a Point
  41. prettyPoint :: Point -> String
  42. prettyPoint (x,y) = "(" ++ show (fromRational x :: Double) ++ "|" ++ show (fromRational y :: Double) ++ ")"
  43.  
  44. -- Nice String representation for an Intersection
  45. prettyIntersect :: Intersection -> String
  46. prettyIntersect (a,b,t) = a ++ " " ++ b ++ " " ++ t
  47.  
  48. -- x-coordinate of the point where two lines intersect, or whether they're identical
  49. intersectLines :: Line -> Line -> Either Bool Point
  50. intersectLines (NLine g1 y1) (NLine g2 y2) = if g1==g2
  51.     then Left $ y1 == y2 -- Identical (True) or Paralell (False)
  52.     else Right (xIntercept, g1*xIntercept + y1) -- Intersection point
  53.     where
  54.         xIntercept = (y2-y1) / (g1-g2)
  55. intersectLines (VLine x1) (NLine g2 y2) = Right (x1, x1*g2 + y2) -- Will always intersect at x1
  56. intersectLines l1@(NLine _ _) l2@(VLine _) = intersectLines l2 l1 -- Other way round
  57. intersectLines (VLine x1) (VLine x2) = Left $ x1 == x2 -- Identical (True) or Paralell (False)
  58.  
  59. -- Checks whether two named Lines intercept
  60. namedIntersect :: NamedLine -> NamedLine -> Maybe Intersection
  61. namedIntersect nl1@(n1,p11,p12) nl2@(n2,p21,p22) =
  62.     case intersectLines l1 l2 of
  63.         Left False -> Nothing -- Lines don't intersect
  64.         Left True -> if commonXRange
  65.             then Just (n1, n2, "overlap") -- Lines overlap
  66.             else Nothing -- Identical lines, but different ranges
  67.         Right p -> if inXRange $ fst p
  68.             then Just (n1, n2, "in " ++ prettyPoint p) -- Lines intersect
  69.             else Nothing -- Lines intersect, but not in range of both lines
  70.     where
  71.         (l1,l2) = ((,) `on` fromNamedLine) nl1 nl2
  72.         (x11,x12,x21,x22) = (fst p11, fst p12, fst p21, fst p22) -- Not very pretty, but in one line
  73.         inXRange n = inRange n x11 x12 && inRange n x21 x22
  74.         commonXRange = inRange x21 x11 x12 || inRange x22 x11 x12 -- One of the two points of the second line has to be in x range of the first line
  75.  
  76. -- Combine each element with each, excluding combining an element with itself
  77. combineList :: [a] -> [(a,a)]
  78. combineList [] = []
  79. combineList (x:xs) = map (\e -> (x,e)) xs ++ combineList xs
  80.  
  81. -- Combine each named line with each
  82. intersections :: [NamedLine] -> [Intersection]
  83. intersections = mapMaybe pairIntersect . combineList
  84.     where
  85.         pairIntersect (l1,l2) = namedIntersect l1 l2
  86.  
  87. main = do
  88.     (inputfile:_) <- getArgs
  89.     input <- readFile inputfile
  90.     let givenLines = map (parseLine . words) $ lines input
  91.         intersectingLines = intersections givenLines
  92.         nonIntersectingLines = map (\(n,_,_) -> n) givenLines \\ -- given line names
  93.             nub (concatMap (\(a,b,_) -> [a,b]) intersectingLines) -- names of all intersecting lines
  94.     putStrLn "Intersecting Lines:"
  95.     mapM_ (putStrLn . prettyIntersect) intersectingLines
  96.     putStrLn "No intersections:"
  97.     mapM_ putStrLn nonIntersectingLines
  98.     where
  99.         parseLine [n,p11,p12,p21,p22] =
  100.             (n, (readRational p11, readRational p12), (readRational p21, readRational p22))
  101.         readRational :: String -> Rational
  102.         readRational s@('.':_) = readRational $ '0':s
  103.         readRational s = toRational (read s :: Double)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement