import Control.Monad.Trans.State as St import Data.Set as S type Coord = (Int, Int) data Command = U | D | L | R parseCommand :: String -> [Command] parseCommand ('U' : ' ': xs) = replicate (read xs) U parseCommand ('D' : ' ': xs) = replicate (read xs) D parseCommand ('L' : ' ': xs) = replicate (read xs) L parseCommand ('R' : ' ': xs) = replicate (read xs) R touching :: Coord -> Coord -> Bool touching (hx, hy) (tx, ty) = hx >= tx -1 && hx <= tx+1 && hy >= ty-1 && hy <= ty+1 updateTail :: Coord -> Coord -> Coord updateTail (hx, hy) (tx, ty) | touching (hx, hy) (tx, ty) = (tx, ty) | hx == tx && hy > ty+1 = (tx, ty+1) | hx == tx && hy < ty-1 = (tx, ty-1) | hy == ty && hx > tx+1 = (tx+1, ty) | hy == ty && hx < tx-1 = (tx-1, ty) | hx > tx && hy > ty = (tx+1, ty+1) | hx > tx && hy < ty = (tx+1, ty-1) | hx < tx && hy > ty = (tx-1, ty+1) | hx < tx && hy < ty = (tx-1, ty-1) updateHead :: Command -> Coord -> Coord updateHead U (x,y) = (x, y-1) updateHead D (x,y) = (x, y+1) updateHead L (x,y) = (x-1, y) updateHead R (x,y) = (x+1, y) data Coords = CS {h :: Coord, t :: Coord} type Cs a = St.State Coords a execute :: Command -> Cs Coord execute cmd = do st <- St.get let head = updateHead cmd (h st) let tail = updateTail head (t st) St.put $ CS head tail pure tail type Coords2 = [Coord] type Cs2 a = St.State Coords2 a initCoords2 :: Coords2 initCoords2 = replicate 10 (0,0) updateTails :: Coord -> [Coord] -> [Coord] updateTails head [] = [] updateTails head (t:ts) = let tail = updateTail head t in tail : updateTails tail ts execute' :: Command -> Cs2 Coord execute' cmd = do st <- St.get let head = updateHead cmd (Prelude.head st) let tails = updateTails head (Prelude.tail st) St.put $ head : tails pure $ last tails main :: IO () main = do x <- concatMap parseCommand . lines <$> getContents putStr "part 1: " let visited = S.fromList $ St.evalState (mapM execute x) (CS (0,0) (0,0)) print $ S.size visited putStr "part 2: " let visited2 = S.fromList $ St.evalState (mapM execute' x) initCoords2 print $ S.size visited2