Day 7
authorUmmon <greg.burri@gmail.com>
Thu, 7 Dec 2017 09:07:07 +0000 (10:07 +0100)
committerUmmon <greg.burri@gmail.com>
Thu, 7 Dec 2017 09:07:07 +0000 (10:07 +0100)
AdventOfCode2017.sln
AdventOfCode2017/AdventOfCode2017.fsproj
AdventOfCode2017/Day7.fs [new file with mode: 0644]
AdventOfCode2017/Program.fs
Tests/Day7 tests.fs [new file with mode: 0644]
Tests/Tests.fsproj

index bc9b5dc..34d98a2 100644 (file)
@@ -40,4 +40,7 @@ Global
        GlobalSection(Performance) = preSolution
                HasPerformanceSessions = true
        EndGlobalSection
+       GlobalSection(Performance) = preSolution
+               HasPerformanceSessions = true
+       EndGlobalSection
 EndGlobal
index 0af4157..dcabd03 100644 (file)
@@ -25,7 +25,7 @@
     <PlatformTarget>AnyCPU</PlatformTarget>
     <DocumentationFile>bin\$(Configuration)\$(AssemblyName).XML</DocumentationFile>
     <Prefer32Bit>true</Prefer32Bit>
-    <StartArguments>6</StartArguments>
+    <StartArguments>7</StartArguments>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -62,6 +62,7 @@
     <Compile Include="Day4.fs" />
     <Compile Include="Day5.fs" />
     <Compile Include="Day6.fs" />
+    <Compile Include="Day7.fs" />
     <Compile Include="Program.fs" />
     <None Include="App.config" />
     <None Include="Data\day1.input">
@@ -79,6 +80,9 @@
     <None Include="Data\day6.input">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
+    <None Include="Data\day7.input">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
     <Content Include="packages.config" />
   </ItemGroup>
   <ItemGroup>
diff --git a/AdventOfCode2017/Day7.fs b/AdventOfCode2017/Day7.fs
new file mode 100644 (file)
index 0000000..869c0a9
--- /dev/null
@@ -0,0 +1,56 @@
+module AdventOfCode2017.Day7
+
+open System
+open System.Linq
+open System.Collections.Generic
+
+type Tower =
+    {
+        Name : string
+        Weight : int
+        Above : List<Tower>
+    }
+
+type Input = (Tower * string list) list
+
+let parseInput (lines : string list) : Input =
+    lines
+    |> List.map (
+        fun line ->
+            let items = line.Split ([| '\r'; '\t'; ' '; ','; ')'; '(' |], StringSplitOptions.RemoveEmptyEntries)
+            {
+                Name = items.[0]
+                Weight = int items.[1]
+                Above = List<Tower> ()
+            },
+            [ for i in 3 .. items.Length - 1 -> items.[i] ]
+    )
+
+let buildTower (input : Input) : Tower =
+    let rootTowers = Dictionary<string, Tower> ()
+
+    for tower, _ in input do
+        rootTowers.Add (tower.Name, tower)
+
+    for tower, towersAbove in input do
+        for towerAbove in towersAbove do
+            tower.Above.Add rootTowers.[towerAbove]
+            rootTowers.Remove towerAbove |> ignore
+
+    rootTowers.First().Value
+
+// Returns the tower and its corrected weight.
+let rec findUnbalanced (tower : Tower) : (Tower * int) option =
+    let rec weight tower =
+        tower.Weight + (tower.Above |> Seq.map weight |> Seq.sum)
+
+    let towersByWeight = tower.Above |> Seq.groupBy weight
+
+    if towersByWeight |> Seq.length > 1 then
+        let unbalanced = towersByWeight |> Seq.minBy (snd >> Seq.length)
+        let others = towersByWeight |> Seq.maxBy (snd >> Seq.length)
+        let delta = fst others - fst unbalanced
+        let unbalanced' = unbalanced |> snd |> Seq.head
+        findUnbalanced unbalanced' |> Option.orElse (Some (unbalanced', unbalanced'.Weight + delta))
+    else
+        tower.Above |> Seq.tryPick (fun t -> findUnbalanced t)
index e64ecf4..7344c5c 100644 (file)
@@ -28,6 +28,11 @@ let day6 () =
     let part1, part2 = Day6.nbRedistribution input
     sprintf "part1 = %A, part2 = %A" part1 part2
 
+let day7 () =
+    let input = File.ReadAllLines "Data/day7.input" |> List.ofArray |> Day7.parseInput
+    let tower = Day7.buildTower input
+    sprintf "part1 = %A, part2 = %A" tower.Name (Day7.findUnbalanced tower |> Option.map (fun (t, w) -> t.Name, w))
+
 let doDay (n : int) =
     let sw = Diagnostics.Stopwatch ()
     sw.Start ()
@@ -39,6 +44,7 @@ let doDay (n : int) =
         | 4 -> day4 ()
         | 5 -> day5 ()
         | 6 -> day6 ()
+        | 7 -> day7 ()
         | _ -> raise <| NotImplementedException ()
     printfn "Result of day %i: %s (time : %i ms)" n result sw.ElapsedMilliseconds
 
diff --git a/Tests/Day7 tests.fs b/Tests/Day7 tests.fs
new file mode 100644 (file)
index 0000000..022e615
--- /dev/null
@@ -0,0 +1,56 @@
+namespace AdventOfCode2017.Tests
+
+open Xunit
+open Xunit.Abstractions
+open Swensen.Unquote
+
+open AdventOfCode2017
+
+type ``Day7 tests`` (output : ITestOutputHelper) =
+
+    [<Fact>]
+    let ``(Part1) From web page`` () =
+        let input =
+            [
+                "pbga (66)"
+                "xhth (57)"
+                "ebii (61)"
+                "havc (66)"
+                "ktlj (57)"
+                "fwft (72) -> ktlj, cntj, xhth"
+                "qoyq (66)"
+                "padx (45) -> pbga, havc, qoyq"
+                "tknk (41) -> ugml, padx, fwft"
+                "jptl (61)"
+                "ugml (68) -> gyxo, ebii, jptl"
+                "gyxo (61)"
+                "cntj (57)"
+            ]
+        let tower = Day7.buildTower (Day7.parseInput input)
+        tower.Name =! "tknk"
+
+    [<Fact>]
+    let ``(Part2) From web page`` () =
+        let input =
+            [
+                "pbga (66)"
+                "xhth (57)"
+                "ebii (61)"
+                "havc (66)"
+                "ktlj (57)"
+                "fwft (72) -> ktlj, cntj, xhth"
+                "qoyq (66)"
+                "padx (45) -> pbga, havc, qoyq"
+                "tknk (41) -> ugml, padx, fwft"
+                "jptl (61)"
+                "ugml (68) -> gyxo, ebii, jptl"
+                "gyxo (61)"
+                "cntj (57)"
+            ]
+        let tower = Day7.buildTower (Day7.parseInput input)
+
+        match Day7.findUnbalanced tower with
+        | Some (tower, weight) ->
+            tower.Name =! "ugml"
+            weight =! 60
+        | None -> failwith "no tower found"
\ No newline at end of file
index fe51460..0bec47c 100644 (file)
@@ -61,6 +61,7 @@
     <Compile Include="Day4 tests.fs" />
     <Compile Include="Day5 tests.fs" />
     <Compile Include="Day6 tests.fs" />
+    <Compile Include="Day7 tests.fs" />
     <Content Include="packages.config" />
     <Content Include="App.config" />
   </ItemGroup>