From 642532a317ef0587fb77521fc903a4847aefb134 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Sat, 7 Dec 2024 01:09:57 -0700 Subject: [PATCH 1/4] Day 6 --- day6.peggy | 1 + day6.ts | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ inputs | 2 +- lib/rect.ts | 16 +++++++++++ test/day6.js | 1 + 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 day6.peggy create mode 100644 day6.ts create mode 100644 test/day6.js diff --git a/day6.peggy b/day6.peggy new file mode 100644 index 0000000..cdcfc7c --- /dev/null +++ b/day6.peggy @@ -0,0 +1 @@ +lines = (@[^\n]+ "\n")* diff --git a/day6.ts b/day6.ts new file mode 100644 index 0000000..3fe8786 --- /dev/null +++ b/day6.ts @@ -0,0 +1,78 @@ +import { Dir, Point, Rect } from './lib/rect.ts'; +import { type MainArgs, parseFile } from './lib/utils.ts'; + +type Parsed = string[][]; + +function path(r: Rect): [Set, Point] { + const [start] = r.filter(val => val === '^'); + let pos = start; + let dir = Dir.N; + const visited = new Set(); + while (true) { + visited.add(pos.toNumber()); + const ahead = pos.inDir(dir); + if (!r.check(ahead)) { + break; + } + const char = r.get(ahead); + if (char === '.' || char === '^') { + pos = ahead; + } else { + dir = (dir + 1) % 4 + } + } + return [visited, start]; +} + +function path2(r: Rect): boolean { + const [start] = r.filter(val => val === '^'); + let pos = start; + let dir = Dir.N; + const visitedWithDir = new Set(); + while (true) { + const pwd = `${pos},${dir}`; + if (visitedWithDir.has(pwd)) { + return true; + } + visitedWithDir.add(pwd); + const ahead = pos.inDir(dir); + if (!r.check(ahead)) { + return false; + } + const char = r.get(ahead); + if (char === '.' || char === '^') { + pos = ahead; + } else { + dir = (dir + 1) % 4 + } + } +} + +function part1(inp: Parsed): number { + const r = new Rect(inp); + const [visited] = path(r) + return visited.size; +} + +function part2(inp: Parsed): number { + const r = new Rect(inp); + const [visited, start] = path(r) + const points = [...visited].map(v => Point.fromNumber(v)) + let tot = 0; + for (const p of points) { + if (p.equals(start)) { + continue; + } + r.set(p, '#'); + if (path2(r)) { + tot++; + } + r.set(p, '.'); + } + return tot; +} + +export default async function main(args: MainArgs): Promise<[number, number]> { + const inp = await parseFile(args); + return [part1(inp), part2(inp)]; +} diff --git a/inputs b/inputs index 545dfbe..1a785f8 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 545dfbe8c1614e19ca6e08fe010b399ffaa355d0 +Subproject commit 1a785f888c1604214aa8fff29383579e32653aab diff --git a/lib/rect.ts b/lib/rect.ts index 30aa188..8da27df 100644 --- a/lib/rect.ts +++ b/lib/rect.ts @@ -145,6 +145,22 @@ export class Point implements PointLike { return `${this.x},${this.y}`; } + toNumber(size = 16): number { + return (this.x << size) | this.y; + } + + static fromNumber(num: number, size = 16): Point { + return new Point(num >> size, num & ((1 << size) - 1)); + } + + static fromString(str: string): Point { + const m = str.match(/(\d+),(\d+)/); + if (!m) { + throw new Error(`Invalid format: "${str}"`); + } + return new Point(Number(m[1]), Number(m[2])); + } + [Symbol.for('Deno.customInspect')](): string { return this.toString(); } diff --git a/test/day6.js b/test/day6.js new file mode 100644 index 0000000..dc70859 --- /dev/null +++ b/test/day6.js @@ -0,0 +1 @@ +export default [4374, 1705]; From 7fd94b1da005edc450389ab4fab55559a76b2105 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Sat, 7 Dec 2024 02:00:34 -0700 Subject: [PATCH 2/4] Day 7 --- day7.peggy | 4 ++++ day7.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ inputs | 2 +- test/day7.js | 1 + 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 day7.peggy create mode 100644 day7.ts create mode 100644 test/day7.js diff --git a/day7.peggy b/day7.peggy new file mode 100644 index 0000000..f481e03 --- /dev/null +++ b/day7.peggy @@ -0,0 +1,4 @@ +lines = (@num ":" _ @num|1.., _|"\n")* + +num = n:$[0-9]+ { return parseInt(n, 10) } +_ = [ \t]+ diff --git a/day7.ts b/day7.ts new file mode 100644 index 0000000..1b0f5fe --- /dev/null +++ b/day7.ts @@ -0,0 +1,57 @@ +import { type MainArgs, parseFile } from './lib/utils.ts'; + +type Parsed = [number, number[]][]; + +// Starting from the front of the reversed list (the endq), see if we can rule +// out any of the operations +function calc(left: number, operands: number[], concat = false): number { + if ((left <= 0) || (Math.floor(left) !== left) || !operands.length) { + return left; + } + const [first, ...rest] = operands; + if (concat) { // Part 2 + // This is most likely to rule out an operand. + // 20 || 123 = 20123. The opposite op is 20123 -- 123, which fails if + // LHS doesn't end in RHS, then truncates. + const sfirst = String(first); + const sleft = String(left); + if (sleft.endsWith(sfirst)) { + if (calc(Number(sleft.slice(0, -sfirst.length)), rest, true) === 0) { + return 0; + } + } + } + if (calc(left - first, rest, concat) === 0) { + return 0; + }; + if (calc(left / first, rest, concat) === 0) { + return 0; + } + return left; +} + +function part1(inp: Parsed): number { + let sum = 0; + for (const [tot, operands] of inp) { + if (calc(tot, operands.reverse()) === 0) { + sum += tot; + } + } + return sum; +} + +function part2(inp: Parsed): number { + let sum = 0; + for (const [tot, operands] of inp) { + // Already reversed + if (calc(tot, operands, true) === 0) { + sum += tot; + } + } + return sum; +} + +export default async function main(args: MainArgs): Promise<[number, number]> { + const inp = await parseFile(args); + return [part1(inp), part2(inp)]; +} diff --git a/inputs b/inputs index 1a785f8..977e7c4 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 1a785f888c1604214aa8fff29383579e32653aab +Subproject commit 977e7c4946d3690df71d455017d993575d975aa8 diff --git a/test/day7.js b/test/day7.js new file mode 100644 index 0000000..9a4562d --- /dev/null +++ b/test/day7.js @@ -0,0 +1 @@ +export default [1038838357795, 254136560217241]; From 1f26361bdeb1d635207579511d1757eaedd2a940 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Sat, 7 Dec 2024 12:46:35 -0700 Subject: [PATCH 3/4] Cleanup --- day7.ts | 62 ++++++++++++++++++++++++++------------------------------- t | 9 +++++++++ 2 files changed, 37 insertions(+), 34 deletions(-) create mode 100644 t diff --git a/day7.ts b/day7.ts index 1b0f5fe..24aeff3 100644 --- a/day7.ts +++ b/day7.ts @@ -1,57 +1,51 @@ import { type MainArgs, parseFile } from './lib/utils.ts'; -type Parsed = [number, number[]][]; +type Line = [number, number[]]; +type Parsed = Line[]; // Starting from the front of the reversed list (the endq), see if we can rule // out any of the operations -function calc(left: number, operands: number[], concat = false): number { - if ((left <= 0) || (Math.floor(left) !== left) || !operands.length) { - return left; +function calc(left: number, operands: number[], concat = false): boolean { + if (!operands.length) { + return left === 0; } const [first, ...rest] = operands; - if (concat) { // Part 2 - // This is most likely to rule out an operand. - // 20 || 123 = 20123. The opposite op is 20123 -- 123, which fails if - // LHS doesn't end in RHS, then truncates. - const sfirst = String(first); + if (concat) { // Part 2 adds concatenation operator const sleft = String(left); - if (sleft.endsWith(sfirst)) { - if (calc(Number(sleft.slice(0, -sfirst.length)), rest, true) === 0) { - return 0; - } + const sfirst = String(first); + const prefix = sleft.slice(0, -sfirst.length) + if ((prefix + sfirst === sleft) && calc(Number(prefix), rest, concat)) { + return true; } } - if (calc(left - first, rest, concat) === 0) { - return 0; - }; - if (calc(left / first, rest, concat) === 0) { - return 0; + + const dleft = left / first; + if (Number.isInteger(dleft) && calc(dleft, rest, concat)) { + return true; } - return left; + + const mleft = left - first; + return (mleft >= 0) && calc(mleft, rest, concat); } function part1(inp: Parsed): number { - let sum = 0; - for (const [tot, operands] of inp) { - if (calc(tot, operands.reverse()) === 0) { - sum += tot; - } - } - return sum; + return inp.reduce( + (t, [tot, operands]) => t + (calc(tot, operands) ? tot : 0), + 0 + ); } function part2(inp: Parsed): number { - let sum = 0; - for (const [tot, operands] of inp) { - // Already reversed - if (calc(tot, operands, true) === 0) { - sum += tot; - } - } - return sum; + return inp.reduce( + (t, [tot, operands]) => t + (calc(tot, operands, true) ? tot : 0), + 0 + ); } export default async function main(args: MainArgs): Promise<[number, number]> { const inp = await parseFile(args); + for (const [_tot, operands] of inp) { + operands.reverse(); + } return [part1(inp), part2(inp)]; } diff --git a/t b/t new file mode 100644 index 0000000..fc6e099 --- /dev/null +++ b/t @@ -0,0 +1,9 @@ +190: 10 19 +3267: 81 40 27 +83: 17 5 +156: 15 6 +7290: 6 8 6 15 +161011: 16 10 13 +192: 17 8 14 +21037: 9 7 18 13 +292: 11 6 16 20 From 945067114d93b4d22d32bf33267fd0817071a5d1 Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Sat, 7 Dec 2024 12:54:23 -0700 Subject: [PATCH 4/4] lint --- day6.ts | 14 +++++++------- day7.ts | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/day6.ts b/day6.ts index 3fe8786..7ff639c 100644 --- a/day6.ts +++ b/day6.ts @@ -4,7 +4,7 @@ import { type MainArgs, parseFile } from './lib/utils.ts'; type Parsed = string[][]; function path(r: Rect): [Set, Point] { - const [start] = r.filter(val => val === '^'); + const [start] = r.filter((val) => val === '^'); let pos = start; let dir = Dir.N; const visited = new Set(); @@ -18,14 +18,14 @@ function path(r: Rect): [Set, Point] { if (char === '.' || char === '^') { pos = ahead; } else { - dir = (dir + 1) % 4 + dir = (dir + 1) % 4; } } return [visited, start]; } function path2(r: Rect): boolean { - const [start] = r.filter(val => val === '^'); + const [start] = r.filter((val) => val === '^'); let pos = start; let dir = Dir.N; const visitedWithDir = new Set(); @@ -43,21 +43,21 @@ function path2(r: Rect): boolean { if (char === '.' || char === '^') { pos = ahead; } else { - dir = (dir + 1) % 4 + dir = (dir + 1) % 4; } } } function part1(inp: Parsed): number { const r = new Rect(inp); - const [visited] = path(r) + const [visited] = path(r); return visited.size; } function part2(inp: Parsed): number { const r = new Rect(inp); - const [visited, start] = path(r) - const points = [...visited].map(v => Point.fromNumber(v)) + const [visited, start] = path(r); + const points = [...visited].map((v) => Point.fromNumber(v)); let tot = 0; for (const p of points) { if (p.equals(start)) { diff --git a/day7.ts b/day7.ts index 24aeff3..2597ec2 100644 --- a/day7.ts +++ b/day7.ts @@ -13,7 +13,7 @@ function calc(left: number, operands: number[], concat = false): boolean { if (concat) { // Part 2 adds concatenation operator const sleft = String(left); const sfirst = String(first); - const prefix = sleft.slice(0, -sfirst.length) + const prefix = sleft.slice(0, -sfirst.length); if ((prefix + sfirst === sleft) && calc(Number(prefix), rest, concat)) { return true; } @@ -31,14 +31,14 @@ function calc(left: number, operands: number[], concat = false): boolean { function part1(inp: Parsed): number { return inp.reduce( (t, [tot, operands]) => t + (calc(tot, operands) ? tot : 0), - 0 + 0, ); } function part2(inp: Parsed): number { return inp.reduce( (t, [tot, operands]) => t + (calc(tot, operands, true) ? tot : 0), - 0 + 0, ); }