<jupyter_start><jupyter_text>Day 12: Rain Risk  -  *WORK IN PROGRESS*https://adventofcode.com/2020/day/12<jupyter_code>inputLines = lines <$> readFile "input/day12.txt"
testInput = [ "F10"
            , "N3"
            , "F7"
            , "R90"
            , "F11" ]<jupyter_output><empty_output><jupyter_text>Part 1<jupyter_code>import Text.Regex.PCRE  -- install with 'stack install regex-pcre'
import Linear.V2  -- install with 'stack install linear'
import Linear.Matrix<jupyter_output><empty_output><jupyter_text>Define data types for the position and direction of the ship.<jupyter_code>newtype Position = Position (V2 Int) deriving (Show)
newtype Direction = Direction (V2 Int) deriving (Show)<jupyter_output><empty_output><jupyter_text>Move the position `n` units into the given direction.<jupyter_code>go (Position pos) (Direction v) n = Position $ pos + V2 n n * v<jupyter_output><empty_output><jupyter_text>Rotate the direction left (counter-clockwise) by 90 degrees.<jupyter_code>left :: Direction -> Direction
left (Direction v) = Direction $ rotationMatrix !* v
    where rotationMatrix = transpose $ V2 (V2 0 1) (V2 (-1) 0)<jupyter_output><empty_output><jupyter_text>North is in the positive $y$-direction in a usual 2d Cartesian coordinate system.<jupyter_code>north = Direction $ V2 0 1<jupyter_output><empty_output><jupyter_text>The other directions are obtained by rotating `north` repeatedly.<jupyter_code>[_, west, south, east] = take 4 . iterate left $ north<jupyter_output><empty_output><jupyter_text>Verify that the directions are as expected.<jupyter_code>(north, east, south, west)<jupyter_output><empty_output><jupyter_text>Rotating left three times by 90 degrees corresponds to rotating right.<jupyter_code>right = left . left . left<jupyter_output><empty_output><jupyter_text>Define a data type for the state of the ship.<jupyter_code>data ShipState = ShipState Position Direction deriving (Show)<jupyter_output><empty_output><jupyter_text>Parse an instruction and return its action on the state of the ship.<jupyter_code>parseInstruction :: String -> (ShipState -> ShipState)
parseInstruction instruction = case head letter of
    'L' -> rotate left
    'R' -> rotate right
    'F' -> goForward
    fixedDirection -> goFixedDirection . parseDirection $ fixedDirection  -- one of NSEW

    where
        [_, letter, argumentStr] = head (instruction =~ "([NSEWLRF])([0-9]+)" :: [[String]])
        
        argument = read argumentStr :: Int
        
        rotate f (ShipState position direction) =
            ShipState position (iterate f direction !! numRotations)
            where numRotations = argument `div` 90
        
        goForward (ShipState position direction) =
            ShipState (go position direction argument) direction
        
        goFixedDirection fixedDirection (ShipState position direction) =
            ShipState (go position fixedDirection argument) direction
        
        parseDirection 'N' = north
        parseDirection 'S' = south
        parseDirection 'E' = east
        parseDirection 'W' = west
parseInstructions = map parseInstruction<jupyter_output><empty_output><jupyter_text>Apply a list of instructions to a given initial state.<jupyter_code>executeInstructions :: ShipState -> [ShipState -> ShipState] -> ShipState
executeInstructions = foldl (flip ($))<jupyter_output><empty_output><jupyter_text>Parse the given instructions and apply them to the specified initial state (ship at $(0, 0)$, direction east).<jupyter_code>solution1 :: [String] -> Int
solution1 input = manhattanDistance finalPosition
    where
        manhattanDistance v = sum $ abs <$> v
        (ShipState (Position finalPosition) _) = executeInstructions initialState instructions
        initialState = ShipState initialPosition initialDirection
        initialPosition = Position $ V2 0 0
        initialDirection = east
        instructions = parseInstructions input<jupyter_output><empty_output><jupyter_text>Verify given example.<jupyter_code>solution1 testInput<jupyter_output><empty_output><jupyter_text>Solution, part 1<jupyter_code>solution1 <$> inputLines<jupyter_output><empty_output><jupyter_text>Part 2 The modifiied rules in part 2 require only few changes:* The `Direction` part of the ship's state is now the waypoint. This does not require any changes in the code though.* The commands `N`, `S`, `E`, `W` move the waypoint rather than the ship.<jupyter_code>parseInstruction2 :: String -> (ShipState -> ShipState)
parseInstruction2 instruction = case head letter of
    'L' -> rotate left
    'R' -> rotate right
    'F' -> goForward
    fixedDirection -> moveWaypoint . parseDirection $ fixedDirection  -- one of NSEW

    where
        [_, letter, argumentStr] = head (instruction =~ "([NSEWLRF])([0-9]+)" :: [[String]])
        
        argument = read argumentStr :: Int
        
        rotate f (ShipState position waypoint) =
            ShipState position (iterate f waypoint !! numRotations)
            where numRotations = argument `div` 90
        
        goForward (ShipState position waypoint) =
            ShipState (go position waypoint argument) waypoint
        
        moveWaypoint fixedDirection (ShipState position waypoint) =
            ShipState position (Direction $ waypointCoordinates + waypointOffset * V2 argument argument)
            where
                Direction waypointOffset = fixedDirection
                Direction waypointCoordinates = waypoint
        
        parseDirection 'N' = north
        parseDirection 'S' = south
        parseDirection 'E' = east
        parseDirection 'W' = west
parseInstructions2 = map parseInstruction2
solution2 :: [String] -> Int
solution2 input = manhattanDistance finalPosition
    where
        manhattanDistance v = sum $ abs <$> v
        (ShipState (Position finalPosition) _) = executeInstructions initialState instructions
        initialState = ShipState initialPosition initialWaypoint
        initialPosition = Position $ V2 0 0
        initialWaypoint = Direction $ V2 10 1
        instructions = parseInstructions2 input
solution2 testInput<jupyter_output><empty_output><jupyter_text>Solution, part 2<jupyter_code>solution2 <$> inputLines<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08809370988446727, 'fraction_numerical': 0.01235558408215661, 'mean_word_length': 3.8280402788536017, 'pattern_counts': {'":': 0, '<': 74, '<?xml version=': 0, '>': 92, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '149134', 'license': 'MIT', 'n_tokens_mistral': 1826, 'n_tokens_neox': 1768, 'n_words': 522, 'path': '2020/day12-haskell.ipynb', 'repo_name': 'freininghaus/adventofcode', 'size': '6232'}