Search results

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import { Dusa } from "https://unpkg.com/dusa@0.0.11/lib/client.js";
const INPUT = `
...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
...........
`
.trim()
.split("\n")
.map((line) => line.split(""));
const DISTANCE = 6;
const dusa = new Dusa(`
# AOC Day 21
#builtin NAT_SUCC s
# Input to start and node facts
start is (pair X Y) :-
field _ Y is Row,
field Row X is "S".
plot ".".
plot "S".
node (pair X Y) :-
field _ Y is Row,
field Row X is Ch,
plot Ch.
# Nodes to connected edges
edge (pair X Y) (pair X (s Y)) :-
node (pair X Y),
node (pair X (s Y)).
edge (pair X Y) (pair (s X) Y) :-
node (pair X Y),
node (pair (s X) Y).
edge A B :- edge B A.
# Locations reachable with N steps remaining
reachable D start :- distance is D.
reachable N B :- reachable (s N) A, edge A B.
`);
dusa.load(INPUT, "field");
dusa.assert({ name: "distance", args: [], value: DISTANCE });
console.log([...dusa.solution.lookup("reachable", 0)].length);

Advent of Code 2023 - Day 1 solutions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// #folder:aoc2023
// @aoc2023
// @title Day 1 solutions
import getAocData from "https://esm.town/v/nbbaier/getAocData";
const data = await getAocData(1);
const input = (await data.text()).split("\n").slice(0, -1);
const numberMap: Map<string, number> = new Map<string, number>([
["one", 1],
["two", 2],
["three", 3],
["four", 4],
["five", 5],
["six", 6],
["seven", 7],
["eight", 8],
["nine", 9],
]);
function findDigits(line: string) {
const foundDigits = Array.from(numberMap.entries()).map((number) => ({
matches: Array.from(
line.matchAll(new RegExp(`${number[0]}|${number[1]}`, "g")),
),
}));
return foundDigits;
}
export const solution = (() => {
const partOne = input
.map(line => Array.from(line).filter(char => /\d/.test(char)))
.map(line => line.length > 1 ? line[0] + line[line.length - 1] : line[0] + line[0])
.map(num => parseInt(num)).reduce((total, num) => total + num, 0);
const partTwo = input.map(line =>
findDigits(line)
.flatMap(el => el.matches.map((match) => ({ digit: match[0], index: match.index })))
.sort((a, b) => a.index - b.index)
.map((element) => /\d/.test(element.digit) ? Number(element.digit) : numberMap.get(element.digit))
)
.map(line => line.length > 1 ? `${line[0]}${line[line.length - 1]}` : `${line[0]}${line[0]}`)
.map(num => parseInt(num))
.reduce((total, num) => total + num, 0);
return { partOne, partTwo };
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// #folder:aoc2023
// @aoc2023
// @title Day 3 solutions
import getAocData from "https://esm.town/v/nbbaier/getAocData";
const data = await (await getAocData(3)).text();
const input = data.split("\n");
const symbolRegex: RegExp = new RegExp(
Array.from(new Set(data.split("\n").map(line => line.split("")).flat())).filter(char => !/[.\d]/.test(char)).map(
escapeRegex,
).join(
"|",
),
"g",
);
const numberRegex: RegExp = /\d+/g;
interface NumMetadata {
line: number;
idx: number;
number: string;
length: number;
adjacentPoints?: number[][];
}
interface SymbolMetadata {
point: number[];
symbol: string;
}
function getAdjacentCoordinates(point: number[]) {
const xPoint = point[0];
const yPoint = point[1];
const offsets = [-1, 0, 1];
const adjacentCoordinates = [];
for (const x of offsets) {
for (const y of offsets) {
if (x !== 0 || y !== 0) {
if (xPoint + x >= 0 && yPoint + y >= 0 && xPoint + x < input[yPoint].length && yPoint < input.length) {
adjacentCoordinates.push([xPoint + x, yPoint + y]);
}
}
}
}
return adjacentCoordinates;
}
function escapeRegex(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function hasOverlap(arr1, arr2) {
return arr1.some(point => arr2.some(compare => compare[0] === point[0] && compare[1] === point[1]));
}
function containsCoord(arr: number[][], target: number[]) {
return arr.some(subArr => JSON.stringify(subArr) === JSON.stringify(target));
}
let numbers: NumMetadata[] = input.flatMap((line, i) => {
let matches = [...line.matchAll(numberRegex)];
return matches.map(match => {
let number: NumMetadata = {
line: i,
idx: match.index,
number: match[0],
length: match[0].length,
};
let adjacentPoints = [];
for (let j = number.idx; j < number.length + number.idx; j++) {
const point = [j, number.line];
adjacentPoints.push(...getAdjacentCoordinates(point));
}
number.adjacentPoints = Array.from(new Set(adjacentPoints));
return number;
});
});
const symbols: SymbolMetadata[] = input.flatMap((row, i) =>
[...row.matchAll(symbolRegex)].map((match) => ({
point: [match.index!, i],
symbol: match[0],
}))
);
const gears: { gear: SymbolMetadata; adjacentNumbers: NumMetadata[]; count: number }[] = symbols.filter(item =>
item.symbol === "*"
).map(
potentialGear => {
const yBase = potentialGear.point[1];
const maybeAdjNums = numbers.filter(number => {
return Math.abs(number.line - yBase) <= 1;
});
return { potentialGear, maybeAdjNums };
},
).map(g => {
let count = 0;
let adjacentNumbers: NumMetadata[] = [];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import * as base64 from "https://deno.land/std/encoding/base64.ts";
const bytes = base64.decodeBase64(
// base64 encoded wasm module
"AGFzbQEAAAABiwEUYAJ/fwF/YAJ/fwBgA39/fwF/YAF/AX9gA39/fwBgAX8AYAR/f39/AGAAAX9gBX9/f39/AGAEf39/fwF/YAZ/f39/f38Bf2AHf39/f39/fwF/YAZ+f39/f38BfmADfn9/AX9gB39/f39/f38AYAJ+fwBgA39+fgBgBH9+fn8AYAV/f39/fwF/YAAAAjcBGF9fd2JpbmRnZW5fcGxhY2Vob2xkZXJfXxpfX3diZ19sb2dfM
);
let imports: any = {};
imports["__wbindgen_placeholder__"] = imports;
let wasm;
const { TextDecoder, TextEncoder } = globalThis;
let cachedTextDecoder = new TextDecoder("utf-8", { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
let cachedUint8Memory0 = null;
function getUint8Memory0() {
if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
let cachedTextEncoder = new TextEncoder("utf-8");
const encodeString = typeof cachedTextEncoder.encodeInto === "function"
? function(arg, view) {
return cachedTextEncoder.encodeInto(arg, view);
}
: function(arg, view) {
const buf = cachedTextEncoder.encode(arg);
view.set(buf);
return {
read: arg.length,
written: buf.length,
};
};
function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length, 1) >>> 0;
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}
let len = arg.length;
let ptr = malloc(len, 1) >>> 0;
const mem = getUint8Memory0();
let offset = 0;
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
offset += ret.written;
}
WASM_VECTOR_LEN = offset;
return ptr;
}
let cachedInt32Memory0 = null;
function getInt32Memory0() {
if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) {
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachedInt32Memory0;
}
const heap = new Array(128).fill(undefined);
heap.push(undefined, null, true, false);
function getObject(idx) {
return heap[idx];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Dusa } from "https://unpkg.com/dusa@0.0.11/lib/client.js";
const INPUT = `
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
`
.trim()
.split(',')
.map((line) => {
const label = line.split('-')[0].split('=')[0];
return {
label,
length: label.length,
ascii: label.split('').map((ch) => ch.charCodeAt(0)),
focal: line[label.length] === '-' ? null : parseInt(line.slice(label.length + 1)),
};
});
const dusa = new Dusa(`
# AOC Day 15, Part 2
#builtin INT_PLUS plus
#builtin INT_TIMES times
#builtin INT_MINUS minus
#builtin NAT_SUCC s
numBoxes is 256.
entries is N :- field _ "entries" is N.
labelLength Label is Len :-
field Ref "length" is Len,
field Ref "label" is Label.
ascii Label N is Ch :-
field _ Entry is Ref,
field Ref "label" is Label,
field Ref "ascii" is Str,
field Str N is Ch.
label Entry is Label :-
field _ Entry is Ref,
field Ref "label" is Label.
instruction Entry is del :-
field _ Entry is Ref,
field Ref "focal" is ().
instruction Entry is add N :-
field _ Entry is Ref,
field Ref "focal" is N, N != ().
# Hash computation
partialHash Label 0 is 0 :- ascii Label _ is _.
partialHash Label (s N) is Next :-
partialHash Label N is Val,
ascii Label N is Code,
X == times 17 (plus Val Code),
mod256 X is Next.
needMod256 X X :-
partialHash Label N is Val,
ascii Label N is Code,
X == times 17 (plus Val Code).
hash Label is Hash :-
partialHash Label (labelLength Label) is Hash.
# LOL, division by repeated subtraction
mod256 X is Y :-
needMod256 X Y,
Y < 256.
needMod256 X (minus Y 256) :-
needMod256 X Y,
Y >= 256.
instructionBox Box Entry Label Instr :-
label Entry is Label,
hash Label is Box,
instruction Entry is Instr.
`);
dusa.load({ data: INPUT, entries: INPUT.length }, 'field');
const instrs = Array.from({ length: 256 }).map((_, i) => []);
for (const [box, _entry, label, instr] of dusa.solution.lookup('instructionBox')) {
instrs[box].push({ label, instr });
}
const EVAL_PROGRAM = `
#builtin INT_PLUS plus
#builtin INT_TIMES times
#builtin INT_MINUS minus
#builtin NAT_SUCC s
contents 0 is nil.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import { Dusa } from "https://unpkg.com/dusa@0.0.11/lib/client.js";
const INPUT = `
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
`
.trim()
.split(",")
.map((line) => ({ length: line.length, ascii: line.split("").map((ch) => ch.charCodeAt(0)) }));
const dusa = new Dusa(`
# AOC Day 15, Part 1
#builtin INT_PLUS plus
#builtin INT_TIMES times
#builtin INT_MINUS minus
#builtin NAT_SUCC s
entries is N :- field _ "entries" is N.
length Entry is Len :-
field _ Entry is Ref,
field Ref "length" is Len.
ascii Entry N is Ch :-
field _ Entry is Ref,
field Ref "ascii" is Str,
field Str N is Ch.
# Hash computation
partial Entry 0 is 0 :- ascii Entry _ is _.
partial Entry (s N) is Next :-
partial Entry N is Val,
ascii Entry N is Code,
X == times 17 (plus Val Code),
mod256 X is Next.
needMod256 X X :-
partial Entry N is Val,
ascii Entry N is Code,
X == times 17 (plus Val Code).
hash Entry is Hash :-
length Entry is Len,
partial Entry Len is Hash.
# LOL, division by repeated subtraction
mod256 X is Y :-
needMod256 X Y,
Y < 256.
needMod256 X (minus Y 256) :-
needMod256 X Y,
Y >= 256.
# Goal
sum 0 is 0.
sum (s N) is (plus Accum Hash) :-
sum N is Accum,
hash N is Hash.
goal is (sum entries).
`);
dusa.load({ data: INPUT, entries: INPUT.length }, "field");
console.log(dusa.solution.get("goal"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....
`
.trim()
.split("\n")
.map((line) => line.split(""));
const PROGRAM = `
# AOC Day 14, Part 2 - Take 1
#builtin INT_MINUS minus
#builtin NAT_SUCC s
#builtin INT_PLUS plus
input X Y is Ch :-
field Root "data" is Data,
field Data Y is Row,
field Row X is Ch.
height is N :- field Root "height" is N.
width is N :- field Root "width" is N.
# PART 1: RANGE CALCULATION
# We're going to calculate all the horizontal ranges
# (hRange (pair X Y) is Width) is Height) and vertical
# ranges (vRange (pair X Y), identifying them with
# their leftmost/topmost point. We'll also create
# a lookup table for every non-blocked point:
# (hRangeFor (pair X Y) is (pair XStart Y)) and
# (vRangeFor (pair X Y) is (pair X YStart)).
# A range (horizontal or vertical) starts on any open
# space (non-#) that is at the left/top boundary or
# to the immediate right/bottom of a # symbol
growHRange (pair 0 Y) 0 :-
input 0 Y is Ch, Ch != "#".
growHRange (pair X Y) X :-
input X Y is Ch, Ch != "#",
XPrev == minus X 1,
input XPrev Y is "#".
growVRange (pair X 0) 0 :-
input X 0 is Ch, Ch != "#".
growVRange (pair X Y) Y :-
input X Y is Ch, Ch != "#",
YPrev == minus Y 1,
input X YPrev is "#".
# We grow a range whenever the next step is non-blocked
growHRange (pair XStart Y) (s X) :-
growHRange (pair XStart Y) X,
input X Y is Ch, Ch != "#".
hRangeFor (pair X Y) is (pair XStart Y) :-
growHRange (pair XStart Y) X,
input X Y is Ch, Ch != "#".
growVRange (pair X YStart) (s Y) :-
growVRange (pair X YStart) Y,
input X Y is Ch, Ch != "#".
vRangeFor (pair X Y) is (pair X YStart) :-
growVRange (pair X YStart) Y,
input X Y is Ch, Ch != "#".
# We've found the range when we get to a blocked point
# or when we get to the end.
hRange (pair X Y) is (minus XEnd X) :-
growHRange (pair X Y) XEnd,
input XEnd Y is "#".
hRange (pair X Y) is (minus width X) :-
growHRange (pair X Y) width.
vRange (pair X Y) is (minus YEnd Y) :-
growVRange (pair X Y) YEnd,
input X YEnd is "#".
vRange (pair X Y) is (minus height Y) :-
growVRange (pair X Y) height.
# PART 2: NEXT-STEP POSITION CALCULATION
# char (dir N) X Y is Ch is the key output of this calculation,
# and it means that after the Nth time you slide rocks in direction
# dir (for dir in { up, down, left, right }), the character at
# position (X, Y) will be Ch. We start with having slid everything
# to the right zero times, since right is the end of our
# up/left/down/right spin cycle
char (right 0) X Y is Ch :- input X Y is Ch.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....
`
.trim()
.split("\n")
.map((line) => line.split(""));
const dusa = new Dusa(`
# AOC Day 14, Part 2 - Take 1
#builtin INT_MINUS minus
#builtin NAT_SUCC s
#builtin INT_PLUS plus
input X Y is Ch :-
field Root "data" is Data,
field Data Y is Row,
field Row X is Ch.
height is N :- field Root "height" is N.
width is N :- field Root "width" is N.
# PART 1: RANGE CALCULATION
# We're going to calculate all the horizontal ranges
# (hRange (pair X Y) is Width) is Height) and vertical
# ranges (vRange (pair X Y), identifying them with
# their leftmost/topmost point. We'll also create
# a lookup table for every non-blocked point:
# (hRangeFor (pair X Y) is (pair XStart Y)) and
# (vRangeFor (pair X Y) is (pair X YStart)).
# A range (horizontal or vertical) starts on any open
# space (non-#) that is at the left/top boundary or
# to the immediate right/bottom of a # symbol
growHRange (pair 0 Y) 0 :-
input 0 Y is Ch, Ch != "#".
growHRange (pair X Y) X :-
input X Y is Ch, Ch != "#",
XPrev == minus X 1,
input XPrev Y is "#".
growVRange (pair X 0) 0 :-
input X 0 is Ch, Ch != "#".
growVRange (pair X Y) Y :-
input X Y is Ch, Ch != "#",
YPrev == minus Y 1,
input X YPrev is "#".
# We grow a range whenever the next step is non-blocked
growHRange (pair XStart Y) (s X) :-
growHRange (pair XStart Y) X,
input X Y is Ch, Ch != "#".
hRangeFor (pair X Y) is (pair XStart Y) :-
growHRange (pair XStart Y) X,
input X Y is Ch, Ch != "#".
growVRange (pair X YStart) (s Y) :-
growVRange (pair X YStart) Y,
input X Y is Ch, Ch != "#".
vRangeFor (pair X Y) is (pair X YStart) :-
growVRange (pair X YStart) Y,
input X Y is Ch, Ch != "#".
# We've found the range when we get to a blocked point
# or when we get to the end.
hRange (pair X Y) is (minus XEnd X) :-
growHRange (pair X Y) XEnd,
input XEnd Y is "#".
hRange (pair X Y) is (minus width X) :-
growHRange (pair X Y) width.
vRange (pair X Y) is (minus YEnd Y) :-
growVRange (pair X Y) YEnd,
input X YEnd is "#".
vRange (pair X Y) is (minus height Y) :-
growVRange (pair X Y) height.
# PART 2: NEXT-STEP POSITION CALCULATION
# char (dir N) X Y is Ch is the key output of this calculation,
# and it means that after the Nth time you slide rocks in direction
# dir (for dir in { up, down, left, right }), the character at
# position (X, Y) will be Ch. We start with having slid everything
# to the right zero times, since right is the end of our
# up/left/down/right spin cycle
char (right 0) X Y is Ch :- input X Y is Ch.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
`
.trim()
.split("\n");
const PROGRAM = `
#builtin NAT_SUCC s
#builtin STRING_CONCAT concat
value N is "#" :- char N is "#".
value N is "." :- char N is ".".
value N is { "#", "." } :- char N is "?".
startRun 0 is true :- value 0 is "#".
startRun (s N) is true :-
value N is ".",
value (s N) is "#".
startRun N is false :- value N is ".".
startRun (s N) is false :- value N is "#", value (s N) is "#".
startRun N is { false? } :- char N is _.
scan 0 is Lens :- runs is Lens.
scan (s N) is Lens :-
startRun N is false,
scan N is Lens.
scan (s N) is Lens :-
startRun N is true,
scan N is (cons Len Lens).
runAt N is Len :-
startRun N is true,
scan N is (cons Len Lens).
value N is "#" :-
runAt N is (s Len).
runAt (s N) is Len :-
runAt N is (s Len).
value N is "." :-
runAt N is 0.
#forbid startRun N is true, scan N is nil.
#forbid scan length is cons _ _.
#forbid length is Len, runAt Len is (s _).
result 0 is "".
result (s N) is (concat Accum Ch) :-
result N is Accum,
value N is Ch.
resolution is Str :- length is Len, result Len is Str.
`;
let accum = 0;
for (const line of INPUT) {
const [str, runstr] = line.split(" ");
const chars = str.split("");
const runs = runstr.split(",").map((n) => parseInt(n));
let dusalist = { name: "nil" };
for (let i = runs.length - 1; i >= 0; i--) {
dusalist = { name: "cons", args: [runs[i], dusalist] };
}
const dusa = new Dusa(PROGRAM);
dusa.assert(...chars.map((ch, i) => ({ name: "char", args: [i], value: ch })));
dusa.assert({ name: "length", args: [], value: chars.length });
dusa.assert({ name: "runs", args: [], value: dusalist });
let count = 0;
for (const solution of dusa.solutions) {
console.log(`${str} ${[...solution.lookup("resolution")][0][0]} ${runstr}`);
count += 1;
}
console.log(count);
accum += count;
}
console.log(accum);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#
`
.trim()
.split("\n\n")
.map((grid) => grid.split("\n").map((line) => line.split("")));
const PROGRAM = `
# AOC Day 13 Solution
#builtin NAT_SUCC s
#builtin INT_MINUS minus
#builtin INT_PLUS plus
#builtin INT_TIMES times
input (s X) (s Y) is Ch :-
field Root Y is Row,
field Row X is Ch.
# Characters for Part 1 are exactly the inputs
char part1 X Y is Ch :- input X Y is Ch.
# Characters for Part 2 are the inputs with
# one smudge - a "#" turned to a "." (could
# symmetrically be a "." turned to a "#")
smudge is { pair X Y? } :-
input X Y is "#",
finishedPart1.
char part2 X Y is Ch :-
smudge is (pair SX SY),
input X Y is Ch,
SX != X.
char part2 X Y is Ch :-
smudge is (pair SX SY),
input X Y is Ch,
SY != Y.
char part2 X Y is "." :-
smudge is (pair X Y).
# finishedPart1 is a hack to make sure we only pick the
# smudge location after we've solved Part 1
finishedPart1 :- reflectVerticalLine part1 is Axis.
finishedPart1 :- reflectHorizontalLine part1 is Axis.
# Each part admits exactly one horizontal or vertical reflection
reflection part1 is { horizontal, vertical }.
reflection part2 is { horizontal, vertical }.
# The way the problem is set up, every row/column EXCEPT the last
# one is a valid candidate for reflection
reflectHorizontalLine Part is { Y? } :-
reflection Part is horizontal,
char Part _ Y is _,
char Part _ (s Y) is _.
reflectVerticalLine Part is { X? } :-
reflection Part is vertical,
char Part X _ is _,
char Part (s X) _ is _.
# The way we reflect is that we actually assert that the reflected
# image to the left/top of the mirror exists on top of the
# right/bottom part. In any case where the mirror is incorrectly
# placed, this will result in a contradiction and invalidate the
# solution.
char Part X Reflection is Ch :-
reflectHorizontalLine Part is Axis,
char Part X Y is Ch,
Y <= Axis,
Reflection == plus (s Axis) (minus Axis Y).
char Part Reflection Y is Ch :-
reflectVerticalLine Part is Axis,
char Part X Y is Ch,
X <= Axis,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
`
.trim()
.split("\n");
const PROGRAM = `
#builtin NAT_SUCC s
#builtin INT_PLUS plus
getWays 0 noRun RunList :- runs is RunList.
# When we're NOT in a run
numWays N noRun nil is 0 :-
getWays N noRun nil,
char N is "#".
numWays N noRun RunList is NWays :-
getWays N noRun RunList,
RunList == (cons (s Len) SubList),
char N is "#",
numWays (s N) (run Len) SubList is NWays.
getWays (s N) (run Len) SubList :-
getWays N noRun RunList,
RunList == (cons (s Len) SubList),
char N is Ch, Ch != ".".
numWays N noRun RunList is NWays :-
getWays N noRun RunList,
char N is ".",
numWays (s N) noRun RunList is NWays.
getWays (s N) noRun RunList :-
getWays N noRun RunList,
char N is Ch, Ch != "#".
numWays N noRun nil is NWaysDot :-
getWays N noRun nil,
char N is "?",
numWays (s N) noRun nil is NWaysDot.
numWays N noRun RunList is (plus NWaysDot NWaysHash) :-
getWays N noRun RunList,
char N is "?",
numWays (s N) noRun RunList is NWaysDot,
RunList == (cons (s Len) SubList),
numWays (s N) (run Len) SubList is NWaysHash.
# End of a run
numWays N (run 0) RunList is 0 :-
getWays N (run 0) RunList,
char N is "#".
numWays N (run 0) RunList is NWays :-
getWays N (run 0) RunList,
char N is Ch, Ch != "#",
numWays (s N) noRun RunList is NWays.
getWays (s N) noRun RunList :-
getWays N (run 0) RunList,
char N is Ch, Ch != "#".
# Middle of a run
numWays N (run (s Len)) RunList is 0 :-
getWays N (run (s Len)) RunList,
char N is ".".
numWays N (run (s Len)) RunList is NWays :-
getWays N (run (s Len)) RunList,
char N is Ch, Ch != ".",
numWays (s N) (run Len) RunList is NWays.
getWays (s N) (run Len) RunList :-
char N is Ch, Ch != ".",
getWays N (run (s Len)) RunList.
# End of the road
numWays N State (cons Len RunList) is 0 :-
getWays N State (cons Len RunList),
length is N.
numWays N (run (s Len)) RunList is 0 :-
getWays N (run (s Len)) RunList,
length is N.
numWays N noRun nil is 1 :-
getWays N noRun nil,
length is N.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#.....
`
.trim()
.split("\n")
.map((line) => line.trim().split(""));
const PROGRAM = `
# AOC Day 11
#builtin NAT_SUCC s
#builtin INT_TIMES times
#builtin INT_PLUS plus
#builtin INT_MINUS minus
# Process input to get X, Y coordinates
charAt X Y is Ch :-
field _ "map" is List,
field List Y is Cols,
field Cols X is Ch.
width is W :- field _ "width" is W.
expansionFactor is F :- field _ "expansionFactor" is F.
# Use default reasoning to find out which rows/cols have stars
hasStar y Y is { false? } :- charAt _ Y is _.
hasStar y Y is true :- charAt _ Y is "#".
hasStar x X is { false? } :- charAt X _ is _.
hasStar x X is true :- charAt X _ is "#".
needsRemap x 0 0.
needsRemap y 0 0.
remap Axis Old is (minus (plus New expansionFactor) 1) :-
needsRemap Axis Old New,
hasStar Axis Old is false.
remap Axis Old is New :-
needsRemap Axis Old New,
hasStar Axis Old is true.
needsRemap Axis (s Old) (s New) :-
remap Axis Old is New.
starAt (remap x X) (remap y Y) :-
charAt X Y is "#".
`;
function bigabs(x) {
return x < 0 ? -x : x;
}
for (const expansionFactor of [2, 10, 100, 1000000]) {
const dusa = new Dusa(PROGRAM);
dusa.load({ map: INPUT, width: INPUT[0].length, expansionFactor }, "field");
const solution = dusa.sample();
const stars = [...solution.lookup("starAt")];
let accum = 0n;
for (const [index, [x1, y1]] of stars.entries()) {
for (const [x2, y2] of stars.slice(index + 1)) {
accum += bigabs(x1 - x2) + bigabs(y1 - y2);
}
}
console.log(accum);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#.....
`
.trim()
.split("\n")
.map((line) => line.trim().split(""));
const PROGRAM = `
# AOC Day 11
#builtin NAT_SUCC s
#builtin INT_TIMES times
#builtin INT_PLUS plus
#builtin INT_MINUS minus
# Process input to get X, Y coordinates
charAt X Y is Ch :-
field _ "map" is List,
field List Y is Cols,
field Cols X is Ch.
width is W :- field _ "width" is W.
expansionFactor is F :- field _ "expansionFactor" is F.
# Use default reasoning to find out which rows/cols have stars
hasStar y Y is { false? } :- charAt _ Y is _.
hasStar y Y is true :- charAt _ Y is "#".
hasStar x X is { false? } :- charAt X _ is _.
hasStar x X is true :- charAt X _ is "#".
needsRemap x 0 0.
needsRemap y 0 0.
remap Axis Old is (minus (plus New expansionFactor) 1) :-
needsRemap Axis Old New,
hasStar Axis Old is false.
remap Axis Old is New :-
needsRemap Axis Old New,
hasStar Axis Old is true.
needsRemap Axis (s Old) (s New) :-
remap Axis Old is New.
starAt (remap x X) (remap y Y) :-
charAt X Y is "#".
starDist (tuple X YA X YB) is (minus YA YB) :-
starAt X YA,
starAt X YB,
YA > YB.
starDist (tuple XA YA XB YB) is (plus (minus XA XB) (minus YA YB)) :-
starAt XA YA,
starAt XB YB,
XA > XB, YA >= YB.
starDist (tuple XA YA XB YB) is (plus (minus XA XB) (minus YB YA)) :-
starAt XA YA,
starAt XB YB,
XA > XB, YA < YB.
`;
for (const expansionFactor of [2, 10, 100, 1000000]) {
const dusa = new Dusa(PROGRAM);
dusa.load({ map: INPUT, width: INPUT[0].length, expansionFactor }, "field");
const solution = dusa.sample();
let accum = 0n;
for (const [_, dist] of solution.lookup("starDist")) {
accum += dist;
}
console.log(accum);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
.F----7F7F7F7F-7....
.|F--7||||||||FJ....
.||.FJ||||||||L7....
FJL7L7LJLJ||LJ.L-7..
L--J.L7...LJS7F-7L7.
....F-J..F7FJ|L7L7L7
....L7.F7||L7|.L7L7|
.....|FJLJ|FJ|F7|.LJ
....FJL-7.||.||||...
....L---J.LJ.LJLJ...
`
.trim()
.split("\n")
.map((line) => line.trim().split(""));
const dusa = new Dusa(`
# AOC Day 10, Part 2
#builtin INT_PLUS plus
#builtin INT_MINUS minus
# Process input to get X, Y coordinates
charAt (pair X Y) is Ch :-
field _ "map" is List,
field List Y is Cols,
field Cols X is Ch.
# Which connections does a character imply?
connectX "L" 1. connectY "L" -1.
connectX "J" -1. connectY "J" -1.
connectX "F" 1. connectY "F" 1.
connectX "7" -1. connectY "7" 1.
connectY "|" -1. connectY "|" 1.
connectX "-" -1. connectX "-" 1.
# Edges require agreement between the source and destination
# about there being a connection
# Horizontal edges (only X changes)
edge A B :-
charAt A is Ch, A == pair X1 Y,
connectX Ch Delta, X2 == plus X1 Delta,
B == pair X2 Y, charAt B is Ch2,
connectX Ch2 (minus 0 Delta).
# Vertical edges (only Y changes)
edge A B :-
charAt A is Ch, A == pair X Y1,
connectY Ch Delta, Y2 == plus Y1 Delta,
B == pair X Y2, charAt B is Ch2,
connectY Ch2 (minus 0 Delta).
# The start is handled specially, with its connections
# being implied by the neighbors
start is A :- charAt A is "S".
# Horizontal neighbor(s) of start
edge start (pair X Y) :-
charAt (pair X Y) is Ch,
connectX Ch Delta,
start == pair (plus X Delta) Y.
# Vertical neighbor(s) of start
edge start (pair X Y) :-
charAt (pair X Y) is Ch,
connectY Ch Delta,
start == pair X (plus Y Delta).
edge A B :- edge B A.
# We want to trace our path around the loop,
# but only in one of the two possible directions
after start is { A? } :- edge start A.
after B is C :-
after A is B,
edge B C,
C != A.
# Part 2 solution
onEdge A is { false? } :- charAt A is _, after _ is _.
onEdge A is true :- after A is _.
# RA: X-D, Y A: X, Y LA: X+D, Y
# v
# RB: X-D, Y+D B: X, Y+D LB: X+D, Y+D
orientation X Y is Delta :-
after (pair X Y) is (pair X Y2),
Delta == minus Y2 Y.
side RA is right :-
orientation X Y is Delta,
RA == pair (minus X Delta) Y,
onEdge RA is false.
side RB is right :-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { Dusa } from "https://unpkg.com/dusa@0.0.10/lib/client.js";
const INPUT = `
7-F7-
.FJ|7
SJLL7
|F--J
LJ.LJ
`
.trim()
.split("\n")
.map((line) => line.trim().split(""));
const dusa = new Dusa(`
# AOC Day 10, Part 1
#builtin INT_PLUS plus
#builtin INT_MINUS minus
# Process input to get X, Y coordinates
charAt (pair X Y) is Ch :-
field _ "map" is List,
field List Y is Cols,
field Cols X is Ch.
# Which connections does a character imply?
connectX "L" 1. connectY "L" -1.
connectX "J" -1. connectY "J" -1.
connectX "F" 1. connectY "F" 1.
connectX "7" -1. connectY "7" 1.
connectY "|" -1. connectY "|" 1.
connectX "-" -1. connectX "-" 1.
# Edges require agreement between the source and destination
# about there being a connection
# Horizontal edges (only X changes)
edge A B :-
charAt A is Ch, A == pair X1 Y,
connectX Ch Delta, X2 == plus X1 Delta,
B == pair X2 Y, charAt B is Ch2,
connectX Ch2 (minus 0 Delta).
# Vertical edges (only Y changes)
edge A B :-
charAt A is Ch, A == pair X Y1,
connectY Ch Delta, Y2 == plus Y1 Delta,
B == pair X Y2, charAt B is Ch2,
connectY Ch2 (minus 0 Delta).
# The start is handled specially, with its connections
# being implied by the neighbors
start is A :- charAt A is "S".
# Horizontal neighbor(s) of start
edge start (pair X Y) :-
charAt (pair X Y) is Ch,
connectX Ch Delta,
start == pair (plus X Delta) Y.
# Vertical neighbor(s) of start
edge start (pair X Y) :-
charAt (pair X Y) is Ch,
connectY Ch Delta,
start == pair X (plus Y Delta).
edge A B :- edge B A.
# We want to trace our path around the loop,
# but only in one of the two possible directions
after start is { A? } :- edge start A.
after B is C :-
after A is B,
edge B C,
C != A.
distance A is 1 :- after start is A.
distance B is (plus N 1) :-
distance A is N,
A != start,
after A is B.
# The goal value is half the length, which we calculate in a
# very cheesy manner
goal is D :-
distance _ is D,
plus D D == distance start.
`);
dusa.load({ map: INPUT }, "field");
console.log([...dusa.sample().lookup("goal")]);