Skip to content

Commit 7a377d7

Browse files
Refactored day 5
1 parent cf1c80f commit 7a377d7

File tree

1 file changed

+98
-90
lines changed

1 file changed

+98
-90
lines changed

day05/index.js

+98-90
Original file line numberDiff line numberDiff line change
@@ -42,108 +42,116 @@ function parse(raw) {
4242
};
4343
}
4444

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+
}
4882

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 = {};
5093
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);
8897
});
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);
90101
}
91102

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;
109114
}
110115
}
111-
return false;
112-
});
116+
}
117+
return true;
118+
}
113119

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+
}
115123

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);
135149
}
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-
});
143150

144-
log(changed);
151+
return newUpdate.reverse();
152+
});
145153

146-
return changed.map(u => u[Math.floor(u.length/2)] * 1).reduce((p, c) => p+c);
154+
return getSumOfMiddleValues(modified);
147155
}
148156

149157
const {rules, updates} = parse(raw);

0 commit comments

Comments
 (0)