GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
+ GlobalSection(Performance) = preSolution
+ HasPerformanceSessions = true
+ EndGlobalSection
EndGlobal
<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>
<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">
<None Include="Data\day6.input">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
+ <None Include="Data\day7.input">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
--- /dev/null
+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)
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 ()
| 4 -> day4 ()
| 5 -> day5 ()
| 6 -> day6 ()
+ | 7 -> day7 ()
| _ -> raise <| NotImplementedException ()
printfn "Result of day %i: %s (time : %i ms)" n result sw.ElapsedMilliseconds
--- /dev/null
+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
<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>