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..7ff639c --- /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/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..2597ec2 --- /dev/null +++ b/day7.ts @@ -0,0 +1,51 @@ +import { type MainArgs, parseFile } from './lib/utils.ts'; + +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): boolean { + if (!operands.length) { + return left === 0; + } + const [first, ...rest] = operands; + if (concat) { // Part 2 adds concatenation operator + const sleft = String(left); + const sfirst = String(first); + const prefix = sleft.slice(0, -sfirst.length); + if ((prefix + sfirst === sleft) && calc(Number(prefix), rest, concat)) { + return true; + } + } + + const dleft = left / first; + if (Number.isInteger(dleft) && calc(dleft, rest, concat)) { + return true; + } + + const mleft = left - first; + return (mleft >= 0) && calc(mleft, rest, concat); +} + +function part1(inp: Parsed): number { + return inp.reduce( + (t, [tot, operands]) => t + (calc(tot, operands) ? tot : 0), + 0, + ); +} + +function part2(inp: Parsed): number { + 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/inputs b/inputs index 545dfbe..977e7c4 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 545dfbe8c1614e19ca6e08fe010b399ffaa355d0 +Subproject commit 977e7c4946d3690df71d455017d993575d975aa8 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/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 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]; 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];