summary refs log blame commit diff stats
path: root/day21.fsx
blob: 5d379be4d45e6ce3e043aa6eb51b79c76cffe9fc (plain) (tree)























































































                                                                                                        
#r "nuget: FSHarpx.Collections, 3.0.1"
open FSharpx.Collections
open System.IO
open System.Text.RegularExpressions

let swapPos x y str =
    str |> String.mapi (fun i v -> 
                        match i with
                        | _ when i = x -> str.[y]
                        | _ when i = y -> str.[x]
                        | _ -> v)


let swapLetter x y str =
    str |> String.mapi (fun i v -> 
                        match v with
                        | _ when v = x -> y
                        | _ when v = y -> x
                        | _ -> v)

let rotateSteps steps dir (str: string) =
    let steps = steps % str.Length
    let  ls = str |> List.ofSeq |> if dir = "right" then List.rev else id
    List.fold (fun (s, c) e -> if s <> 0 then (s-1 , List.append c.Tail [e]) else (0, c)) (steps, ls) ls
    |> snd |> if dir = "right" then List.rev else id
    |> List.map string 
    |> String.concat ""

let rotatePos (letter: char) (str: string) =
    let index = str.IndexOf letter
    rotateSteps (index + 1 + (if index >= 4 then 1 else 0)) "right" str

let reversePos (x: int) (y: int) (str: string) =
    let reversed = Seq.rev str[x..y] |> Seq.map string |> String.concat ""
    (if x > 0 then str[..x-1] else "") + reversed + str[y+1..]

let movePos (x: int) (y: int) (str:string) =
    str |> Seq.mapi (fun i v ->
                        match i with
                        | _ when i = x -> ""
                        | _ when i = y -> if x < y 
                                          then (string v + string str[x]) 
                                          else (string str[x] + string v)
                        | _ -> string v)
    |> String.concat ""

let (|Regex|_|) pattern input = 
    let m = Regex.Match(input, pattern)
    if m.Success then Some(List.tail [for g in m.Groups -> g.Value])
    else None

let processLine (str: string): string -> string =
    match str with
    | Regex "swap position (\d) with position (\d)" [x; y] -> swapPos (int x) (int y)
    | Regex "swap letter (\w) with letter (\w)" [x; y] -> swapLetter (char x) (char y)
    | Regex "rotate (left|right) (\d) steps?" [dir; steps] -> rotateSteps (int steps) dir
    | Regex "rotate based on position of letter (\w)" [letter] -> rotatePos (char letter)
    | Regex "reverse positions (\d) through (\d)" [x; y] -> reversePos (int x) (int y)
    | Regex "move position (\d) to position (\d)" [x; y] -> movePos (int x) (int y) 
    | _ -> id

// part 1
let program = File.ReadAllLines "day21.txt" |> Array.map processLine |> Array.reduce (>>)
let starter = "abcdefgh"
program starter |> printfn "%s"

// part 2
let target = "fbgdceah" 
let charsToString: char list -> string =
    List.map string >> String.concat ""

// permute all possible inputs to find which one yields the target
let rec permute x =
    let rec distribute e = function
        | [] -> [[e]]
        | x::xs' as xs -> (e::xs)::[for xs in distribute e xs' -> x::xs]

    match x with
    | [] -> [[]]
    | e::xs -> List.collect (distribute e) (permute xs)

starter 
|> Seq.toList 
|> permute 
|> List.map charsToString
|> List.map (fun x -> (x, program x))
|> List.filter (snd >> ((=) target))
|> printfn "%A"