In [1]:
import Data.List.Split
In [2]:
inputLine = head <$> lines <$> readFile "input/day1.txt"
In [3]:
instructions = splitOn "," . filter (/= ' ') <$> inputLine
take 5 <$> instructions -- just to see if it works
In [4]:
-- Takes an initial direction and a list of instructions and returns a list of single-block steps
getSteps :: (Int, Int) -> [String] -> [(Int, Int)]
getSteps _ [] = []
getSteps direction (instruction:is) =
replicate distance newDirection ++ getSteps newDirection is
where
turn = head instruction -- L or R
distance = read (tail instruction)
evalTurn :: Char -> (Int, Int) -> (Int, Int)
evalTurn 'L' (dx, dy) = (-dy, dx)
evalTurn 'R' (dx, dy) = (dy, -dx)
newDirection = evalTurn turn direction
In [5]:
-- Applies a single-block step to a given position and returns the new position
applyStep :: (Int, Int) -> (Int, Int) -> (Int, Int)
applyStep (x, y) (dx, dy) = (x + dx, y + dy)
In [6]:
-- Takes a list of steps and returns the final position if (0, 0) was the initial position
evalSteps = foldl applyStep (0, 0)
In [7]:
-- Manhattan distance from the origin
distanceFromOrigin :: (Int, Int) -> Int
distanceFromOrigin (x, y) = abs x + abs y
In [8]:
-- Solution of part 1
distanceFromOrigin . evalSteps . getSteps (0, 1) <$> instructions
In [9]:
import qualified Data.Set as Set
In [10]:
-- * Takes a list of steps
-- * Assumes that (0, 0) is the initial position
-- * Returns a list of tuples where:
-- * the first tuple element is the position
-- * the second tuple element is the set of positions that have been visited previously.
evalSteps' = scanl update ((0, 0), Set.empty)
where
update (position, alreadyVisited) step = (applyStep position step, position `Set.insert` alreadyVisited)
In [11]:
-- Solution of part 2
distanceFromOrigin . fst . head . filter (uncurry Set.member) . evalSteps' . getSteps (0, 1) <$> instructions