73 lines
2.1 KiB
Haskell
73 lines
2.1 KiB
Haskell
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 |