@@ -18,66 +18,98 @@ const schematics = [
1818] ;
1919
2020Deno . test ( "Day 3: Gear Ratios" , async ( t ) => {
21- await t . step ( "Example" , async ( t ) => {
22- await t . step (
23- "findPartNumbers()" ,
24- ( ) =>
25- assertEquals ( findPartNumbers ( schematics ) , [
26- { n : 467 , s : [ "*" ] } ,
27- { n : 114 , s : [ ] } ,
28- { n : 35 , s : [ "*" ] } ,
29- { n : 633 , s : [ "#" ] } ,
30- { n : 617 , s : [ "*" ] } ,
31- { n : 58 , s : [ ] } ,
32- { n : 592 , s : [ "+" ] } ,
33- { n : 755 , s : [ "*" ] } ,
34- { n : 664 , s : [ "$" ] } ,
35- { n : 598 , s : [ "*" ] } ,
36- { n : 1 , s : [ ] } ,
37- ] ) ,
38- ) ;
21+ await t . step ( "Part 1" , async ( t ) => {
22+ await t . step ( "Example" , async ( t ) => {
23+ await t . step (
24+ "findPartNumbers()" ,
25+ ( ) =>
26+ assertEquals ( findPartNumbers ( schematics ) , [
27+ { n : 467 , s : [ { s : "*" , col : 3 , row : 1 } ] } ,
28+ { n : 35 , s : [ { s : "*" , col : 3 , row : 1 } ] } ,
29+ { n : 633 , s : [ { s : "#" , col : 6 , row : 3 } ] } ,
30+ { n : 617 , s : [ { s : "*" , col : 3 , row : 4 } ] } ,
31+ { n : 592 , s : [ { s : "+" , col : 5 , row : 5 } ] } ,
32+ { n : 755 , s : [ { s : "*" , col : 5 , row : 8 } ] } ,
33+ { n : 664 , s : [ { s : "$" , col : 3 , row : 8 } ] } ,
34+ { n : 598 , s : [ { s : "*" , col : 5 , row : 8 } ] } ,
35+ ] ) ,
36+ ) ;
37+
38+ await t . step ( `Calculate the example solution` , ( ) =>
39+ assertEquals (
40+ sum (
41+ findPartNumbers ( schematics )
42+ . map ( ( { n } ) => n ) ,
43+ ) ,
44+ 4361 ,
45+ ) ) ;
46+ } ) ;
3947
40- await t . step ( `Calculate the example solution` , ( ) =>
48+ await t . step ( "Solution" , async ( ) => {
49+ const numbers = findPartNumbers (
50+ ( await Deno . readTextFile ( "./input/day03.txt" ) ) . split ( "\n" ) ,
51+ ) ;
4152 assertEquals (
4253 sum (
43- findPartNumbers ( schematics )
44- . filter ( ( { s } ) => s . length > 0 )
54+ numbers
4555 . map ( ( { n } ) => n ) ,
4656 ) ,
47- 4361 ,
48- ) ) ;
57+ 529618 ,
58+ ) ;
59+ } ) ;
4960 } ) ;
5061
51- await t . step ( "Solution" , async ( ) => {
52- const numbers = findPartNumbers (
53- ( await Deno . readTextFile ( "./input/day03.txt" ) ) . split ( "\n" ) ,
54- ) ;
55- assertEquals (
56- sum (
57- numbers . filter ( ( { s } ) => s . length > 0 )
58- . map ( ( { n } ) => n ) ,
59- ) ,
60- 529618 ,
61- ) ;
62+ /**
63+ * A gear is any * symbol that is adjacent to exactly two part numbers.
64+ * Its gear ratio is the result of multiplying those two numbers together.
65+ *
66+ * In this schematic, there are two gears. The first is in the top left;
67+ * it has part numbers 467 and 35, so its gear ratio is 16345. The second
68+ * gear is in the lower right; its gear ratio is 451490.
69+ *
70+ * Adding up all of the gear ratios produces 467835.
71+ */
72+ await t . step ( "Part 2" , async ( t ) => {
73+ await t . step ( `Solve example` , ( ) =>
74+ assertEquals (
75+ sum (
76+ findGears ( schematics ) . map ( ( [ g1 , g2 ] ) => g1 * g2 ) ,
77+ ) ,
78+ 467835 ,
79+ ) ) ;
80+
81+ await t . step ( "Solution" , async ( ) =>
82+ assertEquals (
83+ sum (
84+ findGears (
85+ ( await Deno . readTextFile ( "./input/day03.txt" ) ) . split ( "\n" ) ,
86+ ) . map ( ( [ g1 , g2 ] ) => g1 * g2 ) ,
87+ ) ,
88+ 77509019 ,
89+ ) ) ;
6290 } ) ;
6391} ) ;
6492
6593/**
6694 * Find the part numbers in the schematics and the adjacent symbol(s)
95+ *
96+ * TODO: refactor to search for symbols first and then numbers. This will make the code work better for part 2.
6797 */
6898const findPartNumbers = (
6999 schematics : string [ ] ,
70- ) : { n : number ; s : string [ ] } [ ] => {
71- const numbers : { n : number ; s : string [ ] } [ ] = [ ] ;
100+ ) : { n : number ; s : Symbol [ ] } [ ] => {
101+ const numbers : { n : number ; s : Symbol [ ] } [ ] = [ ] ;
72102 let currentNumber : string [ ] = [ ] ;
73- let adjacentSymbols : string [ ] = [ ] ;
103+ let adjacentSymbols : Symbol [ ] = [ ] ;
74104
75105 // Add parsed number with symbols to stash
76106 const addNumber = ( ) => {
77107 if ( currentNumber . length > 0 ) {
78108 numbers . push ( {
79109 n : parseInt ( currentNumber . join ( "" ) ) ,
80- s : [ ...new Set ( adjacentSymbols ) ] ,
110+ s : adjacentSymbols
111+ // Remove duplicate symbols
112+ . reduce < Symbol [ ] > ( uniqueSymbols , [ ] ) ,
81113 } ) ;
82114 }
83115 currentNumber = [ ] ;
@@ -97,17 +129,19 @@ const findPartNumbers = (
97129 }
98130 }
99131 addNumber ( ) ;
100- return numbers ;
132+ return numbers . filter ( ( { s } ) => s . length > 0 ) ;
101133} ;
102134
135+ type Symbol = { s : string ; col : number ; row : number } ;
136+
103137/**
104138 * Find adjacent symbols for a given field
105139 */
106140const findAdjacentSymbols = (
107141 schematics : string [ ] ,
108142 row : number ,
109143 col : number ,
110- ) : string [ ] =>
144+ ) : Symbol [ ] =>
111145 [
112146 findSymbolAt ( schematics , row , col - 1 ) , // left
113147 findSymbolAt ( schematics , row - 1 , col - 1 ) , // top left
@@ -117,7 +151,7 @@ const findAdjacentSymbols = (
117151 findSymbolAt ( schematics , row + 1 , col + 1 ) , // bottom right
118152 findSymbolAt ( schematics , row + 1 , col ) , // bottom
119153 findSymbolAt ( schematics , row + 1 , col - 1 ) , // bottom left
120- ] . filter ( ( s ) => s !== undefined ) as string [ ] ;
154+ ] . filter < Symbol > ( ( s ) : s is Symbol => s ?. s !== undefined ) ;
121155
122156/**
123157 * Return the symbol at a given position
@@ -126,11 +160,52 @@ const findSymbolAt = (
126160 schematics : string [ ] ,
127161 row : number ,
128162 col : number ,
129- ) : string | undefined => {
163+ ) : Symbol | undefined => {
130164 if ( col < 0 ) return undefined ; // Outside of schematics
131165 if ( row < 0 ) return undefined ; // Outside of schematics
132166 const c = schematics [ row ] ?. [ col ] ;
133167 if ( / \d / . test ( c ) ) return undefined ; // a number
134168 if ( c === "." ) return undefined ; // a dot, ignore
135- return c ;
169+ return {
170+ s : c ,
171+ col,
172+ row,
173+ } ;
174+ } ;
175+
176+ const findGears = ( schematics : string [ ] ) => {
177+ const parts = findPartNumbers ( schematics ) ;
178+ // From the known part numbers
179+ return parts
180+ // ... find the symbols with "*"
181+ . filter ( ( { s } ) => s . find ( ( { s } ) => s === "*" ) )
182+ // ... and extract the symbol
183+ . map (
184+ ( { s } ) => s ,
185+ )
186+ . flat ( )
187+ // Remove duplicates
188+ . reduce < Symbol [ ] > ( uniqueSymbols , [ ] )
189+ . reduce < Array < [ number , number ] > > (
190+ ( gears , symbol ) => {
191+ // Find the part numbers with this symbol
192+ const matchingParts = parts . filter ( ( { s } ) =>
193+ s . find ( ( { col, row } ) => col === symbol . col && row === symbol . row )
194+ ) ;
195+ return matchingParts . length === 2
196+ ? [ ...gears , matchingParts . map ( ( { n } ) => n ) as [ number , number ] ]
197+ : gears ;
198+ } ,
199+ [ ] ,
200+ ) ;
201+ } ;
202+
203+ const uniqueSymbols = ( symbols : Symbol [ ] , symbol : Symbol ) => {
204+ if (
205+ symbols . find ( ( { col, row } ) => col === symbol . col && row === symbol . row ) ===
206+ undefined
207+ ) {
208+ return [ ...symbols , symbol ] ;
209+ }
210+ return symbols ;
136211} ;
0 commit comments