Last year I discovered by chance a code competition that happens in the month
of December: The Advent of Code.
This event consists of small puzzles release every day of December, up to the
25th. Which amounts to 25 problems! (I can do math).
Last year I tried doing some on Bash, and latter switched to Julia, but never
got to finish them.
This year I decided to use Julia, because I want to
test it for these general problems. Some problems are very hard, and I want
to have the packages at my disposal.
There is a leaderboard, which revolves around the first 100 solvers for
each day. Most problems are solved in the first 5 minutes of its release, and
the 100th place is usually less than 15 minutes. The best I got was 30 minutes,
getting the 135th position and zero points. However, I’m usually not trying to
get there because the problems are release at 3am for me (Brazil), and by that
time I’m usually sleeping.
This year I’m trying to follow more closely the dates, managed to solve one by
day until a gigantic workload halted my progress for a week. Yesterday I
returned to these problems and solved the missing ones up until now.
If you like coding, you should give it a try. My code for this year is
available at GitHub, but only open
it if you decided not to play. Be warned that most of my answers were poorly
written because I still want to do it fast, and am not worried about cleaning
of efficiency.
First, what is Test Driven Development (TDD)?
Well, I’m not an expert, so don’t quote me, but in practice it means that you
develop your code to fulfill tests that you define prior to beginning your work.
You do not define all your tests first, though. You define a single test,
and produce code to pass it. Then you define another code, and produce code to
pass both. And so forth until you complete your specification.
Add tests: These should be useful, and should fail.
Run tests: Verify that the test fails. If not, go back to 1.
Write code: Write enough code to pass the test.
Run tests: Verify that all tests pass. If some of the tests fail,
go back to 3.
Refactor: Now that everything passes, make the code looks nicer. This
is harder for non-seasoned programmers, becauses it’s vague. Essentially,
it means removing duplicate code, magic numbers, clarifying names, etc.
Run tests: Again. Should be done during refactoring, to guarantee
you’re not breaking anything. But just to be very clear: your tests should
pass at the end of refactoring.
Repeat.
This is one way of describing TDD, but there are other. Many others, by the way.
In fact, there are many images describing it, so you can print one and staple it
around.
Julia
First, we are gonna follow the package layout in Julia. This post mentions it at the end.
Basically, we need
Folder PackageName.jl
Folder src
PackageName.jl
Folder test
runtests.jl
README.md
LICENSE.md
In our example, we’re gonna write a program to convert Roman numbers to decimal,
and vice-versa.
This was inspired by this
site.
Important: You should use git, but I’ll skip it here
Let’s begin writing the outline of the project
This defines the building blocks. Note that test_digits.jl does not exist. We’re
gonna create it to test the individuals digits.
Our testing environment will consist of having a terminal open at all
times at the root of this project. Our testing command will be
There are different ways to issue the same command, but this is locally good.
Tests
Julia comes with a Base.Test package, which is the least you should use.
For all basic things it is enough. It provides the @test macro, which you can
use as
We’re gonna go a step beyong and use FactCheck.jl.
This provides more information about the tests.
We’re gonna implement the function roman_to_dec which receives a string with
roman numerals and returns the decimal equivalent of the number.
With FactCheck, our first test will be
When we run our test, we’ll get
Look, roman_to_dec not defined. Well, let’s define it.
Running again, we get an even better message
Expected 1, nothing ocurred. Well, that’s easy.
Done. We’re successful. Rejoice. Back to work.
We’ve written a test, we’ve tested it, we’ve written code to fix it, we tested
it. Not much to refactor, this is a silly example.
Repeat. Let’s improve the tests.
Running, we obtain
Now, that’s better. Improving the code.
This too will pass. Notice that this example is very silly. It is instructional,
of course. On a real application, you could start with all digits at once, for
instance.
More tests and solutions:
Now we can refactor, because it’s getting very ugly.
We can also refactor the test.
Test. Now we can add more tests for digits, and it will be much easier (because
it’s refactored) to both create the test and to solve it.
Understanding the logic now, you can add all the rest of the digits at once. Remember to test before start fixing, even though is very easy now.
This could be a breaking moment on your code. If, when trying to fix it, you
realize it’s not as simple as you expected. Remove the test, and add a smaller
one. At this time it will be very useful to have been using git.
Next test
We’ve completed a test. Let’s do the next.
Double digits are more complex that single digits (by at least at factor of 2?
🙂 ). Let’s break it down using context.
Testing this will fail (as it should), with a KeyError: II not found, because
we’re using the dictionary, and “II” is not in it.
Before reading the solution, try to fix it yourself. There are many ways to do
it.
This fixes it. Now to refactor. You may have noticed that s[i] does not work
inside digits. That is because julia differentiates characters and single
digits strings (like C, unlike Python). One refactor option is to change the
dictionary to use chars.
Another option is to use a better variable instead of s, since it start to
become a nuisance to read.
Yet another, is to use another way to make the sum.
Since this post explains the usage of TDD, it ends here.
You can continue with this problem until you can make a complete conversor of
roman to decimal.