diff --git a/day1/mod.ts b/day1/mod.ts index a8b3cba..d774539 100644 --- a/day1/mod.ts +++ b/day1/mod.ts @@ -45,7 +45,7 @@ export function solve2(data: number[][]): number { if (import.meta.main) { const data_path = new URL("input.txt", import.meta.url).pathname; - const data = await Deno.readTextFile(data_path); - console.log(solve1(parse(data))); - console.log(solve2(parse(data))); + const data = parse(await Deno.readTextFile(data_path)); + console.log(solve1(data)); + console.log(solve2(data)); } diff --git a/day1/test.ts b/day1/test.ts index 6ff27b4..fe6f505 100644 --- a/day1/test.ts +++ b/day1/test.ts @@ -3,7 +3,7 @@ import { parse, solve1, solve2 } from "./mod.ts"; Deno.test(async function testExample() { const example_data_path = new URL("example.txt", import.meta.url).pathname; - const example_data = await Deno.readTextFile(example_data_path); - assertEquals(solve1(parse(example_data)), 11); - assertEquals(solve2(parse(example_data)), 31); + const example_data = parse(await Deno.readTextFile(example_data_path)); + assertEquals(solve1(example_data), 11); + assertEquals(solve2(example_data), 31); }); diff --git a/day2/mod.ts b/day2/mod.ts index 0043967..dc72044 100644 --- a/day2/mod.ts +++ b/day2/mod.ts @@ -38,7 +38,7 @@ export function solve2(data: number[][]): number { if (import.meta.main) { const data_path = new URL("input.txt", import.meta.url).pathname; - const data = await Deno.readTextFile(data_path); - console.log(solve1(parse(data))); - console.log(solve2(parse(data))); + const data = parse(await Deno.readTextFile(data_path)); + console.log(solve1(data)); + console.log(solve2(data)); } diff --git a/day2/test.ts b/day2/test.ts index aa5a14a..405dc7b 100644 --- a/day2/test.ts +++ b/day2/test.ts @@ -3,7 +3,7 @@ import { parse, solve1, solve2 } from "./mod.ts"; Deno.test(async function testExample() { const example_data_path = new URL("example.txt", import.meta.url).pathname; - const example_data = await Deno.readTextFile(example_data_path); - assertEquals(solve1(parse(example_data)), 2); - assertEquals(solve2(parse(example_data)), 4); + const example_data = parse(await Deno.readTextFile(example_data_path)); + assertEquals(solve1(example_data), 2); + assertEquals(solve2(example_data), 4); }); diff --git a/day4/mod.ts b/day4/mod.ts index d6cf8a8..a207757 100644 --- a/day4/mod.ts +++ b/day4/mod.ts @@ -95,7 +95,7 @@ export function solve2(data: string[]): number { if (import.meta.main) { const data_path = new URL("input.txt", import.meta.url).pathname; - const data = await Deno.readTextFile(data_path); - console.log(solve1(parse(data))); - console.log(solve2(parse(data))); + const data = parse(await Deno.readTextFile(data_path)); + console.log(solve1(data)); + console.log(solve2(data)); } diff --git a/day4/test.ts b/day4/test.ts index 7b906ca..a3d60ad 100644 --- a/day4/test.ts +++ b/day4/test.ts @@ -3,7 +3,7 @@ import { parse, solve1, solve2 } from "./mod.ts"; Deno.test(async function testExample() { const example_data_path = new URL("example.txt", import.meta.url).pathname; - const example_data = await Deno.readTextFile(example_data_path); - assertEquals(solve1(parse(example_data)), 18); - assertEquals(solve2(parse(example_data)), 9); + const example_data = parse(await Deno.readTextFile(example_data_path)); + assertEquals(solve1(example_data), 18); + assertEquals(solve2(example_data), 9); }); diff --git a/day5/deno.json b/day5/deno.json new file mode 100644 index 0000000..6e040f3 --- /dev/null +++ b/day5/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@scope/day5", + "version": "0.1.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/day5/example.txt b/day5/example.txt new file mode 100644 index 0000000..9d146d6 --- /dev/null +++ b/day5/example.txt @@ -0,0 +1,28 @@ +47|53 +97|13 +97|61 +97|47 +75|29 +61|13 +75|53 +29|13 +97|29 +53|29 +61|53 +97|53 +61|29 +47|13 +75|47 +97|75 +47|61 +75|61 +47|29 +75|13 +53|13 + +75,47,61,53,29 +97,61,53,29,13 +75,29,13 +75,97,47,61,53 +61,13,29 +97,13,75,29,47 diff --git a/day5/mod.ts b/day5/mod.ts new file mode 100644 index 0000000..b22b1a3 --- /dev/null +++ b/day5/mod.ts @@ -0,0 +1,231 @@ +class DirectedGraph { + edges: Map>; + tempEdges: Map>; + + constructor() { + this.edges = new Map(); + this.tempEdges = new Map(); + } + + addEdge(from: number, to: number) { + if (this.edges.get(from) === undefined) { + this.edges.set(from, new Set()); + } + this.edges.get(from)!.add(to); + } + + hasEdge(from: number, to: number): boolean { + return (this.edges.get(from) ?? new Set()).has(to); + } + + restrict(nodes: Set): DirectedGraph { + const result = new DirectedGraph(); + + for (const [from, tos] of this.edges) { + if (nodes.has(from)) { + for (const to of tos) { + if (nodes.has(to)) { + result.addEdge(from, to); + } + } + } + } + + return result; + } + + hasCycleFrom( + node: number, + visited: Set, + stack: Set, + ): boolean { + if (stack.has(node)) { + return true; + } + + if (visited.has(node)) { + return false; + } + + visited.add(node); + stack.add(node); + + for (const neighbor of this.edges.get(node) ?? new Set()) { + if (this.hasCycleFrom(neighbor, visited, stack)) { + return true; + } + } + + for (const neighbor of this.tempEdges.get(node) ?? new Set()) { + if (this.hasCycleFrom(neighbor, visited, stack)) { + return true; + } + } + + stack.delete(node); + return false; + } + + hasCycle(): boolean { + const visited = new Set(); + const stack = new Set(); + + for (const node of Object.keys(this.edges).map(Number)) { + if (stack.size > 0) { + alert("stack not empty"); + } + if (this.hasCycleFrom(node, visited, stack)) { + return true; + } + } + + for (const node of Object.keys(this.tempEdges).map(Number)) { + if (stack.size > 0) { + alert("stack not empty"); + } + if (this.hasCycleFrom(node, visited, stack)) { + return true; + } + } + + return false; + } + + topological_sort(): number[] { + const nodes: Set = new Set(); + + for (const [from, tos] of this.edges) { + nodes.add(from); + for (const to of tos) { + nodes.add(to); + } + } + + for (const [from, tos] of this.tempEdges) { + nodes.add(from); + for (const to of tos) { + nodes.add(to); + } + } + + const parents = new Map>(); + + for (const node of nodes) { + parents.set(node, new Set()); + } + + for (const [from, tos] of this.edges) { + for (const to of tos) { + parents.get(to)!.add(from); + } + } + + for (const [from, tos] of this.tempEdges) { + for (const to of tos) { + parents.get(to)!.add(from); + } + } + + const result = []; + + while (parents.size > 0) { + const next_nodes = [...parents.entries()].find(([node, parent]) => { + return parent.size === 0; + }); + if (next_nodes === undefined) { + return []; + } + + const node = next_nodes[0]; + + result.push(node); + parents.delete(node); + + for (const parent of parents.values()) { + parent.delete(node); + } + } + + if (result.length === nodes.size) { + return result; + } else { + return []; + } + } +} + +export function parse(data_r: string): [number[][], number[][]] { + const data = data_r.trim(); + const [rules_s, pages_s, _] = data.split("\n\n"); + const rules = rules_s.split("\n").map((rule) => + rule.split("|").map((r) => Number(r)) + ); + + const pages = pages_s.split("\n").map((page) => page.split(",").map(Number)); + + return [rules, pages]; +} + +export function solve1(data: [number[][], number[][]]): number { + const [rules, pages] = data; + + const dirGraph = new DirectedGraph(); + + rules.forEach(([from, to]) => { + dirGraph.addEdge(from, to); + }); + + return pages.map((page) => { + const pageGraph = dirGraph.restrict(new Set(page)); + + for (let i = 0; i < page.length - 1; i++) { + pageGraph.addEdge(page[i], page[i + 1]); + } + const top_sort = pageGraph.topological_sort(); + if (top_sort.length === 0) { + return 0; + } else { + return page[(page.length - 1) / 2]; + } + }).reduce((a, b) => a + b, 0); +} + +export function solve2(data: [number[][], number[][]]): number { + const [rules, pages] = data; + + const dirGraph = new DirectedGraph(); + + rules.forEach(([from, to]) => { + dirGraph.addEdge(from, to); + }); + + const topological_sort = dirGraph.topological_sort(); + + const topological_index = new Map(); + topological_sort.forEach((node, index) => { + topological_index.set(node, index); + }); + + return pages.map((page) => { + const pageGraph = dirGraph.restrict(new Set(page)); + + const fixed = pageGraph.topological_sort(); + + for (let i = 0; i < page.length - 1; i++) { + pageGraph.addEdge(page[i], page[i + 1]); + } + const top_sort = pageGraph.topological_sort(); + if (top_sort.length === 0) { + return fixed[(fixed.length - 1) / 2]; + } else { + return 0; + } + }).reduce((a, b) => a + b, 0); +} + +if (import.meta.main) { + const data_path = new URL("input.txt", import.meta.url).pathname; + const data = parse(await Deno.readTextFile(data_path)); + console.log(solve1(data)); + console.log(solve2(data)); +} diff --git a/day5/test.ts b/day5/test.ts new file mode 100644 index 0000000..17cbc26 --- /dev/null +++ b/day5/test.ts @@ -0,0 +1,9 @@ +import { assertEquals } from "@std/assert"; +import { parse, solve1, solve2 } from "./mod.ts"; + +Deno.test(async function testExample() { + const example_data_path = new URL("example.txt", import.meta.url).pathname; + const example_data = parse(await Deno.readTextFile(example_data_path)); + assertEquals(solve1(example_data), 143); + assertEquals(solve2(example_data), 123); +}); diff --git a/deno.json b/deno.json index d06ad49..4dcaf82 100644 --- a/deno.json +++ b/deno.json @@ -4,7 +4,8 @@ "./day1", "./day2", "./day3", - "./day4" + "./day4", + "./day5" ] }, "imports": {