@@ -42,108 +42,116 @@ function parse(raw) {
42
42
} ;
43
43
}
44
44
45
- function part1 ( rules , updates ) {
46
- log ( rules ) ;
47
- log ( updates ) ;
45
+ /**
46
+ * Represents an abstract graph.
47
+ * Stores nodes (array) and edges (map of node => Set of children).
48
+ */
49
+ class Graph {
50
+ constructor ( nodes , edges ) {
51
+ this . nodes = nodes ;
52
+ this . edges = edges ;
53
+ }
54
+
55
+ /**
56
+ * Finds a leaf node in the graph
57
+ * @returns a leaf node
58
+ */
59
+ leaf ( ) {
60
+ return this . nodes . find ( n => this . edges [ n ] . size === 0 ) ;
61
+ }
62
+
63
+ clone ( ) {
64
+ let e2 = { ...this . edges } ;
65
+ this . nodes . forEach ( n => e2 [ n ] = new Set ( e2 [ n ] ) )
66
+
67
+ let n2 = [ ...this . nodes ] ;
68
+
69
+ return new Graph ( n2 , e2 ) ;
70
+ }
71
+
72
+ /**
73
+ * Completely removes the specified node from the graph
74
+ * @param {* } n
75
+ */
76
+ delete ( n ) {
77
+ this . nodes . forEach ( n2 => this . edges [ n2 ] . delete ( n ) ) ;
78
+ this . nodes . splice ( this . nodes . indexOf ( n ) , 1 ) ;
79
+ delete this . edges [ n ] ;
80
+ }
81
+ }
48
82
49
- const g = { } ;
83
+ /**
84
+ * Converts a list of rules into a graph.
85
+ * Each page number is a node.
86
+ * Each rule is an edge.
87
+ *
88
+ * @param {[string[]] } rules List of rules (array of pairs of page numbers)
89
+ * @returns the graph
90
+ */
91
+ function createGraphFromRules ( rules ) {
92
+ let edges = { } ;
50
93
rules . forEach ( ( [ a , b ] ) => {
51
- if ( ! g [ a ] ) g [ a ] = new Set ( ) ;
52
- if ( ! g [ b ] ) g [ b ] = new Set ( ) ;
53
- g [ a ] . add ( b ) ;
54
- } ) ;
55
- log ( g ) ;
56
-
57
- // const ranks = {};
58
- // for(let i=0; true; i++) {
59
- // const nodes = Object.getOwnPropertyNames(g);
60
- // const leaves = nodes.filter(k => g[k].size === 0);
61
- // log(leaves);
62
- // if (leaves.length === 0) {
63
- // break;
64
- // }
65
-
66
- // leaves.forEach(l => {
67
- // log(`Removing leaf ${l} with rank ${i}`);
68
- // ranks[l] = i;
69
- // nodes.forEach(n => g[n].delete(l));
70
- // delete g[l];
71
- // });
72
-
73
- // log(g);
74
- // }
75
-
76
- // log(ranks);
77
- // console.log(ranks);
78
-
79
- const valid = updates . filter ( u => {
80
- for ( let i = 0 ; i < u . length - 1 ; i ++ ) {
81
- for ( let j = i + 1 ; j < u . length ; j ++ ) {
82
- if ( g [ u [ j ] ] . has ( u [ i ] ) ) {
83
- return false ;
84
- }
85
- }
86
- }
87
- return true ;
94
+ if ( ! edges [ a ] ) edges [ a ] = new Set ( ) ;
95
+ if ( ! edges [ b ] ) edges [ b ] = new Set ( ) ;
96
+ edges [ a ] . add ( b ) ;
88
97
} ) ;
89
- return valid . map ( u => u [ Math . floor ( u . length / 2 ) ] * 1 ) . reduce ( ( p , c ) => p + c ) ;
98
+ let nodes = Array . from ( new Set ( rules . flatMap ( c => c ) ) ) ;
99
+
100
+ return new Graph ( nodes , edges ) ;
90
101
}
91
102
92
- function part2 ( rules , updates ) {
93
- const g = { } ;
94
- const allNodes = Array . from ( new Set ( rules . flatMap ( c => c ) ) ) ;
95
- log ( allNodes ) ;
96
- rules . forEach ( ( [ a , b ] ) => {
97
- if ( ! g [ a ] ) g [ a ] = new Set ( ) ;
98
- if ( ! g [ b ] ) g [ b ] = new Set ( ) ;
99
- g [ a ] . add ( b ) ;
100
- } ) ;
101
- log ( g ) ;
102
-
103
- const invalid = updates . filter ( u => {
104
- for ( let i = 0 ; i < u . length - 1 ; i ++ ) {
105
- for ( let j = i + 1 ; j < u . length ; j ++ ) {
106
- if ( g [ u [ j ] ] . has ( u [ i ] ) ) {
107
- return true ;
108
- }
103
+ /**
104
+ * Determines whether a list of pages is valid given the rules.
105
+ * @param {string[] } pages
106
+ * @param {{string: string[]} } rules
107
+ * @returns
108
+ */
109
+ function isValid ( pages , rules ) {
110
+ for ( let i = 0 ; i < pages . length - 1 ; i ++ ) {
111
+ for ( let j = i + 1 ; j < pages . length ; j ++ ) {
112
+ if ( rules [ pages [ j ] ] . has ( pages [ i ] ) ) {
113
+ return false ;
109
114
}
110
115
}
111
- return false ;
112
- } ) ;
116
+ }
117
+ return true ;
118
+ }
113
119
114
- log ( invalid ) ;
120
+ function getSumOfMiddleValues ( updates ) {
121
+ return updates . map ( u => u [ Math . floor ( u . length / 2 ) ] * 1 ) . reduce ( ( p , c ) => p + c )
122
+ }
115
123
116
- let changed = invalid . map ( u => {
117
- let g2 = { ...g } ;
118
- Object . getOwnPropertyNames ( g2 ) . forEach ( n => g2 [ n ] = new Set ( g2 [ n ] ) ) ;
119
- const toDelete = allNodes . filter ( n => ! u . some ( v => v === n ) ) ;
120
- log ( toDelete ) ;
121
- toDelete . forEach ( n => {
122
- delete g2 [ n ] ;
123
- } ) ;
124
- u . forEach ( n => {
125
- log ( `Deleting ${ toDelete } from ${ Array . from ( g2 [ n ] ) } ` ) ;
126
- toDelete . forEach ( d => g2 [ n ] . delete ( d ) ) ;
127
- } )
128
- log ( g2 ) ;
129
- const newUpdate = [ ] ;
130
- while ( true ) {
131
- const nodes = Object . getOwnPropertyNames ( g2 ) ;
132
- const leaves = nodes . filter ( n => g2 [ n ] . size === 0 ) ;
133
- if ( leaves . length === 0 ) {
134
- break ;
124
+ function part1 ( rules , updates ) {
125
+ const g = createGraphFromRules ( rules ) ;
126
+ const valid = updates . filter ( u => isValid ( u , g . edges ) ) ;
127
+
128
+ return getSumOfMiddleValues ( valid ) ;
129
+ }
130
+
131
+ function part2 ( rules , updates ) {
132
+ const g = createGraphFromRules ( rules ) ;
133
+
134
+ const modified = updates
135
+ . filter ( u => ! isValid ( u , g . edges ) )
136
+ . map ( u => {
137
+ let g2 = g . clone ( ) ;
138
+
139
+ // Remove all nodes not in the page list
140
+ g2 . nodes
141
+ . filter ( n => ! u . some ( v => v === n ) )
142
+ . forEach ( n => g2 . delete ( n ) ) ;
143
+
144
+ // Extract each leaf from the graph until none left
145
+ const newUpdate = [ ] ;
146
+ for ( let leaf = g2 . leaf ( ) ; leaf ; leaf = g2 . leaf ( ) ) {
147
+ newUpdate . push ( leaf ) ;
148
+ g2 . delete ( leaf ) ;
135
149
}
136
- const leaf = leaves [ 0 ] ;
137
- newUpdate . push ( leaf ) ;
138
- nodes . forEach ( n => g2 [ n ] . delete ( leaf ) ) ;
139
- delete g2 [ leaf ] ;
140
- }
141
- return newUpdate . reverse ( ) ;
142
- } ) ;
143
150
144
- log ( changed ) ;
151
+ return newUpdate . reverse ( ) ;
152
+ } ) ;
145
153
146
- return changed . map ( u => u [ Math . floor ( u . length / 2 ) ] * 1 ) . reduce ( ( p , c ) => p + c ) ;
154
+ return getSumOfMiddleValues ( modified ) ;
147
155
}
148
156
149
157
const { rules, updates} = parse ( raw ) ;
0 commit comments