Skip to content

Instantly share code, notes, and snippets.

@travisby
Last active December 3, 2016 19:18
Show Gist options
  • Save travisby/faea92ca332ff3add0ff2b5abaad43d9 to your computer and use it in GitHub Desktop.
Save travisby/faea92ca332ff3add0ff2b5abaad43d9 to your computer and use it in GitHub Desktop.
import Text.Read
import Data.Maybe
import Data.List.Split
import Data.Set hiding (map, foldl)
-- S is straight
-- we need S for when we break all of the instructions into length=1 vectors, to support
-- intermediary steps
data Direction = L | R | S deriving (Show, Eq)
data Instruction = Instruction { turnDirection :: Direction, distance :: Int } deriving Show
data Coordinate = Coordinate Int Int deriving (Show, Eq, Ord)
data Vector = Vector Coordinate Coordinate deriving Show
data CompassDirection = North | South | East | West deriving (Show, Eq, Ord)
data Traveler = Traveler {coordinate :: Coordinate, direction :: CompassDirection } deriving (Show, Ord)
-- we don't care about a traveler's direction, only his placement
instance Eq Traveler where a == b = (coordinate a) == (coordinate b)
taxiCabDistance :: Vector -> Int
taxiCabDistance (Vector (Coordinate x1 y1) (Coordinate x2 y2)) = (abs x2 - x1) + (abs y2 - y1)
taxiCabDistanceFromStart :: Coordinate -> Int
taxiCabDistanceFromStart = taxiCabDistance . Vector (Coordinate 0 0)
toInstruction :: String -> Maybe Instruction
toInstruction ('L':xs)
| Just i <- readMaybe xs
= Just $ Instruction L i
toInstruction ('R':xs)
| Just i <- readMaybe xs
= Just $ Instruction R i
toInstruction _ = Nothing
breakApartInstructions :: Instruction -> [Instruction]
breakApartInstructions (Instruction dir dist) = (Instruction dir 1):(take (dist-1) (repeat (Instruction S 1)))
-- if any fail, we want all to fail
toInstructions :: [String] -> Maybe [Instruction]
toInstructions xs = if (any isNothing instructions) then Nothing else Just $ map fromJust instructions
where instructions = map toInstruction xs
-- TODO I bet there's a better algebraic way to do this
applyInstruction :: Traveler -> Instruction -> Traveler
applyInstruction (Traveler (Coordinate x y) dir) (Instruction turn dist)
| (dir == East) && (turn == L) = Traveler (Coordinate x (y + dist)) North
| (dir == West) && (turn == R) = Traveler (Coordinate x (y + dist)) North
| (dir == West) && (turn == L) = Traveler (Coordinate x (y - dist)) South
| (dir == East) && (turn == R) = Traveler (Coordinate x (y - dist)) South
| (dir == South) && (turn == L) = Traveler (Coordinate (x + dist) y) East
| (dir == North) && (turn == R) = Traveler (Coordinate (x + dist) y) East
| (dir == North) && (turn == L) = Traveler (Coordinate (x - dist) y) West
| (dir == South) && (turn == R) = Traveler (Coordinate (x - dist) y) West
| (dir == North) && (turn == S) = Traveler (Coordinate x (y + dist)) North
| (dir == South) && (turn == S) = Traveler (Coordinate x (y - dist)) South
| (dir == East) && (turn == S) = Traveler (Coordinate (x + dist) y) East
| (dir == West) && (turn == S) = Traveler (Coordinate (x - dist) y) West
applyInstructions :: Traveler -> [Instruction] -> Traveler
applyInstructions = foldl applyInstruction
-- TODO better name. This is to get the intermediate steps that the traveler took
getInstructions :: Traveler -> [Instruction] -> [Traveler]
getInstructions = scanl applyInstruction
-- TODO set implementation, will be much faster
findFirstDuplicate :: (Ord a) => [a] -> a
findFirstDuplicate = findFirstDuplicateInternal empty
where
findFirstDuplicateInternal s [] = error "No duplicate"
findFirstDuplicateInternal s (x:xs) = if member x s then x else findFirstDuplicateInternal (insert x s) xs
main = do
stdin <- getContents
-- init avoids the trailing \n
putStrLn . show . taxiCabDistanceFromStart . coordinate . applyInstructions (Traveler (Coordinate 0 0) North) $ fromJust . toInstructions $ splitOn ", " $ init stdin
putStrLn . show . taxiCabDistanceFromStart . findFirstDuplicate . map coordinate $ getInstructions (Traveler (Coordinate 0 0) North) $ concat . map breakApartInstructions $ fromJust . toInstructions $ splitOn ", " $ init stdin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment