import Control.Arrow
import Data.List
import Prelude hiding (Left, Right)
data Direction = North | East | South | West
deriving (Eq, Show)
data Turn = Left | Right
data Command = N Int | S Int | E Int | W Int | L Int | R Int | F Int
deriving (Read, Show)
data State =
St { facing :: Direction
, position :: (Int, Int)
, waypoint :: (Int, Int)
}
deriving Show
adjust :: String -> String
adjust [] = undefined
adjust (c:cs) = c:' ':cs
directions :: [Direction]
directions = [North, East, South, West]
move :: Direction -> Int -> (Int, Int) -> (Int, Int)
move North = second . (+)
move South = second . (subtract)
move East = first . (+)
move West = first . (subtract)
turn :: Turn -> Int -> Direction -> Direction
turn t i dir =
directions !! ((4 + ((dirpos dir + i * diroffs t) `mod` 4)) `mod` 4)
where dirpos North = 4
dirpos East = 1
dirpos South = 2
dirpos West = 3
diroffs Left = -1
diroffs Right = 1
drive :: State -> Command -> State
drive st cmd =
case cmd of
(N i) -> st { position = move North i pos }
(E i) -> st { position = move East i pos }
(W i) -> st { position = move West i pos }
(S i) -> st { position = move South i pos }
(F i) -> st { position = move dir i pos }
(R i) -> st { facing = turn Right (i `div` 90) dir }
(L i) -> st { facing = turn Left (i `div` 90) dir }
where pos = position st
dir = facing st
rotate :: Turn -> Int -> (Int, Int) -> (Int, Int)
rotate dir i way = iterate (single dir) way !! i
where
single Left (x,y) = (-y, x)
single Right (x,y) = (y, -x)
actual_drive :: State -> Command -> State
actual_drive st cmd =
case cmd of
(F i) -> st { position = (x + xoffs*i, y + yoffs*i) }
(N i) -> st { waypoint = move North i way }
(E i) -> st { waypoint = move East i way }
(W i) -> st { waypoint = move West i way }
(S i) -> st { waypoint = move South i way }
(R i) -> st { waypoint = rotate Right (i `div` 90) way }
(L i) -> st { waypoint = rotate Left (i `div` 90) way }
where (x,y) = position st
dir = facing st
way :: (Int, Int)
way@(xoffs, yoffs) = waypoint st
main :: IO ()
main = do
directions <- lines <$> readFile "input.txt"
let dirs = ((read . adjust) <$> directions) :: [Command]
let part1 = foldl' drive (St East (0,0) (0,0)) dirs
print $ part1
print $ uncurry (+) $ (abs *** abs) $ position part1
let part2 = foldl' actual_drive (St East (0,0) (10,1)) dirs
print $ part2
print $ uncurry (+) $ (abs *** abs) $ position part2