Last active
December 3, 2016 19:18
-
-
Save travisby/faea92ca332ff3add0ff2b5abaad43d9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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