diff --git a/advent-of-code/Program.fs b/advent-of-code/Program.fs index 025c56b..e8cb781 100644 --- a/advent-of-code/Program.fs +++ b/advent-of-code/Program.fs @@ -32,4 +32,8 @@ let main _ = printfn $"%A{day7Star1}" let day7Star2 = day_7.Star2.main printfn $"%A{day7Star2}" + let day8Star1 = day_8.Star1.main + printfn $"%A{day8Star1}" + let day8Star2 = day_8.Star2.main + printfn $"%A{day8Star2}" 0 diff --git a/advent-of-code/tasks/day-8/Star1.fs b/advent-of-code/tasks/day-8/Star1.fs new file mode 100644 index 0000000..cd81ef3 --- /dev/null +++ b/advent-of-code/tasks/day-8/Star1.fs @@ -0,0 +1,81 @@ +module advent_of_code.tasks.day_8.Star1 + +open System +open System.IO + +let stream = new StreamReader("tasks/day-8/input.txt") + +type Point3D = + struct + val X: float + val Y: float + val Z: float + new(s: string) = + let splitS = s.Split(',') |> Array.map float + let x, y, z = splitS[0], splitS[1], splitS[2] + { X = x; Y = y; Z = z } + + member this.GetDistanceFrom(p: Point3D) = + ((p.X - this.X) ** 2 + (p.Y - this.Y) ** 2 + (p.Z - this.Z) ** 2) |> sqrt + + override this.ToString() = + $"Point3D X = {this.X} Y = {this.Y} Z = {this.Z}" + end + +type DisjointSet = + struct + val Parent: Map + val Size: Map + new(parent: Map, size: Map) = + { + Parent = parent + Size = size + } + new(points: Point3D array) = + { + Parent = points |> Array.fold (fun (map: Map) (point: Point3D) -> map.Add(point, point)) Map.empty + Size = points |> Array.fold (fun (map: Map) (point: Point3D) -> map.Add(point, 1)) Map.empty + } + + member this.Find (point: Point3D) = + let p2 = this.Parent[point] + if (p2 = point) then + this, point + else + let disjoint, p = this.Find p2 + let parent = disjoint.Parent.Add(point, p) + DisjointSet(parent, this.Size), p + + member this.Union (connection: Point3D * Point3D) = + let p1, p2 = connection.Deconstruct() + let disjointSet, p1 = this.Find(p1) + let disjointSet, p2 = disjointSet.Find(p2) + if p1 = p2 then + disjointSet + else + let parent = disjointSet.Parent.Add(p2, p1) + let size = disjointSet.Size.Add(p1, disjointSet.Size[p1] + disjointSet.Size[p2]).Add(p2, 0) + DisjointSet(parent, size) + end + +let connectionFilter (ci: (Point3D * Point3D) * int) = + let c, i = ci.Deconstruct() + let p1, p2 = c.Deconstruct() + i % 2 = 0 && p1.GetDistanceFrom p2 <> 0 + +let removeIndex (ci: (Point3D * Point3D) * int) = + let c, _ = ci.Deconstruct() + c + +let sortKey (c: Point3D * Point3D) = + let p1, p2 = c.Deconstruct() + p1.GetDistanceFrom p2 + +let main = + let points = stream.ReadToEnd().Split('\n') |> Array.map Point3D + let connections = ((Array.allPairs points points) |> Array.sortBy sortKey |> Array.mapi (fun i el -> el, i) |> Array.filter connectionFilter |> Array.map removeIndex)[.. 999] + let disjointSet = DisjointSet(points) + let completeDS = connections |> Array.fold (fun (ds: DisjointSet) (connection: Point3D * Point3D) -> ds.Union(connection)) disjointSet + let sizes = completeDS.Size.Values |> Seq.sort |> Array.ofSeq + Array.Reverse sizes + sizes[..2] |> Array.fold (*) 1 diff --git a/advent-of-code/tasks/day-8/Star2.fs b/advent-of-code/tasks/day-8/Star2.fs new file mode 100644 index 0000000..e98c1ad --- /dev/null +++ b/advent-of-code/tasks/day-8/Star2.fs @@ -0,0 +1,88 @@ +module advent_of_code.tasks.day_8.Star2 + +open System +open System.IO + +let stream = new StreamReader("tasks/day-8/input.txt") + +type Point3D = + struct + val X: float + val Y: float + val Z: float + new(s: string) = + let splitS = s.Split(',') |> Array.map float + let x, y, z = splitS[0], splitS[1], splitS[2] + { X = x; Y = y; Z = z } + + member this.GetDistanceFrom(p: Point3D) = + ((p.X - this.X) ** 2 + (p.Y - this.Y) ** 2 + (p.Z - this.Z) ** 2) |> sqrt + + override this.ToString() = + $"Point3D X = {this.X} Y = {this.Y} Z = {this.Z}" + end + +type DisjointSet = + struct + val Parent: Map + val Size: Map + new(parent: Map, size: Map) = + { + Parent = parent + Size = size + } + new(points: Point3D array) = + { + Parent = points |> Array.fold (fun (map: Map) (point: Point3D) -> map.Add(point, point)) Map.empty + Size = points |> Array.fold (fun (map: Map) (point: Point3D) -> map.Add(point, 1)) Map.empty + } + + member this.Find (point: Point3D) = + let p2 = this.Parent[point] + if (p2 = point) then + this, point + else + let disjoint, p = this.Find p2 + let parent = disjoint.Parent.Add(point, p) + DisjointSet(parent, this.Size), p + + member this.Union (connection: Point3D * Point3D) = + let p1, p2 = connection.Deconstruct() + let disjointSet, p1 = this.Find(p1) + let disjointSet, p2 = disjointSet.Find(p2) + if p1 = p2 then + disjointSet + else + let parent = disjointSet.Parent.Add(p2, p1) + let size = disjointSet.Size.Add(p1, disjointSet.Size[p1] + disjointSet.Size[p2]).Add(p2, 0) + DisjointSet(parent, size) + end + +let connectionFilter (ci: (Point3D * Point3D) * int) = + let c, i = ci.Deconstruct() + let p1, p2 = c.Deconstruct() + i % 2 = 0 && p1.GetDistanceFrom p2 <> 0 + +let removeIndex (ci: (Point3D * Point3D) * int) = + let c, _ = ci.Deconstruct() + c + +let sortKey (c: Point3D * Point3D) = + let p1, p2 = c.Deconstruct() + p1.GetDistanceFrom p2 + +let addIfNecessary (dsn: DisjointSet * float) (connection: Point3D * Point3D) = + let ds, n = dsn.Deconstruct() + let p1, p2 = connection.Deconstruct() + if (n <> 0) then + ds, n + else + let newDisjointSet = ds.Union(connection) + newDisjointSet, if newDisjointSet.Size.Values |> Seq.max = 1000 then p1.X * p2.X else n + +let main = + let points = stream.ReadToEnd().Split('\n') |> Array.map Point3D + let connections = ((Array.allPairs points points) |> Array.sortBy sortKey |> Array.mapi (fun i el -> el, i) |> Array.filter connectionFilter |> Array.map removeIndex) + let disjointSet = DisjointSet(points) + let _, n = connections |> Array.fold addIfNecessary (disjointSet, 0) + n |> int