@@ -61,6 +61,7 @@ import Shared
61
61
/// Of course, the actual engine schematic is much larger. **What is the sum of
62
62
/// all of the part numbers in the engine schematic?**
63
63
///
64
+ ///
64
65
@main
65
66
struct GearRatios : AsyncParsableCommand
66
67
{
@@ -83,14 +84,99 @@ struct GearRatios: AsyncParsableCommand
83
84
}
84
85
85
86
87
+ struct Part
88
+ {
89
+ struct Symbol
90
+ {
91
+ struct Location : Equatable , CustomStringConvertible
92
+ {
93
+ let line : Int
94
+ let column : Int
95
+ var description : String { " (L: \( self . line) , C: \( self . column) " }
96
+ }
97
+
98
+ let character : Character
99
+ let location : Location
100
+ }
101
+
102
+ struct Identifier
103
+ {
104
+ let number : Int
105
+ let border : [ Symbol . Location ]
106
+ }
107
+
108
+ let symbol : Symbol
109
+ let numbers : [ Int ]
110
+
111
+ init ( symbol: Symbol , identifiers: [ Identifier ] )
112
+ {
113
+ self . symbol = symbol
114
+ self . numbers = identifiers. map ( \. number)
115
+ }
116
+ }
117
+
118
+
86
119
// MARK: - Command Execution
87
120
88
121
extension GearRatios
89
122
{
90
123
mutating func run( ) async throws
91
124
{
92
- let input : AsyncLineSequence = FileHandle . standardInput. bytes. lines // URL.homeDirectory.appending(path: "Desktop/input.txt").lines
93
- try await input. reduce ( into: [ ] ) { $0. append ( $1) } . forEach { print ( $0) }
125
+ let input : AsyncLineSequence = URL . homeDirectory. appending ( path: " Desktop/input.txt " ) . lines
126
+ let lines = try await input. reduce ( into: [ ] )
127
+ {
128
+ $0. append ( $1)
129
+ }
130
+
131
+ typealias FoundTokens = ( symbols: [ Part . Symbol ] , identifiers: [ Part . Identifier ] )
132
+
133
+ let ( symbols, identifiers) : FoundTokens = lines. enumerated ( ) . reduce ( into: FoundTokens ( [ ] , [ ] ) )
134
+ {
135
+ foundTokens, lineIndexAndline in let ( lineIndex, line) = lineIndexAndline
136
+
137
+ // Find symbols in line.
138
+ line. ranges ( of: /[^\d\.]/ ) . forEach
139
+ {
140
+ symbolRange in
141
+
142
+ let symbolIndex : Int = line. distance ( from: line. startIndex, to: symbolRange. lowerBound)
143
+
144
+ foundTokens. symbols. append ( Part . Symbol ( character: line [ symbolRange] . first!, location: Part . Symbol. Location ( line: lineIndex, column: symbolIndex) ) )
145
+ }
146
+
147
+ // Find identifiers (integers) in line, and the border around them.
148
+ line. ranges ( of: /\d+/ ) . forEach
149
+ {
150
+ idRange in
151
+
152
+ let idStartIndex : Int = line. distance ( from: line. startIndex, to: idRange. lowerBound)
153
+ let idEndIndex : Int = line. distance ( from: line. startIndex, to: idRange. upperBound)
154
+ let borderRange : ClosedRange < Int > = ( ( idStartIndex - 1 ) ... idEndIndex) . clamped ( to: ( 0 ... ( line. count - 1 ) ) )
155
+
156
+ let startLocation = Part . Symbol. Location ( line: lineIndex, column: borderRange. lowerBound)
157
+ let endLocation = Part . Symbol. Location ( line: lineIndex, column: borderRange. upperBound)
158
+ let aboveLocations : [ Part . Symbol . Location ] = borderRange. map { . init( line: ( lineIndex - 1 ) , column: $0) }
159
+ let belowLocations : [ Part . Symbol . Location ] = borderRange. map { . init( line: ( lineIndex + 1 ) , column: $0) }
160
+ let borderLocations : [ Part . Symbol . Location ] = ( [ startLocation, endLocation] + aboveLocations + belowLocations )
161
+
162
+ foundTokens. identifiers. append ( Part . Identifier ( number: Int ( line [ idRange] ) !, border: borderLocations) )
163
+ }
164
+ }
165
+
166
+ let parts : [ Part ] = symbols. reduce ( into: [ ] )
167
+ {
168
+ parts, symbol in
169
+ parts. append ( Part (
170
+ symbol: symbol,
171
+ identifiers: identifiers. filter {
172
+ $0. border. contains ( symbol. location)
173
+ } )
174
+ )
175
+ }
176
+
177
+ let partNumberSum : Int = parts. flatMap ( \. numbers) . reduce ( 0 , + )
178
+
179
+ print ( partNumberSum)
94
180
}
95
181
}
96
182
0 commit comments