about summary refs log tree commit diff
path: root/users/sterni/exercises/aoc
diff options
context:
space:
mode:
Diffstat (limited to 'users/sterni/exercises/aoc')
-rw-r--r--users/sterni/exercises/aoc/.gitignore2
-rw-r--r--users/sterni/exercises/aoc/2021/default.nix10
-rwxr-xr-xusers/sterni/exercises/aoc/2021/solutions.bqn484
-rw-r--r--users/sterni/exercises/aoc/2022/.skip-subtree1
-rw-r--r--users/sterni/exercises/aoc/2022/01/1.bqn7
-rw-r--r--users/sterni/exercises/aoc/2022/01/1.k1
-rw-r--r--users/sterni/exercises/aoc/2022/02/2.bqn7
-rw-r--r--users/sterni/exercises/aoc/2022/02/2.k1
-rw-r--r--users/sterni/exercises/aoc/2022/03/3.bqn8
-rw-r--r--users/sterni/exercises/aoc/2022/03/3.k1
-rw-r--r--users/sterni/exercises/aoc/2022/04/4.bqn11
-rw-r--r--users/sterni/exercises/aoc/2022/05/5.bqn18
-rw-r--r--users/sterni/exercises/aoc/2022/06/6.bqn4
-rw-r--r--users/sterni/exercises/aoc/2022/06/6.k1
-rw-r--r--users/sterni/exercises/aoc/2022/07/7.bqn24
-rw-r--r--users/sterni/exercises/aoc/2022/08/8.bqn15
-rw-r--r--users/sterni/exercises/aoc/2022/09/9.bqn17
-rw-r--r--users/sterni/exercises/aoc/2022/10/10.bqn25
-rw-r--r--users/sterni/exercises/aoc/2022/11/11.bqn41
-rw-r--r--users/sterni/exercises/aoc/2022/12/12.bqn16
-rw-r--r--users/sterni/exercises/aoc/2022/13/13.bqn14
-rw-r--r--users/sterni/exercises/aoc/2022/15/15.bqn18
-rw-r--r--users/sterni/exercises/aoc/2022/16/16.k21
-rw-r--r--users/sterni/exercises/aoc/2022/17/17.bqn51
-rw-r--r--users/sterni/exercises/aoc/2022/18/18.bqn14
-rw-r--r--users/sterni/exercises/aoc/2022/20/20.bqn13
-rw-r--r--users/sterni/exercises/aoc/2022/21/21.bqn25
-rw-r--r--users/sterni/exercises/aoc/2022/25/25.bqn4
-rw-r--r--users/sterni/exercises/aoc/2022/25/25.k1
-rw-r--r--users/sterni/exercises/aoc/2022/README.md8
-rw-r--r--users/sterni/exercises/aoc/2022/default.nix53
-rw-r--r--users/sterni/exercises/aoc/lib.bqn18
32 files changed, 934 insertions, 0 deletions
diff --git a/users/sterni/exercises/aoc/.gitignore b/users/sterni/exercises/aoc/.gitignore
new file mode 100644
index 0000000000..b9720d7451
--- /dev/null
+++ b/users/sterni/exercises/aoc/.gitignore
@@ -0,0 +1,2 @@
+/*/input
+/*/*/input
\ No newline at end of file
diff --git a/users/sterni/exercises/aoc/2021/default.nix b/users/sterni/exercises/aoc/2021/default.nix
new file mode 100644
index 0000000000..d3ed563ec6
--- /dev/null
+++ b/users/sterni/exercises/aoc/2021/default.nix
@@ -0,0 +1,10 @@
+{ depot ? import ../../../../.. { }
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+pkgs.mkShell {
+  nativeBuildInputs = [
+    pkgs.cbqn
+  ];
+}
diff --git a/users/sterni/exercises/aoc/2021/solutions.bqn b/users/sterni/exercises/aoc/2021/solutions.bqn
new file mode 100755
index 0000000000..755c944046
--- /dev/null
+++ b/users/sterni/exercises/aoc/2021/solutions.bqn
@@ -0,0 +1,484 @@
+#!/usr/bin/env BQN
+
+⟨Xor⟩ ← •Import "../lib.bqn"
+
+#
+# Utilities
+#
+
+⟨IsAsciiNum,ReadInt,ReadDec,SplitOn,_fix⟩ ← •Import •path∾"/../lib.bqn"
+
+ReadInput ← {•file.Lines ∾ •path‿"/input/day"‿(•Fmt 𝕩)}
+
+#
+# 2021-12-01
+#
+
+# part 1
+
+day1ExampleInput ← 199‿200‿208‿210‿200‿207‿240‿269‿260‿263
+day1Input ← ReadDec¨ReadInput 1
+
+# NB: Because distance from the ground is never smaller than zero, it's
+# no problem that nudge inserts a zero at the end of the right list
+PositiveDeltaCount ← +´∘(⊢<«)+˝˘∘↕
+
+! 7 = 1 PositiveDeltaCount day1ExampleInput
+
+•Out "Day 1.1: "∾•Fmt 1 PositiveDeltaCount day1Input
+
+# part 2
+
+! 5 = 3 PositiveDeltaCount day1ExampleInput
+
+•Out "Day 1.2: "∾•Fmt 3 PositiveDeltaCount day1Input
+
+#
+# 2021-12-02
+#
+
+# part 1
+
+day2ExampleInput ← ⟨
+  "forward 5",
+  "down 5",
+  "forward 8",
+  "up 3",
+  "down 8",
+  "forward 2",
+⟩
+
+day2Input ← ReadInput 2
+
+ParseSubmarineCommand ← (((↕2)⊸((((-1)⊸⋆)∘(2⊸|))×(=⟜(⌊∘(÷⟜2))))∘("duf"⊸⊐)∘⊑)×ReadDec∘(IsAsciiNum/⊢))
+
+SubmarineDestProduct ← {×´+´ParseSubmarineCommand¨𝕩}
+
+! 150 = SubmarineDestProduct day2ExampleInput
+
+•Out "Day 2.1: "∾•Fmt SubmarineDestProduct day2Input
+
+# part 2
+
+SubmarineAimedDestProduct ← {
+  ×´+´((×´)∘(1⊸↓)≍(1⊸⊑))¨ (<0‿0‿0) (⊢∾((⊑∘⌽⊣)+(⊑⊢)))` ParseSubmarineCommand¨𝕩
+}
+
+! 900 = SubmarineAimedDestProduct day2ExampleInput
+
+•Out "Day 2.2: "∾•Fmt SubmarineAimedDestProduct day2Input
+
+#
+# 2021-12-03
+#
+
+BinTable ← '0'-˜>
+
+day3ExampleInput ← BinTable ⟨
+  "00100",
+  "11110",
+  "10110",
+  "10111",
+  "10101",
+  "01111",
+  "00111",
+  "11100",
+  "10000",
+  "11001",
+  "00010",
+  "01010",
+⟩
+
+day3Input ← BinTable ReadInput 3
+
+DeBinList ← ((2⊸×)+⊣)´⌽
+_tableAggr ← {((÷⟜2)∘(/⟜⥊)´∘⌽∘≢𝔽(+˝))𝕩}
+GammaRate ← < _tableAggr
+
+! 22 = DeBinList GammaRate day3ExampleInput
+! 9  = DeBinList ¬GammaRate day3ExampleInput
+
+•Out "Day 3.1: "∾•Fmt (¬×○DeBinList⊢) GammaRate day3Input
+
+_lifeSupportRating ← {
+  # Need to rename the arguments, otherwise the ternary expr becomes a function
+  bitPos ← 𝕨
+  Cmp ← 𝔽
+
+  crit ← Cmp _tableAggr 𝕩
+  matchPos ← bitPos ⊑˘ crit ((⥊˜⟜≢)=⊢) 𝕩
+  match ← matchPos/𝕩
+  {1=≠match?⊏match;(bitPos+1) Cmp _lifeSupportRating match}
+}
+
+OxygenGeneratorRating ← DeBinList 0 ≤_lifeSupportRating ⊢
+CO2ScrubberRating ← DebinList 0 >_lifeSupportRating ⊢
+
+! 23 = OxygenGeneratorRating day3ExampleInput
+! 10 = CO2ScrubberRating day3ExampleInput
+
+•Out "Day 3.2: "∾•Fmt (OxygenGeneratorRating×CO2ScrubberRating) day3Input
+
+#
+# 2021-12-04
+#
+
+day4Numbers ← ReadDec¨ ',' SplitOn ⊑ReadInput 4
+day4Boards ← ReadDec¨>˘(' '⊸SplitOn¨)> (<⟨⟩) SplitOn 2↓ReadInput 4
+
+BoardWins ← {C ← ∨´∘(∧´˘) ⋄ (C∨C∘⍉)𝕩}
+
+_CallNumber ← {(𝕗∊⥊𝕩) (∨⍟(¬∘BoardWins∘⊢))˘ 𝕨}
+
+BoardWinScores ← {
+  𝕩 (0⊸</×) (⊢-») (+´)∘(BoardWins˘/(+´⥊)˘∘(𝕨⊸×⟜¬))¨ (<0⥊˜≢𝕨) (𝕨 _CallNumber)`𝕩
+}
+
+day4WinScores ← day4Boards BoardWinScores day4Numbers
+
+•Out "Day 4.1: "∾•Fmt ⊑day4WinScores
+•Out "Day 4.2: "∾•Fmt ⊑⌽day4WinScores
+
+#
+# 2021-12-06
+#
+
+day6ExampleInput ← ⟨3,4,3,1,2⟩
+day6Input ← ReadDec¨ ',' SplitOn ⊑ReadInput 6
+
+LanternfishPopulation ← {+´ (1⊸⌽+(⊑×((6⊸=)∘↕∘≠)))⍟𝕨 9↑≠¨⊔ 𝕩}
+
+! 26 = 18 LanternfishPopulation day6ExampleInput
+! 5934 = 80 LanternfishPopulation day6ExampleInput
+
+•Out "Day 6.1: "∾•Fmt 80 LanternfishPopulation day6Input
+•Out "Day 6.2: "∾•Fmt 256 LanternfishPopulation day6Input
+
+#
+# 2021-12-07
+#
+
+# part 1
+
+day7ExampleInput ← ⟨16,1,2,0,4,2,7,1,2,14⟩
+day7Input ← ReadDec¨ ','  SplitOn ⊑ReadInput 7
+
+PossiblePositions ← (⌊´+⟜(↕1⊸+)⌈´)
+FuelConsumption ← +˝∘|∘(-⌜)
+_lowestFuelPossible ← {⌊´∘(𝔽⟜PossiblePositions)˜ 𝕩}
+
+! 37 = FuelConsumption _lowestFuelPossible day7ExampleInput
+
+•Out "Day 7.1: "∾•Fmt FuelConsumption _lowestFuelPossible day7Input
+
+# part 2
+
+TriNum ← 1⊸+×÷⟜2
+
+FuelConsumption2 ← +˝∘(TriNum¨)∘|∘(-⌜)
+
+! 168 = FuelConsumption2 _lowestFuelPossible day7ExampleInput
+
+•Out "Day 7.2: "∾•Fmt FuelConsumption2 _lowestFuelPossible day7Input
+
+#
+# 2021-12-09
+#
+
+# part 1
+
+ParseHeightMap ← ((≠≍(≠⊑))⥊∾)∘-⟜'0'
+
+day9ExampleInput ← ParseHeightMap ⟨
+  "2199943210",
+  "3987894921",
+  "9856789892",
+  "8767896789",
+  "9899965678"
+⟩
+day9Input ← ParseHeightMap ReadInput 9
+
+Rotate ← (⍉⌽)∘⊢⍟⊣ # counter clockwise
+LowPoints ← {∧´𝕩⊸(⊣<((-⊢) Rotate ∞⊸»˘∘Rotate˜))¨ ↕4}
+
+RiskLevelSum ← (+´⥊)∘(1⊸+×LowPoints)
+
+! 15 = RiskLevelSum day9ExampleInput
+
+•Out "Day 9.1: "∾•Fmt RiskLevelSum day9Input
+
+# part 2
+
+NumberBasins ← ((1⊸+⊒⌾⥊)×⊢)∘LowPoints
+Basins ← {𝕩⊸((<⟜9⊣)∧(«⌈»⌈«˘⌈»˘⌈⊢)∘⊢) _fix NumberBasins 𝕩}
+LargestBasinsProduct ← {×´ 3↑ ∨ 1↓ ≠¨ ⊔⥊Basins 𝕩}
+
+! 1134 = LargestBasinsProduct day9ExampleInput
+
+•Out "Day 9.2: "∾•Fmt LargestBasinsProduct day9Input
+
+#
+# 2021-12-10
+#
+
+day10ExampleInput ← ⟨
+  "[({(<(())[]>[[{[]{<()<>>",
+  "[(()[<>])]({[<{<<[]>>(",
+  "{([(<{}[<>[]}>{[]{[(<()>",
+  "(((({<>}<{<{<>}{[]{[]{}",
+  "[[<[([]))<([[{}[[()]]]",
+  "[{[{({}]{}}([{[{{{}}([]",
+  "{<[[]]>}<{[{[{[]{()[[[]",
+  "[<(<(<(<{}))><([]([]()",
+  "<{([([[(<>()){}]>(<<{{",
+  "<{([{{}}[<[[[<>{}]]]>[]]",
+⟩
+day10Input ← ReadInput 10
+
+# part 1
+
+opp ← "([{<"
+clp ← ")]}>"
+SwapParen ← (opp∾⌽clp)⊸((⊑⊐)⊑(⌽⊣))
+
+ParenStacks ← ((<⟨⟩)⊸(((⊑∊)⟜clp⊢)◶(∾˜⟜SwapParen)‿(1⊸↓⊣)`))
+LegalParens ← ((1⊸↑)¨∘»∘ParenStacks ((∊⟜opp⊢)∨(≡⟜⋈)¨) ⊢)
+
+_ScoreFor_ ← {𝕗⊸(𝕘⊸⊐⊏⊣) 𝕩}
+
+SyntaxScore ← +´∘(0‿3‿57‿1197‿25137 _ScoreFor_ (" "∾clp))∘∾∘(1⊸↑∘(¬∘LegalParens/⊢)¨)
+
+! 26397 = SyntaxScore day10ExampleInput
+•Out "Day 10.1: "∾•Fmt SyntaxScore day10Input
+
+# part 2
+
+AutocompleteScore ← {
+  Score ← (5⊸×⊸+)˜´∘⌽∘((1+↕4) _ScoreFor_ clp)
+  # TODO(sterni): we compute ParenStacks twice here
+  ((⌊÷⟜2)∘≠⊑⊢) ∧ Score∘(⊑⌽)∘ParenStacks¨ (∧´∘LegalParens¨/⊢) 𝕩
+}
+
+! 288957 = AutocompleteScore day10ExampleInput
+•Out "Day 10.2: "∾•Fmt AutocompleteScore day10Input
+
+#
+# 2021-12-11
+#
+
+day11Input ← '0'-˜> ReadInput 11
+day11ExampleInput ← >⟨
+  ⟨5,4,8,3,1,4,3,2,2,3,⟩,
+  ⟨2,7,4,5,8,5,4,7,1,1,⟩,
+  ⟨5,2,6,4,5,5,6,1,7,3,⟩,
+  ⟨6,1,4,1,3,3,6,1,4,6,⟩,
+  ⟨6,3,5,7,3,8,5,4,7,8,⟩,
+  ⟨4,1,6,7,5,2,4,6,4,5,⟩,
+  ⟨2,1,7,6,8,4,1,7,2,1,⟩,
+  ⟨6,8,8,2,8,8,1,1,3,4,⟩,
+  ⟨4,8,4,6,8,4,8,5,5,4,⟩,
+  ⟨5,2,8,3,7,5,1,5,2,6,⟩,
+⟩
+
+# part 1
+
+OctopusFlash ← {
+  ((⥊⟜0)∘≢𝕊⊢) 𝕩;
+  flashing ← (¬𝕨)∧9<𝕩
+  energy ← ((«˘»)+(»˘«)+(»˘»)+(«˘«)+(»˘)+(«˘)+«+») flashing
+  ((𝕨∨flashing)⊸𝕊)⍟(0<+´⥊flashing) energy+𝕩
+}
+
+OctopusStep ← ((9⊸≥)×⊢)∘OctopusFlash∘(1⊸+)
+OctopusFlashCount ← {+´⥊0=>(OctopusStep⊣)`(1+𝕨)⥊<𝕩}
+
+! 1656 = 100 OctopusFlashCount day11ExampleInput
+•Out "Day 11.1: "∾•Fmt 100 OctopusFlashCount day11Input
+
+# part 2
+
+_iterCountUntil_ ← {
+  0 𝕊 𝕩;
+  𝔾◶⟨((𝕨+1)⊸𝕊)∘𝔽, 𝕨˙⟩ 𝕩
+}
+
+OctopusAllFlashing ← OctopusStep _iterCountUntil_ (∧´∘⥊∘(0⊸=))
+
+! 195 = OctopusAllFlashing day11ExampleInput
+
+•Out "Day 11.2: "∾•Fmt OctopusAllFlashing day11Input
+
+#
+# 2021-12-13
+#
+
+SplitFoldingInstructions ← ("fold along"⊸(⊣≡≠⊸↑)¨⊔⊢)∘(0⊸(≠⟜≠¨/⊢))
+day13ExampleInput ← SplitFoldingInstructions ⟨
+  "6,10",
+  "0,14",
+  "9,10",
+  "0,3",
+  "10,4",
+  "4,11",
+  "6,0",
+  "6,12",
+  "4,1",
+  "0,13",
+  "10,12",
+  "3,4",
+  "3,0",
+  "8,4",
+  "1,10",
+  "2,14",
+  "8,10",
+  "9,0",
+  "",
+  "fold along y=7",
+  "fold along x=5",
+⟩
+day13Input ← SplitFoldingInstructions ReadInput 13
+
+ParseDots ← ReadDec¨∘(','⊸SplitOn)¨
+ParseFolds ← (⊑∘'y'⊸∊≍ReadDec∘(IsAsciiNum/⊢))¨
+day13ExampleDots ← ParseDots ⊑ day13ExampleInput
+
+# part 1
+
+# 𝕨=0 => x, 𝕨=1 => y
+# 𝕩 is coordinate to fold around
+# 𝕗 is input dot list (see ParseDots)
+_Fold ← {⍷∘((𝕩⊸(((2⊸×⊣)-⊢)⌊⊢)∘⊑≍1⊸⊑)¨⌾(⌽¨⍟𝕨)) 𝕗}
+
+! 17 = ≠ 1 day13ExampleDots _Fold 7
+
+day13Dots ← ParseDots ⊑ day13Input
+day13Folds ← ParseFolds 1 ⊑ day13Input
+
+•Out "Day 13.1: "∾•Fmt ≠ (day13Dots _Fold)´ ⊑day13Folds
+
+# part 2
+
+PerformAllFolds ← {𝕩 {(𝕨 _Fold)´𝕩}˜´ ⌽𝕨}
+DotMatrix ← {
+  ⟨width, height⟩ ← 1+⌈˝∘‿2⥊∾𝕩
+  {𝕩? '█';' '}¨ height‿width⥊≠¨⊔((⊣+(width⊸×)∘⊢)´)¨ 𝕩
+}
+
+•Out "Day 13.2:"
+•Out •Fmt DotMatrix day13Folds PerformAllFolds day13Dots
+
+#
+# 2021-12-14
+#
+
+day14Polymer ← ⊑ReadInput 14
+day14Mapping ← 2↓ReadInput 14
+
+lp ← (2⊸↑)¨ day14Mapping
+le ← ⍷∾lp
+
+# returns array as long as 𝕨 detailing how many times the element
+# at any given index occurs in 𝕩.
+Counts ← ((≠⊣)↑(/⁼)∘⊐)
+
+deltaPairs ← {
+  addedPairs ← ((-1)⊸⊑¨day14Mapping) (⌽⌾(0⊸⊑))∘(∾¨)¨ lp
+  removedPairs ← ⋈¨ (2⊸↑)¨ lp
+  addedPairs (-○(lp⊸Counts))¨ removedPairs
+}
+
+pairCount ← lp Counts ⥊∘(⋈˘) 2↕day14Polymer
+
+PairInsert ← {𝕩 +´ 𝕩רdeltaPairs}
+
+pairElementCount ← (le⊸Counts)¨lp
+
+ElementRarityDiff ← {
+ ((-1)⊸⊑-⊑)∧ ⌈2÷˜ +´ pairElementCount×PairInsert⍟𝕩 pairCount
+}
+
+•Out "Day 14.1: "∾•Fmt ElementRarityDiff 10
+•Out "Day 14.2: "∾•Fmt ElementRarityDiff 40
+
+#
+# 2021-12-15
+#
+
+day15ExampleInput ← >⟨
+  1‿1‿6‿3‿7‿5‿1‿7‿4‿2
+  1‿3‿8‿1‿3‿7‿3‿6‿7‿2
+  2‿1‿3‿6‿5‿1‿1‿3‿2‿8
+  3‿6‿9‿4‿9‿3‿1‿5‿6‿9
+  7‿4‿6‿3‿4‿1‿7‿1‿1‿1
+  1‿3‿1‿9‿1‿2‿8‿1‿3‿7
+  1‿3‿5‿9‿9‿1‿2‿4‿2‿1
+  3‿1‿2‿5‿4‿2‿1‿6‿3‿9
+  1‿2‿9‿3‿1‿3‿8‿5‿2‿1
+  2‿3‿1‿1‿9‿4‿4‿5‿8‿1
+⟩
+day15Input ← '0'-˜ ((≠⋈≠∘⊑)⥊∾)ReadInput 15
+
+LowestRiskLevel ← {
+  start ← 0˙⌾⊑ (⥊⟜∞) ≢𝕩
+  ir ← (1⊑≢𝕩)⥊∞
+  Step ← {𝕩 ⌊ 𝕨 + (ir⊸«⌊ir⊸»⌊∞⊸«˘⌊∞⊸»˘) 𝕩}
+  ⊑⌽⥊ 𝕩⊸Step _fix start
+}
+
+! 40 = LowestRiskLevel day15ExampleInput
+
+•Out "Day 15.1: "∾•Fmt LowestRiskLevel day15Input
+
+FiveByFiveMap ← {(9⊸|)⌾(-⟜1) ∾(<𝕩)+ +⌜˜↕5}
+
+! 315 = LowestRiskLevel FiveByFiveMap day15ExampleInput
+
+•Out "Day 15.2: "∾•Fmt LowestRiskLevel FiveByFiveMap day15Input
+
+#
+# 2021-12-20
+#
+
+ParsePic ← (⋈⟜0)∘('#'⊸=)∘>
+
+day20ExampleAlgo ← '#'="..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#"
+day20ExamplePic ← ParsePic ⟨"#..#.", "#....", "##..#", "..#..", "..###"⟩
+day20Input ← ReadInput 20
+day20Algo ← '#'=⊑day20Input
+day20Pic ← ParsePic 2↓day20Input
+
+GrowAxis ← {(⊢ (-1)⊸⌽∘∾ (⥊⟜𝕨)∘(2˙⌾⊑)∘≢) 𝕩}
+Grow ← {𝕨 GrowAxis 𝕨 GrowAxis˘ 𝕩}
+
+Enhance ← {
+  inf ← 1⊑𝕩
+  npic ← ((⊑⟜𝕨)∘DebinList∘⥊)˘˘ 3‿3↕ (inf⊸Grow)⍟2 ⊑𝕩
+  ninf ← 𝕨⊑˜511×inf
+  npic⋈ninf
+}
+_EnhancedPixelCount ← {+´⥊⊑ (𝕨⊸Enhance)⍟𝕗 𝕩}
+
+! 35 = day20ExampleAlgo 2 _EnhancedPixelCount day20ExamplePic
+! 3351 = day20ExampleAlgo 50 _EnhancedPixelCount day20ExamplePic
+
+•Out "Day 20.1: "∾•Fmt day20Algo 2 _EnhancedPixelCount day20Pic
+•Out "Day 20.2: "∾•Fmt day20algo 50 _EnhancedPixelCount day20Pic
+
+#
+# 2021-12-25
+#
+
+day25Input ← ".>v" ⊐ > ReadInput 25
+day25ExampleInput ← ".>v"⊐∘‿10⥊"v...>>.vv>.vv>>.vv..>>.>v>...v>>v>>.>.v.v>v.vv.v..>.>>..v....vv..>.>v.v.v..>>v.v....v..v.>"
+
+MoveHerd ← {(𝕩∧𝕩≠𝕨)+𝕨× (𝕨=𝕩) (Xor⟜(1⊸⌽)∨⊢) (0=𝕩)∧(-1)⌽𝕨=𝕩}
+
+_fixCount ← {
+  1 𝕊 𝕩;
+  𝕩 ≡◶⟨(𝕨+1)⊸𝕊, 𝕨˙⟩ 𝔽 𝕩
+}
+
+MoveAllHerds ← (2⊸MoveHerd)∘(1⊸MoveHerd˘)
+
+! 58 = MoveAllHerds _fixCount day25ExampleInput
+•Out "Day 25.1: "∾•Fmt MoveAllHerds _fixCount day25Input
diff --git a/users/sterni/exercises/aoc/2022/.skip-subtree b/users/sterni/exercises/aoc/2022/.skip-subtree
new file mode 100644
index 0000000000..39d1894495
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/.skip-subtree
@@ -0,0 +1 @@
+nix solutions don't use readTree and the rest is non-nix
diff --git a/users/sterni/exercises/aoc/2022/01/1.bqn b/users/sterni/exercises/aoc/2022/01/1.bqn
new file mode 100644
index 0000000000..022b476aa9
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/01/1.bqn
@@ -0,0 +1,7 @@
+lib ← •Import •path∾"/../../lib.bqn"
+input ← lib.ReadDec¨¨ (<"") lib.SplitOn •FLines •path∾"/input"
+
+a‿·‿b ← +`3↑∨+´¨ input
+
+•Out "day01.1: "∾•Fmt a
+•Out "day01.2: "∾•Fmt b
diff --git a/users/sterni/exercises/aoc/2022/01/1.k b/users/sterni/exercises/aoc/2022/01/1.k
new file mode 100644
index 0000000000..42d64dfb6c
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/01/1.k
@@ -0,0 +1 @@
+(+\e@3#>e:(+/.'1_)'(&0=#'i)_i:0:"input")_1
diff --git a/users/sterni/exercises/aoc/2022/02/2.bqn b/users/sterni/exercises/aoc/2022/02/2.bqn
new file mode 100644
index 0000000000..65e3c817bb
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/02/2.bqn
@@ -0,0 +1,7 @@
+lib ← •Import •path∾"/../../lib.bqn"
+i ← 3|"ABCXYZ"⊸⊐¨ ' ' ⊑¨∘lib.SplitOn¨ •FLines •path∾"/input"
+S1 ← {1+𝕩+3×3|1+𝕩-𝕨}
+S2 ← {𝕨 S1 3|𝕨+𝕩-1}
+
+•Out "day02.1: "∾•Fmt +´S1´¨i
+•Out "day02.2: "∾•Fmt +´S2´¨i
diff --git a/users/sterni/exercises/aoc/2022/02/2.k b/users/sterni/exercises/aoc/2022/02/2.k
new file mode 100644
index 0000000000..9b6d10058d
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/02/2.k
@@ -0,0 +1 @@
++/{{y+1+3*3!1+y-x}[x]'y,3!x+y-1}.'3!"ABCXYZ"?(0:"input")_'1
diff --git a/users/sterni/exercises/aoc/2022/03/3.bqn b/users/sterni/exercises/aoc/2022/03/3.bqn
new file mode 100644
index 0000000000..642fccd450
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/03/3.bqn
@@ -0,0 +1,8 @@
+i ← •FLines •path∾"/input"
+c ← ∾(↕26)⊸+¨"aA"
+P ← {1+⊑c⊐⊑𝕩}
+S ← (∊/⊣)
+G ← ((⊣/(↕÷˜⟜≠))⊔⊢)
+
+•Out "day03.1: "∾•Fmt +´(P S˝)¨2‿∘⊸⥊¨i
+•Out "day03.2: "∾•Fmt +´3(P S´)¨∘G i
diff --git a/users/sterni/exercises/aoc/2022/03/3.k b/users/sterni/exercises/aoc/2022/03/3.k
new file mode 100644
index 0000000000..3e31f5f32c
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/03/3.k
@@ -0,0 +1 @@
++/'(58*r<1)+r:-96+(,/{?y@&~^x?y}/')'((2 0N#)';0N 3#)@\:0:"input"
diff --git a/users/sterni/exercises/aoc/2022/04/4.bqn b/users/sterni/exercises/aoc/2022/04/4.bqn
new file mode 100644
index 0000000000..0b8f1b4500
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/04/4.bqn
@@ -0,0 +1,11 @@
+⟨SplitOn, ReadDec⟩ ← •Import "../../lib.bqn"
+
+Sections ← {
+  a‿b ← ReadDec¨ (<'-') SplitOn 𝕩
+  ↕⌾(-⟜a) 1+b
+}
+i ← ∘‿2⥊Sections¨ ∾(<',') SplitOn¨ •FLines "input"
+Is ← ∊´∘((⍋≠¨)⊏⊢)
+
+•Out "day04.1: "∾•Fmt +´(∧´Is)˘ i
+•Out "day04.2: "∾•Fmt +´(∨´Is)˘ i
diff --git a/users/sterni/exercises/aoc/2022/05/5.bqn b/users/sterni/exercises/aoc/2022/05/5.bqn
new file mode 100644
index 0000000000..15b0dfc805
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/05/5.bqn
@@ -0,0 +1,18 @@
+⟨ReadDec, SplitOn, IsAsciiNum⟩ ← •Import "../../lib.bqn"
+rs‿rc ← (<"") SplitOn •FLines "../05/input"
+
+stacks ← {
+  count ← '0'-˜⊑⌽' ' (≠/⊢) ⊑⌽rs
+  ' ' (≠/⊢)¨<˘ (count×4) ((»∘(0⊸=)∘(4⊸|)∘↕⊣)/↑) ⍉> (-1)↓rs
+}
+
+cmds ← {0‿1‿1-˜ ReadDec¨ ((∧´IsAsciiNum)¨/⊢) (<' ') SplitOn 𝕩}¨ rc
+
+_ApplyCmd ← {
+  s Fn _self c‿f‿t :
+  m‿k ← 2↑ c ((≤⟜(↕≠))⊔⊢) f⊑s
+  (Fn m)⊸∾⌾(t⊸⊑) k˙⌾(f⊸⊑) s
+}
+
+•Out "day05.1: "∾⊑¨stacks ⌽_ApplyCmd˜´ ⌽ cmds
+•Out "day05.2: "∾⊑¨stacks ⊢_ApplyCmd˜´ ⌽ cmds
diff --git a/users/sterni/exercises/aoc/2022/06/6.bqn b/users/sterni/exercises/aoc/2022/06/6.bqn
new file mode 100644
index 0000000000..041a2e9100
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/06/6.bqn
@@ -0,0 +1,4 @@
+i ← ⊑•FLines "input"
+FirstMarker ← {𝕩+⊑/(∧´∘¬⊒)˘𝕩↕i}
+•Out "day06.1: "∾•Fmt FirstMarker 4
+•Out "day06.2: "∾•Fmt FirstMarker 14
diff --git a/users/sterni/exercises/aoc/2022/06/6.k b/users/sterni/exercises/aoc/2022/06/6.k
new file mode 100644
index 0000000000..3dc0de0a3e
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/06/6.k
@@ -0,0 +1 @@
+4 14{x+*&x=#'?'x':y}\:1:"input"
diff --git a/users/sterni/exercises/aoc/2022/07/7.bqn b/users/sterni/exercises/aoc/2022/07/7.bqn
new file mode 100644
index 0000000000..2fc387f340
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/07/7.bqn
@@ -0,0 +1,24 @@
+lib ← •Import "../../lib.bqn"
+cmds ← 1↓ '$' ((+`= ⟜(⊑¨))⊔⊢) •FLines "input"
+paths ← (<⟨⟩) {
+  𝕨 𝕊 "$ ls": 𝕨;
+  𝕨 𝕊 "$ cd /": ⟨⟩;
+  𝕨 𝕊 "$ cd ..": (-1)↓𝕨;
+  𝕨 𝕊 𝕩: 𝕨∾<5↓𝕩 # "$ cd …"
+}` ⊑¨cmds
+ParseLs ← {
+  dirs‿files ← 2↑((lib.IsAsciiNum∘⊑∘⊑)¨⊔⊢) ((<' ')⊸lib.SplitOn)¨ 1↓𝕩
+  (1⊑¨dirs)⋈(lib.ReadDec 0⊸⊑)¨files
+}
+dirlists ← ParseLs⌾(1⊸⊑)¨⥊⋈˘(("$ cd"⊸≢⟜(4⊸↑)∘⊑¨)∘(1⊸⊏)˘/⊢) (⍒≠¨paths)⊏⍉paths≍cmds
+DirSize ← {⊑𝕨 (⊑∘(1⊸⊑¨∘⊣⊐⊢)⊑⊣) <𝕩}
+DirName ← ∾'/'⊸∾¨
+dirsizes ← ⊑¨ ⟨⟩ {
+  szs 𝕊 ⟨dir, subdirs‿files⟩:
+  Canon ← DirName dir⊸∾⟜⋈
+  sz ← +´files∾szs⊸DirSize∘Canon¨ subdirs
+  szs∾<sz⋈DirName dir
+}˜´ ⌽dirlists
+
+•Out "day07.1: "∾•Fmt +´ 100000 (≥/⊢) dirsizes
+•Out "day07.2: "∾•Fmt (30000000-70000000-⌈´dirsizes) ⌊´∘(≤/⊢) dirsizes
diff --git a/users/sterni/exercises/aoc/2022/08/8.bqn b/users/sterni/exercises/aoc/2022/08/8.bqn
new file mode 100644
index 0000000000..91a16d9573
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/08/8.bqn
@@ -0,0 +1,15 @@
+i ← >'0'-˜•FLines "input"
+Visible ← {
+  _vis ← {(⌈`∘(¯1⊸»˘⌾⍉)<⊢)⌾𝕏 𝕗}
+  ∨´𝕩 _vis¨ ⟨⊢,⌽,⍉,⌽⍉⟩
+}
+
+•Out "day08.1: "∾•Fmt +´⥊Visible i
+
+ViewingDistances ← {
+  DirView ← {≠1(»⟜(∧`(⊑𝕩)⊸>)/⊢) 1↓𝕩}
+  _spliceDir ← {! =´≢𝕗 ⋄ 𝕏⁼(⊢↓(⊏⟜(𝕏𝕗))∘⊣)´¨ ⋈⌜˜↕≠𝕗}
+  ×´ DirView¨¨ 𝕩 _spliceDir¨ ⟨⊢, ⌽˘, ⍉, ⌽˘⍉⟩
+}
+
+•Out "day08.2: "∾•Fmt ⌈´⥊ViewingDistances i
diff --git a/users/sterni/exercises/aoc/2022/09/9.bqn b/users/sterni/exercises/aoc/2022/09/9.bqn
new file mode 100644
index 0000000000..fff38b5913
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/09/9.bqn
@@ -0,0 +1,17 @@
+⟨SplitOn,ReadDec⟩ ← •Import "../../lib.bqn"
+i ← ReadDec⌾(1⊸⊑)¨ (<' ')⊸SplitOn¨ •FLines "input"
+
+UnitDelta ← (⊢÷(|+0⊸=))
+ExpandStep ← {
+  𝕊 "L"‿l: 𝕊 (-l)‿0;
+  𝕊 "R"‿r: 𝕊 r‿0;
+  𝕊 "U"‿u: 𝕊 0‿u;
+  𝕊 "D"‿d: 𝕊 0‿(-d);
+  𝕊 delta: ((⌈´|)⥊<∘UnitDelta) delta
+}
+
+Step ← {knots 𝕊 delta: {h 𝕊 t: (UnitDelta h-t) +⍟(1<⌈´|h-t) t}` (delta⊸+)⌾⊑ knots}
+Visited ← {+´0=⊒(¯1⊸⊑)¨(<𝕨⥊<0‿0) Step` ∾ExpandStep¨ 𝕩}
+
+•Out "day09.1: "∾•Fmt  2 Visited i
+•Out "day09.2: "∾•Fmt 10 Visited i
diff --git a/users/sterni/exercises/aoc/2022/10/10.bqn b/users/sterni/exercises/aoc/2022/10/10.bqn
new file mode 100644
index 0000000000..04e3d6a8e5
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/10/10.bqn
@@ -0,0 +1,25 @@
+⟨SplitOn,ReadDec⟩ ← •Import "../../lib.bqn"
+# Instead of implementing the VM described in the problem, translate the
+# program to instructions with equivalent timing for a similar VM that
+# only needs 1 cycle for every instruction.
+is ← ∾{"noop": <"noop"; 𝕩: (<"noop")∾<ReadDec⌾(1⊸⊑) (<' ') SplitOn 𝕩}¨ •FLines "input"
+
+Op ← {x 𝕊 "noop": x;x 𝕊 "addx"‿i: x+i}
+Draw ← {𝕊 c‿x‿pic: pic∨(↕240)((c-1)⊸=∘⊣∧∊)(⌊⌾(÷⟜40)c)+¯1+x+↕3}
+_vm ← {
+  is _self s: (⊑s)≥≠is? s;
+  is _self prev‿sum‿x‿pic:
+  cycle ← prev+1
+  is _self ⟨
+    cycle,
+    sum+x×cycle×⊑cycle∊20‿60‿100‿140‿180‿220,
+    x Op (¯1+cycle)⊑is,
+    Draw cycle‿x‿pic
+  ⟩
+}
+
+·‿sum‿·‿pic ← is _vm 1‿0‿1‿(240⥊0)
+
+•Out "day10.1: "∾•Fmt sum
+•Out "day10.2:"
+•Show ".#" ⊏˜ ∘‿40⥊pic
diff --git a/users/sterni/exercises/aoc/2022/11/11.bqn b/users/sterni/exercises/aoc/2022/11/11.bqn
new file mode 100644
index 0000000000..12b9b5097a
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/11/11.bqn
@@ -0,0 +1,41 @@
+# needs export BQNLIBS=/path/to/mlochbaum/bqn-libs
+⟨ReadDec,ImportBqnLibs⟩ ← •Import "../../lib.bqn"
+⟨Split⟩ ← ImportBqnLibs "strings.bqn"
+MakeOp ← {
+  𝕊 a‿"+"‿b: 𝕊 a‿+‿b;
+  𝕊 a‿"*"‿b: 𝕊 a‿×‿b;
+  𝕊 a‿op‿b:
+  is‿xs ← (<"old") (≡¨⊔⊢) a‿b
+  {op´ (𝕩⋆≠xs) ∾ReadDec¨ is}
+}
+ParseMonkey ← {
+  ·‿items‿op‿if‿then‿else:
+  {
+    initial ⇐ ReadDec¨ ", " Split 18↓items
+    op ⇐ MakeOp " " Split 19↓op
+    if ⇐ ReadDec 21↓if
+    then ⇐ ReadDec 29↓then
+    else ⇐ ReadDec 30↓else
+  }
+}
+monkeys ← ParseMonkey¨ 1↓' '((+`(≠⟜⊑)¨)⊔⊢)0(≠⟜≠¨/⊢)•FLines "input"
+items ← {𝕩.initial}¨ monkeys
+lim ← ×´{𝕩.if}¨ monkeys
+
+Sim ← {
+  div 𝕊 len:
+  Turn ← {
+    items 𝕊 turnidx:
+    i ← (≠monkeys)|turnidx
+    m ← i⊑monkeys
+
+    worry ← lim|⌊div÷˜ m.Op¨ i⊑items
+    else‿then ← 2↑0 (=⟜(m.if⊸|)⊔⊢) worry
+
+    ⟨then, else⟩⊸(∾˜¨)⌾(m.then‿m.else⊸⊏) ⟨⟩˙⌾(i⊸⊑) items
+  }
+  ×´2↑∨+˝(<items) ((≠⊑)⊸(>((↕⊣)=|)¨)×(≠¨˘)∘>∘(⊣»Turn`)) ↕len×≠items
+}
+
+•Out "day11.1: "∾•Fmt 3 Sim 20
+•Out "day11.2: "∾•Fmt 1 Sim 10000
diff --git a/users/sterni/exercises/aoc/2022/12/12.bqn b/users/sterni/exercises/aoc/2022/12/12.bqn
new file mode 100644
index 0000000000..cf42f6f899
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/12/12.bqn
@@ -0,0 +1,16 @@
+⟨ImportBqnLibs,_fix⟩ ← •Import "../../lib.bqn"
+⟨ReplaceAll⟩ ← ImportBqnLibs "strings.bqn"
+i ← >•FLines "input"
+
+elevation ← 'a'-˜⟨"S","E"⟩‿⟨"a","z"⟩ ReplaceAll⌾⥊ i
+starts ← (⊏⟜∞‿0)¨⟨'S'=i,0=elevation⟩
+end ← 'E'=i
+
+Step ← {
+  𝕊 steps:
+  Go ← {𝕏⁼((⊢∾¨↕∘≢)(≤⟜(∞⊸»˘∘+⟜1))˜𝕏elevation)⊑>((⥊⟜∞)∘≢⊸⋈)˜∞⊸»˘1+𝕏steps}
+  steps⌊´Go¨⟨⊢,⌽˘,⍉,⍉⌽⟩
+}
+Shortest ← {⊑end/⊸⊏○⥊Step _fix 𝕩}
+
+•Out¨ "day12.1: "‿"day12.2: "∾¨ •Fmt∘Shortest¨ starts
diff --git a/users/sterni/exercises/aoc/2022/13/13.bqn b/users/sterni/exercises/aoc/2022/13/13.bqn
new file mode 100644
index 0000000000..0242cc5093
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/13/13.bqn
@@ -0,0 +1,14 @@
+lib ← •Import "../../lib.bqn"
+str ← lib.ImportBqnLibs "strings.bqn"
+i ← >⟨"[","]"⟩‿⟨"⟨","⟩"⟩⊸(•BQN str.ReplaceAll)¨¨0((⟨⟩⊸≡¨¯1˙⍟⊣¨(+`(=⟜≠)¨))⊔⊢)•FLines "input"
+
+Ord ← {
+  i1 𝕊 i2: 1‿1≡•Type¨ i1‿i2? ¯1‿1‿0⊑˜i1(=+≤)i2;
+  i1 𝕊 l2: 1‿0≡•Type¨ i1‿l2? l2 Ord˜ ⋈i1;
+  l1 𝕊 i2: 0‿1≡•Type¨ l1‿i2? l1 Ord ⋈i2;
+  l1 𝕊 l2: 0‿0≡•Type¨ l1‿l2?
+  ⊑1↑0(≠/⊢)l1 Ord¨○((l1⌈○≠l2)⊸(↑⌾(+⟜1))) l2
+}
+
+•Out "day13.1: "∾•Fmt +´1+/(1⊸=Ord´)˘i
+•Out "day13.2: "∾•Fmt ×´1‿2++´˘¯1=⟨⟨2⟩⟩‿⟨⟨6⟩⟩Ord⌜⥊i
diff --git a/users/sterni/exercises/aoc/2022/15/15.bqn b/users/sterni/exercises/aoc/2022/15/15.bqn
new file mode 100644
index 0000000000..e47355856b
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/15/15.bqn
@@ -0,0 +1,18 @@
+lib ← •Import "../../lib.bqn"
+
+F ← ¬∘('-'⊸=∨lib.IsAsciiNum)
+i ← ⌽˘˘∘‿2‿2⥊lib.ReadDec¨>(0⊸<⟜≠¨/⊢)∘((F ¯1˙⍟⊣¨(+`F))⊔⊢)¨ •FLines "input"
+
+ssp ← 4000000
+
+sds ← (⊏˘∾˘(+´˘(|(-˝))˘)) i
+
+# _fix is needed to deal with e.g. ⟨0‿15, 5‿8, 12‿23⟩
+MergeRanges ← ((⊑∾⊑∘⌽)∘∧∘∾)¨∘(+`∘((<∞‿∞)⊸»{<´1‿2⊏𝕨∾𝕩}¨⊢)⊔⊢) lib._fix
+
+Range ← {cky 𝕊 y‿x‿d: x+¯1‿1×d-|cky-y}
+RangesY ← {<˘∧𝕩(⊣Range˘({cky 𝕊 y‿·‿d: d≥|y-cky}˘/⊢))sds}
+OutRangeY ← {(1<≠)◶⟨0˙,𝕩⊸+∘(ssp⊸×⟜(+⟜1))∘(1⊸⊑)∘∾⟩ MergeRanges ssp⌊0⌈RangesY 𝕩}
+
+•Out "day15.1: "∾•Fmt +´-˜´¨MergeRanges RangesY 2÷˜ssp
+•Out "day15.2: "∾•Fmt +´OutRangeY¨↕ssp
diff --git a/users/sterni/exercises/aoc/2022/16/16.k b/users/sterni/exercises/aoc/2022/16/16.k
new file mode 100644
index 0000000000..40d5ace60e
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/16/16.k
@@ -0,0 +1,21 @@
+/ parsing
+(f;r;t):+{x[1 4],,9_x^'","}'" "\' 0:"input"
+(f;t):`s$''(f;t)
+r:f!{`I$x[&(x<58)&47<x]}' r
+g:f!t
+
+/ total flow scoring
+tf: {+/+\{x,(30-#x)#0}((r.)'x)*`XX=':x}
+
+/ valves to open
+vto: f^(=r).0;
+
+/ paths to keep after each step
+best: {x[(1000&#x)#>tf'x]}
+
+p:{[n;ps]
+  ms:ps[&~fin:(#vto)={#?x[&`X=':x]}'ps];
+  rt: best[ps[&fin],(ms[w],'(,*|)'ms[w:&{(0|/vto=l)&~|/0&':x=l:*|x}'ms]),,/{x,/:,'g[*|x]}' ms];
+  $[n>1;o[n-1;rt];rt]}
+
+*tf'p[29;,,`AA]
diff --git a/users/sterni/exercises/aoc/2022/17/17.bqn b/users/sterni/exercises/aoc/2022/17/17.bqn
new file mode 100644
index 0000000000..21b94221aa
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/17/17.bqn
@@ -0,0 +1,51 @@
+jets ← '>'= "<>" (∊˜/⊢) •FChars "input"
+pieces ← >¨⟨1‿1‿1‿1⟩‿⟨0‿1‿0,1‿1‿1,0‿1‿0⟩‿⟨0‿0‿1,0‿0‿1,1‿1‿1⟩‿⟨⋈1,⋈1,⋈1,⋈1⟩‿⟨1‿1,1‿1⟩
+w ← 7
+initial ← 0‿w⥊0
+
+# Warning: mutated global!
+ji ← 0
+_try ← {(⊢ 𝕩˙⍟(≠○(+´∘⥊∘∨⟜𝕨)) 𝔽) 𝕩}
+Fall ← {
+  pushed ← 𝕨 ((ji⊑jets)◶«‿»)˘ _try 𝕩
+  ji ↩ (≠jets)|ji+1
+  fallen ← 𝕨 » _try pushed
+  𝕨 𝕊⍟(pushed≢fallen) fallen
+}
+Height ← ≠∘(∨´˘/⊢)
+ThrowPiece ← {
+  piece ← 𝕩 (|˜⟜≠⊑⊢) pieces
+  chamber ← (((3+≠piece)⊸+∘⊑∘(1⊸↑)∘⌽∘(1⊸+)∘/∨´˘)↑⊢)⌾⌽𝕨
+  falling ← (≠chamber)↑(»⍟2 w⊸↑)˘piece
+  chamber (⊣∨Fall) falling
+}
+
+•Out "day17.1: "∾•Fmt Height initial ThrowPiece˜´ ⌽↕2022
+
+# https://mlochbaum.github.io/BQN/doc/control.html#while
+While ← {𝕩{𝔽⍟𝔾∘𝔽_𝕣_𝔾∘𝔽⍟𝔾𝕩}𝕨@}´
+{
+  target ← 1000000000000
+  ji ↩ 0 ⋄ i ← 0 ⋄ res ← @
+
+  chamber ← initial
+  cycles ← ⟨≠pieces,≠jets⟩⥊<⟨⟩
+
+  While {𝕤⋄res=@}‿{𝕤
+    chamber ↩ chamber ThrowPiece i
+    i +↩ 1
+
+    t ← i|˜≠pieces
+    cycles ↩ {
+      new ← 𝕩∾<i⋈Height chamber
+      res ↩ {𝕊 𝕩:
+        ⟨pl,hl⟩‿· ← chk ← ¯2↑new
+        pd‿hd ← -´⌽chk
+        @˙⍟(0≠pd|target-pl) hl+hd×pd÷˜target-pl
+      }⍟(1<≠new) @
+      new
+    }⌾(t‿ji⊸⊑) cycles
+  }
+
+  •Out "day17.2: "∾•Fmt res
+}
diff --git a/users/sterni/exercises/aoc/2022/18/18.bqn b/users/sterni/exercises/aoc/2022/18/18.bqn
new file mode 100644
index 0000000000..76ec569fed
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/18/18.bqn
@@ -0,0 +1,14 @@
+lib ← •Import "../../lib.bqn"
+
+i ← (lib.ReadDec¨(<',')⊸lib.SplitOn)¨ •FLines "input"
+dim ← 1+⌈´i
+cubes ← i∊˜↕dim
+
+views ← ⟨0‿1‿2, 1‿2‿0, 2‿0‿1⟩
+Exposed ← {(6×+´⥊𝕩)-2×+´views{+´⥊(∧˝˘)2↕𝕨⍉𝕩}¨<𝕩}
+Interior ← {(¬𝕩)∧´views{((lib.Xor`∘((∊∧⊢)∨»∘(∊⌾⌽∧⊢)))⎉1)⌾(𝕨⊸⍉)𝕩}¨<𝕩}
+Displace ← {⌈´(⥊⊢‿⌽⋈⌜views){F‿a 𝕊 𝕩:((-∘¬∘(»((0⊸=⊣)∧>)⊢)⌈⊢)⎉1)⌾(F a⊸⍉)𝕩}¨<𝕩}
+Exterior ← (⊢-○Exposed ¯1⊸=∘(Displace lib._fix)∘(-∘Interior+⊢))
+
+•Out "day18.1: "∾•Fmt Exposed cubes
+•Out "day18.2: "∾•Fmt Exterior cubes
diff --git a/users/sterni/exercises/aoc/2022/20/20.bqn b/users/sterni/exercises/aoc/2022/20/20.bqn
new file mode 100644
index 0000000000..8d4c905e87
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/20/20.bqn
@@ -0,0 +1,13 @@
+⟨ReadDec⟩ ← •Import "../../lib.bqn"
+enc ← ReadDec¨ •FLines "input"
+
+CoordSum ← +´∘(1000‿2000‿3000⊸((⊢≠⊸|+⟜(⊑∘(/=⟜0)∘⊢))⊏⊢))
+Mix ← {
+  M ← {m 𝕊 i:
+    l ← ≠m
+    i {n ← (l-1)|(𝕩⊑m)+⊑/𝕩=𝕨 ⋄ (n⊸↑(∾⟜𝕩)⊸∾n⊸↓) 𝕩(≠/⊢)𝕨}˜´ ⌽↕l
+  }
+  CoordSum ((⊢M⍟𝕨↕∘≠)⊏⊢) 𝕩
+}
+•Out "day20.1: "∾•Fmt 1 Mix enc
+•Out "day20.2: "∾•Fmt 10 Mix 811589153×enc
diff --git a/users/sterni/exercises/aoc/2022/21/21.bqn b/users/sterni/exercises/aoc/2022/21/21.bqn
new file mode 100644
index 0000000000..2f91f55d44
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/21/21.bqn
@@ -0,0 +1,25 @@
+⟨ImportBqnLibs, IsAsciiNum, ReadDec⟩ ← •Import "../../lib.bqn"
+⟨ReplaceAll, Split⟩ ← ImportBqnLibs "strings.bqn"
+
+i ← ": "⊸Split¨ •FLines "input"
+ReplaceInts ← {
+  𝕊 𝕩: 𝕊´ 2↑(¬∘(∧´IsAsciiNum∘⊑∘⌽)¨⊔⊢) 𝕩;
+  # TODO: Efficient replace on tokens
+  is 𝕊 es: (((•Fmt⍟(0⊸≠•Type))¨⌾(1⊸⊑) <˘⍉>is)⊸ReplaceAll⌾(1⊸⊑))¨ es
+}
+
+c ← 0
+CanEval ← (IsAsciiNum∨∊⟜"+-/* ")
+Eval ← {
+  a‿s‿b ← " " Split 𝕩
+  f ← ⊑+‿-‿×‿÷⊏˜"+-*/"⊐s
+  a F○ReadDec b
+}
+EvalExprs ← {
+  p‿e ← 2↑((∧´CanEval∘⊑∘⌽)¨⊔⊢) 𝕩
+  ev ← (Eval⌾(⊑⌽))¨ e
+  c +↩1
+  (⊑(⊑¨ev)∊˜<"root")◶⟨EvalExprs∘(ReplaceInts⟜p),1⊸⊑⊑⟩ ev
+}
+
+•Show EvalExprs ReplaceInts i
diff --git a/users/sterni/exercises/aoc/2022/25/25.bqn b/users/sterni/exercises/aoc/2022/25/25.bqn
new file mode 100644
index 0000000000..921099141f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/25/25.bqn
@@ -0,0 +1,4 @@
+c ← "=-012"
+F ← +´∘(⊢×(5⊸⋆)∘⌽∘↕∘≠)∘(-⟜2)∘(c⊸⊐)
+T ← {c⊏˜5|2+𝕩 {(⌊5÷˜𝕨+2) (𝕊⟜(𝕨⊸∾))⍟(0<𝕨) 𝕩} ⟨⟩}
+•Out "day25.1: "∾T +´F¨ •FLines "input"
diff --git a/users/sterni/exercises/aoc/2022/25/25.k b/users/sterni/exercises/aoc/2022/25/25.k
new file mode 100644
index 0000000000..df956f002f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/25/25.k
@@ -0,0 +1 @@
+c@2+{(1_o,0)+x+-5*o:2<x}/5\+/{5/x-2}'(c:"=-012")?0:"input"
diff --git a/users/sterni/exercises/aoc/2022/README.md b/users/sterni/exercises/aoc/2022/README.md
new file mode 100644
index 0000000000..65d51dd21f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/README.md
@@ -0,0 +1,8 @@
+# sterni's [Advent of Code 2022](adventofcode.com/2022)
+
+I'm trying to do it in BQN again to redeem myself for my unfinished [AoC 2021](../2021),
+but will allow myself falling back to another language if I get stuck, so I actually
+complete this one.
+~~I also plan to write additional solutions in Nix (when I have the time) in order to
+throw `//tvix/eval` against some new problems.~~
+We'll see how it goes, as my December promises to be quite busy.
diff --git a/users/sterni/exercises/aoc/2022/default.nix b/users/sterni/exercises/aoc/2022/default.nix
new file mode 100644
index 0000000000..01134d1306
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/default.nix
@@ -0,0 +1,53 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (pkgs.buildPackages) cbqn ngn-k;
+
+  # input files are not checked in
+  meta.ci.skip = true;
+
+  BQNLIBS = pkgs.fetchFromGitHub {
+    owner = "mlochbaum";
+    repo = "bqn-libs";
+    rev = "d56d8ea0b8c294fac7274678d9ab112553a03f42";
+    sha256 = "1c1bkqj62v8m13jgaa32ridy0fk5iqysq5b2qwxbqxhky5zwnk9h";
+  };
+in
+
+depot.nix.readTree.drvTargets {
+  shell = pkgs.mkShell {
+    name = "aoc-2022-shell";
+    packages = [
+      cbqn
+      ngn-k
+    ];
+
+    inherit BQNLIBS;
+  };
+
+  bqn = pkgs.runCommand "bqn-aoc-2022"
+    {
+      nativeBuildInputs = [
+        cbqn
+      ];
+
+      aoc = builtins.path {
+        name = "bqn-aoc-2022";
+        path = ./../.;
+        # Need lib.bqn from ../ and all inputs as well as bqn files from ./*
+        filter = path: type:
+          lib.hasSuffix ".bqn" path || (
+            lib.hasPrefix (toString ./.) path
+            && (
+              type == "directory"
+              || lib.hasSuffix "/input" path
+            )
+          );
+      };
+
+      inherit meta BQNLIBS;
+    }
+    ''
+      find "$aoc/2022" -name '*.bqn' -exec BQN {} \; | tee "$out"
+    '';
+}
diff --git a/users/sterni/exercises/aoc/lib.bqn b/users/sterni/exercises/aoc/lib.bqn
new file mode 100644
index 0000000000..e870a5dfa4
--- /dev/null
+++ b/users/sterni/exercises/aoc/lib.bqn
@@ -0,0 +1,18 @@
+IsAsciiNum ⇐ ('0'⊸≤∧≤⟜'9')
+IsAlpha ⇐ (('a'⊸≤∧≤⟜'z')∨('A'⊸≤∧≤⟜'Z'))
+
+# based on leah2's function
+ReadInt ⇐ {
+  𝕨 𝕊 𝕩: '-'=⊑𝕩? -𝕨 𝕊 1↓𝕩;
+  𝕨 𝕊 𝕩: (𝕨⊸×+⊣)´∘⌽-⟜'0'𝕩
+}
+ReadDec ⇐ 10⊸ReadInt
+
+SplitOn ⇐ ((⊢ (-1˙)⍟⊣¨ +`∘(1⊸»<⊢))∘(≡¨)⊔⊢)
+SplitAt ← ((⊣≤↕∘≠∘⊢)⊔⊢)
+
+_fix ⇐ {𝕩 𝕊∘⊢⍟≢ 𝔽 𝕩}
+
+ImportBqnLibs ⇐ {•Import 𝕩∾˜"/"∾˜¯1↓1⊑•SH "printenv"‿"BQNLIBS"}
+
+Xor ⇐ (¬⊸∧∨∧⟜¬)