DEV Community

Discussion on: Advent of Code 2020 Solution Megathread - Day 7: Handy Haversacks

Collapse
 
ntreu14 profile image
Nicholas Treu

F#:

open System.IO

type InnerBag = {
  InnerBagName: string
  InnerBagQuantity: int
}

let rec parseInnerBags acc = function
  | [] | "no"::"other"::["bags."] -> acc

  | innerQuantity::innerAdj::innerColor::_::rest ->
    let bagName = sprintf "%s %s" innerAdj innerColor
    let bag = { InnerBagName=bagName; InnerBagQuantity=int innerQuantity }
    parseInnerBags (bag::acc) rest

  | pattern -> failwithf "cannot parse inner pattern %A" pattern

let parseLine acc (s: string) = 
  match List.ofArray <| s.Split(' ') with
    | adj::color::_::_::"no"::"other"::["bags."] ->
      let bagName = sprintf "%s %s" adj color
      Map.add bagName [] acc

    | adj::color::_::_::rest ->
      let bagName = sprintf "%s %s" adj color
      Map.add bagName (parseInnerBags [] rest) acc

    | pattern -> failwithf "cannot parse pattern %A" pattern

let rec findAllBagsContaining soughtBag allBags = 
  allBags 
    |> Map.filter (fun _ innerBags -> innerBags |> List.exists (fun bag -> bag.InnerBagName = soughtBag))
    |> Map.toList
    |> List.collect (fst >> fun bagName -> bagName::findAllBagsContaining bagName allBags)

let rec countBagsInsideOf bagName allBags =
  match Map.tryFind bagName allBags with
    | Some innerBags ->
        innerBags |> List.sumBy (fun innerBag -> 
          innerBag.InnerBagQuantity + countBagsInsideOf innerBag.InnerBagName allBags * innerBag.InnerBagQuantity
        )

    | None -> 0 // this should never happen

let bags = 
  File.ReadAllLines "input.txt" |> Array.fold parseLine Map.empty

// Part 1
findAllBagsContaining "shiny gold" bags
  |> Set.ofList
  |> Set.count
  |> printfn "%d"

// Part2
countBagsInsideOf "shiny gold" bags
  |> printfn "%d"
Enter fullscreen mode Exit fullscreen mode