summary refs log blame commit diff stats
path: root/day14.fsx
blob: 0232ff852d84fb4368056d54ccb4af7e36091168 (plain) (tree)























































                                                                                                           
#r "nuget: FSharpx.Collections, 3.0.1"
open System.Security.Cryptography
open System.Text
open FSharpx.Collections

let salt = "yjdafjpo"
let md5 (data: string): string = 
    use md5 = MD5.Create()
    (StringBuilder (), md5.ComputeHash(Encoding.ASCII.GetBytes data))
    ||> Array.fold (fun sb b -> sb.Append(b.ToString("x2")))
    |> string

// returns Some char being repeated for n many times in input hash, otherwise None if no repetitions
let getSameChar (n: int) (hash: string) : char option =
    let sameChar (window: char array): char =
        Array.reduce (fun x y -> if x = y then y else char 0) window
    hash
    |> Seq.windowed n
    |> Seq.map sameChar
    |> Seq.filter (fun x -> x <> char 0)
    |> Seq.toArray
    |> fun x -> if x.Length > 0 then Some x[0] else None

// returns true if any of the hashes contain ch repeated 5 times
let hashRepeats (ch: char) (hashes: Deque<string>): bool =
    Seq.map (getSameChar 5) hashes
    |> Seq.filter (function | Some x -> x = ch | None -> false)
    |> fun x -> Seq.length x > 0

// hashes contains hash of salt + index, plus the next 1000 hashes
let rec getPadKey (currInd: int) (indices: int list) (hashes: Deque<string>) (hashfunc: string -> string) =
    if indices.Length = 64 then indices.Head
    else
        let current, rest = hashes.Uncons
        let hasMatch =
            match getSameChar 3 current with
            | Some x -> hashRepeats x rest
            | None -> false
        
        let newHashes = rest.Conj <| hashfunc (salt + string (currInd + 1001))
        if hasMatch then
            getPadKey (currInd + 1) (currInd::indices) newHashes hashfunc
        else
            getPadKey (currInd + 1) indices newHashes hashfunc

let part1hash = md5
let part2hash =
    seq { for n in 0..2016 do yield md5 }
    |> Seq.reduce (>>)

// part 1
let p1first1000 = Deque.ofList [ for i in 0..1000 do yield part1hash (salt + string i) ]
getPadKey 0 [] p1first1000 part1hash |> printfn "%d"
// part 2
let p2first1000 = Deque.ofList [ for i in 0..1000 do yield part2hash (salt + string i) ]
getPadKey 0 [] p2first1000 part2hash |> printfn "%d"