You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
7.1 KiB
288 lines
7.1 KiB
--- |
|
title: "Advent of Code: Day 1" |
|
tags: |
|
- Advent of Code |
|
description: Day one of the Advent of Code |
|
date: 2021-12-01 |
|
--- |
|
|
|
So, it's December all ready. I haven't written anywhere near as many blogs this |
|
year as I would like to. Hopefully the [Advent of |
|
Code](https://adventofcode.com/2021/) this year will give me some things to |
|
write about and make this years list of blogs look better. |
|
|
|
This is the first year I am doing the Advent of Code so don't really know what |
|
to expect. However, with day 1 done, I think I'll probably enjoy it. |
|
|
|
If you're reading this, it is no longer day 1. I will refrain from publishing |
|
the solutions until at least the day after they go live. However, if previous |
|
years are anything to go by, you should still be able to view the puzzles and |
|
have a go for yourself: |
|
|
|
<https://adventofcode.com/2021/day/1> |
|
|
|
I am going to use the project puzzles this year to try and learn C. I have used |
|
it a little bit but certainly wouldn't call myself proficient. This is supposed |
|
to be a way for me to get better so if you're looking for well formed C code by |
|
someone who knows what they are doing, you should probably look elsewhere. |
|
|
|
My solutions are available here: |
|
|
|
<https://git.jonathanh.co.uk/jab2870/Advent-of-Code/src/branch/master/day1> |
|
|
|
## Part 1 |
|
|
|
The first part of today's challenge involved taking a list of numbers and |
|
counting the number of times a number is higher than the previous number. |
|
|
|
The example they gave is with this list of numbers: |
|
|
|
``` |
|
199 |
|
200 |
|
208 |
|
210 |
|
200 |
|
207 |
|
240 |
|
269 |
|
260 |
|
263 |
|
``` |
|
|
|
I put those in a text file called example.txt. |
|
|
|
### Bash |
|
|
|
I started by implementing it in bash. Why? Because I know bash and wanted to |
|
make sure I had understood the problem before trying to battle with C. |
|
|
|
```bash |
|
#!/usr/bin/env bash |
|
|
|
file="$1" |
|
increasing=0 |
|
last=99999999 |
|
|
|
while read line; do |
|
if [ "$line" -gt "$last" ]; then |
|
increasing="$(( increasing + 1 ))" |
|
fi |
|
last="$line" |
|
done < <(cat "$file") |
|
|
|
echo "$increasing were increasing" |
|
``` |
|
|
|
There is not a huge amount going on here. If you are at all familiar with bash, |
|
you can probably see what is going on here. I initially set up variables to hold |
|
the file name (passed in as the first argument), a counter for the number of |
|
times we increase from one line to the next and the value of the last line we've |
|
seen that I initially set to a large number. |
|
|
|
Then we enter the loop. It is worth noting that the loop had to be of the form: |
|
|
|
```bash |
|
while read line; do |
|
# Do stuff |
|
done < <(cat "$file") |
|
``` |
|
|
|
Rather than: |
|
|
|
```bash |
|
cat "$file" | while read line; do |
|
# Do stuff |
|
done |
|
``` |
|
|
|
The reason for this is that pipes are executed in a sub shell meaning they can't |
|
change variables in the initial shell. In other words, with the second method, |
|
we wouldn't be able to update the increasing and last variables from inside the |
|
loop. |
|
|
|
Inside the loop, I check to see if the current value is larger than the previous |
|
value and if it is I increment the `increacing` variable by 1. I then set the |
|
`last` variable so it can be used for the next iteration. |
|
|
|
Finally, once all the lines have been done, I print out the number of lines that |
|
were increasing. |
|
|
|
|
|
### C |
|
|
|
So, in a surprise to nobody, my C implementation is not as short as the bash |
|
one. However, it wasn't as bad as I thought it would be. |
|
|
|
Step 1 was trying to work out how to read a file in to c one file at a time. |
|
|
|
Given that I knew the format of the lines, I was able to use fscanf to get each |
|
of them. |
|
|
|
Although the syntax is slightly different, the logic is very similar between the |
|
Bash and C implementations. |
|
|
|
```c |
|
#include <stdio.h> |
|
|
|
int main( int argc, char *argv[] ){ |
|
if( argc == 2 ) { |
|
unsigned int last = ~0; |
|
unsigned int increasing = 0; |
|
unsigned int value; |
|
|
|
// Read the values into an array |
|
FILE *fp=fopen(argv[1], "r"); |
|
while (!feof (fp)){ |
|
fscanf(fp, "%d\n", &value); |
|
if ( value > last ) |
|
increasing++; |
|
last = value; |
|
} |
|
fclose(fp); |
|
|
|
|
|
printf( "%d were increasing\n", increasing ); |
|
|
|
return 0; |
|
} else { |
|
printf("You need to provide a file\n"); |
|
return 1; |
|
} |
|
} |
|
``` |
|
|
|
## Part 2 |
|
|
|
The extension for part 2 was to change the program so that rather than comparing |
|
lines with the previous line, the sum of 3 consecutive lines should be compared. |
|
|
|
``` |
|
199 A |
|
200 A B |
|
208 A B C |
|
210 B C D |
|
200 E C D |
|
207 E F D |
|
240 E F G |
|
269 F G H |
|
260 G H |
|
263 H |
|
``` |
|
|
|
So, the sum of the numbers marked with A would be compared to the sum of those |
|
marked with B. Then B with C and so on. |
|
|
|
I think the intended way of doing this is to first create an array type |
|
structure of sums then use the code from part 1 to work out how many of these |
|
sums are increasing. However, a short cut can be taken. The sum of the next |
|
group of numbers is always going to be the sum of the previous numbers plus the |
|
next digit, minus the first. It follows therefore that we can compare the groups |
|
by comparing the differences of every 3 numbers rather than adjacent numbers. |
|
|
|
So, for example |
|
|
|
$$ A = 199 + 200 + 208 $$ |
|
|
|
$$ B = 200 + 208 + 210 $$ |
|
|
|
We can confirm that group B increases from group A because 210 is greater than |
|
199. |
|
|
|
In order to do this, I want to be able to easily reference individual entries by |
|
their index so rather than doing the comparisons on the first pass through the |
|
file, I first create an array. I can then compare an entry with the entry 3 |
|
before it. |
|
|
|
### Bash |
|
|
|
```bash |
|
#!/usr/bin/env bash |
|
|
|
file="$1" |
|
increasing=0 |
|
|
|
readarray -t "arr" < "$file" |
|
|
|
length="${#arr[@]}" |
|
|
|
for i in $(seq 3 "$length"); do |
|
curr="${arr[$i]}" |
|
prev3="${arr[$((i - 3))]}" |
|
|
|
if [ -n "$curr" ] && [ "$curr" -gt "$prev3" ]; then |
|
increasing="$((increasing + 1))" |
|
fi |
|
done |
|
|
|
echo "$increasing were increacing" |
|
``` |
|
|
|
You'll see that bash has a nice built-in for turning a file into an array. |
|
However, any bonus points it it might get for that, it looses by having the |
|
worst syntax ever for getting the length of that array. Who thought that |
|
`${#arr[@]}` would ever be a good idea‽ |
|
|
|
### C |
|
|
|
```c |
|
#include <stdio.h> |
|
|
|
int countLines(char filename[]){ |
|
FILE *fp; |
|
fp=fopen(filename, "r"); |
|
long int lines =0; |
|
|
|
if ( fp == NULL ) { |
|
return -1; |
|
} |
|
|
|
while (EOF != (fscanf(fp, "%*[^\n]"), fscanf(fp,"%*c"))) |
|
++lines; |
|
|
|
fclose(fp); |
|
return lines; |
|
} |
|
|
|
int main( int argc, char *argv[] ){ |
|
if( argc == 2 ) { |
|
// 1 less because file ends with trailing new line |
|
int lines=countLines(argv[1]); |
|
int values[lines]; |
|
int line=0; |
|
FILE *fp=fopen(argv[1], "r"); |
|
int increasing = 0; |
|
|
|
// Read the values into an array |
|
while (!feof (fp)){ |
|
fscanf(fp, "%d\n", &values[line]); |
|
|
|
if ( (line >= 3) && (values[line] > values[line-3]) ){ |
|
|
|
increasing++; |
|
|
|
} |
|
|
|
line++; |
|
} |
|
fclose(fp); // closing file |
|
|
|
printf( "Increasing: %d\n", increasing ); |
|
|
|
return 0; |
|
} else { |
|
printf("You need to provide a file\n"); |
|
return 1; |
|
} |
|
} |
|
``` |
|
|
|
So this is obviously longer than the bash version but mainly because there isn't |
|
a nice way of putting a text file into an array. I have to first iterate through |
|
the file to determine how many lines there are, then allocate that much space for |
|
the array, then re-run through the file to get the values. |
|
|
|
Initially, I then had a third loop where I iterated through the newly created |
|
array to determine if there was an increase. However, I then realised I could do |
|
that whilst populating the array. |
|
|
|
|