@@ -61,6 +61,7 @@ import Shared
6161/// Of course, the actual engine schematic is much larger. **What is the sum of
6262/// all of the part numbers in the engine schematic?**
6363///
64+ ///
6465@main
6566struct GearRatios : AsyncParsableCommand
6667{
@@ -83,14 +84,99 @@ struct GearRatios: AsyncParsableCommand
8384}
8485
8586
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+
86119// MARK: - Command Execution
87120
88121extension GearRatios
89122{
90123 mutating func run( ) async throws
91124 {
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)
94180 }
95181}
96182
0 commit comments