open System.IO open System.Text.RegularExpressions type Move = | Spin of offset: int | Exchange of first: int * second: int | Partner of first: char * second: char let (|MoveRegex|_|) pattern input= let m = Regex(pattern).Match(input) if m.Success then Some(List.tail [for x in m.Groups -> x.Value]) else None let parseMove str = match str with | MoveRegex "s(\d+)" [x] -> Spin(int x) | MoveRegex "x(\d+)/(\d+)" [x; y] -> Exchange(int x, int y) | MoveRegex "p(\w)/(\w)" [x; y] -> Partner(char x, char y) | _ -> failwith "invalid input" let swapPos x y (chars: char array) = chars |> Array.mapi (fun i v -> match i with | _ when i = x -> chars.[y] | _ when i = y -> chars.[x] | _ -> v) let swapLetter x y (chars: char array) = let indexOf elem = chars |> Array.findIndex((=) elem) swapPos (indexOf x) (indexOf y) chars let spinChars steps (str: char array) = let startIndex = str.Length - steps let first, last = str[..startIndex - 1], str[startIndex..] Array.append last first let executeMove chars move = match move with | Spin x -> spinChars x chars | Exchange (x, y) -> swapPos x y chars | Partner (x, y) -> swapLetter x y chars let rec executeMoves state moves seen = let newState = Array.fold executeMove state moves if List.contains newState seen then let r = List.rev seen let l = List.length seen let m = 1000000000 % l r.[m] else executeMoves newState moves (newState :: seen) let () = let moves = (File.ReadAllText "day16.txt").Trim().Split(',') |> Array.map parseMove let initialState = ("abcdefghijklmnop" |> Array.ofSeq) let finalState = Array.fold executeMove initialState moves // part 1 finalState |> Array.map string |> String.concat "" |> printfn "%s" // part 2 executeMoves initialState moves [initialState] |> Array.map string |> String.concat "" |> printfn "%s"