The goal of this repository is to contain a solution to each day of Advent of Code 2023 in a different programming language. Originally, all implementations were going to be in OCaml, as that was also the original plan of a good friend of mine. However, I soon got the idea to write the solution for each day in a different language, as doing so would force me to learn many languages I had limited or no experience in. Some of the languages used were inspired by this challenge, though the language order was more or less what I felt like based on the problems of each day. Due to various factors, the challenge was not completed in any particular order.
Below are basic overviews of my experiences and/or views of each day's language.
OCaml is the first functional programming language I've seriously used thanks to CMSC330 at UMD, and to this day it's my favorite. UTop is a great REPL for testing out small snippets of code, and pattern matching made the solution, particularly part two, much easier to understand while writing it. Admittedly, I didn't have the greatest experience with regex in OCaml, but otherwise it was one of my favorite languages throughout the challenge.
This was my first time using any dialect of Lisp, and my second functional programming language. Although I had a difficult time adjusting to Racket, the documentation was fairly decent in getting me acquainted with it, and in hindsight I could have read the documentation better for my solution. I wasn't the most fond of the language while writing the solution for day 2, but I'll likely have grown on it by the next time I use it.
The last time I used Go before day 3 was a few years ago, when my ego told me I was a much better programmer than I actually was. Now that I have gotten fairly comfortable in C, writing Go was a breeze.
MATLAB has one of my least favorite languages I've used. It's not my least productive language, as that would probably go to assembly when considering languages with some sort of practical application. However, over the two years since I was originally exposed to the language, my perception of it has only gotten worse with each new quirk of the language I encounter. Some relevant to this discussion are member access, strings vs. char arrays, and functions.
Member access is clunky not only because array indexing is done with
parentheses as opposed to square brackets as in most languages, but cell
array access is done with curly braces. It's not the worst thing in the world,
but when the likes of strsplit
returns a cell array instead of an array or
list as it would in most other languages, the annoyance of differentiating
array and cell array access syntax with different bracket types gets
irritating.
In MATLAB, strings are denoted with double quotes, while char vectors are
denoted with single quotes. While they're treated as the same in many
instances, there are select instances such as regex where their differences
become a hindrance. For example, as seen in
MATLAB's regexp
documentation,
the type of return value of many output modes changes depending on multiple
factors, with many factors noting a dependence of whether the input str
is a
string or char vector. This makes it more difficult to predict the accurate
behavior of a function based on intuition, which is in contrast to many other
languages which simply operate on strings, and maybe a regex type for the
expression.
MATLAB function modules are imported solely based on which files are on the MATLAB PATH when running a script. They are not included by imports. They are not included by running multiple files at the same time. There aren't even well documented namespaces to limit naming collisions. This should speak for itself.
More reasons why MATLAB has a terrible programming language are documented at https://www.rath.org/matlab-is-a-terrible-programming-language.html. It sucks, because for as much as I dislike the MATLAB ecosystem as a whole, it is good at what it's often used for: numerical computations and simulations. So then why is the language itself so terrible? Why are students consistently forced to use it as a programming language akin to Python when it is terrible at mimicking Python?
On top of all that, in spite of being free, GNU Octave makes several of
MATLAB's issues even worse. It's an admirable project, but one that's simply
not ready to be any sort of a MATLAB replacement. Not only are there several
functions not implemented at this time, but several of the ones that are
implemented are done so differently than MATLAB. For example, while
regexp('123 456', ": +| +\\| +", 'split')
returns {'123'} {'456'}
in
MATLAB, the same function call returns {'123 456'}
in Octave. Additionally,
functions have different definition rules, as while in MATLAB they must be
declared at the bottom of a script file, doing so in Octave will yield
undefined name errors. It's unfortunate, as I'd love to use it over MATLAB
for use in classes, but I can't because too much of the project is not ready.
In progress.
MIPS assembly was painful, but I learned a whole lot from doing it. Up to
this point, I had done AVR and MIPS (in SPIM) assembly for classes, where AVR
was often supplemented with helper functions and libraries for terminal I/O,
and SPIM had high level system calls such as print_int
, print_str
,
and read_int
for the only available I/O interface (the built-in console).
Up until completing this day's solution, I had not yet written a proper
application in assembly. That changed with this day's solution, as the
environment used was Linux on an emulated MIPS III architecture.
Implementation details are in the day 6 README, but in short, the lack of
convenience system calls and the C standard library forced me to reimplement
functions which I had taken for granted in SPIM and high level languages.
They're not perfect solutions, and the read functions are especially flawed
considering that only one character is read per system call, but implementing
them myself has made me much more competent with both assembly and system call
programming.
I made a mistake in Rust which nearly doubled my code: I used enums as values instead of enums with associated values. Otherwise, the manner in which I wrote the solution was some sort of combination of C and OCaml for the most part. However, this seems to be one of those cases where there is so much to Rust beneath the surface that my opinions on it are mostly inaccurate at this point in time.
In progress.
Swift was a surprisingly nice language to write with mostly great documentation. However, it's evident that the language was does not have a userbase of many command-line program developers, as finding information on file I/O was more difficult than it needed to be, and I found the easiest way to print to standard error was through a libc function.
In progress.
If Clojure were my first Lisp dialect, I don't think I would have had a good experience with the language. Racket has substantially better new user guides with the Quick Introduction and The Racket Reference easily accessible from the Racket front page. Meanwhile, Clojure only has the "Learn Clojure guide", which I found inadequate in learning the language. There is a community driven documentation which I found to be very useful, but it's only listed under "Other learning resources" in "Getting Started".
Java, being the primary language of the JVM, is a language I don't enjoy using much. It is object-oriented to its core, and over time its (and the industry standard) approach to object-oriented programming has grown less favorable in my eyes over time. The access to several useful data structures from the standard library is extremely helpful, but I find several other things like file I/O, basic structure definitions, and even the main method to be overcomplicated due to Java's fundamental design. Additionally, its implementation of features such as generics and functional programming seem to be handicapped by the language's history, as the former has weird edge cases such as generic array instantiations needing to be casted, while the latter has rules such as requiring functional interfaces for lambda instantiation. I found Scala to be a more enjoyable language, as for example, the language cleans up lambdas and adds tuples to make functional programming easier. However, it's still heavily based on Java at the end of the day, which is evident through its implementation of structures and the main method.
With all that said, I liked Clojure quite a bit. The language is fundamentally a Lisp rather than a Java-like, and with it being my second Lisp dialect, it clicked with me much more than my first attempt with Racket. Java interop is a big part of the language, but Java doesn't heavily influence the fundamentals of Clojure like it does for the likes of Scala.
In progress.
Hy is an interesting language to reflect on. It seems to take many cues from
Clojure, but it's much more tightly integrated with Python than Clojure is
with Java, with Hy source code being directly compiled to Python ast objects.
It has some additional features, such as with
expressions evaluating to
values, but its workflow is essentially the same as Python's in the form of a
Lisp. I liked it, but until the language (and hopefully the REPL) develops
further, I think I'll stick with Python for anything I'd need Python for.
After all, Hy was only version 0.27 at the time of writing the solution for
day 13.
For better or worse, Lua is a fairly simple scripting language. I wasn't in love with the language, nor did I hate it to my core because table indexing starts at 1. I was pleasantly surprised how easy it is to implement and use higher-order functions, and while I only used tables to implement arrays, they seem much more powerful beyond the surface.
In progress.
In progress.
In progress.
In progress.
In progress.
In progress.
In progress.
In progress.
In progress.
In progress.
In progress.
Disclaimer: all of the code in this repository was solely tested on Kubuntu 22.04. Some of the code will not work in Windows, and your mileage may vary with other environments such as WSL, macOS, and BSD. The code should at least be architecture independent, except for day 6's MIPS assembly and day 15's Arduino "server" code.
Clone the repository with
git clone https://github.com/WGeckle80/advent-of-code-2023.git
To run a particular day's solution, download the puzzle input file, and change into the respective day's directory. Install the required software for the solution under the "Usage" section of the day's README. Installation instructions are not provided for most days because of potential user preferences to install from a package manager, install in a sandboxed environment such as Flatpak or Snap, run installation instructions from a website, or compile from source. After installation of a solution's required software, run the remaining instructions under "Usage."