summary refs log tree commit diff
path: root/Part1.hs
blob: 6b244c6ac07b024d742613e4c3906e434f97c14a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
module Main where

import System.Environment
import Data.Char
import Data.List
import Debug.Trace

import Data.Maybe

import Prelude hiding (Left, Right)

data Facing = Right | Down | Left | Up deriving (Show, Enum)


clockwise :: Facing -> Facing
counterclockwise :: Facing -> Facing
clockwise Up = Right
clockwise Right = Down
clockwise Down = Left
clockwise Left = Up

counterclockwise = foldl (.) id $ replicate 3 clockwise

data Tile = Open | Wall | Empty deriving (Show,Eq)
parseTile :: Char -> Tile
parseTile '.' = Open
parseTile '#' = Wall
parseTile ' ' = Empty

data Map = Map {
  tiles :: [[Tile]],
  width :: Int,
  height :: Int
} deriving Show
data Position = Position (Int, Int) Facing deriving Show

data Instruction = MoveInstruction Int | RotateL | RotateR deriving Show

parseInstructions :: String -> [Instruction]
parseInstructions [] = []
parseInstructions ('R':xs) = RotateR:parseInstructions xs
parseInstructions ('L':xs) = RotateL:parseInstructions xs
parseInstructions s = let x = span isDigit s in MoveInstruction (read (fst x) :: Int):parseInstructions (snd x)

parse :: String -> IO (Map, [Instruction])
parse f = do
  content <- lines <$> readFile f
  let tiles = map (map parseTile) (take (length content - 2) content)
  return (Map tiles (foldr (max.length) 0 tiles) $ length tiles , reverse $ parseInstructions $ last content)

runInstruction :: Map -> Instruction -> Position -> Position
runInstruction m (MoveInstruction n)    p = fromMaybe p $ move n p m
runInstruction _ RotateR (Position p r) = Position p $ clockwise r
runInstruction _ RotateL (Position p r) = Position p $ counterclockwise r

nextPosition :: Position -> Position
nextPosition (Position (x,y) Up)    = Position (x,y-1) Up
nextPosition (Position (x,y) Down)  = Position (x,y+1) Down
nextPosition (Position (x,y) Left)  = Position (x-1,y) Left
nextPosition (Position (x,y) Right) = Position (x+1,y) Right

wrapPosition :: Map -> Position -> Position
wrapPosition m (Position (x,y) r) = Position (x', y') r
  where
    y' = y `mod` height m
    x' = x `mod` width m

move :: Int -> Position -> Map -> Maybe Position
move 1 p m = case tile of
  Open -> Just p'
  Wall -> Nothing
  Empty -> move 1 p' m
  where
    p' = wrapPosition m $ nextPosition p
    (Position (x,y) _) = p'
    row = tiles m !! y
    tile = if x < length row then row !! x else Empty
move n p m = Just $ fromMaybe p' $ move (n-1) p' m
  where p' = fromMaybe p $ move 1 p m

runInstructions ::  Map -> Position -> [Instruction] -> Position
runInstructions = foldr . runInstruction

main :: IO ()
main = do
  args <- getArgs
  (map, instructions) <- parse (head args)
  let starting = maybe undefined ((`Position` Right).(,0)) $ elemIndex Open (head (tiles map))
  let (Position (x,y) r) = runInstructions map starting instructions
  let n = (1000 * (y+1)) +  (4 * (x+1)) + fromEnum r
  print n