diff --git a/interactive/examples/aoc2023/.gitignore b/interactive/examples/aoc2023/.gitignore new file mode 100644 index 000000000..e8e450bed --- /dev/null +++ b/interactive/examples/aoc2023/.gitignore @@ -0,0 +1 @@ +gen/ diff --git a/interactive/examples/aoc2023/GAPS.md b/interactive/examples/aoc2023/GAPS.md new file mode 100644 index 000000000..f0521c2f6 --- /dev/null +++ b/interactive/examples/aoc2023/GAPS.md @@ -0,0 +1,74 @@ +# Language gaps, from the AoC 2023 survey + +Thirteen recurring gaps hit while expressing the 33 parts in this suite, +ranked by how many days each would have unlocked or simplified. They are +recorded together here rather than as thirteen issues: most are one-line +asks whose real content is the workaround they forced, and they rank +against each other better than they read alone. Each entry names what is +missing, how the programs coped, and the smallest change that would have +sufficed. + +1. **Integer division family: `/`, `%`, shifts.** Worked around on four + days — enumeration fixpoints (day 6), parity folds (10p1), relational + modulo via a k-table cross join (15p1), a 40-step binary-halving gadget + (18) — and a named blocker for days 8, 23, 24. Minimal fix: `/` and `%` + BinOps. The highest value per implementation line in the survey. +2. **No `sum`/`max` reducers.** Every solved day paid this tax: sum is + `collect` + `fold` (materializing a list to add numbers), max is `min` + over `BIG - x` or a negate — the latter interacting badly with corgi's + encoding-order contract for negatives. Minimal fix: `sum` and `max` + beside `min` and `count`. +3. **`distinct` erases the value to unit**, so every iterative loop's state + must be packed into keys and re-projected around every join (days 3, 5, + 10, 14, 16, 18, 19, 22). Minimal fix: a value-preserving idempotent + reducer — `distinct` on rows, or blessing `min` as the var-reducer idiom + (days 17 and 22 show it works). +4. **No scalar let-bindings or user scalar functions**; repeated + subexpressions are copy-pasted or staged through extra `map`s (days 1p2, + 7, 18, 19 — day 19's projections had to be machine-generated). Minimal + fix: `let x = t in t` in the scalar grammar. +5. **No collection-level abstraction** (parameterized subgraphs): day + 14p2's four roll directions and day 16's two beam programs are + copy-pasted pipelines. Minimal fix: named subgraph definitions with + collection parameters. +6. **No string/char literals**; all text logic is numeric charcode + comparison (days 1, 10, 13, 15, 16, 18, 19). Minimal fix: `'a'` char + literals, `"abc"` as list-of-codes sugar. +7. **`a - b` is multiset subtraction**; the natural "set minus" reading + silently corrupts counts when `b` has rows outside `a` (bit day 10; + latent everywhere). Idiom: `a - (a |> join b)`. Minimal fix: an `except` + operator. +8. **One `EDGES_FILE` round-robined across inputs** makes multi-relation + programs awkward; the tag+filter workaround (days 5, 19, 22) then runs + into corgi's arity-uniformity contract. Minimal fix: `EDGES_FILE_0`, + `EDGES_FILE_1`, ... +9. **No extrinsic iteration bound** (SQL's `RETURN AT RECURSION LIMIT`); + bounds must be encoded in the data. This is ergonomics, not expressive + power: a one-round stall (`var d = x;` then `x + (d | negate)` fires at + round 0 only) seeds a marker that marches `k -> k+1` under + `filter($0[0] < K)` with O(1) live rows per round — the full + delayed-pulse stepwise driver, in the language today. The residual ask + is sugar: an iteration cap on scopes, or a blessed pulse idiom in the + examples. +10. **No substring/window primitive**: k-char windows need k−1 self-joins + (day 1p2); line equality via collected lists (day 13). Minimal fix: a + windowing flatmap or list-slicing scalars. +11. **A reduce's List value cannot be re-projected as a single field**: + bare `$1` splices, `$1[0]` takes an element; idiom `if(1, $1, 0)` + (day 13). Minimal fix: a whole-value field form, e.g. `val($n)`. +12. **`&&` exists but `||` doesn't** (workaround `or(a,b)`); trivial + grammar fix (day 10). +13. **No non-integer arithmetic** (float/decimal, sqrt): the day 25 + blocker; contributes to 6 and 24. Worth fixing only if DDIR wants + numeric-analysis workloads — everything else in AoC fits i64. + +## Near-misses that are not gaps + +Two things initially read as gaps and turned out not to be. The +delayed-pulse driver above (entry 9) is fully expressible via the `var` +stall — the survey's first pass got this wrong, and the construction is +worth knowing. And several days where SQL's stepwise idioms would not +transliterate ended up with *better* programs for it: day 14's closed-form +roll and day 22's interval-overlap fall are shorter, clearer, and more +incremental than their SQL counterparts. Absence of a feature is +occasionally load-bearing. diff --git a/interactive/examples/aoc2023/README.md b/interactive/examples/aoc2023/README.md new file mode 100644 index 000000000..652f26c91 --- /dev/null +++ b/interactive/examples/aoc2023/README.md @@ -0,0 +1,63 @@ +# AoC 2023 in DDIR + +A language-coverage and regression suite: 33 parts across 17 days of Advent +of Code 2023, expressed in pipe-form DDIR (`.ddp`) and checked against the +answers of Materialize's sqllogictest oracles +(`test/sqllogictest/advent-of-code/2023/`) — same inputs, same expected +answers, oracle quirks reproduced where needed to match. + +Inputs are committed in their dense original text form (`dayNN/input.txt`, +the compact puzzle text as it appears in the slt oracles); `transcribe.py` +mechanically regenerates the i64 fact files the programs read (text → +`(line, pos, charcode)`, grids → `(row, col, cell)`, names → ids) into the +gitignored `gen/` directory at runtime. All puzzle logic lives in the +`.ddp` programs. `transcribe.py --pad` additionally writes day05's +arity-padded fact files for the corgi backend, which crashes on +mixed-arity inputs. + +## Run + + cargo build --release --example ddir + ./run.sh # every part on the vec backend vs expected.txt; nonzero exit on any mismatch + +`run.sh` first runs `transcribe.py` (python3) to regenerate `gen/`, then +runs each part as `EDGES_FILE=gen/ ddir --backend=vec +dayNN/partN.ddp 10 0 1 0`; the answer is the `Int` on the +`[partN]` inspect line. + +`run.sh` asserts the **vec** backend only; corgi currently disagrees or +crashes on 4 of these parts (known open issues, minimized separately) and +is not asserted yet. + +## Verdicts + +CLEAN = direct expression; AWKWARD = right answer via a workaround idiom. + +| day | p1 | p2 | note | +|-----|----|----|------| +| 01 | clean | awkward | no string literals: digit words matched as charcode filters over k-wide windows (k−1 self-joins) | +| 02 | clean | clean | anti-join as `games - bad`; max via `min` of (BIG − x) | +| 03 | awkward | awkward | run assembly needs 2 fixpoints; `distinct` erases values, so loop state is packed into keys | +| 04 | clean | clean | p2: card copies ARE collection multiplicities; the cascade is a bare `var` fixpoint | +| 05 | clean | awkward | stage-chain fixpoint, identity fallback = anti-join; p2 range split/holes all key-packed | +| 06 | awkward | — | no `/` or sqrt: hold times enumerated by fixpoint; slt has no separate part 2 | +| 07 | clean | awkward | hand type = pairwise-equality count; p2 stages maps to stand in for scalar lets | +| 10 | clean | awkward | loop = fixpoint, n/2 via parity fold; p2: `-` is multiset subtraction, set-minus needs an explicit anti-join | +| 11 | clean | clean | gap rows/cols by anti-join; L1 over all-pairs (slt input has no gaps, so p1 = p2) | +| 13 | awkward | clean | whole-line equality via collected Lists; p2 smudge = exactly one mismatched mirrored cell | +| 14 | clean | awkward | p1: closed-form roll, zero recursion; p2: round-tagged fixpoint with 4 hand-unrolled direction pipelines | +| 15 | awkward | clean | no `%`: modulo done relationally via a k-table cross join; p2 boxes need no sequencing | +| 16 | clean | clean | beam automaton with a literal transition table via flatmap | +| 17 | clean | clean | Bellman-Ford: `var mc = seeds + moves \| min`; p2 = 3 filter tweaks on p1 | +| 18 | awkward | awkward | shoelace prefix fixpoint; /2 via a 40-step binary-decomposition gadget; p2 hex decoded from charcodes | +| 19 | clean | clean | first-match = min (priority, next) inside the fixpoint; p2 reproduces the slt's negative-product quirk | +| 22 | clean | clean | interval overlap, no cell expansion; p2's count-inside-fixpoint converges fine | + +Days 08, 09, 12, 20, 21, 23, 24 are absent because their slt files carry no +usable oracle (EXPLAIN-only, stubbed, or self-inconsistent); day 25's oracle +is an artifact of floating-point spectral iteration, which DDIR (integers +only) cannot chase. + +The survey behind this suite also produced a ranked catalogue of 13 +recurring language gaps (integer division/modulo, `max`/`sum` reducers, +value-preserving `distinct`, scalar lets, ...): see [GAPS.md](GAPS.md). diff --git a/interactive/examples/aoc2023/day01/input.txt b/interactive/examples/aoc2023/day01/input.txt new file mode 100644 index 000000000..1f82e6e40 --- /dev/null +++ b/interactive/examples/aoc2023/day01/input.txt @@ -0,0 +1,9 @@ +noveneiner9 +seventwoseven114 +1two4two +hell0l1 +79430242 +159a951 +0 +seven2seven +h4mb5rg diff --git a/interactive/examples/aoc2023/day01/part1.ddp b/interactive/examples/aoc2023/day01/part1.ddp new file mode 100644 index 000000000..a10cc25e5 --- /dev/null +++ b/interactive/examples/aoc2023/day01/part1.ddp @@ -0,0 +1,18 @@ +-- AoC 2023 day 1 part 1: per line, first digit * 10 + last digit; sum. +-- input 0: (line, pos, charcode) + +let digits = input 0 + | filter($0[2] >= 48 && $0[2] <= 57) + | key($0[0] ; $0[1], $0[2] - 48); +-- (line ; pos, digit) + +let first = digits | min | map($0 ; $1[1]); +let last = digits | map($0 ; 0 - $1[0], $1[1]) | min | map($0 ; $1[1]); + +let calib = first | join(last, ($0 ; $1[0] * 10 + $2[0])); +-- (line ; value) + +export "result" = calib + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day01/part2.ddp b/interactive/examples/aoc2023/day01/part2.ddp new file mode 100644 index 000000000..6cb6783b0 --- /dev/null +++ b/interactive/examples/aoc2023/day01/part2.ddp @@ -0,0 +1,43 @@ +-- AoC 2023 day 1 part 2: digits and spelled-out digit words count; sum of +-- first*10+last per line. Words matched by charcode against windows built +-- from k successive self-joins (no substring primitive, no string literals). +-- input 0: (line, pos, charcode) + +let chars = input 0 | key($0[0], $0[1] ; $0[2]); +-- (line, pos ; code) + +let s2 = chars | map($0[0], $0[1] + 1 ; $0[1], $1[0]) + | join(chars, ($0[0], $1[0] ; $1[1], $2[0])); +let s3 = s2 | map($0[0], $0[1] + 2 ; $0[1], $1[0], $1[1]) + | join(chars, ($0[0], $1[0] ; $1[1], $1[2], $2[0])); +let s4 = s3 | map($0[0], $0[1] + 3 ; $0[1], $1[0], $1[1], $1[2]) + | join(chars, ($0[0], $1[0] ; $1[1], $1[2], $1[3], $2[0])); +let s5 = s4 | map($0[0], $0[1] + 4 ; $0[1], $1[0], $1[1], $1[2], $1[3]) + | join(chars, ($0[0], $1[0] ; $1[1], $1[2], $1[3], $1[4], $2[0])); +-- sk: (line, pos ; c0..c{k-1}) + +let digits = chars | filter($1[0] >= 48 && $1[0] <= 57) + | map($0[0] ; $0[1], $1[0] - 48); + +let w_zero = s4 | filter($1[0]==122 && $1[1]==101 && $1[2]==114 && $1[3]==111) | map($0[0] ; $0[1], 0); +let w_one = s3 | filter($1[0]==111 && $1[1]==110 && $1[2]==101) | map($0[0] ; $0[1], 1); +let w_two = s3 | filter($1[0]==116 && $1[1]==119 && $1[2]==111) | map($0[0] ; $0[1], 2); +let w_three = s5 | filter($1[0]==116 && $1[1]==104 && $1[2]==114 && $1[3]==101 && $1[4]==101) | map($0[0] ; $0[1], 3); +let w_four = s4 | filter($1[0]==102 && $1[1]==111 && $1[2]==117 && $1[3]==114) | map($0[0] ; $0[1], 4); +let w_five = s4 | filter($1[0]==102 && $1[1]==105 && $1[2]==118 && $1[3]==101) | map($0[0] ; $0[1], 5); +let w_six = s3 | filter($1[0]==115 && $1[1]==105 && $1[2]==120) | map($0[0] ; $0[1], 6); +let w_seven = s5 | filter($1[0]==115 && $1[1]==101 && $1[2]==118 && $1[3]==101 && $1[4]==110) | map($0[0] ; $0[1], 7); +let w_eight = s5 | filter($1[0]==101 && $1[1]==105 && $1[2]==103 && $1[3]==104 && $1[4]==116) | map($0[0] ; $0[1], 8); +let w_nine = s4 | filter($1[0]==110 && $1[1]==105 && $1[2]==110 && $1[3]==101) | map($0[0] ; $0[1], 9); + +let findings = digits + w_zero + w_one + w_two + w_three + w_four + w_five + w_six + w_seven + w_eight + w_nine; +-- (line ; pos, digit) + +let first = findings | min | map($0 ; $1[1]); +let last = findings | map($0 ; 0 - $1[0], $1[1]) | min | map($0 ; $1[1]); +let calib = first | join(last, ($0 ; $1[0] * 10 + $2[0])); + +export "result" = calib + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day02/input.txt b/interactive/examples/aoc2023/day02/input.txt new file mode 100644 index 000000000..3b9787f1c --- /dev/null +++ b/interactive/examples/aoc2023/day02/input.txt @@ -0,0 +1,5 @@ +Game 1: 1 red, 4 green, 4 blue; 3 red, 4 blue; 2 red, 1 green; 1 green, 1 red +Game 2: 20 green, 1 red; 1 blue, 4 red; 1 green, 1 red, 1 blue; 1 green, 1 red, 1 blue +Game 3: 8 red, 1 blue, 5 green; 9 red, 3 blue, 3 green; 3 green, 4 red +Game 4: 0 blue, 8 red, 4 green; 7 green, 8 blue, 9 red; 2 green, 5 red, 1 blue +Game 5: 20 red, 10 blue, 9 green; 2 blue, 3 red, 7 green; 1 green, 3 blue diff --git a/interactive/examples/aoc2023/day02/part1.ddp b/interactive/examples/aoc2023/day02/part1.ddp new file mode 100644 index 000000000..9bae18c25 --- /dev/null +++ b/interactive/examples/aoc2023/day02/part1.ddp @@ -0,0 +1,13 @@ +-- AoC 2023 day 2 part 1: sum of game ids where every set fits 13g/12r/14b. +-- input 0: (game, set, green, red, blue) + +let games = input 0 | key($0[0] ;) | distinct; +let bad = input 0 + | filter(not($0[2] <= 13 && $0[3] <= 12 && $0[4] <= 14)) + | key($0[0] ;) | distinct; +let good = games - bad; + +export "result" = good + | key(; $0[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day02/part2.ddp b/interactive/examples/aoc2023/day02/part2.ddp new file mode 100644 index 000000000..aacb59298 --- /dev/null +++ b/interactive/examples/aoc2023/day02/part2.ddp @@ -0,0 +1,16 @@ +-- AoC 2023 day 2 part 2: per game, max needed of each color; sum of products. +-- max via min of (BIG - x), avoiding negatives. +-- input 0: (game, set, green, red, blue) + +let maxg = input 0 | key($0[0] ; 1000000 - $0[2]) | min | map($0 ; 1000000 - $1[0]); +let maxr = input 0 | key($0[0] ; 1000000 - $0[3]) | min | map($0 ; 1000000 - $1[0]); +let maxb = input 0 | key($0[0] ; 1000000 - $0[4]) | min | map($0 ; 1000000 - $1[0]); + +let power = maxg + | join(maxr, ($0 ; $1[0] * $2[0])) + | join(maxb, ($0 ; $1[0] * $2[0])); + +export "result" = power + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day03/input.txt b/interactive/examples/aoc2023/day03/input.txt new file mode 100644 index 000000000..9b317e101 --- /dev/null +++ b/interactive/examples/aoc2023/day03/input.txt @@ -0,0 +1,7 @@ +...14...954......104...98..........11...222.........38.104....708..........................217..................330.................19.. +.......@...................*...............................*.664........677................@....459.........187..........73............. +....41............178.....398....*...548..495..........983.........99.........282......409........*...........$.248...............165... +......261......300...............704.&.......*.......*........9.65..904.....6....*773....=.....680../511...2*.....=..99*....*..../...... +..........200..............398.......22...100...........&...........10.......*.......73.....833...*...........*......300.............22. +..................@.100....*...........*...............*....19...300.....*.................@....954.......................200........... +.....-....&..@............828...........@268..844....534...................563.........409........$..........244.........722.286........ diff --git a/interactive/examples/aoc2023/day03/part1.ddp b/interactive/examples/aoc2023/day03/part1.ddp new file mode 100644 index 000000000..a05a5a324 --- /dev/null +++ b/interactive/examples/aoc2023/day03/part1.ddp @@ -0,0 +1,47 @@ +-- AoC 2023 day 3 part 1: sum of numbers adjacent (8-way) to a symbol. +-- input 0: (row, col, charcode); '.' = 46, digits 48..57. +-- NB: `distinct` erases values, so loop state is packed into keys. + +let cells = input 0 | filter($0[2] != 46) | key($0[0], $0[1] ; $0[2]); +let numerals = cells | filter($1[0] >= 48 && $1[0] <= 57) | map($0 ; $1[0] - 48); +let symbols = cells | filter(not($1[0] >= 48 && $1[0] <= 57)); + +-- run starts: digit cells with no digit to the left +let has_left = numerals | map($0[0], $0[1] + 1 ; $1[0]) | join(numerals, ($0 ; $2[0])); +let starts = numerals - has_left; + +-- grow runs rightward: key (row, start, end_exclusive, value) +runs: { + let grow = run | map($0[0], $0[2] ; $0[1], $0[3]) + | join(numerals, ($0[0], $1[0], $0[1] + 1, $1[1] * 10 + $2[0] ;)); + var run = (starts | map($0[0], $0[1], $0[1] + 1, $1[0] ;)) + grow | distinct; +} +let extendable = runs::run | map($0[0], $0[2] ; $0[1], $0[3]) + | join(numerals, ($0[0], $1[0], $0[1], $1[1] ;)); +let maximal = runs::run - extendable; +-- key (row, start, end, value) + +-- label each digit cell with its run start: key (row, col, start) +labels: { + let ext = label | map($0[0], $0[1] + 1 ; $0[2]) | join(numerals, ($0[0], $0[1], $1[0] ;)); + var label = (starts | map($0[0], $0[1], $0[1] ;)) + ext | distinct; +} + +-- digit cells adjacent to a symbol +let symnbrs = symbols + | flatmap(list(tuple(-1,-1), tuple(-1,0), tuple(-1,1), + tuple(0,-1), tuple(0,0), tuple(0,1), + tuple(1,-1), tuple(1,0), tuple(1,1))) + | map($0[0] + $1[1][0], $0[1] + $1[1][1] ;) | distinct; + +let active_runs = labels::label + | map($0[0], $0[1] ; $0[2]) | join(symnbrs, ($0[0], $1[0] ;)) | distinct; +-- key (row, start) of runs adjacent to a symbol + +let part_numbers = maximal | map($0[0], $0[1] ; $0[3]) | join(active_runs, ($0 ; $1[0])); +-- (row, start ; value) + +export "result" = part_numbers + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day03/part2.ddp b/interactive/examples/aoc2023/day03/part2.ddp new file mode 100644 index 000000000..8948c71ff --- /dev/null +++ b/interactive/examples/aoc2023/day03/part2.ddp @@ -0,0 +1,52 @@ +-- AoC 2023 day 3 part 2: gears = '*' adjacent to exactly two part numbers; +-- sum of the two numbers' product per gear. +-- input 0: (row, col, charcode) + +let cells = input 0 | filter($0[2] != 46) | key($0[0], $0[1] ; $0[2]); +let numerals = cells | filter($1[0] >= 48 && $1[0] <= 57) | map($0 ; $1[0] - 48); +let gears = cells | filter($1[0] == 42); + +let has_left = numerals | map($0[0], $0[1] + 1 ; $1[0]) | join(numerals, ($0 ; $2[0])); +let starts = numerals - has_left; + +runs: { + let grow = run | map($0[0], $0[2] ; $0[1], $0[3]) + | join(numerals, ($0[0], $1[0], $0[1] + 1, $1[1] * 10 + $2[0] ;)); + var run = (starts | map($0[0], $0[1], $0[1] + 1, $1[0] ;)) + grow | distinct; +} +let extendable = runs::run | map($0[0], $0[2] ; $0[1], $0[3]) + | join(numerals, ($0[0], $1[0], $0[1], $1[1] ;)); +let maximal = runs::run - extendable; +let partvals = maximal | map($0[0], $0[1] ; $0[3]); +-- (row, start ; value) + +labels: { + let ext = label | map($0[0], $0[1] + 1 ; $0[2]) | join(numerals, ($0[0], $0[1], $1[0] ;)); + var label = (starts | map($0[0], $0[1], $0[1] ;)) + ext | distinct; +} +let labelcells = labels::label | map($0[0], $0[1] ; $0[2]); + +let gearnbrs = gears + | flatmap(list(tuple(-1,-1), tuple(-1,0), tuple(-1,1), + tuple(0,-1), tuple(0,0), tuple(0,1), + tuple(1,-1), tuple(1,0), tuple(1,1))) + | map($0[0] + $1[1][0], $0[1] + $1[1][1] ; $0[0], $0[1]); +-- (nbr_cell ; gear) + +let pairs = gearnbrs + | join(labelcells, ($1[0], $1[1], $0[0], $2[0] ;)) + | distinct; +-- key (gear_r, gear_c, part_row, part_start) + +let gearvals = pairs + | map($0[2], $0[3] ; $0[0], $0[1]) + | join(partvals, ($1[0], $1[1] ; $2[0])); +-- (gear ; part value) + +export "result" = gearvals + | collect + | filter(len($1) == 2) + | map(; $1[0][0] * $1[1][0]) + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day04/input.txt b/interactive/examples/aoc2023/day04/input.txt new file mode 100644 index 000000000..7f0a6d002 --- /dev/null +++ b/interactive/examples/aoc2023/day04/input.txt @@ -0,0 +1,10 @@ +Card 1: 91 58 89 8 19 64 92 28 22 1 | 6 94 21 70 81 59 5 35 24 31 43 69 91 12 51 53 98 50 70 98 47 6 9 49 50 +Card 2: 49 56 57 80 28 9 3 19 55 6 | 35 25 76 45 35 73 12 93 29 23 50 33 75 36 4 33 90 84 1 9 44 62 99 80 85 +Card 3: 97 29 93 95 66 40 97 9 58 11 | 22 56 90 13 40 84 83 70 65 80 73 84 58 93 98 79 46 51 47 70 8 50 43 70 +Card 4: 62 79 90 45 63 70 75 26 14 92 | 70 5 69 58 80 64 72 4 36 24 40 76 79 16 79 11 80 88 49 92 15 24 5 49 22 +Card 5: 54 26 80 65 14 46 77 59 12 20 | 96 89 95 25 19 22 34 9 24 86 87 63 16 31 5 22 91 71 8 80 33 2 65 67 78 +Card 6: 22 10 58 44 5 97 97 57 88 8 | 54 50 79 45 2 40 90 30 82 37 29 99 50 90 51 84 97 62 8 4 89 82 86 59 65 +Card 7: 65 94 76 4 41 40 1 6 50 96 | 82 90 42 92 22 18 29 96 47 91 71 2 5 3 42 73 45 26 15 13 29 37 7 63 81 +Card 8: 73 19 52 43 47 54 6 86 12 34 | 25 70 26 15 35 10 65 81 48 72 98 48 18 94 8 34 6 44 79 25 77 27 78 61 28 +Card 9: 32 51 38 86 17 56 42 4 67 38 | 55 5 26 91 98 11 52 1 48 13 55 95 60 15 16 51 54 22 91 8 26 70 26 35 92 +Card 10: 6 40 74 5 31 63 1 5 12 64 | 88 7 91 54 4 62 37 66 5 69 59 78 17 47 61 2 6 56 36 59 2 71 63 87 72 diff --git a/interactive/examples/aoc2023/day04/part1.ddp b/interactive/examples/aoc2023/day04/part1.ddp new file mode 100644 index 000000000..2a5ee34ea --- /dev/null +++ b/interactive/examples/aoc2023/day04/part1.ddp @@ -0,0 +1,13 @@ +-- AoC 2023 day 4 part 1 (slt semantics): per card, numbers appearing >1 +-- time across all 35 slots; points = 2^(k-1); sum. +-- input 0: (card, side, idx, value) + +let dups = input 0 | key($0[0], $0[3] ;) | count | filter($1[0] > 1); +let points = dups + | map($0[0] ; $0[1]) | collect + | map($0 ; fold($1, 0, if(^1 == 0, 1, ^1 * 2))); + +export "result" = points + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day04/part2.ddp b/interactive/examples/aoc2023/day04/part2.ddp new file mode 100644 index 000000000..2f1992909 --- /dev/null +++ b/interactive/examples/aoc2023/day04/part2.ddp @@ -0,0 +1,26 @@ +-- AoC 2023 day 4 part 2: cascading card copies; total card count. +-- Copies are collection multiplicities; the cascade is a var fixpoint. +-- input 0: (card, side, idx, value) + +let winners = input 0 | filter($0[1] == 0) | key($0[0], $0[3] ;); +let ours = input 0 | filter($0[1] == 1) | key($0[0], $0[3] ;); +let matches = ours | join(winners, ($0 ;)) | map($0[0] ;) | count; +-- (card ; m): number of matching (ours, winners) pairs + +let cards = input 0 | key($0[0] ;) | distinct; + +-- prize targets: key (card, target) for target in card+1 .. card+m +wt: { + let step = t | map($0[0] ; $0[1]) | join(matches, ($0[0], $1[0] + 1, $2[0] ;)) + | filter($0[1] <= $0[0] + $0[2]) | map($0[0], $0[1] ;); + var t = (matches | filter($1[0] >= 1) | map($0[0], $0[0] + 1 ;)) + step | distinct; +} +let wtflat = wt::t | map($0[0] ; $0[1]); + +-- instances: multiplicity of (card ;) = copies of that card +insts: { + let spawned = inst | join(wtflat, ($2[0] ;)); + var inst = cards + spawned | arrange; +} + +export "result" = insts::inst | key(;) | count | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day05/input.txt b/interactive/examples/aoc2023/day05/input.txt new file mode 100644 index 000000000..00b666e47 --- /dev/null +++ b/interactive/examples/aoc2023/day05/input.txt @@ -0,0 +1,165 @@ +seeds: 141812878 853583433 69532151 734372491 182396959 4723992392 8947211973 5 4238233746 414976297 3674819199 51868842 + +seed-to-soil map: +47738968 98357 182795944 +7626588292 7848955494 927588242 +34324971 6812781938 837374212 +2166766538 726174459 6843311291 +239754864 684391 555585341 +418284536 8839949654 642749254 +9423585483 8225843682 329442345 +8712565737 9399753119 5 +74289536 9359574415 717691786 + +soil-to-fertilizer map: +8986393527 4888517941 585279262 +7613871113 9877446651 84269233 +2423271674 7586366221 7659569 +3622861142 916381775 74546981 +3996339781 3244535459 98765225 +586791635 8419253759 59179897 +7576358959 6127297299 519542837 +7876479671 6556651697 2721518 +8412917365 865866259 75627132 +8919467753 2818783878 5 +4729257824 8988777624 275397538 +2626168725 5 375375337 +7963626731 8731755354 562155822 +4484955989 2 31166388 +6394952326 9434564563 344217764 +5818263571 8292867479 46133627 +6635767943 663896613 39649384 +1179179943 58595967 326966131 +461712455 2756694296 566766331 +267854195 942359332 92191415 +1676162127 2415173728 7527572 +8235458679 517558444 8481131 +2193675972 5252589743 48214883 +4312181391 5588755717 5725572 +451918428 7726618552 39256967 +4734828421 378251365 56849366 +7864169188 795712941 852726511 +6498165846 819955597 825591722 +6737759128 1943367748 153632389 +81836871 3619315963 5999745237 +5533126476 1625724578 11196339 +814358688 9878499637 982477257 +4386394269 3737453299 68828912 +1 535278815 55 +9455234351 63518366 96697897 +298142454 9711262844 545237968 +6548387663 5498764299 556695254 +4421182848 3813437218 18532535 + +fertilizer-to-water map: +5188185212 713592736 572122634 +3996516993 8368372653 29257663 +7629744559 8723951436 36722366 +517117434 6799613578 62912912 +7444887598 1378359775 142239913 +8946622323 461231638 98648353 +895954325 5555555 419161656 +1269235415 8199792497 62639226 +277191562 35368196 783296989 +2994552458 6347418557 828619684 +7 245618496 85983123 +599564274 6975655213 441448691 +7312274117 4916534261 32238555 +444977452 8543586232 53382156 +682172835 2567878724 62974419 +8912874438 5539912916 51886594 +4257175799 1517431879 97672792 +9481282796 996563263 8713257 +8269679713 2281488697 77222754 +2566468873 4747453236 37516457 +9491212658 1459322382 56814988 +142239541 25962654 99568155 +3475998687 9 537527222 +7223664422 4413723656 2795999379 +5781744284 5851593946 52411132 +2615616119 2198338791 93894182 +725796923 6293593866 672373647 +5291123587 1 45165371 + +water-to-light map: +9483826148 971239373 57981779 +866572267 5355262121 658481698 +6149496513 2245492751 89485475 +7743881746 5949772182 31781539 +915838939 73797715 9628255536 +49687174 729495672 53487321 +1774464825 4513912578 89436881 +734978995 62382679 39621782 +8579142462 9 79959373 +3999434125 1722538882 315735941 +9758171681 5256942648 79846743 +676724382 222319917 43462458 +9842566327 2265547154 99978196 +3885113248 6341877917 53731483 +7451763582 1 22747236 +2222665944 8558249644 858131997 +2222613482 1369627666 23224913 +2122417343 892313426 54167146 +9888331148 1128482359 94371883 +55684927 195154981 59748169 +8819525361 7614218117 73293139 +241798649 664138941 854619722 +9716684718 3664657195 253795579 +3431191398 9414159725 8345323 +4742633341 757873782 42893665 +5976897431 6681469458 1532556 +5 947566478 61872123 +8793878654 8231529123 34617151 +136924369 6845534171 1385592 +538695647 894877235 85583498 +3327525594 1748821684 434778753 + +light-to-temperature map: +6778296876 1588184826 9373785 +5958282911 253795579 867819861 +422679395 2761173364 193474551 +253795579 4218627316 4 +5652112248 987641937 721917256 +2349984397 7942568282 71771239 +8969933137 5781654773 394652 +1898117348 8342374669 537694616 +4219243162 8214944547 736996395 +5912961492 8868498531 27622883 +346958533 4173157962 51979735 +1916631453 9117978288 18581458 +2945679922 4497199716 5152578 +9179535252 5162391577 549347823 + +temperature-to-humidity map: +7239471736 657945134 92565479 +4691325348 8569173453 532754925 +5919891224 6397216467 3372871 +3822197491 2782624568 698583215 +6125121482 639986598 98215788 +6574396598 665121945 619454968 +2412529992 2776226287 1626339 +2272785524 417214458 621918434 +5629812155 68588342 911161561 +9324482921 2524842229 67458368 +5932612196 2357165667 93374591 +1274118925 4785728782 685125685 +4352611699 7119321598 69449264 +6387566129 5474547438 5352628 +3928922836 7972535629 916644549 +1662726326 6558668448 362791743 +836597248 451582864 36438897 +738226654 681268313 785154987 +9563614959 8661586162 448483715 +9984139884 5113486123 19999477 +2755987212 2214749752 872249174 +887416884 6486692479 38549881 + +humidity-to-location map: +858893435 311438446 562265762 +2454586869 3679665416 225353643 +9519322712 63232264 128625398 +441771557 9 2371977356 +5 8193625898 775413179 +1952973915 6235154499 759143921 +6572 58451 75391174 +6941443142 589862612 261347316 diff --git a/interactive/examples/aoc2023/day05/part1.ddp b/interactive/examples/aoc2023/day05/part1.ddp new file mode 100644 index 000000000..995a6a6c1 --- /dev/null +++ b/interactive/examples/aoc2023/day05/part1.ddp @@ -0,0 +1,21 @@ +-- AoC 2023 day 5 part 1: chain each seed through 7 remapping stages; min. +-- input 0: tagged rows -- (0, seed) | (1, stage, dst, src, len) + +let seeds = input 0 | filter($0[0] == 0) | key(0, $0[1] ;); +let entries = input 0 | filter($0[0] == 1) | key($0[1] ; $0[2], $0[3], $0[4]); +-- (stage ; dst, src, len) + +chain: { + let cur = active | filter($0[0] <= 6); + let cands = cur | map($0[0] ; $0[1]) + | join(entries, ($0[0], $1[0], $2[0], $2[1], $2[2] ;)) + | filter($0[3] <= $0[1] && $0[1] < $0[3] + $0[4]); + -- key (stage, idx, dst, src, len) + let mapped = cands | map($0[0] + 1, $0[1] + $0[2] - $0[3] ;); + let covered = cands | map($0[0], $0[1] ;) | distinct; + let unmapped = (cur - covered) | map($0[0] + 1, $0[1] ;); + var active = seeds + mapped + unmapped | distinct; +} + +export "result" = chain::active | filter($0[0] == 7) + | key(; $0[1]) | min | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day05/part2.ddp b/interactive/examples/aoc2023/day05/part2.ddp new file mode 100644 index 000000000..26d2e966d --- /dev/null +++ b/interactive/examples/aoc2023/day05/part2.ddp @@ -0,0 +1,47 @@ +-- AoC 2023 day 5 part 2: seed RANGES through the 7 stages; min location. +-- Ranges split into mapped intersections and identity holes each stage. +-- input 0: tagged rows -- (0, start, len) | (1, stage, dst, src, len) + +let seeds = input 0 | filter($0[0] == 0) | key(0, $0[1], $0[1] + $0[2] ;); +let entries = input 0 | filter($0[0] == 1) | key($0[1] ; $0[2], $0[3], $0[4]); + +ranges: { + let cur = active | filter($0[0] <= 6); + -- key (stage, s, e) + + let isect = cur | map($0[0] ; $0[1], $0[2]) + | join(entries, ($0[0], $1[0], $1[1], $2[0], $2[1], $2[2] ;)) + | map($0[0], $0[1], $0[2], + if($0[1] > $0[4], $0[1], $0[4]), + if($0[2] < $0[4] + $0[5], $0[2], $0[4] + $0[5]), + $0[3], $0[4] ;) + | filter($0[3] < $0[4]); + -- key (stage, s, e, cs, ce, dst, src) + + let mapped = isect | map($0[0] + 1, $0[3] + $0[5] - $0[6], $0[4] + $0[5] - $0[6] ;); + + -- candidate hole starts per interval I=(stage,s,e): s and every ce + let cand = (cur | map($0[0], $0[1], $0[2], $0[1] ;)) + + (isect | map($0[0], $0[1], $0[2], $0[4] ;)) | distinct; + -- key (stage, s, e, h) + + let csets = isect | map($0[0], $0[1], $0[2] ; $0[3]); + -- (I ; cs) + + let pairs = cand | map($0[0], $0[1], $0[2] ; $0[3]) + | join(csets, ($0[0], $0[1], $0[2], $1[0] ; $2[0])) + | filter($1[0] >= $0[3]); + -- (I, h ; cs) for cs >= h + + let hend = pairs | min; + let holes_bounded = hend | filter($0[3] < $1[0]) | map($0[0] + 1, $0[3], $1[0] ;); + + let withcs = pairs | map($0 ;) | distinct; + let nocs = cand - withcs; + let holes_tail = nocs | filter($0[3] < $0[2]) | map($0[0] + 1, $0[3], $0[2] ;); + + var active = seeds + mapped + holes_bounded + holes_tail | distinct; +} + +export "result" = ranges::active | filter($0[0] == 7) + | key(; $0[1]) | min | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day06/input.txt b/interactive/examples/aoc2023/day06/input.txt new file mode 100644 index 000000000..073fc4d3a --- /dev/null +++ b/interactive/examples/aoc2023/day06/input.txt @@ -0,0 +1,6 @@ +16 18 +20 20 +27 30 +50 47 +60 49 +78 62 diff --git a/interactive/examples/aoc2023/day06/part1.ddp b/interactive/examples/aoc2023/day06/part1.ddp new file mode 100644 index 000000000..f81ebedc7 --- /dev/null +++ b/interactive/examples/aoc2023/day06/part1.ddp @@ -0,0 +1,24 @@ +-- AoC 2023 day 6: product over races of #{hold t : t*(time-t) > dist}. +-- No sqrt/div in DDIR: enumerate hold times by fixpoint instead of the +-- SQL's closed-form quadratic (which the slt itself computes two ways, +-- disagreeing by float drift; enumeration is exact). +-- input 0: (time, distance) + +let races = input 0 | key($0[0] ; $0[1]); + +cands: { + let step = c | filter($0[1] + 1 < $0[0]) | map($0[0], $0[1] + 1 ;); + var c = (races | map($0[0], 1 ;)) + step | distinct; +} + +let wins = cands::c + | map($0[0] ; $0[1]) + | join(races, ($0[0], $1[0], $2[0] ;)) + | filter($0[1] * ($0[0] - $0[1]) > $0[2]) + | map($0[0] ;) | count; +-- (time ; n_wins) + +export "result" = wins + | key(; $1[0]) | collect + | map(; fold($1, 1, ^1 * ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day07/input.txt b/interactive/examples/aoc2023/day07/input.txt new file mode 100644 index 000000000..00fd5e6cf --- /dev/null +++ b/interactive/examples/aoc2023/day07/input.txt @@ -0,0 +1,34 @@ +67AJ5 568 +79Q36 923 +99325 894 +8JK7Q 177 +5J663 919 +82574 158 +229T2 817 +355A6 917 +K43K3 767 +92765 525 +8QKT8 129 +35J28 541 +4967J 132 +784T9 621 +6A6A9 327 +9KAJ4 643 +9T479 463 +6QT5K 932 +Q7T66 738 +4333T 611 +8TJ29 215 +7TT7Q 472 +9T8J6 275 +66Q85 835 +KTA66 697 +TA876 326 +27858 512 +79Q99 749 +AA5QA 457 +792K6 762 +KK6TA 635 +5KT55 349 +TQ495 158 +5J2TK 432 diff --git a/interactive/examples/aoc2023/day07/part1.ddp b/interactive/examples/aoc2023/day07/part1.ddp new file mode 100644 index 000000000..caa9ab5ac --- /dev/null +++ b/interactive/examples/aoc2023/day07/part1.ddp @@ -0,0 +1,40 @@ +-- AoC 2023 day 7 part 1: rank hands (type, then card order); sum rank*bid. +-- Type from the pairwise-equality count: 5oak=10 4oak=6 FH=4 3oak=3 2p=2 p=1 hi=0. +-- input 0: (id, c1..c5, bid) charcodes + +let hands = input 0 | key(; + $0[0], + ($0[1]==$0[2]) + ($0[1]==$0[3]) + ($0[1]==$0[4]) + ($0[1]==$0[5]) + + ($0[2]==$0[3]) + ($0[2]==$0[4]) + ($0[2]==$0[5]) + + ($0[3]==$0[4]) + ($0[3]==$0[5]) + + ($0[4]==$0[5]), + if($0[1] < 58, $0[1] - 48, if($0[1] == 84, 10, if($0[1] == 74, 11, if($0[1] == 81, 12, if($0[1] == 75, 13, 14))))), + if($0[2] < 58, $0[2] - 48, if($0[2] == 84, 10, if($0[2] == 74, 11, if($0[2] == 81, 12, if($0[2] == 75, 13, 14))))), + if($0[3] < 58, $0[3] - 48, if($0[3] == 84, 10, if($0[3] == 74, 11, if($0[3] == 81, 12, if($0[3] == 75, 13, 14))))), + if($0[4] < 58, $0[4] - 48, if($0[4] == 84, 10, if($0[4] == 74, 11, if($0[4] == 81, 12, if($0[4] == 75, 13, 14))))), + if($0[5] < 58, $0[5] - 48, if($0[5] == 84, 10, if($0[5] == 74, 11, if($0[5] == 81, 12, if($0[5] == 75, 13, 14))))), + $0[6]); +-- (; id, t, v1..v5, bid) + +let less = hands | join(hands, ( + if($1[1] < $2[1], 1, if($1[1] > $2[1], 0, + if($1[2] < $2[2], 1, if($1[2] > $2[2], 0, + if($1[3] < $2[3], 1, if($1[3] > $2[3], 0, + if($1[4] < $2[4], 1, if($1[4] > $2[4], 0, + if($1[5] < $2[5], 1, if($1[5] > $2[5], 0, + if($1[6] < $2[6], 1, 0))))))))))), + $2[0] ;)); +-- key (left_is_less, right_id) + +let ranks = less | filter($0[0] == 1) | map($0[1] ;) | count; +-- (id ; #smaller) + +let bids = hands | map($1[0] ; $1[7]); + +-- ranks misses the weakest hand (zero smaller); add every bid once instead. +let above = ranks | join(bids, ($0 ; $1[0] * $2[0])); + +export "result" = above + bids + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day07/part2.ddp b/interactive/examples/aoc2023/day07/part2.ddp new file mode 100644 index 000000000..14a8a64e6 --- /dev/null +++ b/interactive/examples/aoc2023/day07/part2.ddp @@ -0,0 +1,53 @@ +-- AoC 2023 day 7 part 2: J is a joker (worth 1; joins the largest group). +-- Doubled pair-count type: hi=0 p=2 2p=4 3=6 FH=8 4=12 5=20; jokers add +-- (g1+j)(g1+j-1) - g1(g1-1). No scalar let: stage via successive maps. +-- input 0: (id, c1..c5, bid) charcodes + +let stage = input 0 | key(; + $0[0], + -- doubled non-joker pair count + 2 * ( (($0[1]==$0[2]) && ($0[1]!=74)) + (($0[1]==$0[3]) && ($0[1]!=74)) + (($0[1]==$0[4]) && ($0[1]!=74)) + (($0[1]==$0[5]) && ($0[1]!=74)) + + (($0[2]==$0[3]) && ($0[2]!=74)) + (($0[2]==$0[4]) && ($0[2]!=74)) + (($0[2]==$0[5]) && ($0[2]!=74)) + + (($0[3]==$0[4]) && ($0[3]!=74)) + (($0[3]==$0[5]) && ($0[3]!=74)) + + (($0[4]==$0[5]) && ($0[4]!=74)) ), + -- joker count + ($0[1]==74) + ($0[2]==74) + ($0[3]==74) + ($0[4]==74) + ($0[5]==74), + -- largest non-joker group + fold(list( + if($0[1]==74, 0, 1 + ($0[2]==$0[1]) + ($0[3]==$0[1]) + ($0[4]==$0[1]) + ($0[5]==$0[1])), + if($0[2]==74, 0, 1 + ($0[1]==$0[2]) + ($0[3]==$0[2]) + ($0[4]==$0[2]) + ($0[5]==$0[2])), + if($0[3]==74, 0, 1 + ($0[1]==$0[3]) + ($0[2]==$0[3]) + ($0[4]==$0[3]) + ($0[5]==$0[3])), + if($0[4]==74, 0, 1 + ($0[1]==$0[4]) + ($0[2]==$0[4]) + ($0[3]==$0[4]) + ($0[5]==$0[4])), + if($0[5]==74, 0, 1 + ($0[1]==$0[5]) + ($0[2]==$0[5]) + ($0[3]==$0[5]) + ($0[4]==$0[5]))), + 0, if(^0 > ^1, ^0, ^1)), + if($0[1]==74, 1, if($0[1] < 58, $0[1] - 48, if($0[1] == 84, 10, if($0[1] == 81, 12, if($0[1] == 75, 13, 14))))), + if($0[2]==74, 1, if($0[2] < 58, $0[2] - 48, if($0[2] == 84, 10, if($0[2] == 81, 12, if($0[2] == 75, 13, 14))))), + if($0[3]==74, 1, if($0[3] < 58, $0[3] - 48, if($0[3] == 84, 10, if($0[3] == 81, 12, if($0[3] == 75, 13, 14))))), + if($0[4]==74, 1, if($0[4] < 58, $0[4] - 48, if($0[4] == 84, 10, if($0[4] == 81, 12, if($0[4] == 75, 13, 14))))), + if($0[5]==74, 1, if($0[5] < 58, $0[5] - 48, if($0[5] == 84, 10, if($0[5] == 81, 12, if($0[5] == 75, 13, 14))))), + $0[6]); +-- (; id, pc2, j, g1, v1..v5, bid) + +let hands = stage | map(; + $1[0], + $1[1] + ($1[3] + $1[2]) * ($1[3] + $1[2] - 1) - $1[3] * ($1[3] - 1), + $1[4], $1[5], $1[6], $1[7], $1[8], $1[9]); +-- (; id, type2, v1..v5, bid) + +let less = hands | join(hands, ( + if($1[1] < $2[1], 1, if($1[1] > $2[1], 0, + if($1[2] < $2[2], 1, if($1[2] > $2[2], 0, + if($1[3] < $2[3], 1, if($1[3] > $2[3], 0, + if($1[4] < $2[4], 1, if($1[4] > $2[4], 0, + if($1[5] < $2[5], 1, if($1[5] > $2[5], 0, + if($1[6] < $2[6], 1, 0))))))))))), + $2[0] ;)); + +let ranks = less | filter($0[0] == 1) | map($0[1] ;) | count; +let bids = hands | map($1[0] ; $1[7]); +let above = ranks | join(bids, ($0 ; $1[0] * $2[0])); + +export "result" = above + bids + | key(; $1[0]) | collect + | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day10/input.txt b/interactive/examples/aoc2023/day10/input.txt new file mode 100644 index 000000000..5a3144ebd --- /dev/null +++ b/interactive/examples/aoc2023/day10/input.txt @@ -0,0 +1,56 @@ +SJJL|-.LFSS.S-|7-L-L-L|F7FS|J.LL-.|J.L7SJ|.J-7F..-S7|7SFSSSJLLJ|-..L..J-L7JJS.J-7-LJ-|.J7||.LF.7.7SF7LL-.|7-7F77|LFJFSS77JF.S.|.-F +J..-.--S|SL.|S.7L7-|SFL.FJF7L-LJ-SFL|L|--F.-FS|7.LJLJ.-.|LF|J.L-JJJ7F|S..FSSSLL-F.-FFL7-7LLLFJ.LFSSJFF7F.7F|F7LJ|F||L7.7.7|7-|J77S +JL7|JSJ|FLLSLFF|-7JSJSJFL|L.J|||J-S.S..|-SF-|----JS.-J|JJ.|-F||.FL..JLL7.-LJ7FSJFL..|F7JJJ|LLJFFJJFLL7.LJJJF--FFLS-|LLLS-..-|S|J7F +|JFF-L|F7|.F.L7J-7F-.LSF7J-|S7|FLS--JF.SF.S.S.LSLL..F7.SS7L|7J|.---|7JL|7SLL|SS.JLLL|L.7..JL||JL|JL.S777.|-JSSL|S.7JJFLJ.SJ-.|L|FJ +7|L-S7|JFL.SJ7FL|.LLJ.F7SF-.-SL77-FJ7S-J|SS-|L-J|J.7F|.7.S.LS-|.JJJ|.S|.|777-LJ-.J.7-LSSL|SJ.J.-F.-LSS-SJ.LL|S7|FJL77.FFFJJ.-LS-|- +SJ.-|..-LF7-FS.S|7F|LJ--SS-SFFF||S7|FF.7..F7|JFJJJ--7JLS||S7J--J-JF-|7SFJ|7.||||7LS|-..FJ.|S77JF.F77L.|.-F.L.-J-FL-JFL-|.S.LJ|J.L7 +FF7F-.7.-LS-FF.--LL.|.7L-J7FS-.F--S7.LJLL.-7FF|7J7L..S7LJL7-JLF-S7JJ|-LS7S-|JLF7LFSJSF7L-F.7S-|7S-.F||.J.-F|-SJL|SFF7|-|F--||7LJ7F +S.J7L.F|S-.F|FSJ|--|L.JS77JL7.SS7|FJFFF-F|.SF7LL.JFL7SJJLL|LLSS.L|F.|F-|S|JJ--FJLFJJ7..77.S|LJFJJS.-7S-LJ|L|S-J|FLLLJFSSFS.J-FS|LJ +SF.77J-L-7|FF-J-77-L-F|J.|F.J7SJL..SF||S77.7J|J.L-|-7L-JSJ.S.-FL-F7FL|SSFS7-|S-.L.FF-L-F..J-FS77.JJ-SF7SS--.JLLSJ.F7LS.JS|..J|J|7S +J|L-7|77|-F-SF7-..7|.77L7.LJLLL..LSJFLJ|-|L7|-S||||F|LLF77-LF||-|S.JL.|.LF7S7.FFF--SFF.-J-F.F7JJ.7JLJ-7S-LLSFLJ7.-JF.SSLLSSSJSSF7. +.|.L.|.|FJ.L||.77L-SJ.|F7-.L7LJ7LJ||.SJF-.||SF..L|JSL.-S.SFLSJLJ7S-FFFJ7F.J-L...|LFLF-SSLS7F|7J.-.F||.|L|JLJ|LJ...FSJ||---7|.--LF. +LFJ|7L|S||..J|7S..J--.77F7-7F.S-J777.||FFL|.|-FJSSFFLSJ.|J-.JS-FSS.|F7.S.SJ-JJS.-S|LJJ-JLJS-7SSF7JS7.|J77-JS7J.F|JFFF|S-F||SS.|LFF +FJ-|F7.F-LLF-.J7LS|LJSSF|.7S||-F|7S.J.|JSJLLJF-LS|SFJ7LL7SSS7S.S-|.S7||SLFSLFL|.LJ-S77|L|F|S-.7F7F.JJ-FL|J7S.7J.FJ7S|JJ|L7F7F|S|.| +..JF.-L|JFL-FS|FJ77..S|-SLFLL7||SL|-L-.|.7JLF7J--7J-7S7L77JLJFS.J.7..7S7F7|S7|.LFLJS-.L|LS7FJFL-L-L-7S77-LJ-.JFFJ.J7L.FJSJLFSJ|L7F +FFLS|-S.|F|SLFL|SF|JLS|FFFSJLJ|F.J|LSJJ7FS|LL7-.J-LFJ|7JJ.7|J.-JF.L...LS7LFJJF7JJ.L...|S..-7.-S.JFLLSLFS|JFJ-.L|L.L7FSJ.|LFL-F7F.7 +SJ-7LS7.-FL|F.FJ|7|SJL7-J77L-FF.F-|7-|7JL.SF7|FSL-J|.LL.|.7LSJS||7SL7.7.JSFFJFS-7.LLLS..7L.SFFJ--|-SFF7||SJS-7|-|J-.SSJ|.77.|-L.F- +.LJJ-L7.---.S-7.7|FS..SJ|SLJ|FS|SLLLFJ7.F-F7LL7S-S-JF.7F7LL-J-.|L|-|-.L.7SSS|SL||-|JFFL.J7SJSF.F.F7S|FL--LLJ-L.SJ.LLJLLFS7J-.|FJ.J +JSFLJ|.JJFFLF|F|LJ||L7-J7JS.|LL7JSFSJJSSLFJ7|L.FJJLL-FFLSS.FJ-.SSSLLJL777FL.-..7SF-.-.SFF-.J7F|JJS---L|.L|-.FSJ-||S-LJFS.S7|J-7J-S +L77SLFJ7FJ.|-|.|7LJS7LL.-7.LFJLSFJ|S-FJ..LJ.7-SJ-|LJ77|JFLSF|.SJ.|L--L|S7-SS.-.F-JS--|SLS-SS-F77LF..JS-FJF.SJ7.FJ|-SF-LSLL||J.|-.7 +.-S|S--FSJ--7|F7-L|-SS-JL|FLFFJL7-FJJSJS7SJL7.SS|S|.LLSS7FS-.J-JSF.|777--7.|J|7S|.L|L|SL|SSF|7L.F|JF.FJ--.J-|S.S.SSS..SL-|S.S.L|-J +7JSJ7|J7--LSS|7SLFSJ-.SF-|77||....7.L7J|7|7|.-.LJ..LSJ7|SFLSFJF|JFL7F|7|LJFJ7|LJSSJ.7-|7JSJL.J|F.|.J7-F7-F|-S.-LS-.-JS.SSS|JLS||F- +SJL.LL7JJS-S|-SF-LS.7FL-F7L|F7.SFFF7J77-SLF|.LFFLLSS7S|----SLL7FL7||LFFSJLF-.|7F--JF7..JL---S.SSSJF|SLSJ.-L|LFJJL-7S-7FF--SL||FLSL +F7S.|7SL-|.F|.L.-.7L|LLL-L7SJL-JJJ|J-FJLSLF7F7JFJ7.FL|7J.7F.|.JSL.|L7S---.F-.-.FSL|LLSFLF|7.7-|J7-|-7..-SJF-|S|J7LL7FSJ|F.7JJF.7|J +S.L|SL|JF..SLJL|S.F7-||77LLFJ.S|S-LL.J-7|FF-SJJJL|J.FLJLL7JFFJ..FFJLSSSS-|-77-S-.F777--|S.---.F-SLL|S7S7SJLSF-S.-|LJ7-L--7..-|.7L. +F.|7.-JL|7L.JFF|LLLJF-S7F|LF-7LL|7|.J-S..S|FFJ7.FSL.|-LJ-|JLLL.LLL-7-F-|L-L-|.77SJSF-SF7.SFJL|.F|.SS..FL7L.7FSL7SL7SJSSFSS|SF.-F77 +.SLL.JL|L.77S.77L|SJF..FL|-F.S-SS.7SS-LLFS|L.S7J7L|F|L-7|JJ-77SF-77|LFLSF7LJFS||LJJ|J7.-LFF-L.|LF.LS|J-.LJ|J.L|--J7.S.-L.-LFF7F77- +--J-.SL--L7JJSFJLJS.FL.7-F7-SJS.L.S.LF|7J-|77FS|-J-|S.FSLS.7.J|J.LLF-LL.S--7S.-LSS|JLJF.S|.F7SLSS.L-L.|F-JL|LFS-S.J77-L7.|7|S|.S.| +7SJS-|F-.F...LFSJFLFLFFFJ||JL--J|S.LJ-LL|7S-FF-J.7F.JJ|L-.JF7J|L|S-.LJ|S.SFSLLS.L7SJFSJ.SL--L|F.S.SLS.|S.-..7F7|LSF|SJFS7FJ7S|J.-J +7...J.L--7S-SF.LSJ7JLJFL7LFFLJ..|FFL-7FSJ|S|LLL.FL7-LL|-JFF-||L-JF7FLJJLL-|S...S.|L.-FJF.FS-.7.JJ|.L7J.LSSS7.S..J-7|-SFSSF7S..SL7F +LJFFJJFLS..|.L7J.-S7-.-|--|7JJFL-.|JF|.S.-.J|F7|SFSF|-L-SJLL.-.---L7FS-LL7|FS7L.J|||S-F|SJFL7F7|-L7L.SS.FL.LSF-7|F.F.|7FJLJF7L--SL +-J-S.F--.SFF..L7JJ.FSLJLLSS7F7JJJ.JLLLF|.7SFSL7L|-FSS.|7LS777LL7--.7F-.L|L.-F-7..|LS-J.LJL..F.S-J7-7LL7-L7LF|LS.J7LL|77S-F.S.|.7FS +J|LJ7LS|-.|7L777|-|LL77JF--7LSF|L||S...-.JLS.|..7-|LL7S.JF.-.JFJF7JFFL.|S-7.J.7.JJ-LJL-7LF--LL|J.L|-LL7L-FF7-.-SL.S7.LF.|JFS--7.FS +7-L7SSSL7S|S|-L|.7.S|-7.SJSF7|S7J7|JLLSFJS.FL.L-LSFF.SF-7-7-S||S.JLFSLS-F-S|LSFS.JL-77|7.7-7.|FJL|SL-.JLLS-J|.SJF|-|LFSLJF-JFSJ|F. +SLL.-JJJLFFSFF|JJLF-|-SSFL7.|-FF|-J7.SS|FS7J7.S7.7|-FS7L-.SJ-|FF.7-|SF..7S7F-S-S||L-7|SFF-.S|--|7S-L7JJLFJ..|.SJFS.7J---L--.S|F-7J +.F.FLJJ|J|JF|LJ-SL.|77FJ-LL|S-JJ7F-LLSL-SFLLFJ7SS|JJFFF7JFF-J.-..|7F-7|-JFF.FJJ.L.|J|SJJ|..J||J.J|.|7-S---F..-SLS.7.7SFF-L.7.S||-7 +JF-SFL.S|LL7FF-.F...JSS||.7-JFJLFS.J|7|S7SJ|-J.|-|L|JS.7F-7F|F|.|JSJSS7FLLS.S7--L-7.--SSLJL||77-F|.FJ7|7JS|F7JJ-L-J-|JSSSF..LF-LJS +L-F|F77SFS7JS-JFS77FF|SJFFLJS|LJ7J-.L.L--FSS77S.J.7FLLSLS|77LFLJ|7|L-7..-F77S.-S--F..|JLF|-7L.S7J|F|-LJ.FJ7|.7SSL.7JS.-.L|-FLL-JF7 +7F|FF-SLLJL|||FL-F.77L.L-J.J|F-||.7.-S7F-7F-7.|LSS7LL.SJS--L|L-F.JSS-|7FSSSLJ.-|L|7F|F-LS.J-LLL-SLFF.F7SFSJLJ-S.LLFJF|7JLF.S7-F--F +-L7J-FL-L|...7.SL.FSJ||7--FF7L7.J-FSL-SFL.F|7|||7F.-F..--L-S--JLFJ7J.|7-LJ.L|L..L|.7|7.|S7FLLJS-J|S7|JJSFJJ.7JLJ7FS-L7S7FSSF-L-S7| +.J|.JJL7--SS-S|L-LLLF|S7JJ7L-F-S.|-|7FL-.7SJ7S.FSF.|S7|-|L.L..-L.-LJL-|7-7L.7S7F|77JL77SL7|JLLF|LJS|LJFL-FSL.F.-JSS7-FF|S.||..S7SF +JF-77L-L.FS-SLLJFSL.-7|S.L7.S.JLS7SJ.7J7|LJJSLSJF.-F|SFFS|||F77-.-.J||7FLJF--7LL|L.7.77SF|FJF-LJFJS|-FJ|L|L.7JJ|F..|L.LL..J-|J-|.L +7.|L-.-SFFS||.-|JLLLJ7-7JF|7F||SL-FS-J7SSJFJ--|J-SJ-|F||JL7F|F|J.LJS-JJ-S|JS.J.|.|7-F.7|LS--F7-.F|JJ.LS--.-|.J|J|SF.FS-JS.LLLF7S-F +SFL.FJF7S.JF7JS7--|S.JJ7F--SS7L|SJ.J.SJ|SJ77-SLSFJJ7|-77-S7L-7S..-.L.JS.S7FFF|-77.SS-LF|LSFLFLJJL|FL.-LL|J-JFJ7-.L|.LSJ.77LJFF7L7. +.L-F-F.FS7.F7JL7.7J7J|J|SFSJ7LLSSFJ-FSJ-.7.SLFJJF.FJJ|7.L777J7.S77...7|SFS-.L7.FS.J.-S.FJS7SJLJS-J|LJ-.J|L--F|S||SLJFJJJJSSF-L.FLJ +|7.SS-FSS-FL||SL.7L|7.7FL.77S|F.S-.LFSS.SJJ-77JFL7F-SS.SLFLFSJ|.LF7SJJFJ||F7F|SLF.||JSF-J-..-77S.|.S||F7--7FL7LLS7||-.7|JJ-.7FLS-F +7JL-..7.L.-SS.J7FS-L-L|F.JLF-.|.7.F-SL.-7JF.S7-FSJ-|F.LJ--FJ.SLF--|L7.F-L7-S-77.S.7.JFSLJ-FS77JJ|||S7J-JSJ.|-|F7L--L7S|LF-J|LS..-- +F.-JSF-.SF7LFF-F7-.|.77SF|.SFL.-S|L7-7.--F77SSSL-FSLF7FL7|SLFLS|FF7L-JJJL7.J-|-|F-.-L7-.JJ|F7.7-F-L.LJ7JJSLJFL.7|L7LS7|L-FJ7L-LJJS +|-J7.L---.7-77-7FLF7.JJ-LS.|-S|-S7JS7SJ|.J|L..SJ.J|7SL.7|.LJ.S-7F--.7S.J-.SS7.7SS7-SFLJ|SJ-|L7|S.FF7S-7S.SF7S.|JL-S-F|.777FJSFL77J +7JFF7-|S.J.FSJ-.L|S|L.-JJJSLS7F|7J-7FLS7SF|.|J7LJ-|S7..7SJL...SS7J.|L|SFJ.7F-.--F.777J77L.F|-L-|.7SS..7JL-S-L.J7FFFSSS77-S..L|..L| +J7JFL-||F.S--LFLS7L|.L.SJL.-LFFSJJ.7FJL|SJ-JJJS7|LSL7.F-J7.-|L-J.|.JL77J7J.|..FLSJ.LLJ||LLL|.SF77S--J-.-|S7F||77F|JJLF|-L..|-|7--7 +.|7SJS-....7LJ.7L-SF--|L.LLFJSFF|.JF.-LFF7LJJ7.JJ..7L|-S.FLJ7-F7S-||J77SFJ.LSLLF|.F-F7-|FF7FJ.7LF.L-J-.F7.F7JFFS|--L7-.-S|7-LF|..S +7F7.JLSJ7|7L.FJ|J7JJLF7JJFJ7.L-J-FJFFLL|.LFL-L|-|J|-||J-F-.JF-|7J..7F.S|J7.SFJS7.JLLL-S7SJSF-77JL-LF77L-S|FS.JLJ7SLF|FJ.F|-S|L|-JF +.J-.|J--F--J-77-|7-JJ.7SJSJJ7|F|.FLJ.7JFJ.J-L.S|-7|-J7.FLSS|J-|--.JS7-L.7L7S7JSJJ||-S-L-7J|SLJF7FJSS|.L.F-F7LL.F7.7-L.7SL|.777|J77 +J7-7.|S-LS7|FJLJ-SF||S7777-|.77FSL|SFLFF.SS7|L-.JFSJ7.|L|LJF|J|7-S7S7-FL.FJJ77|SL|J.SJ-|FL-|7.JJS-S|SS|L-7LJ-S7-J-F-JLL-|LSJ|JLJ7. +S77SS7J.JFFL7|S-L.SSFL.S|SJ||JJ7.SSJS|LJ--.J|-.-S7JLJJSFJ-L-FS|SL77JLJ-LJS|FJL---L|.S7LF-JJ7-|S|....SJLJSJ-.LJ.FL7.FLS.7L.-LJ-S7|S +FL|JJF|FFFJ.JJSL--F.FJFJ-FLLLJ|F|SLLSL77JS7L.777--JFJ|F|JS|.|.-|7L..L-SL7-7JSF-|.J.J--S|JSS|S.F|.L7LSSFSL-.F|F|7S--JJ|S-F7F-7LLFL| diff --git a/interactive/examples/aoc2023/day10/part1.ddp b/interactive/examples/aoc2023/day10/part1.ddp new file mode 100644 index 000000000..8da57f94d --- /dev/null +++ b/interactive/examples/aoc2023/day10/part1.ddp @@ -0,0 +1,34 @@ +-- AoC 2023 day 10 part 1: loop length / 2 from S through mutual pipe links. +-- codes: '-'=45 '|'=124 'F'=70 'L'=76 'J'=74 '7'=55 '.'=46 'S'=83 +-- input 0: (row, col, code), 1-indexed + +let symbols = input 0 | key($0[0], $0[1] ; $0[2]); +let pipes = symbols | filter($1[0] != 46 && $1[0] != 83); + +let e1 = pipes | map($0[0], $0[1], + if($1[0] == 124, $0[0] - 1, if($1[0] == 70, $0[0] + 1, if($1[0] == 76, $0[0] - 1, $0[0]))), + if($1[0] == 45, $0[1] - 1, if($1[0] == 74, $0[1] - 1, if($1[0] == 55, $0[1] - 1, $0[1]))) ;); +let e2 = pipes | map($0[0], $0[1], + if($1[0] == 124, $0[0] + 1, if($1[0] == 74, $0[0] - 1, if($1[0] == 55, $0[0] + 1, $0[0]))), + if($1[0] == 45, $0[1] + 1, if($1[0] == 70, $0[1] + 1, if($1[0] == 76, $0[1] + 1, $0[1]))) ;); +let r1 = e1 | map($0[2], $0[3], $0[0], $0[1] ;); +let r2 = e2 | map($0[2], $0[3], $0[0], $0[1] ;); +let scell = symbols | filter($1[0] == 83); +let s4 = (scell | map($0[0], $0[1], $0[0] + 1, $0[1] ;)) + + (scell | map($0[0], $0[1], $0[0], $0[1] + 1 ;)) + + (scell | map($0[0], $0[1], $0[0] - 1, $0[1] ;)) + + (scell | map($0[0], $0[1], $0[0], $0[1] - 1 ;)); + +let symm = e1 + e2 + r1 + r2 + s4 | count | filter($1[0] == 2) + | map($0[0], $0[1] ; $0[2], $0[3]); +-- (r1, c1 ; r2, c2), mutual links only + +reach: { + let next = r | join(symm, ($2[0], $2[1] ;)); + var r = (scell | map($0[0], $0[1] ;)) + next | distinct; +} + +export "result" = reach::r + | key(; 1) | collect + | map(; fold($1, tuple(0, 0), tuple(1 - ^1[0], ^1[1] + ^1[0]))[1]) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day10/part2.ddp b/interactive/examples/aoc2023/day10/part2.ddp new file mode 100644 index 000000000..ddd3e7fa3 --- /dev/null +++ b/interactive/examples/aoc2023/day10/part2.ddp @@ -0,0 +1,58 @@ +-- AoC 2023 day 10 part 2: cells enclosed by the loop. Diagonal parity walk: +-- Enclosed(r+1,c+1) = Enclosed(r,c) toggled by loop pipes J - | F at (r,c). +-- (The slt resolves S to '???', which never toggles; we mirror that.) +-- input 0: (row, col, code), 1-indexed + +let symbols = input 0 | key($0[0], $0[1] ; $0[2]); +let pipes = symbols | filter($1[0] != 46 && $1[0] != 83); + +let e1 = pipes | map($0[0], $0[1], + if($1[0] == 124, $0[0] - 1, if($1[0] == 70, $0[0] + 1, if($1[0] == 76, $0[0] - 1, $0[0]))), + if($1[0] == 45, $0[1] - 1, if($1[0] == 74, $0[1] - 1, if($1[0] == 55, $0[1] - 1, $0[1]))) ;); +let e2 = pipes | map($0[0], $0[1], + if($1[0] == 124, $0[0] + 1, if($1[0] == 74, $0[0] - 1, if($1[0] == 55, $0[0] + 1, $0[0]))), + if($1[0] == 45, $0[1] + 1, if($1[0] == 70, $0[1] + 1, if($1[0] == 76, $0[1] + 1, $0[1]))) ;); +let r1 = e1 | map($0[2], $0[3], $0[0], $0[1] ;); +let r2 = e2 | map($0[2], $0[3], $0[0], $0[1] ;); +let scell = symbols | filter($1[0] == 83); +let s4 = (scell | map($0[0], $0[1], $0[0] + 1, $0[1] ;)) + + (scell | map($0[0], $0[1], $0[0], $0[1] + 1 ;)) + + (scell | map($0[0], $0[1], $0[0] - 1, $0[1] ;)) + + (scell | map($0[0], $0[1], $0[0], $0[1] - 1 ;)); + +let symm = e1 + e2 + r1 + r2 + s4 | count | filter($1[0] == 2) + | map($0[0], $0[1] ; $0[2], $0[3]); + +reach: { + let next = r | join(symm, ($2[0], $2[1] ;)); + var r = (scell | map($0[0], $0[1] ;)) + next | distinct; +} +let loop = reach::r; +-- key (r, c) of loop cells (incl. S) + +-- loop cells whose ORIGINAL symbol toggles parity (S resolves to '???': never) +let toggles = loop | join(symbols, ($0 ; $2[0])) + | filter(or(or($1[0] == 74, $1[0] == 45), or($1[0] == 124, $1[0] == 70))) + | map($0 ;); + +status: { + let cur = st | map($0[0], $0[1] ; $0[2]) | join(symbols, ($0 ; $1[0])); + -- in-grid status cells (r, c ; e) + let tog = cur | join(toggles, ($0[0] + 1, $0[1] + 1, 1 - $1[0] ;)); + let matched = cur | join(toggles, ($0 ; $1[0])); + let nontog = (cur - matched) | map($0[0] + 1, $0[1] + 1, $1[0] ;); + var st = (symbols | filter(or($0[0] == 1, $0[1] == 1)) | map($0[0], $0[1], 0 ;)) + tog + nontog | distinct; +} + +let enclosed = status::st | filter($0[2] == 1) | map($0[0], $0[1] ;); + +-- pipe = reach minus S cells with no symm out-edge (slt keeps those countable) +let ssrc = symm | map($0 ;) | distinct; +let scellk = scell | map($0[0], $0[1] ;); +let isoS = scellk - (scellk | join(ssrc, ($0 ;))); +let pipe = loop - isoS; + +-- set-minus needs an anti-join: bare `-` is multiset subtraction and lets +-- non-enclosed pipe cells cancel unrelated enclosed cells in the count. +let enc_on_pipe = enclosed | join(pipe, ($0 ;)); +export "result" = (enclosed - enc_on_pipe) | key(;) | count | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day11/input.txt b/interactive/examples/aoc2023/day11/input.txt new file mode 100644 index 000000000..44cfb3997 --- /dev/null +++ b/interactive/examples/aoc2023/day11/input.txt @@ -0,0 +1,57 @@ +#?##..#?.???..##??...??....#....?....#?...#.?..?.#...#....?#..#..##...???.##?...?#####.....##..?.??.?........?.#..#?##..#.###??......??.?#.. +.#...#.#?.#...?.#??..##.?##?.#......##.?...?...??#.?.?..?.###??.#.......#...?#.##.##.?#.#?..##?#.###.?..#.#?........?.??##..#?....##...?..?? +?.#..###????#.?.#....?#.?.?.?.#.?..?.#.#.#.?.#.??..?.??#????.?.#.#.?...#.#..?#?#?.#...?..#.#...?.?#?.?...#?..?.?.?##.??.?.#......#.??....##. +..#.....?.??..#?...?##?....#??.###??.#.#.#..?.#?..?##..?.?##..##.##?.#?..??#?..#.?.#....#??.?.?.?.??#?#.#...?.#?.??.#??.?.?#.#...?...??#..?# +#.??..#?.?....#.?.?.##.#...???...??.#.#..?..##...?.??.###.???#..#??....?.??.?#?#?...#??....?#.?#?...?..#..#.?..?.?..#.#.#.#...#.??..#.#.?#.. +..##....?....#.??..?.#..?#.?#?..?#?..?....#..??....##?#..##?.#.?.?..??.#......#...#.?.#.#..?...............###?.##.?##..#???##??.?.?###...?. +#.#..#####??#??##.###...#.#.#.#.####..?#.?.##...#.##..#.#..#.?.#.#?.#.#...?....??#.#..?#.##..##?......#?.#.#..?.......#?.?.....#..?.#.#??##. +.##.?.##..?#.##..?#.#....#...##.?.#?#...##??.#?##.?..?#...???.#..?.#?......?.#?#..##???.#...##.#..#?..?.#...?.?..##..?.??##?..?#??..#..?#.?? +#.#.#........?.???..#.###?##.?....?###...#?##?..##..#?.?...#...#??.??.?##...?...????....?.?..#...#...#...#.?.#.....??..???..?#??..?#.#.?.#.? +?.?...#..?.#.#..?..###?.#?#?.##.??...??#...#?#.???.?#?#.#.#?...#....#.?.##...#?##..#..?#.#...??#.#.?..?#.#?#.....#?.......??#.###...#?#..... +..#?...#.#?.#.??..?.?..#.#...?#..#.#......?..#??#...?..#.#...#?..#?.#.#.?.###..????#?..?.??.#..?#?..#........#....?###...#?..#.?#.#?.#??.... +.##..#?##..#?...#?.#.?.....#..?..?#.??..#..#.?...?.##.?.#..###.#?.?#.....##?.??#..?..#..??##?..#?#...##.#.?.?#.?..#...##..??.???..??..#.#... +?.#..#.#?....?.?.?.#.?.#.#?#.#......?.#?....#.#.#.?#.##....?....#....#.#?..#.#.?#...?.?..?..###?.?.?.###..#..??#????#.....#...##.?#..#???.#. +?#..?..?#.#.?.?...??.#.......#?#.?..#..?.#.??.#?.#.#?...##..?..?#??.#???......?.?.....#.?#.#?#??.?.##..??.??.#??##?.#....?#.#.#??.?.?.?.?#.? +....#.#.??.??.?..###?#.?..##.???.?.#?#....##?...##..#.#?#.##?.#......#.?#.?.#..??#.#..#?#..?.........##.#.??.??#.##.##?.?#??..#.?#.##.?.?.?# +..??.###....?#?.???..#.....??.??#...##.?..#...##..#.#..?#.....#.???.....#.#??...#......?.?.#.#??##?.#.??#..#...????.#..#.?#..##?..?.?.?..#.. +#?.....#.???#..##.#?.?#.?....?#??...?.....##...#.#.#.##?..?..#.#....??.?#?.?#..#?....#.?#???.#?#..?.#.?#.???..?.?..?.#.?#?..###??.?##..#.### +????#.##....?.#.??.?....#?..?#...#..#???....#.##..???.#..##?.#.....#.??.#.....??..?....#....???##??....#....?#.?#...?..?..??....?..#.##.???? +..?#?#??..##??#.....?.?#...??.?#...?#####?.?#.?#?##.#.?#?#??.??..?.?...#.??..#...#.#..##?.#..##?#.?.#??#?.#?.??#...#??.#.?#..??.###.?.#..?.? +..?#??.#?.#.?...##.#..#?.??#?..#...?##.#?.#?...##.#..###....?..#.....#?##.??#.##???.?.###...##.???....?.??.?#?.#?.#?.?#.??..###.?.#.?.....## +#..#.....#.#.???.#..??......?.?...?#?.#?.#??.#?#?##..?..??##.#..?..##.#.....##?.??.#..#?.??##?#??.??#??#.#?....?#?....??#..?.??..?#?..##?##. +..?..#??.?.?..?.??....#..#?.???..???.......#?#.?..?....##...#...#..?..?#...?..??.....#.#??#?.###..#?.#.#?.......??..#..?.??..#...##?..#.?..# +?.?....??.#.???.##?.#.?.?.??#.?.###.......?..#...??.#...#.#.?#.?..?...???.##..?.?..####?#....?.#..?...###?...??........#???##..##..??#.#.??. +?#.#?#...?.?....?....?...?.##.....##.##?.....?#####?..#?#.??#??.??#.....?..##..#?.#.#.#?#..#??......#.#?.###.#?#?.?.#.....#...?.#..#?....??. +#?...??...?..?#?#......?.....#.????#?.#?##???.#.?..##.#.##.....?..?.##.##.......?.##?......?#.#?.#.##...?..?#??..#..#..#.#?..?#...??#.?#.?.# +#..?....?#?#......#...#.........??#............#.?..?#.#....#.?#..#.....#??...#.?.?#??..?..??.#.?.#.....#?....?##.#?##..#?#...?#.??.?.??..?. +..###?...???.?#.##???#?.#?.?.?#.#.#.##.?#.####.#?.???.....##.?...#.##..?....#?.?#.?...#.##.?..#..??#..?#..#?#...?..?..??.....?...?.?...?.#?# +#??#.?##.#.?..##?..#.#??#????.?#.?....?...#....??...##?.#.?#?.?.##.#???.?#.?#?..?#.#.#?###?.....##..?..?.#?..?.?.#.#..?.?..?#.?##..#.#..?.#? +.?.#.....##..#?#.??...?...?....????.#?##?....#.#.#.?.#??...?.?.##.#.##.?...##.#???#.?.?......???.?.?..?..??..#.?##..###?.##.##?##.?.###??..? +??.#..###..??#.#.#.#.??.....?#??...?...?#.?...?#.#..#?#..?..#?.#.?#..?.#?.#.?#..#?..##.......??.?##?#??#??....#.......#.#...?#.?.?....?#..#. +?.?#..##?..?....??.#..#....#?....?..?..#.#.#.#?...#....##.??#.##.?.???#.#...?.....?.#...?#..?...??..#.#.?..?........###.#..##.?..#.#..??.... +.....?.??#.?..#?#.#?.??........#????.......###.......?#??.##..#?####...?#.?.?.##??#?...?#.#...?...?...##..???.#.#.??????....#..#.?.?.##.?..? +??.?.#?..#.??.#.#.#.??.#.#??#.#...?...##......?...?...##..?.#?.?#...?..#....##.#?.#..?.....?...#?.?#.?#...?#??#?...??......?#?.??#.#??...?.? +#?.###..#.....#?....#.##...#.##.....#.#.#.#..##.....?..#.....?##...#?.??.......#??##?..###??....?.#?......#.??..#??.#?.#.#.###??.#?..?#?#..# +?.?....?.#??.#....?#?#?.?#.?.....#??#.??.?#?#.#......?.#?...?#..#.....??#.?....??.##...?..#?.#??.#.?.??....#?.?.#...##.?#...##....#.#??..?#. +?#?.?....?.##...??...#?##.#.????##.#..?..?..#..##.??#?.?.??#...?.?...#..?..#..??.?.?#.#.#??..?.#......#...#?.?...?#?.......#.???.#?.##..#?#. +..??.#..??#####.#?.........#.#.?####...?#.?.#??.#??#..?##?#.??#....?.??...?..?..??.#...?..????..#..##?###...#.??.?##..#.#..?.#..##?#.??.?#.? +?.##.?.#.##?.?...#...#?##.#.?.#..?#.?.??##?#??.?.?.#.???...??#.##..?#.??#.??....?..#?..##?#?##???.#..#??.?#???...#?##..#.#..#......??#..###. +###...#...??.#.?..#?..?...#.#?.?.?.?#....#.?#.#..####.?..??##?....?#.?.....#...?#??....??....#.????.?...#.??.#..#..??.?..##..#.?..?..##.#?.. +#....#.?.##.?.......#...##?..#...#.?##..###?..#...?....?#????###.?.....?##.##?.#?.###?.#???#..#....?...#?.....#...?..#.?.#???#.?#.??.?#.#.?? +#...#??......?..#.......??.?..#....#.?#.#?..#.##..??##..#?#.#?...#...?....?.?.?...#??#?#..####...#..?.#?#..#..#.?.####..#?#.##?#.?#?#.....?. +...#?...#...?#?.##.??..#..#?.?.....#.#?#...#.......#..#....?.??...#???.?...?.#??##.?##?##.#??.#?..##??...#..?.....#?#..##??..###...??.??#?#. +.........?.#.?.#?...?..?.??#?#.?.#??......#..#.#.?#?.#??#...#?#?.#?.#..?#...?...##?#.##..?.##......??.?##.?###?.#?##..#.?#?...#..##....?##?# +#??#.##.##..#..#???.?.?#.#...##?##?#.#??.?.#.#??.#.#.#.....?#???#??#..?.#.?..??..?.?....?#.....#.?#.??.?.????#..?.#.??###.??#?..#?..??##..?# +#.#??..#.?....###.....?#.....##?.#?.?.#.??##...?#?#.?...#..?#.?...?#..?#?#.##.?....##....#..#.##?.??..?.?.?#.?###.?.##?...#...??....#.#..#?. +?..?..?.?.????#???.??.#.???.??..##..#..??##?.#?..?#??##...?????.#?..#...#..?#...?..?#..#.#....?....#.#..?..?#??#??##....#?..?.???...#.#.#.?# +..##?#.#....??...........?.##.....?#?..#..?..#?.?.?...#?#.?###...#.#.?#?..?.#.??#?##...?..?........?.#.....##..?#..?..#.??.?...?.?###.#?#... +..?...#.#?#..#?..#..#..#...#..?.....#.#...#..#?.?.....?..??...#.?.?......?.#?##..?#..#.?..#..?#??...#.#.....?#...?.#...#.#..#.#?.#?.?##?#?## +#?.#...##.?.#.####.??.#?.?#?.#...#.?#.??#??.##.#.#..#...#.?.#.#.#.###....?.??.?......?....#????..#.......#..#.###.#.?....?#...#..?.#...#.#.. +#...??...??..?.#?...##.?..?#.??.?.#.#..?..###?.????#....#..?.?#.#.#####?##...#.?...??.??...#.#?#.#?..?#..?#??.##.?.?..#....?.#..##.?..?...#. +?..#?##???..?.##??.?....##.??..?.#?....?..?..?....??#.#.??.?...?#?....#.?.#?????#.?#..#?..##...?????##.....#.#.####.?#.?...#?..?.??##?..#.?? +?....?.#.#?..?.?...#..#.#?.#..#...?.#.?#.?.#.##??..#..#?#?#.#??..#.??.?...?.?..?##?...?.?..##.?..?.???...?#.##?#..??..#?#...?..?....#..#.##? +#?#.?#......?.#??##...?#.#.##.?...?...?...?#..#?###.##?##..?.#?#.?#..#...#....#?#.#??...?#.#?..#?##?..??..?..#?....?#.#?..#.#.##.?#.?.#..##? +.#.....##.##?.?...??..##...#?.#...#...#.??.?...#..?.?.#.##?.?#.#?..#???#?#?#.##..#.#.?...........#.?#..##...##??##..?.?###??#.#........#.?.# +?..#...#.##..?#.?.##..?..?.#....??#...#?..#.#.?.??.?..?#.#.#..??##?.##..?##?#..#?..??#.#.?.?#.?....#.##?#..#....?..##.??###?#...?..#.##.?#?# +.??......?..??##?..??...?...?##?.?###?..##?..#?..???#.##.##.?..#..???..???...??#..?##...##.?..?.??#..#####?.#.#..?......##.?.##.#.?#??#..#.. +.#?..#.?.#...?.???.?..#...?...?.#....?..#........#.??..?...#.#?..?#....#...#.#...?.?.##???..???..#..??.?..#..#.##?..?#.#..##.??##...?#.....? diff --git a/interactive/examples/aoc2023/day11/part1.ddp b/interactive/examples/aoc2023/day11/part1.ddp new file mode 100644 index 000000000..0693bc0f8 --- /dev/null +++ b/interactive/examples/aoc2023/day11/part1.ddp @@ -0,0 +1,35 @@ +-- AoC 2023 day 11: sum of L1 distances between galaxies after gap +-- expansion (factor 1). '#' = 35. +-- input 0: (row, col, code), 1-indexed + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); +let g = cells | filter($1[0] == 35) | map($0 ;); + +let all_rows = cells | map($0[0] ;) | distinct; +let rows_with = g | map($0[0] ;) | distinct; +let row_gaps = all_rows - rows_with; +let all_cols = cells | map($0[1] ;) | distinct; +let cols_with = g | map($0[1] ;) | distinct; +let col_gaps = all_cols - cols_with; + +let gk = g | key(; $0[0], $0[1]); +let rgk = row_gaps | key(; $0[0]); +let cgk = col_gaps | key(; $0[0]); + +let rcnt = gk | join(rgk, ($1[0], $1[1] ; $2[0])) | filter($1[0] < $0[0]) | map($0 ;) | count; +let radj = (rcnt | map($0 ; 1 * $1[0])) + ((g - (g | join(rcnt, ($0 ;)))) | map($0 ; 0)); +let ccnt = gk | join(cgk, ($1[0], $1[1] ; $2[0])) | filter($1[0] < $0[1]) | map($0 ;) | count; +let cadj = (ccnt | map($0 ; 1 * $1[0])) + ((g - (g | join(ccnt, ($0 ;)))) | map($0 ; 0)); + +let gal = radj | join(cadj, ($0[0] + $1[0], $0[1] + $2[0] ;)); +let gs = gal | key(; $0[0], $0[1]); + +let contrib = gs | join(gs, (; + if(or($1[0] < $2[0], ($1[0] == $2[0]) && ($1[1] < $2[1])), + if($1[0] < $2[0], $2[0] - $1[0], $1[0] - $2[0]) + + if($1[1] < $2[1], $2[1] - $1[1], $1[1] - $2[1]), + 0))); + +export "result" = contrib + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day11/part2.ddp b/interactive/examples/aoc2023/day11/part2.ddp new file mode 100644 index 000000000..2912821f1 --- /dev/null +++ b/interactive/examples/aoc2023/day11/part2.ddp @@ -0,0 +1,35 @@ +-- AoC 2023 day 11: sum of L1 distances between galaxies after gap +-- expansion (factor 999999). '#' = 35. +-- input 0: (row, col, code), 1-indexed + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); +let g = cells | filter($1[0] == 35) | map($0 ;); + +let all_rows = cells | map($0[0] ;) | distinct; +let rows_with = g | map($0[0] ;) | distinct; +let row_gaps = all_rows - rows_with; +let all_cols = cells | map($0[1] ;) | distinct; +let cols_with = g | map($0[1] ;) | distinct; +let col_gaps = all_cols - cols_with; + +let gk = g | key(; $0[0], $0[1]); +let rgk = row_gaps | key(; $0[0]); +let cgk = col_gaps | key(; $0[0]); + +let rcnt = gk | join(rgk, ($1[0], $1[1] ; $2[0])) | filter($1[0] < $0[0]) | map($0 ;) | count; +let radj = (rcnt | map($0 ; 999999 * $1[0])) + ((g - (g | join(rcnt, ($0 ;)))) | map($0 ; 0)); +let ccnt = gk | join(cgk, ($1[0], $1[1] ; $2[0])) | filter($1[0] < $0[1]) | map($0 ;) | count; +let cadj = (ccnt | map($0 ; 999999 * $1[0])) + ((g - (g | join(ccnt, ($0 ;)))) | map($0 ; 0)); + +let gal = radj | join(cadj, ($0[0] + $1[0], $0[1] + $2[0] ;)); +let gs = gal | key(; $0[0], $0[1]); + +let contrib = gs | join(gs, (; + if(or($1[0] < $2[0], ($1[0] == $2[0]) && ($1[1] < $2[1])), + if($1[0] < $2[0], $2[0] - $1[0], $1[0] - $2[0]) + + if($1[1] < $2[1], $2[1] - $1[1], $1[1] - $2[1]), + 0))); + +export "result" = contrib + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day13/input.txt b/interactive/examples/aoc2023/day13/input.txt new file mode 100644 index 000000000..044fb8a90 --- /dev/null +++ b/interactive/examples/aoc2023/day13/input.txt @@ -0,0 +1,114 @@ +#.###..## +..###..#. +.##.....# +..###.... +..##..#.. +.##.#..#. +##...##.. +..#.#.#.# +#####.### +....#.##. +.#.###### +....##... +#..##...# + +###.#..###.#....# +...#....#..#..#.# +...#...##.#.#.#.. +##.#...##.#.....# +.#..#....##...##. +.##....#...#.#... +..#.##.##.#..##.# +........#.##.#..# + +#...#..#....... +.#.###.##.#.### +.#.##.......... +##.##.##.###.## +..###.#.....#.. +..###.#.....#.. +##.##.##.###.## +#....#.##...### +#.#.....##..#.. +...###.....#... +..#.....#..#... +##.###.##...... +...#.##.#.#.#.# +..........####. + +...#..... +...#..... +...#..... +#...##... +..#.....# +...#.#### +.##....## +......#.. +#.....#.. +....#.#.# +##...#### + +...##.##.#.## +#....#..#..## +##.##....###. +#..###....#.# +###...##..#.. +#.#...#.#.##. +###..#....... +..##.#.#...## +##..#..#..#.# +......#...### +..#..#.##.... +##.#...#.#... +#..#.....#### +....##.##..#. +####......### + +#.##..#..## +.#.#####..# +.##.##...## +.#.#..#...# +...####.#.. +#..######.. +..#....#... +.#####.##.# +..........# +##.#..#.#.# +.......#... + +.#.#..#....###... +######..#.###.#.. +##..##.#..#..#... +...##...##...#.#. +#...#..####..#.#. +..####.#..#...#.# +####.#......#.#.. +##..##...#.#...#. + +#..#.##.......... +.#....##....#.#.# +.##.....##....### +#####...##...##.. +###.....#...###.# +#....#.#......#.# +#..#...###...#..# +#.#......#.###.#. +#..#.##.......... + +.##.#.#.##..#.#.. +...####...##..#.. +....##...#...##.# +..###..#..#..#### +.#...##...#.###.. +...###.....#...## + +.#...#.####.# +##..#.#....#. +.#..#...#..#. +..#....#.#.#. +.#...##....#. +#.###.##..### +#..#.#....### +.##..#.#.#... +.##.#.##..#.. +#...###..##.. diff --git a/interactive/examples/aoc2023/day13/part1.ddp b/interactive/examples/aoc2023/day13/part1.ddp new file mode 100644 index 000000000..04dc9cd6a --- /dev/null +++ b/interactive/examples/aoc2023/day13/part1.ddp @@ -0,0 +1,39 @@ +-- AoC 2023 day 13 part 1: mirror axes. Axis before row a is valid when no +-- row pair (x, 2a-1-x) has differing lines. Lines/columns compared as +-- collected list values (structural ==). Sum 100*(a-1) + (c-1). +-- input 0: (block, row, col, code), 1-indexed + +let cells = input 0 | key($0[0], $0[1], $0[2] ; $0[3]); + +let rlines = cells | map($0[0], $0[1] ; $0[2], $1[0]) | collect; +-- (b, r ; [(c, code)...]) +let clines = cells | map($0[0], $0[2] ; $0[1], $1[0]) | collect; +-- (b, c ; [(r, code)...]) + +-- row axes ------------------------------------------------------------ +let rcand = cells | map($0[0], $0[1] ;) | distinct; -- (b, a) +-- if(1, $1, 0) carries the collected List as ONE field (bare $1 would splice) +let rlk = rlines | map($0[0] ; $0[1], if(1, $1, 0)); -- (b ; x, L) +let t1 = rcand | map($0[0] ; $0[1]) | join(rlk, ($0[0], $1[0], $2[0] ; $2[1])); +-- (b, a, x ; Lx) +let t2 = t1 | map($0[0], 2 * $0[1] - 1 - $0[2] ; $0[1], $0[2], $1[0]); +-- (b, y ; a, x, Lx) +let rviol = t2 | join(rlines, ($0[0], $1[0] ; ($1[1] < $0[1]) && not($1[2] == $2))) + | filter($1[0] == 1) | map($0 ;) | distinct; +-- (b, a) with a mismatched pair +let rvalid = rcand - rviol; +let rscore = rvalid | key(; 100 * ($0[1] - 1)); + +-- column axes --------------------------------------------------------- +let ccand = cells | map($0[0], $0[2] ;) | distinct; +let clk = clines | map($0[0] ; $0[1], if(1, $1, 0)); +let s1 = ccand | map($0[0] ; $0[1]) | join(clk, ($0[0], $1[0], $2[0] ; $2[1])); +let s2 = s1 | map($0[0], 2 * $0[1] - 1 - $0[2] ; $0[1], $0[2], $1[0]); +let cviol = s2 | join(clines, ($0[0], $1[0] ; ($1[1] < $0[1]) && not($1[2] == $2))) + | filter($1[0] == 1) | map($0 ;) | distinct; +let cvalid = ccand - cviol; +let cscore = cvalid | key(; $0[1] - 1); + +export "result" = rscore + cscore + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day13/part2.ddp b/interactive/examples/aoc2023/day13/part2.ddp new file mode 100644 index 000000000..3e3466330 --- /dev/null +++ b/interactive/examples/aoc2023/day13/part2.ddp @@ -0,0 +1,29 @@ +-- AoC 2023 day 13 part 2: axes with EXACTLY ONE mismatched mirrored cell +-- pair. Sum 100*(a-1) for rows + (c-1) for cols. +-- input 0: (block, row, col, code), 1-indexed + +let cells = input 0 | key($0[0], $0[1], $0[2] ; $0[3]); +let cb = cells | map($0[0] ; $0[1], $0[2], $1[0]); -- (b ; r, c, s) + +-- row axes +let rcand = cells | map($0[0], $0[1] ;) | distinct; +let u1 = rcand | map($0[0] ; $0[1]) + | join(cb, ($0[0], 2 * $1[0] - 1 - $2[0], $2[1] ; $1[0], $2[0], $2[2])); +-- (b, y, c ; a, x, s) +let rmis = u1 | join(cells, ($0[0], $1[0] ; ($1[1] < $0[1]) && not($1[2] == $2[0]))) + | filter($1[0] == 1) | map($0 ;) | count | filter($1[0] == 1); +-- (b, a ; 1): exactly one smudge +let rscore = rmis | key(; 100 * ($0[1] - 1)); + +-- column axes +let ccand = cells | map($0[0], $0[2] ;) | distinct; +let v1 = ccand | map($0[0] ; $0[1]) + | join(cb, ($0[0], $2[0], 2 * $1[0] - 1 - $2[1] ; $1[0], $2[1], $2[2])); +-- (b, r, y ; a, x, s) +let cmis = v1 | join(cells, ($0[0], $1[0] ; ($1[1] < $0[2]) && not($1[2] == $2[0]))) + | filter($1[0] == 1) | map($0 ;) | count | filter($1[0] == 1); +let cscore = cmis | key(; $0[1] - 1); + +export "result" = rscore + cscore + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day14/input.txt b/interactive/examples/aoc2023/day14/input.txt new file mode 100644 index 000000000..761c7ce75 --- /dev/null +++ b/interactive/examples/aoc2023/day14/input.txt @@ -0,0 +1,7 @@ +.O.#....#.#.#.....O..O..O... +.O.#....O....#..#....#####.. +.O..##..O..#O...##..#...O... +O.##.OO#O.#...#.#.#.O..##.O. +O..#..OO#...#..#.#O.#.#.OO.# +#.#.#.O.O##...O...#0##..O... +.O#...#..#.O..O.#.##..#..O.. diff --git a/interactive/examples/aoc2023/day14/part1.ddp b/interactive/examples/aoc2023/day14/part1.ddp new file mode 100644 index 000000000..0fd256894 --- /dev/null +++ b/interactive/examples/aoc2023/day14/part1.ddp @@ -0,0 +1,31 @@ +-- AoC 2023 day 14 part 1: roll rocks north; load = sum(1 + R - r). +-- Closed form, no recursion: each 'O' lands at (nearest blocker above) + +-- (its rank among rocks in the same blocker segment). Sentinel cubes at +-- row 0 remove the no-blocker case; rank counts include self (no defaults). +-- input 0: (row, col, code) 1-indexed; 'O'=79 '#'=35 '.'=46 + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); +let os = cells | filter($1[0] == 79) | map($0 ;); +let cubes = cells | filter($1[0] == 35) | map($0 ;); + +let allcols = cells | map($0[1] ;) | distinct; +let cubesN = cubes + (allcols | map(0, $0[0] ;)); +let cubcol = cubesN | map($0[1] ; $0[0]); -- (c ; rc) + +-- nearest cube above each rock +let b = os | map($0[1] ; $0[0]) + | join(cubcol, ($0[0], $1[0] ; $2[0])) + | filter($1[0] < $0[1]) + | map($0[1], $0[0] ; 1000000 - $1[0]) | min | map($0 ; 1000000 - $1[0]); +-- (r, c ; b) + +let seg = b | map($0[1], $1[0] ; $0[0]); -- (c, b ; r) +let cnt = seg | join(seg, ($0[0], $0[1], $1[0] ; $2[0])) + | filter($1[0] <= $0[2]) | map($0 ;) | count; +-- (c, b, r ; n): rank incl. self +let rolled = cnt | map($0[1] + $1[0], $0[0] ;); -- (newr, c) + +let maxr = cells | key(; 1000000 - $0[0]) | min | map(; 1000000 - $1[0]); +export "result" = rolled | map(; $0[0]) | join(maxr, (; 1 + $2[0] - $1[0])) + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day14/part2.ddp b/interactive/examples/aoc2023/day14/part2.ddp new file mode 100644 index 000000000..b6a7c70d7 --- /dev/null +++ b/interactive/examples/aoc2023/day14/part2.ddp @@ -0,0 +1,82 @@ +-- AoC 2023 day 14 part 2: spin cycles (N,W,S,E) to the slt's recursion +-- limit (the state converges well before). Rock sets tagged by round k; +-- monotone accumulation, k capped at 141. Rolls are closed-form. +-- input 0: (row, col, code) 1-indexed; 'O'=79 '#'=35 '.'=46 + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); +let rocks0 = cells | filter($1[0] == 79) | map(0, $0[0], $0[1] ;); +let cubes = cells | filter($1[0] == 35) | map($0 ;); + +let allcols = cells | map($0[1] ;) | distinct; +let allrows = cells | map($0[0] ;) | distinct; +let maxr = cells | key(; 1000000 - $0[0]) | min | map(; 1000000 - $1[0]); +let maxc = cells | key(; 1000000 - $0[1]) | min | map(; 1000000 - $1[0]); + +-- sentinel cubes on all four borders +let cubesX = cubes + + (allcols | map(0, $0[0] ;)) + + (allcols | key(; $0[0]) | join(maxr, ($2[0] + 1, $1[0] ;))) + + (allrows | map($0[0], 0 ;)) + + (allrows | key(; $0[0]) | join(maxc, ($1[0], $2[0] + 1 ;))); + +let cubcol = cubesX | map($0[1] ; $0[0]); -- (c ; rc) +let cubrow = cubesX | map($0[0] ; $0[1]); -- (r ; cc) + +spin: { + let cur = os | filter($0[0] <= 140); + -- (k, r, c) + + -- roll north: blocker = nearest cube above; rank among segment peers + let bn = cur | map($0[2] ; $0[0], $0[1]) + | join(cubcol, ($0[0], $1[0], $1[1] ; $2[0])) + | filter($1[0] < $0[2]) + | map($0[1], $0[2], $0[0] ; 1000000 - $1[0]) | min | map($0 ; 1000000 - $1[0]); + -- (k, r, c ; b) + let segn = bn | map($0[0], $0[2], $1[0] ; $0[1]); -- (k, c, b ; r) + let osn = segn | join(segn, ($0[0], $0[1], $0[2], $1[0] ; $2[0])) + | filter($1[0] <= $0[3]) | map($0 ;) | count + | map($0[0], $0[2] + $1[0], $0[1] ;); + -- (k, r', c) + + -- roll west + let bw = osn | map($0[1] ; $0[0], $0[2]) + | join(cubrow, ($0[0], $1[0], $1[1] ; $2[0])) + | filter($1[0] < $0[2]) + | map($0[1], $0[0], $0[2] ; 1000000 - $1[0]) | min | map($0 ; 1000000 - $1[0]); + -- (k, r, c ; b) + let segw = bw | map($0[0], $0[1], $1[0] ; $0[2]); -- (k, r, b ; c) + let osw = segw | join(segw, ($0[0], $0[1], $0[2], $1[0] ; $2[0])) + | filter($1[0] <= $0[3]) | map($0 ;) | count + | map($0[0], $0[1], $0[2] + $1[0] ;); + -- (k, r, c') + + -- roll south: blocker = nearest cube below; rank counts peers >= r + let bs = osw | map($0[2] ; $0[0], $0[1]) + | join(cubcol, ($0[0], $1[0], $1[1] ; $2[0])) + | filter($1[0] > $0[2]) + | map($0[1], $0[2], $0[0] ; $1[0]) | min; + -- (k, r, c ; b) + let segs = bs | map($0[0], $0[2], $1[0] ; $0[1]); -- (k, c, b ; r) + let oss = segs | join(segs, ($0[0], $0[1], $0[2], $1[0] ; $2[0])) + | filter($1[0] >= $0[3]) | map($0 ;) | count + | map($0[0], $0[2] - $1[0], $0[1] ;); + + -- roll east + let be = oss | map($0[1] ; $0[0], $0[2]) + | join(cubrow, ($0[0], $1[0], $1[1] ; $2[0])) + | filter($1[0] > $0[2]) + | map($0[1], $0[0], $0[2] ; $1[0]) | min; + -- (k, r, c ; b) + let sege = be | map($0[0], $0[1], $1[0] ; $0[2]); -- (k, r, b ; c) + let ose = sege | join(sege, ($0[0], $0[1], $0[2], $1[0] ; $2[0])) + | filter($1[0] >= $0[3]) | map($0 ;) | count + | map($0[0] + 1, $0[1], $0[2] - $1[0] ;); + -- (k+1, r, c'): next round + + var os = rocks0 + ose | distinct; +} + +let final = spin::os | filter($0[0] == 141); +export "result" = final | map(; $0[1]) | join(maxr, (; 1 + $2[0] - $1[0])) + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day15/input.txt b/interactive/examples/aoc2023/day15/input.txt new file mode 100644 index 000000000..a915ec5e9 --- /dev/null +++ b/interactive/examples/aoc2023/day15/input.txt @@ -0,0 +1 @@ +rn=1,xw-,nn=8,zg=2,lw=4,oo=2,tt-,wv=9,hy=7,rs=8,sm=4,lf-,td=9,zz=1,ca=2,nd- diff --git a/interactive/examples/aoc2023/day15/part1.ddp b/interactive/examples/aoc2023/day15/part1.ddp new file mode 100644 index 000000000..f1b3bb988 --- /dev/null +++ b/interactive/examples/aoc2023/day15/part1.ddp @@ -0,0 +1,27 @@ +-- AoC 2023 day 15 part 1: HASH each command; sum. h = ((h+c)*17) % 256. +-- No % operator: modulo done relationally (cross join a k-table, keep the +-- residue in [0,256)). +-- input 0: (r, pos, code), 1-indexed + +let chars = input 0 | key($0[0], $0[1] ; $0[2]); +let lens = chars | map($0[0] ;) | count; -- (r ; len) + +let ktab = chars | key(;) | distinct + | flatmap(list(0,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)) + | key(; $1[1]); +-- (; k) + +hash: { + let t = st | map($0[0], $0[1] + 1 ; $0[2]) + | join(chars, (; $0[0], $0[1], ($1[0] + $2[0]) * 17)); + -- (; r, i, t) + let next = t | join(ktab, ($1[0], $1[1], $1[2] - 256 * $2[0] ;)) + | filter($0[2] >= 0 && $0[2] < 256); + var st = (lens | map($0[0], 0, 0 ;)) + next | distinct; +} + +export "result" = hash::st | map($0[0] ; $0[1], $0[2]) + | join(lens, ($0[0] ; $1[0], $1[1], $2[0])) | filter($1[0] == $1[2]) + | key(; $1[1]) | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day15/part2.ddp b/interactive/examples/aoc2023/day15/part2.ddp new file mode 100644 index 000000000..91644692b --- /dev/null +++ b/interactive/examples/aoc2023/day15/part2.ddp @@ -0,0 +1,37 @@ +-- AoC 2023 day 15 part 2: lens boxes. A label survives iff its last op is +-- '='; slot = rank of first-op-after-last-delete among box mates. +-- input 0: (r, l1, l2, opchar, digitcode|0); '='=61 +let ops = input 0 | key($0[0] ; $0[1], $0[2], if($0[3] == 61, $0[4] - 48, 0)); + +let ktab = ops | key(;) | distinct + | flatmap(list(0,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)) + | key(; $1[1]); + +let opsk = ops | map($1[0], $1[1] ; $0[0], $1[2]); -- (l1,l2 ; r, op) +let dels = opsk | filter($1[1] == 0) | map($0 ; 1000000 - $1[0]) | min | map($0 ; 1000000 - $1[0]); +-- (l1,l2 ; last delete r) + +let after = opsk | join(dels, ($0 ; $1[0], $1[1], $2[0])) | filter($1[0] > $1[2]) | map($0 ; $1[0], $1[1]); +let nodel = opsk - (opsk | join(dels, ($0 ; $1[0], $1[1]))); +let finals = after + nodel; -- (l1,l2 ; r, op) + +let last = finals | map($0 ; 1000000 - $1[0], $1[1]) | min | map($0 ; $1[1]); +let first = finals | map($0 ; $1[0]) | min; -- (l1,l2 ; minr) +let present = last | filter($1[0] > 0); -- (l1,l2 ; focal) + +-- box = HASH(label): two rounds of *17 mod 256 (relational modulo) +let t1 = present | key(; $0[0], $0[1], $1[0]); -- (; l1,l2,focal) +let h1 = t1 | join(ktab, ($1[0], $1[1] ; $1[2], $1[0] * 17 - 256 * $2[0])) + | filter($1[1] >= 0 && $1[1] < 256); -- (l1,l2 ; focal, h1) +let t2 = h1 | key(; $0[0], $0[1], $1[0], ($1[1] + $0[1]) * 17); +let hh = t2 | join(ktab, ($1[0], $1[1] ; $1[2], $1[3] - 256 * $2[0])) + | filter($1[1] >= 0 && $1[1] < 256); -- (l1,l2 ; focal, box) + +let boxed = hh | join(first, ($1[1] ; $2[0], $1[0])); -- (box ; minr, focal) +let rank = boxed | join(boxed, ($0[0], $1[0], $1[1] ; $2[0])) + | filter($1[0] <= $0[1]) | map($0 ;) | count; -- (box, minr, focal ; slot) + +export "result" = rank | key(; (1 + $0[0]) * $1[0] * $0[2]) + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day16/input.txt b/interactive/examples/aoc2023/day16/input.txt new file mode 100644 index 000000000..c6b9f66e0 --- /dev/null +++ b/interactive/examples/aoc2023/day16/input.txt @@ -0,0 +1,25 @@ +-............./...\................\......-.......\....|...--....................\.............\...\.-........ +/\...............\.........|....|..../...-....\...\../...........\..\..................................|...... +.\..........\..../....../............-.-.........|........../.............|.............-......./..........\.. +.\.......\..........\.......\.\........................\.....|...\.../........|..............\\...........|..- +.\...........\.....\../...-\..|....-.................-.|.....|/\...\................././.........\.\.../...... +./....\...\..............|...\.......\...-.....|..|...../...../..-.....................\.......\...../...|..|. +...........|.-.........|.....-.../......\./.........\\....\...\..|./...-.............\........................ +-.......................\.............\........\.\...........-....................../...........\....|.-...... +-............\..........-./.....\\......\.......|.....-..................-.-\\.....|............-....\........ +..............-....|..................-......|....-.../....................-....\...........\................. +................\.......\../.............../......|.....\............-.....\...\....................|..-.../.- +|\.............-......./...........-........../......\-.........-.....................................|\...... +.....\.....||.........-............../............|....-...........\.\.....................................|\. +......................./...\......\......./|...\..........|...\.../...........\....\./..-..........\.......... +.....\................\.\..............\./.\..-......|........../.....\..\.........|....\....\.....\.......... +...\...\...\.......|.....\\.\..\........\.-.....\.|..................................|......-................. +\|...../............................|.../.\......\......-.............|....|...|...-.......|.....\............ +.\/..........-..........|........./...................\.........../\.......-.............../............./\.-. +.......\.......\...\............\.-.../.......\....................|.../..............\./.........-......\.-.. +../...\...-...|.|../......\......\...\...-................................\.........-........-............./.. +...\.../..|........\...|...../../...|............-..........|/...............|..........\|................./.. +......\/.\......|..................-...\.......\..|........./.-......-...........-...\......|..........-|/-... +.\.........-........./........................|...........\/....\-......\...\../\............................. +|.....\.\.....|-.............|.......|...........\..|\..........\..|........................|....-.......\//.. +..........-.............\.........|......\.......\.../..../.\.-..........\....../........................\.... diff --git a/interactive/examples/aoc2023/day16/part1.ddp b/interactive/examples/aoc2023/day16/part1.ddp new file mode 100644 index 000000000..1eeed94db --- /dev/null +++ b/interactive/examples/aoc2023/day16/part1.ddp @@ -0,0 +1,28 @@ +-- AoC 2023 day 16 part 1: beam bouncing; count energized cells. +-- dirs: r=0 l=1 u=2 d=3; '.'=46 '-'=45 '|'=124 '/'=47 '\'=92 +-- input 0: (row, col, code) 1-indexed + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); + +let shift = cells | key(;) | distinct | flatmap(list( + tuple(0,46,0,1,0), tuple(0,45,0,1,0), tuple(0,124,1,0,3), tuple(0,124,-1,0,2), + tuple(0,47,-1,0,2), tuple(0,92,1,0,3), + tuple(1,46,0,-1,1), tuple(1,45,0,-1,1), tuple(1,124,1,0,3), tuple(1,124,-1,0,2), + tuple(1,47,1,0,3), tuple(1,92,-1,0,2), + tuple(2,46,-1,0,2), tuple(2,45,0,1,0), tuple(2,45,0,-1,1), tuple(2,124,-1,0,2), + tuple(2,47,0,1,0), tuple(2,92,0,-1,1), + tuple(3,46,1,0,3), tuple(3,45,0,1,0), tuple(3,45,0,-1,1), tuple(3,124,1,0,3), + tuple(3,47,0,-1,1), tuple(3,92,0,1,0))) + | key($1[1][0], $1[1][1] ; $1[1][2], $1[1][3], $1[1][4]); +-- (dir, sym ; dr, dc, newdir) + +beam: { + let onsym = l | map($0[0], $0[1] ; $0[2]) | join(cells, ($1[0], $2[0] ; $0[0], $0[1])); + -- (dir, sym ; r, c) + let next = onsym | join(shift, ($1[0] + $2[0], $1[1] + $2[1], $2[2] ;)); + var l = (cells | filter($0[0] == 1 && $0[1] == 1) | map(1, 1, 0 ;)) + next | distinct; +} + +export "result" = beam::l | map($0[0], $0[1] ;) + | join(cells, ($0 ;)) | distinct + | key(;) | count | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day16/part2.ddp b/interactive/examples/aoc2023/day16/part2.ddp new file mode 100644 index 000000000..384037206 --- /dev/null +++ b/interactive/examples/aoc2023/day16/part2.ddp @@ -0,0 +1,44 @@ +-- AoC 2023 day 16 part 2: best edge seed. Beams tagged by source (sd, si). +-- Mirrors the slt exactly, including its 'u' seeds sitting at row=MAX(c). +-- input 0: (row, col, code) 1-indexed + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); + +let shift = cells | key(;) | distinct | flatmap(list( + tuple(0,46,0,1,0), tuple(0,45,0,1,0), tuple(0,124,1,0,3), tuple(0,124,-1,0,2), + tuple(0,47,-1,0,2), tuple(0,92,1,0,3), + tuple(1,46,0,-1,1), tuple(1,45,0,-1,1), tuple(1,124,1,0,3), tuple(1,124,-1,0,2), + tuple(1,47,1,0,3), tuple(1,92,-1,0,2), + tuple(2,46,-1,0,2), tuple(2,45,0,1,0), tuple(2,45,0,-1,1), tuple(2,124,-1,0,2), + tuple(2,47,0,1,0), tuple(2,92,0,-1,1), + tuple(3,46,1,0,3), tuple(3,45,0,1,0), tuple(3,45,0,-1,1), tuple(3,124,1,0,3), + tuple(3,47,0,-1,1), tuple(3,92,0,1,0))) + | key($1[1][0], $1[1][1] ; $1[1][2], $1[1][3], $1[1][4]); + +let rows = cells | map($0[0] ;) | distinct; +let cols = cells | map($0[1] ;) | distinct; +let minc = cells | key(; $0[1]) | min; +let maxc = cells | key(; 1000000 - $0[1]) | min | map(; 1000000 - $1[0]); +let minr = cells | key(; $0[0]) | min; + +let seeds = (rows | key(; $0[0]) | join(minc, ($1[0], $2[0], 0, 0, $1[0] ;))) + + (rows | key(; $0[0]) | join(maxc, ($1[0], $2[0], 1, 1, $1[0] ;))) + + (cols | key(; $0[0]) | join(minr, ($2[0], $1[0], 3, 3, $1[0] ;))) + + (cols | key(; $0[0]) | join(maxc, ($2[0], $1[0], 2, 2, $1[0] ;))); +-- key (r, c, dir, sd, si) + +beam: { + let onsym = l | map($0[0], $0[1] ; $0[2], $0[3], $0[4]) + | join(cells, ($1[0], $2[0] ; $0[0], $0[1], $1[1], $1[2])); + -- (dir, sym ; r, c, sd, si) + let next = onsym | join(shift, ($1[0] + $2[0], $1[1] + $2[1], $2[2], $1[2], $1[3] ;)); + var l = seeds + next | distinct; +} + +let energized = beam::l | map($0[0], $0[1] ; $0[3], $0[4]) + | join(cells, ($0[0], $0[1], $1[0], $1[1] ;)) | distinct + | map($0[2], $0[3] ;) | count; +-- (sd, si ; n) + +export "result" = energized | key(; 1000000 - $1[0]) | min | map(; 1000000 - $1[0]) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day17/input.txt b/interactive/examples/aoc2023/day17/input.txt new file mode 100644 index 000000000..5b12ee132 --- /dev/null +++ b/interactive/examples/aoc2023/day17/input.txt @@ -0,0 +1,11 @@ +62838899848717171482491386857 +97364142198727715957423491912 +86369399573486615223592185179 +65896629415741215317915596532 +87429913559342885454881133182 +87599176619626884624793447611 +69826949796636945138977813282 +97787786569751297721492648197 +56111693893781611276884581493 +11326495731998819531787964758 +45631262918273787936318151868 diff --git a/interactive/examples/aoc2023/day17/part1.ddp b/interactive/examples/aoc2023/day17/part1.ddp new file mode 100644 index 000000000..666d6dd8a --- /dev/null +++ b/interactive/examples/aoc2023/day17/part1.ddp @@ -0,0 +1,29 @@ +-- AoC 2023 day 17 part 1: min-cost crucible path; state (r,c,dr,dc,steps), +-- straight while steps<3, turns reset steps. min-reducer fixpoint. +-- input 0: (row, col, cost) 1-indexed + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); +let seeds = (cells | filter($0[0] == 1 && $0[1] == 1) | map(1, 1, 1, 0, 0 ; 0)) + + (cells | filter($0[0] == 1 && $0[1] == 1) | map(1, 1, 0, 1, 0 ; 0)); + +dijk: { + let straight = mc | filter($0[4] < 3) + | map($0[0] + $0[2], $0[1] + $0[3] ; $0[2], $0[3], $0[4], $1[0]) + | join(cells, ($0[0], $0[1], $1[0], $1[1], $1[2] + 1 ; $1[3] + $2[0])); + let turna = mc + | map($0[0] + $0[3], $0[1] + $0[2] ; $0[3], $0[2], $1[0]) + | join(cells, ($0[0], $0[1], $1[0], $1[1], 1 ; $1[2] + $2[0])); + let turnb = mc + | map($0[0] - $0[3], $0[1] - $0[2] ; 0 - $0[3], 0 - $0[2], $1[0]) + | join(cells, ($0[0], $0[1], $1[0], $1[1], 1 ; $1[2] + $2[0])); + var mc = seeds + straight + turna + turnb | min; +} + +let maxr = cells | key(; 1000000 - $0[0]) | min | map(; 1000000 - $1[0]); +let maxc = cells | key(; 1000000 - $0[1]) | min | map(; 1000000 - $1[0]); +let corner = maxr | join(maxc, (; $1[0], $2[0])); + +export "result" = dijk::mc | key(; $0[0], $0[1], $1[0]) + | join(corner, (; $1[0] == $2[0], $1[1] == $2[1], $1[2])) + | filter($1[0] && $1[1]) | key(; $1[2]) | min + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day17/part2.ddp b/interactive/examples/aoc2023/day17/part2.ddp new file mode 100644 index 000000000..a1ac420f8 --- /dev/null +++ b/interactive/examples/aoc2023/day17/part2.ddp @@ -0,0 +1,29 @@ +-- AoC 2023 day 17 part 2: ultra crucible; straight while steps<10, turns +-- (and the goal) require steps>=4. +-- input 0: (row, col, cost) 1-indexed + +let cells = input 0 | key($0[0], $0[1] ; $0[2]); +let seeds = (cells | filter($0[0] == 1 && $0[1] == 1) | map(1, 1, 1, 0, 0 ; 0)) + + (cells | filter($0[0] == 1 && $0[1] == 1) | map(1, 1, 0, 1, 0 ; 0)); + +dijk: { + let straight = mc | filter($0[4] < 10) + | map($0[0] + $0[2], $0[1] + $0[3] ; $0[2], $0[3], $0[4], $1[0]) + | join(cells, ($0[0], $0[1], $1[0], $1[1], $1[2] + 1 ; $1[3] + $2[0])); + let turna = mc | filter($0[4] >= 4) + | map($0[0] + $0[3], $0[1] + $0[2] ; $0[3], $0[2], $1[0]) + | join(cells, ($0[0], $0[1], $1[0], $1[1], 1 ; $1[2] + $2[0])); + let turnb = mc | filter($0[4] >= 4) + | map($0[0] - $0[3], $0[1] - $0[2] ; 0 - $0[3], 0 - $0[2], $1[0]) + | join(cells, ($0[0], $0[1], $1[0], $1[1], 1 ; $1[2] + $2[0])); + var mc = seeds + straight + turna + turnb | min; +} + +let maxr = cells | key(; 1000000 - $0[0]) | min | map(; 1000000 - $1[0]); +let maxc = cells | key(; 1000000 - $0[1]) | min | map(; 1000000 - $1[0]); +let corner = maxr | join(maxc, (; $1[0], $2[0])); + +export "result" = dijk::mc | filter($0[4] >= 4) | key(; $0[0], $0[1], $1[0], $0[4]) + | join(corner, (; $1[0] == $2[0], $1[1] == $2[1], $1[2], $1[3])) + | filter($1[0] && $1[1]) | key(; $1[2]) | min + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day18/input.txt b/interactive/examples/aoc2023/day18/input.txt new file mode 100644 index 000000000..f2ced3b66 --- /dev/null +++ b/interactive/examples/aoc2023/day18/input.txt @@ -0,0 +1,15 @@ +R 1 (#53d732) +L 5 (#292431) +U 6 (#4c7272) +L 9 (#49ace3) +U 3 (#7b94e6) +R 1 (#5579d4) +L 9 (#1d7886) +U 3 (#171219) +R 9 (#45fa39) +R 11 (#222422) +U 6 (#91c869) +L 8 (#7581c5) +U 9 (#46aab5) +R 9 (#6f72a5) +L 7 (#42abb1) diff --git a/interactive/examples/aoc2023/day18/part1.ddp b/interactive/examples/aoc2023/day18/part1.ddp new file mode 100644 index 000000000..c2a2dbcdf --- /dev/null +++ b/interactive/examples/aoc2023/day18/part1.ddp @@ -0,0 +1,38 @@ +-- AoC 2023 day 18 part 1: lavaduct area via shoelace + perimeter/2 + 1. +-- input 0: (line, dircode, steps, h1..h6); U=85 D=68 L=76 R=82 + +let instr = input 0 | key($0[0] ; + if($0[1] == 85, -1, if($0[1] == 68, 1, 0)), + if($0[1] == 76, -1, if($0[1] == 82, 1, 0)), + $0[2]); +-- (line ; dr, dc, steps) + +path: { + let step = p | map($0[0] ; $0[1], $0[2]) + | join(instr, ($0[0] + 1, $1[0] + $2[0] * $2[2], $1[1] + $2[1] * $2[2] ;)); + var p = (instr | filter($0[0] == 1) | map(1, 0, 0 ;)) + step | distinct; +} + +let terms = path::p | map($0[0] + 1 ; $0[1], $0[2]) + | join(path::p | map($0[0] ; $0[1], $0[2]), + (; ($1[0] + $2[0]) * ($1[1] - $2[1]))); +let s = terms | collect | map(; fold($1, 0, ^1 + ^0[0])) | map(; if($1[0] < 0, 0 - $1[0], $1[0])); +let perim = instr | key(; $1[2]) | collect | map(; fold($1, 0, ^1 + ^0[0])); +let n = s | join(perim, (; $1[0] + $2[0])); +-- (; |S| + P), even; halve by binary decomposition (no division) + +let powtab = instr | key(;) | distinct + | flatmap(list(549755813888, 274877906944, 137438953472, 68719476736, 34359738368, 17179869184, 8589934592, 4294967296, 2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1)) + | key($1[0] ; $1[1]); +-- (i ; 2^(39-i)) + +halve: { + let next = st | map($0[0] ; $0[1], $0[2]) + | join(powtab, ($0[0] + 1, + if($1[0] >= 2 * $2[0], $1[0] - 2 * $2[0], $1[0]), + if($1[0] >= 2 * $2[0], $1[1] + $2[0], $1[1]) ;)); + var st = (n | map(0, $1[0], 0 ;)) + next | distinct; +} + +export "result" = halve::st | filter($0[0] == 40) | key(; $0[2] + 1) + | arrange | inspect(part1); \ No newline at end of file diff --git a/interactive/examples/aoc2023/day18/part2.ddp b/interactive/examples/aoc2023/day18/part2.ddp new file mode 100644 index 000000000..d18699108 --- /dev/null +++ b/interactive/examples/aoc2023/day18/part2.ddp @@ -0,0 +1,38 @@ +-- AoC 2023 day 18 part 2: directions/steps decoded from the hex color. +-- last hex digit: 0=R 1=D 2=L 3=U; first five = steps. +-- input 0: (line, dircode, steps, h1..h6) + +let instr = input 0 | key($0[0] ; + if($0[8] == 51, -1, if($0[8] == 49, 1, 0)), + if($0[8] == 50, -1, if($0[8] == 48, 1, 0)), + ((((if($0[3] <= 57, $0[3] - 48, $0[3] - 87)) * 16 + if($0[4] <= 57, $0[4] - 48, $0[4] - 87)) * 16 + if($0[5] <= 57, $0[5] - 48, $0[5] - 87)) * 16 + if($0[6] <= 57, $0[6] - 48, $0[6] - 87)) * 16 + if($0[7] <= 57, $0[7] - 48, $0[7] - 87)); + +path: { + let step = p | map($0[0] ; $0[1], $0[2]) + | join(instr, ($0[0] + 1, $1[0] + $2[0] * $2[2], $1[1] + $2[1] * $2[2] ;)); + var p = (instr | filter($0[0] == 1) | map(1, 0, 0 ;)) + step | distinct; +} + +let terms = path::p | map($0[0] + 1 ; $0[1], $0[2]) + | join(path::p | map($0[0] ; $0[1], $0[2]), + (; ($1[0] + $2[0]) * ($1[1] - $2[1]))); +let s = terms | collect | map(; fold($1, 0, ^1 + ^0[0])) | map(; if($1[0] < 0, 0 - $1[0], $1[0])); +let perim = instr | key(; $1[2]) | collect | map(; fold($1, 0, ^1 + ^0[0])); +let n = s | join(perim, (; $1[0] + $2[0])); +-- (; |S| + P), even; halve by binary decomposition (no division) + +let powtab = instr | key(;) | distinct + | flatmap(list(549755813888, 274877906944, 137438953472, 68719476736, 34359738368, 17179869184, 8589934592, 4294967296, 2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1)) + | key($1[0] ; $1[1]); +-- (i ; 2^(39-i)) + +halve: { + let next = st | map($0[0] ; $0[1], $0[2]) + | join(powtab, ($0[0] + 1, + if($1[0] >= 2 * $2[0], $1[0] - 2 * $2[0], $1[0]), + if($1[0] >= 2 * $2[0], $1[1] + $2[0], $1[1]) ;)); + var st = (n | map(0, $1[0], 0 ;)) + next | distinct; +} + +export "result" = halve::st | filter($0[0] == 40) | key(; $0[2] + 1) + | arrange | inspect(part2); \ No newline at end of file diff --git a/interactive/examples/aoc2023/day19/input.txt b/interactive/examples/aoc2023/day19/input.txt new file mode 100644 index 000000000..0f65a7f7a --- /dev/null +++ b/interactive/examples/aoc2023/day19/input.txt @@ -0,0 +1,16 @@ +in{x<1164:zoz,s>1473:A,a<8576:ask,A} +ask{m<2275:rsx,zoz} +rsx{a>8922:A,s>4213:A,R} +zoz{m>5813:A,s>4522:A,x<245:R,krw} +krw{a>3747:dqu,a>299:R,a<927:A,A} +ton{a<8226:ktx,m>1965:ktx,s>3591:uhu,dqu} +dqu{m<6866:uhu,s>4649:A,R} +uhu{a<6293:A,lel} +lel{a<6145:ktx,A} +ktx{s>8889:R,a>3215:R,R} + +{x=61,m=818,a=525,s=29} +{x=225,m=7722,a=964,s=466} +{x=528,m=3628,a=914,s=8823} +{x=13,m=675,a=5933,s=9} +{x=9693,m=8583,a=125,s=787} diff --git a/interactive/examples/aoc2023/day19/part1.ddp b/interactive/examples/aoc2023/day19/part1.ddp new file mode 100644 index 000000000..3042da137 --- /dev/null +++ b/interactive/examples/aoc2023/day19/part1.ddp @@ -0,0 +1,26 @@ +-- AoC 2023 day 19 part 1: run each part through the workflows; first +-- matching rule (min priority) decides; sum x+m+a+s of accepted. +-- input 0: rules (1, state, prio, field, cmp, val, next) / parts (0, x, m, a, s, 0, 0) + +let rulesbys = input 0 | filter($0[0] == 1) | key($0[1] ; $0[2], $0[3], $0[4], $0[5], $0[6]); +-- (state ; prio, field, cmp, val, next) +let parts = input 0 | filter($0[0] == 0) | key(0, $0[1], $0[2], $0[3], $0[4] ;); + +move: { + let mtch = mv | map($0[0] ; $0[1], $0[2], $0[3], $0[4]) + | join(rulesbys, ($0[0], $1[0], $1[1], $1[2], $1[3] ; + if($2[2] == 0, + if($2[1] == 0, $1[0], if($2[1] == 1, $1[1], if($2[1] == 2, $1[2], $1[3]))) < $2[3], + if($2[1] == 0, $1[0], if($2[1] == 1, $1[1], if($2[1] == 2, $1[2], $1[3]))) > $2[3]), + $2[0], $2[4])) + | filter($1[0] == 1) | map($0 ; $1[1], $1[2]) + | min; + -- (state,x,m,a,s ; first prio, next) + let moved = mtch | map($1[1], $0[1], $0[2], $0[3], $0[4] ;); + var mv = parts + moved | distinct; +} + +export "result" = move::mv | filter($0[0] == -1) + | key(; $0[1] + $0[2] + $0[3] + $0[4]) + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day19/part2.ddp b/interactive/examples/aoc2023/day19/part2.ddp new file mode 100644 index 000000000..e605d4e98 --- /dev/null +++ b/interactive/examples/aoc2023/day19/part2.ddp @@ -0,0 +1,36 @@ +-- AoC 2023 day 19 part 2: hypercube regions through the workflow tree. +-- Faithful to the slt: no emptiness clamping, so impossible regions +-- contribute negative products (their oracle answer is negative too). +-- input 0: rules (1, state, prio, field, cmp, val, next) / parts (0, ...) + +let rules = input 0 | filter($0[0] == 1) | key($0[1], $0[2] ; $0[3], $0[4], $0[5], $0[6]); +-- (state, prio ; field, cmp, val, next) + +flow: { + let cur = reg | map($0[0], $0[1] ; $0[2], $0[3], $0[4], $0[5], $0[6], $0[7], $0[8], $0[9]); + let b1 = cur | join(rules, ($2[3], 1, + if(($2[0] == 0) && ($2[1] == 1), if($2[2] + 1 > $1[0], $2[2] + 1, $1[0]), $1[0]), + if(($2[0] == 0) && ($2[1] == 0), if($2[2] - 1 < $1[1], $2[2] - 1, $1[1]), $1[1]), + if(($2[0] == 1) && ($2[1] == 1), if($2[2] + 1 > $1[2], $2[2] + 1, $1[2]), $1[2]), + if(($2[0] == 1) && ($2[1] == 0), if($2[2] - 1 < $1[3], $2[2] - 1, $1[3]), $1[3]), + if(($2[0] == 2) && ($2[1] == 1), if($2[2] + 1 > $1[4], $2[2] + 1, $1[4]), $1[4]), + if(($2[0] == 2) && ($2[1] == 0), if($2[2] - 1 < $1[5], $2[2] - 1, $1[5]), $1[5]), + if(($2[0] == 3) && ($2[1] == 1), if($2[2] + 1 > $1[6], $2[2] + 1, $1[6]), $1[6]), + if(($2[0] == 3) && ($2[1] == 0), if($2[2] - 1 < $1[7], $2[2] - 1, $1[7]), $1[7]) ;)); + let b2 = cur | join(rules, ($0[0], $0[1] + 1, + if(($2[0] == 0) && ($2[1] == 0), if($2[2] > $1[0], $2[2], $1[0]), $1[0]), + if(($2[0] == 0) && ($2[1] == 1), if($2[2] < $1[1], $2[2], $1[1]), $1[1]), + if(($2[0] == 1) && ($2[1] == 0), if($2[2] > $1[2], $2[2], $1[2]), $1[2]), + if(($2[0] == 1) && ($2[1] == 1), if($2[2] < $1[3], $2[2], $1[3]), $1[3]), + if(($2[0] == 2) && ($2[1] == 0), if($2[2] > $1[4], $2[2], $1[4]), $1[4]), + if(($2[0] == 2) && ($2[1] == 1), if($2[2] < $1[5], $2[2], $1[5]), $1[5]), + if(($2[0] == 3) && ($2[1] == 0), if($2[2] > $1[6], $2[2], $1[6]), $1[6]), + if(($2[0] == 3) && ($2[1] == 1), if($2[2] < $1[7], $2[2], $1[7]), $1[7]) ;)); + var reg = (rules | key(;) | distinct | map(0, 1, 1, 4000, 1, 4000, 1, 4000, 1, 4000 ;)) + + b1 + b2 | arrange; +} + +export "result" = flow::reg | filter($0[0] == -1) + | key(; (1 + $0[3] - $0[2]) * (1 + $0[5] - $0[4]) * (1 + $0[7] - $0[6]) * (1 + $0[9] - $0[8])) + | collect | map(; fold($1, 0, ^1 + ^0[0])) + | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/day22/input.txt b/interactive/examples/aoc2023/day22/input.txt new file mode 100644 index 000000000..5bf460533 --- /dev/null +++ b/interactive/examples/aoc2023/day22/input.txt @@ -0,0 +1,25 @@ +3,5,62~3,9,62 +5,5,623~5,1,623 +8,3,176~8,2,176 +7,6,17~7,8,17 +9,3,821~9,3,163 +2,9,71~2,6,71 +3,3,514~3,3,390 +7,4,494~7,9,494 +5,9,842~5,9,840 +9,1,41~9,1,296 +5,4,276~5,4,94 +3,3,838~3,6,838 +9,8,425~6,8,425 +1,2,55~1,8,55 +1,4,249~3,4,249 +8,8,541~5,8,541 +5,4,634~5,4,365 +4,9,745~4,9,293 +3,6,621~3,6,287 +4,9,645~4,9,389 +7,1,712~0,1,712 +8,2,69~7,2,69 +2,3,374~8,3,374 +7,9,495~0,9,495 +4,9,200~8,9,200 diff --git a/interactive/examples/aoc2023/day22/part1.ddp b/interactive/examples/aoc2023/day22/part1.ddp new file mode 100644 index 000000000..f1ebf5bcd --- /dev/null +++ b/interactive/examples/aoc2023/day22/part1.ddp @@ -0,0 +1,39 @@ +-- AoC 2023 day 22 part 1: bricks fall (interval overlap + max-rest fixpoint); +-- count bricks that uniquely support nothing. +-- input 0: (r, x1,y1,z1, x2,y2,z2) + +-- slt's generate_series drops bricks with ANY reversed axis; mirror that. +let bricks = input 0 | filter($0[1] <= $0[4] && $0[2] <= $0[5] && $0[3] <= $0[6]) + | key($0[0] ; $0[1], $0[2], $0[3], $0[4], $0[5], $0[6]); +-- (r ; xlo, ylo, zlo, xhi, yhi, zhi) + +let bu = bricks | key(; $0[0], $1[0], $1[1], $1[2], $1[3], $1[4], $1[5]); +let ovl = bu | join(bu, ($1[0], $2[0] ; + (if($1[1] > $2[1], $1[1], $2[1]) <= if($1[4] < $2[4], $1[4], $2[4])) + && (if($1[2] > $2[2], $1[2], $2[2]) <= if($1[5] < $2[5], $1[5], $2[5])) + && ($1[3] < $2[3]), + $1[6] - $1[3] + 1)) + | filter($1[0] == 1) | map($0[0] ; $0[1], $1[1]); +-- (lower ; upper, height_lower) for xy-overlapping pairs + +-- resting z per brick, stored negated (mz = BIG - z) so `min` computes max +rests: { + let lifted = rest | join(ovl, ($2[0] ; $1[0] - $2[1])); + var rest = (bricks | map($0[0] ; 999999)) + lifted | min; +} +let rest = rests::rest; +-- (r ; BIG - resting z), BIG = 1000000 + +-- direct supports: rest_top(lower) + 1 == rest_z(upper) +let s1 = ovl | join(rest, ($1[0] ; $0[0], $2[0] - $1[1])); +-- (upper ; lower, required mz_upper) +let sup = s1 | join(rest, ($1[0], $0[0], $1[1] == $2[0] ;)) + | filter($0[2] == 1) | map($0[0], $0[1] ;); +-- (lower, upper) + +let uniq2 = sup | map($0[1] ;) | count | filter($1[0] == 1) | map($0 ;); + +let critical = sup | map($0[1] ; $0[0]) | join(uniq2, ($1[0] ;)) | distinct; +let total = input 0 | key(;) | count; -- ALL lines, per the slt +export "result" = total | join(critical | key(;) | count, (; $1[0] - $2[0])) + | arrange | inspect(part1); diff --git a/interactive/examples/aoc2023/day22/part2.ddp b/interactive/examples/aoc2023/day22/part2.ddp new file mode 100644 index 000000000..e38184cdf --- /dev/null +++ b/interactive/examples/aoc2023/day22/part2.ddp @@ -0,0 +1,51 @@ +-- AoC 2023 day 22 part 2: transitive falls -- (a, b) when every supporter of b +-- falls with a; count pairs. +-- input 0: (r, x1,y1,z1, x2,y2,z2) + +-- slt's generate_series drops bricks with ANY reversed axis; mirror that. +let bricks = input 0 | filter($0[1] <= $0[4] && $0[2] <= $0[5] && $0[3] <= $0[6]) + | key($0[0] ; $0[1], $0[2], $0[3], $0[4], $0[5], $0[6]); +-- (r ; xlo, ylo, zlo, xhi, yhi, zhi) + +let bu = bricks | key(; $0[0], $1[0], $1[1], $1[2], $1[3], $1[4], $1[5]); +let ovl = bu | join(bu, ($1[0], $2[0] ; + (if($1[1] > $2[1], $1[1], $2[1]) <= if($1[4] < $2[4], $1[4], $2[4])) + && (if($1[2] > $2[2], $1[2], $2[2]) <= if($1[5] < $2[5], $1[5], $2[5])) + && ($1[3] < $2[3]), + $1[6] - $1[3] + 1)) + | filter($1[0] == 1) | map($0[0] ; $0[1], $1[1]); +-- (lower ; upper, height_lower) for xy-overlapping pairs + +-- resting z per brick, stored negated (mz = BIG - z) so `min` computes max +rests: { + let lifted = rest | join(ovl, ($2[0] ; $1[0] - $2[1])); + var rest = (bricks | map($0[0] ; 999999)) + lifted | min; +} +let rest = rests::rest; +-- (r ; BIG - resting z), BIG = 1000000 + +-- direct supports: rest_top(lower) + 1 == rest_z(upper) +let s1 = ovl | join(rest, ($1[0] ; $0[0], $2[0] - $1[1])); +-- (upper ; lower, required mz_upper) +let sup = s1 | join(rest, ($1[0], $0[0], $1[1] == $2[0] ;)) + | filter($0[2] == 1) | map($0[0], $0[1] ;); +-- (lower, upper) + +let uniq2 = sup | map($0[1] ;) | count | filter($1[0] == 1) | map($0 ;); + +let supL = sup | map($0[0] ; $0[1]); -- (lower ; upper) +let supcnt = sup | map($0[1] ;) | count; -- (b ; #supporters) +let seed = sup | map($0[1] ; $0[0]) | join(uniq2, ($1[0], $0[0] ;)); +-- (a, b): b uniquely supported by a + +trans: { + let fell = st | map($0[1] ; $0[0]) | join(supL, ($1[0], $2[0] ;)) + | map($0 ;) | count; + -- (a, b ; #supporters of b falling with a) + let full = fell | map($0[1] ; $0[0], $1[0]) + | join(supcnt, ($1[0], $0[0], $1[1] == $2[0] ;)) + | filter($0[2] == 1) | map($0[0], $0[1] ;); + var st = seed + full | distinct; +} + +export "result" = trans::st | key(;) | count | arrange | inspect(part2); diff --git a/interactive/examples/aoc2023/expected.txt b/interactive/examples/aoc2023/expected.txt new file mode 100644 index 000000000..d753192b0 --- /dev/null +++ b/interactive/examples/aoc2023/expected.txt @@ -0,0 +1,34 @@ +# day part answer (vec backend; oracle: Materialize sqllogictest advent-of-code/2023) +01 1 278 +01 2 391 +02 1 8 +02 2 2567 +03 1 11374 +03 2 1587570 +04 1 184 +04 2 978 +05 1 60602459 +05 2 5 +06 1 1180707528 +07 1 340665 +07 2 332531 +10 1 1439 +10 2 2073 +11 1 129655908 +11 2 129655908 +13 1 100 +13 2 16 +14 1 146 +14 2 105 +15 1 2021 +15 2 6155 +16 1 15 +16 2 613 +17 1 156 +17 2 190 +18 1 73 +18 2 34133752459 +19 1 42458 +19 2 -257636238955235 +22 1 23 +22 2 3 diff --git a/interactive/examples/aoc2023/run.sh b/interactive/examples/aoc2023/run.sh new file mode 100755 index 000000000..ade1193b1 --- /dev/null +++ b/interactive/examples/aoc2023/run.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Run every AoC 2023 program on the vec backend and check the answers. +# Usage: ./run.sh [path/to/ddir] (build with: cargo build --release --example ddir) +cd "$(dirname "$0")" || exit 1 +DDIR=${1:-../../../target/release/examples/ddir} +python3 transcribe.py || exit 1 # dense dayNN/input.txt -> gen/dayNN/ fact files +fail=0 +while read -r day part expected; do + case "$day" in ''|'#'*) continue;; esac + dir=day$day + inp=gen/$dir/input.txt + [ -f "gen/$dir/input$part.txt" ] && inp=gen/$dir/input$part.txt # day05/day15: per-part inputs + arity=$(head -1 "$inp" | awk '{print NF}') + got=$(EDGES_FILE=$inp "$DDIR" --backend=vec "$dir/part$part.ddp" "$arity" 10 0 1 0 2>&1 \ + | sed -n "s/.*\\[part$part\\].*Int(\\(-\\{0,1\\}[0-9]*\\)).*/\\1/p") + if [ "$got" = "$expected" ]; then + echo "day$day part$part: ok ($got)" + else + echo "day$day part$part: FAIL (expected $expected, got '$got')" + fail=1 + fi +done < expected.txt +exit $fail diff --git a/interactive/examples/aoc2023/transcribe.py b/interactive/examples/aoc2023/transcribe.py new file mode 100644 index 000000000..a0b3623bb --- /dev/null +++ b/interactive/examples/aoc2023/transcribe.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +"""Regenerate the i64 fact files the .ddp programs consume from the dense +puzzle inputs committed as dayNN/input.txt (the compact text form of the +Materialize sqllogictest oracles). Output goes to gen/dayNN/ (gitignored); +run.sh invokes this before running the suite. + +Per-day modes: grids become "row col charcode" (or digit) cell facts, 0- or +1-indexed to mirror each day's SQL; text lines become charcode or parsed +numeric facts; numeric rows pass through; day05 and day15 split one dense +input into the two per-part fact files. Passing --pad additionally writes +day05's zero-padded uniform-arity copies (input1p.txt / input2p.txt) for +the corgi backend, which crashes on mixed-arity inputs. +""" +import os, re, sys + +HERE = os.path.dirname(os.path.abspath(__file__)) + + +def dense(day): + with open(os.path.join(HERE, day, 'input.txt')) as f: + return f.read().rstrip('\n') + + +def emit(day, rows, name='input.txt'): + path = os.path.join(HERE, 'gen', day) + os.makedirs(path, exist_ok=True) + with open(os.path.join(path, name), 'w') as f: + for row in rows: + print(*row, file=f) + + +def cells(text, base, cell=ord): + """Grid/text -> one fact per character: (row, col, cell(ch)).""" + return [(r, c, cell(ch)) for r, line in enumerate(text.split('\n'), base) + for c, ch in enumerate(line, base)] + + +def day02(text): + """'Game N: 1 red, 4 green; ...' -> (game, set, green, red, blue).""" + rows = [] + for line in text.split('\n'): + m = re.match(r"Game (\d+): (.*)", line) + for k, s in enumerate(m.group(2).split(';')): + cnt = dict.fromkeys(('green', 'red', 'blue'), 0) + for n, color in re.findall(r"(\d+) (\w+)", s): + cnt[color] = int(n) + rows.append((m.group(1), k, cnt['green'], cnt['red'], cnt['blue'])) + return rows + + +def day04(text): + """'Card N: w.. | h..' -> (card, side, idx, value); side 0 = winning.""" + rows = [] + for line in text.split('\n'): + m = re.match(r"Card\s+(\d+): (.*)", line) + for side, nums in enumerate(m.group(2).split('|')): + rows += [(int(m.group(1)), side, i, v) + for i, v in enumerate(nums.split())] + return rows + + +def day05(pad): + """Seeds + 7 maps -> input1: '0 seed' / '1 stage dst src len'; + input2: seeds read as (start, len) pairs, same map entries.""" + blocks = dense('day05').split('\n\n') + seeds = [int(x) for x in blocks[0].split(':')[1].split()] + entries = [(1, stage, *line.split()) + for stage, b in enumerate(blocks[1:]) + for line in b.strip().split('\n') + if line.strip() and not line.endswith('map:')] + parts = { + 'input1.txt': [(0, s) for s in seeds] + entries, + 'input2.txt': [(0, seeds[i], seeds[i + 1]) + for i in range(0, len(seeds), 2)] + entries, + } + for name, rows in parts.items(): + emit('day05', rows, name) + if pad: + arity = max(len(r) for r in rows) + emit('day05', [r + (0,) * (arity - len(r)) for r in rows], + name.replace('.txt', 'p.txt')) + + +def day07(text): + """'XXXXX bid' -> (id, c1..c5, bid) with hand charcodes.""" + return [(i, *(ord(c) for c in hand), bid) + for i, (hand, bid) in enumerate(ln.split() for ln in text.split('\n'))] + + +def day13(text): + """Blank-line-separated grids -> (block, row, col, charcode), 1-indexed.""" + return [(b, r, c, ord(ch)) for b, blk in enumerate(text.split('\n\n'), 1) + for r, line in enumerate(blk.split('\n'), 1) + for c, ch in enumerate(line, 1)] + + +def day15(): + """Comma-separated commands -> input1: (cmd, pos, charcode); + input2: (cmd, l1, l2, opchar, digitcode_or_0).""" + cmds = dense('day15').split(',') + emit('day15', [(r, i, ord(ch)) for r, cmd in enumerate(cmds, 1) + for i, ch in enumerate(cmd, 1)], 'input1.txt') + rows = [] + for r, cmd in enumerate(cmds, 1): + if cmd.endswith('-'): + rows.append((r, ord(cmd[0]), ord(cmd[1]), ord('-'), 0)) + else: + lab, foc = cmd.split('=') + rows.append((r, ord(lab[0]), ord(lab[1]), ord('='), ord(foc))) + emit('day15', rows, 'input2.txt') + + +def day18(text): + """'D N (#xxxxxx)' -> (line, dircode, N, h1..h6) with hex charcodes.""" + rows = [] + for i, line in enumerate(text.split('\n'), 1): + m = re.match(r"([UDLR]) (\d+) \(#([0-9a-f]{6})\)", line) + rows.append((i, ord(m.group(1)), m.group(2), *(ord(c) for c in m.group(3)))) + return rows + + +def day19(text): + """Workflows + parts. State names -> ids (in=0, A=-1, R=-2, rest 3+); + rules: (1, state, prio, field, cmp, val, next); parts: (0, x, m, a, s, 0, 0).""" + wf_txt, parts_txt = text.split('\n\n') + ids = {'in': 0, 'A': -1, 'R': -2} + sid = lambda name: ids.setdefault(name, len(ids)) + rows = [] + for line in wf_txt.strip().split('\n'): + m = re.match(r"(\w+)\{(.*)\}", line) + st = sid(m.group(1)) + for prio, rule in enumerate(m.group(2).split(','), 1): + mm = re.match(r"([xmas])([<>])(\d+):(\w+)", rule) + if mm: + rows.append((1, st, prio, 'xmas'.index(mm.group(1)), + 0 if mm.group(2) == '<' else 1, + int(mm.group(3)), sid(mm.group(4)))) + else: + rows.append((1, st, prio, 0, 1, 0, sid(rule))) # always: x > 0 + for line in parts_txt.strip().split('\n'): + x, m_, a, s = map(int, re.findall(r"=(\d+)", line)) + rows.append((0, x, m_, a, s, 0, 0)) + return rows + + +def day22(text): + """'x,y,z~x,y,z' -> (line, x1, y1, z1, x2, y2, z2), 1-indexed.""" + return [(i, *a.split(','), *b.split(',')) + for i, (a, b) in enumerate((ln.split('~') for ln in text.split('\n')), 1)] + + +MODES = { + 'day01': lambda t: cells(t, 0), # text lines, 0-indexed + 'day02': day02, + 'day03': lambda t: cells(t, 0), # grid, 0-indexed + 'day04': day04, + 'day06': lambda t: [ln.split() for ln in t.split('\n')], # numeric passthrough + 'day07': day07, + 'day10': lambda t: cells(t, 1), # grid, 1-indexed + 'day11': lambda t: cells(t, 1), + 'day13': day13, + 'day14': lambda t: cells(t, 1), + 'day16': lambda t: cells(t, 1), + 'day17': lambda t: cells(t, 1, cell=int), # digit grid + 'day18': day18, + 'day19': day19, + 'day22': day22, +} + +if __name__ == '__main__': + days = [a for a in sys.argv[1:] if not a.startswith('--')] + for day in days or sorted(MODES) + ['day05', 'day15']: + if day == 'day05': + day05('--pad' in sys.argv[1:]) + elif day == 'day15': + day15() + else: + emit(day, MODES[day](dense(day)))