The data is formatted as a series of integers, with each Cadet's data separated by a blank line. The data is stored in the file input.txt
. A sample input file is provided below:
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
Given the input file, input.txt
, figure out which 3 Cadets have the most Calories in their rucksacks and find the total number of Calories each has.
Submit your answer as an integer wrapped in the flag{}
braces (for example, flag{24000}
).
The biggest challenge here is doing this in C. We need to read the file, parse the data, and then sort the data. When we solve this problem, we need to consider the following:
Let's discuss each of these in turn.
From the binex sections, we know we read files in C using fopen()
. In case you don't remember the arguments, it takes two:
FILE *fopen(const char *filename, const char *mode);
Our filename is input.txt, and we only want to open the file for reading (i.e., r
). Since this is not binary data, we don't use the b
flag.
When we open a file, we should always check that the file opened correctly. If it didn't, we should exit the program. We can do this with the following code:
FILE* fp = fopen("input.txt", "r"); if (fp == NULL) exit(EXIT_FAILURE);
Now that the file is open, we need to read the data. We can use getline()
to read data line-by-line. This function takes three arguments:
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
Let's break this down a little more:
lineptr
is a pointer to a character array (i.e., a string). This is where the data will be stored.n
is the size of the character array. If the data is larger than this, the function will reallocate the array to be larger.stream
is the file we are reading from.ssize_t
, a signed integer indicating the number of characters read or -1
if there was an error (such as EOF).We can use this function to read the data from the file. We will need to loop through the file until we reach the end. We can do this with the following code:
char* line = NULL; size_t len = 0; ssize_t read; while ((read = getline(&line, &len, fp)) != -1) { // logic goes here }
This code will read the file line-by-line, storing the data in line
. We can then process the data in the while
loop.
Now that we have the data, we need to process it. We need to store the data in a data structure and then sort it. We can use a static array to handle this because it is easier to sort than a linked list.
Let's define a variable called total
to store each Cadet's total number of Calories. Remember that static arrays cannot have their size changed, so we choose something large enough to hold the data. There are around 2500
lines, which means there can be a max of around 1250
Cadets (since a newline must separate each one).
We'll also need a variable to keep track of the current Cadet and a variable to track that Cadet's sum. We'll use the following initializations:
int totals[1250] = {0}; uint16_t i = 0; uint32_t sum = 0;
Now, we must decide on our logic as we process each line. The best way to do this is as follows:
We can do this with the following code:
if (read == 1) { // if blank line, store total totals[i] = sum; ++i; sum = 0; } else { // else, add to total sum += atoi(line); }
Why did we use read == 1
? Remember that getline()
returns the number of characters read. If we read a blank line, we only read one character (the newline). If we read a number, we read more than one character. This is why we check for read == 1
.
atoi()
to convert the string to an integer. This function is unsafe, but it's good enough for this challenge. Since we are allowed to assume that the file only contains integers, we can use this function.=This code successfully reads the data into the array. Now, it's time for processing.
We need to sort the data in the array. We can do this using the qsort()
function. This function takes four arguments:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
This one's quite a complicated function, so let's break it down:
base
is the array we are sorting.nmemb
is the number of elements in the array.size
is the size of each element in the array. We most often use sizeof
and pass in the data type.compar
is a functor that compares two elements. We'll most often define this ourselves because it's easier.Let's write the compar
function. It takes two const void*
arguments. These are pointers to the elements in the array. We must cast these to the correct data type, and then compare them. We can do this with the following code:
int compare(const void* a, const void* b) { return (*(int*)b - *(int*)a); }
This code will sort the array from largest to smallest. How do we know this? The man
pages tell us this:
The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
Therefore, if a
is greater than b
, we return a negative number. If a
is less than b
, we return a positive number. If they are equal, we return 0
. This is why we return b - a
.
a - b
.Now that we have the compar
function, we can sort the array. We can do this with the following code:
qsort(totals, i, sizeof(int), compare);
This code will sort the array in descending order. Now, we need to print the top three Cadets. We can do this with the following code:
printf("flag{%d}\n", totals[0] + totals[1] + totals[2]);
Finally, we need to free the memory we allocated. We can do this with the following code:
fclose(fp); if (line) free(line); exit(EXIT_SUCCESS);
If we compile and run this code, we get the following output:
$ gcc -o solve solve.c && ./solve flag{209914}
The full solution is below:
#include <stdio.h> #include <stdint.h> #include <stdlib.h> int compare(const void* a, const void* b) { return (*(int*)b - *(int*)a); } int main(void) { // open the file FILE* fp = fopen("input.txt", "r"); if (fp == NULL) exit(EXIT_FAILURE); char* line = NULL; size_t len = 0; ssize_t read; // initialize array int totals[1250] = {0}; uint16_t i = 0; uint32_t sum = 0; // read the file till no more lines while ((read = getline(&line, &len, fp)) != -1) { if (read == 1) { // if blank line, store total totals[i] = sum; ++i; sum = 0; } else { // else, add to total sum += atoi(line); } } // sort the array qsort(totals, i, sizeof(int), compare); // print the sum of the top 3 printf("flag{%d}\n", totals[0] + totals[1] + totals[2]); fclose(fp); if (line) free(line); exit(EXIT_SUCCESS); }