Skip to content

LiquidFun/adventofcode

Repository files navigation

Advent of Code - 260/500 ⭐

2024 - 50 ⭐ - Python

2023 - 50 ⭐ - Python

2022 - 50 ⭐ - Kotlin

2021 - 50 ⭐ - Julia

2020 - 50 ⭐ - Rust

2019 - 10 ⭐ - OCaml

The above tiles are clickable, leading to the solution of the corresponding day.


The graphic above has been created using aoc_tiles. Feel free to use it, it's easy to set up!


My solutions:

  • are self-contained and use no util files and mostly no libraries.
  • are written in a very concise and functional style (only 5/100 solutions have >=70 loc as calculated by cloc)
  • try to be (too) clever, this is not code written for readability, but for pushing language expressibility.
  • solve both parts of each problem at once. I believe structuring the code so that it solves both parts is often an interesting challenge.
  • expect input via stdin and output (generally) 2 lines to stdout with the answer to part 1 and part 2.
  • (generally) parse input as it is given without modifying or pasting it into the code as an array. Parsing the strings in as little code as possible is a huge part of AoC.

and are written in a different programming language each year (no longer true):

  • 2024: I wanted to try Mojo this year, but it did not seem ready yet, as it is not open source and the installer seemed to collect suspicious telemetry. And I still wanted to try to get on the leaderboard, so I ended up again using Python. I did not manage to reach the leaderboard this time, as the competition using LLMs was hard to beat. However, I focused even more on writing elegant code, with more focus on readability this time. This year has some of my most elegant (and dense) code I have ever written. On average 20.6 lines of code per day (15.6 lines of code per day when excluding day 24). See day 4, day 11 or day 23
  • 2023: Initially I wanted to use Rust for this year, but instead I focused on getting on the leaderboard with Python (successfully on day 18 and 25!). In the last days I even started using libraries (Z3/networkx). Python is my strongest language, so no surprises there. I did 5 days in Rust as well, but found it too frustrating and had too little time for 2 solutions per day. 23.3 loc on average per day.
  • 2022: Using Kotlin for the first time. Playing with streams a lot, many days are completed by only using streams. The first 15 days have Python solutions as well in order to be able to submit quicker and get better times on the leaderboard. The Kotlin solutions have on average 42.5 loc.
  • 2021: Using Julia for the first time. Because it supports numpy-like element-wise operations natively this was a great choice for AoC. If I used more Julia it would likely be my go to language for AoC right now. 33 loc on average per day.
  • 2020: Using Rust for the first time. Solved 8 days initially. Revisited in 2024 and solved all of them. This time I liked Rust much more. I ended up writing very concise and functional Rust. After getting used to it it was quite fun. All rust solutions are below 100 loc (as calculated by cloc), all but one are below 60, with an average of 32.6 loc per day.

Not part of the repository yet, since these are incomplete and the code is not good (might revisit them later):

  • 2019: First time participating, solved 5 days with Python. Trying OCaml in 2024.

Running

  • 2024: python 01.py < input.in with Python 3.12
  • 2023: python 01.py < input.in with Python 3.12
  • 2022: kotlinc 01.kt -include-runtime -d 01.jar && kotlin 01.jar < example.in (or use intellij, kotlinc is very slow via CLI, 5+ seconds to compile)
  • 2021: julia 1.jl < input.in
  • 2020: cargo run --release --bin 01 < input.in or with autoresolve current dir, auto-refresh and time-taken: cargo watch -s 'time cargo run --release --bin '$(basename $PWD)' < '$(basename $PWD)'/input.in' with Rust version 1.80.1
  • 2019: ocaml 01.ml < input.in with OCaml 5.2.0

In order to test the programs you can pipe the input to the program, for example: julia 1.jl < input.in. The correct output is saved in the input.ans. Instead of doing this manually, I use my program-tester.sh script (see here), which runs the given program on all *.in files in the directory, and tests whether the corresponding *.ans file matches the given output. I have mapped that program on Enter in vim, which makes testing programs easy.

Programs are initialized with the init-day.sh script. I.e. typing init-day.sh 10 initializes the 10th day by creating a folder named 10 and downloading the input test case with the session.cookie.

My favorite solution is for problem 2023/18:

def solve(instructions, s=2, xy=0.):
    for d in instructions:
        s += abs(d.real+d.imag) + d.imag*xy.real - d.real*xy.imag
        xy += d
    print(int(s // 2))

split = [line.split() for line in open(0)]
solve(1j**"RDLU".index(d) * int(num) for d, num, _ in split)
solve(1j**int(c[7]) * int(c[2:7], 16) for *_, c in split)

The problem is about computing the area of a polygon on a grid, part 1 using small sizes, part 2 increasing them enormously. In part 1 I managed to get 74th place on the leaderboard by implementing a naive solution which painted all lines in a 2D array, found the first line, and guessed that the polygon has no intersections with itself. Then a floodfill was applied on one side of the polygon. I was lucky guessing the correct side which meant I was fast enough for the leaderboard. The solution for part 2 had 123 lines of code and did take me quite a while to code. However, through various optimizations I was able to reduce it to 8 lines. I learned afterwards about the Shoelace formula used to compute the area of a simple polygon. Furthermore, I used imaginary numbers in order to encode the x,y coordinates, shortening the code even more.