gaffclant.dev blog

home | Silly goofy funny blog about tech stuffs

Advent of Code - Day 1

December 07, 2023 — gaffclant

I know I'm late on this now shush. It's that time of the year again! Advent of code is here for 2023, and I plan on making a post for every single day. I am learning C still, so I figured AOC would be a great way to learn more about the language.


What is Advent of Code?

For the uninformed, Advent of Code is a yearly event where from December 1st to December 25th a coding challenge is posted for you to solve, typically in two parts. Therefore, it acts as a programmers advent calendar with 25 fun coding problems for us to cry over, how fun!

Onto the problem...

Part 1

Day 1 asks us to take a string on each line of a file and form a single two-digit number using the first and last number in the string. We then need to add up all these numbers to get our solution. In their example, we are given

1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet

So we would get the numbers 12, 38, 15, and 77. After adding them we get the solution 142.

My approach

String parsing... in C... what fun. Anyway, the first thing I needed to figure out was how I planned on getting the first and last numbers. Looking through the C standard library, I found strcspn, which essentially returns the index of the first character in a string that is in a list of characters. So by calling strcspn(line, "0123456789"); I could get the index of the first number. Finding the index of the last number proved to take a couple extra steps, however. I figured to keep it simple, I would stick with using strcspn but after reversing the string. I found a really simple algorithm to reverse a string in C, so I decided to move forward with that.

// Simple function to reverse a string
void reverse(char *str) {
    size_t len = strlen(str);
    for (size_t i = 0, k = len - 1; i < (len / 2); i++, k--) {
        char temp = str[k];
        str[k] = str[i];
        str[i] = temp;
    }
}

Once the string was reversed, I could fetch the last number by just calling strcspn on the reversed string. Finally, I can add to the result by multiplying the first number by ten and adding it to the second number. Run the code on input.txt, print the result and... boom! part 1 was solved!

Refactoring

Initially, I was making a copy of line and storing it in a second string and waiting until the end of the main loop to calculate the numbers. I realized I could bypass this by simply calculating the first number and then reversing line. Since I already had the tens place stored, I didn't need to worry about the first number at all anymore. I could simply call strcspn and forget about it. For readability sake, I decided to store tens and ones as their own variables, instead of just iterating result after each calculation.

Part 2

To add to the challenge, they ask us to also treat the actual words as numbers as well. That being, one, two, three, etc. This was a bit of a problem because it meant I could no longer rely on one algorithm for my entire solution. It was time to think a little more about the problem.

My approach

I figured I should still use strcspn to save the indexes of the first and last numbers, so I could later compare those indexes with the first and last instances of one of the words.

size_t firstidx = strcspn(line, "0123456789");
reverse(line);
size_t lastidx = strlen(line) - strcspn(line, "0123456789") - 1;
reverse(line);

But what if there isn't an actual number? Well, strcspn will just set the result to the length of the string so we have no problems for firstidx since the indexes only get smaller from there. But to make sure we can also check for the last index, I decided to set lastidx to 0 if firstidx is the length of the array. Since we need to check for better options with the words later, we need to store the current tens and ones immediately after getting firstidx and lastidx so that if we don't find any words, they are already set. Now onto actually finding those words... First, we need to iterate over each of the valid words (one, two, three...) and check for each one. To actually check for them, I needed to find a way to look for a string inside of a string. And once again, after searching through the C standard library, I found a nice little function called strstr, which looks for a string inside of another string and returns its index. This is perfect! strstr makes it easy to find the first instance of a string, but what about the last? I decided that what I could do was make a small loop that constantly looks for the requested word inside of the string, and then if there is another instance of that word, cut everything before and including the word out of the string.

while (strstr(lastword + strlen(words[i]), words[i])) {
    lastword = strstr(lastword + strlen(words[i]), words[i]);
}

And once that while loop fails, we can move on to grabbing the indexes.

if (firstidx > (size_t)(firstword - line)) {
    firstidx = firstword - line;
    tens = i + 1;
}
if (lastidx <= (size_t)(lastword - line)) {
    lastidx = lastword - line;
    ones = i + 1;
}

Now here is when I realized I had been ignoring something very important for a while now. What if that word isn't in the line? It seems simple but I had completely overlooked it! To fix this, I added a simple check at the top of the loop.

char *firstword = strstr(line, words[i]);
char *lastword = strstr(line, words[i]);
if (!firstword) {
    continue;
}

Now all that is left is to add to the result and... once again, we have a solution!

Refactoring

I once again got rid of the redundant reversed string and simply reversed line after getting the last index of a number. Other than that, I struggle to see anything else that could be massively improved.

What have I learned?

Just on day 1, I managed to learn 2 C functions I didn't know existed, and I learned quite a bit about string manipulation in C in general. I struggled at times from trying to oversimplify my solution when what I had worked just fine, and part 2 really threw me for a whirl when I first got around to thinking about it.

Resources

Really, Thank you for taking the time to read! Please consider sharing if you feel like this helped you at all, and feel free to message me on matrix or email me with any comments or suggestions on how I can improve my writing style, my code, or both.

Tags: advent-of-code, C, programming, strings