-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay07.swift
134 lines (96 loc) · 2.97 KB
/
Day07.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import Foundation
import Tools
final class Day07Solver: DaySolver {
let dayNumber: Int = 7
struct Input {
let programs: [String: Program]
}
struct Program {
let id: String
let weight: Int
let holding: [String]
}
private var rootProgramID: String!
func solvePart1(withInput input: Input) -> String {
let programs = input.programs
for subProgramID in programs.keys {
if programs.values.contains(where: { $0.holding.contains(subProgramID) }) == false {
rootProgramID = subProgramID
return subProgramID
}
}
fatalError()
}
private struct Node {
let program: Program
var childNodes: [Node] = []
var combinedWeight: Int {
childNodes.map(\.combinedWeight).reduce(into: program.weight) { $0 += $1 }
}
var mostCommonChildWeight: Int? {
var weightCounter: [Int: Int] = [:]
for childNode in childNodes {
let weight = childNode.combinedWeight
weightCounter[weight, default: 0] += 1
}
return weightCounter.sorted(by: { $0.value > $1.value }).first?.key
}
var isUnbalanced: Bool {
!isBalanced
}
var isBalanced: Bool {
if childNodes.isEmpty {
return true
}
let baseWeight = childNodes[0].combinedWeight
for childNode in childNodes[1 ..< childNodes.count] {
if childNode.combinedWeight != baseWeight {
return false
}
}
return true
}
init(program: Program, allPrograms: [String: Program]) {
self.program = program
for subProgramID in program.holding {
childNodes.append(Node(program: allPrograms[subProgramID]!, allPrograms: allPrograms))
}
}
func findUnbalancedNode() -> Node? {
guard isUnbalanced else {
return nil
}
for childNode in childNodes where childNode.isUnbalanced {
return childNode.findUnbalancedNode() ?? childNode
}
return self
}
func findChildNodesWithUnMatchedWeight(_ weight: Int) -> Node? {
childNodes.first(where: { $0.combinedWeight != weight })
}
}
func solvePart2(withInput input: Input) -> Int {
let programs = input.programs
let rootNode = Node(program: programs[rootProgramID]!, allPrograms: programs)
let unbalancedNode = rootNode.findUnbalancedNode()!
let mostCommonWeight = unbalancedNode.mostCommonChildWeight!
let oddNode = unbalancedNode.findChildNodesWithUnMatchedWeight(mostCommonWeight)!
let newWeight = oddNode.program.weight - (oddNode.combinedWeight - mostCommonWeight)
return newWeight
}
func parseInput(rawString: String) -> Input {
let programs: [String: Program] = rawString.allLines().map { line in
let components = line.components(separatedBy: " ")
let id = components[0]
let weight = Int(String(components[1][1 ..< components[1].count - 1]))!
var subIDs: [String] = []
if components.count >= 3 {
for i in 3 ..< components.count {
subIDs.append(String(components[i].replacingOccurrences(of: ",", with: "")))
}
}
return (id, Program(id: id, weight: weight, holding: subIDs))
}.reduce(into: [:]) { $0[$1.0] = $1.1 }
return .init(programs: programs)
}
}