158 lines
3.8 KiB
C
158 lines
3.8 KiB
C
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define STRBUF_SIZE 200
|
|
#define DEFAULT_FILE "input.txt"
|
|
#define ISDIGIT(X) (X >= '0' && X <= '9')
|
|
|
|
// Parses a found number.
|
|
// The provided location can be any digit of the
|
|
// number; this function will automatically
|
|
// seek the first digit before processing.
|
|
// Erases numbers as they are processed.
|
|
uint16_t parse_num_at(char *str, uint8_t idx)
|
|
{
|
|
|
|
// Check input
|
|
if (str[idx] < '0' || str[idx] > '9') return 0;
|
|
|
|
// Find first digit of number
|
|
while (ISDIGIT(str[idx]) && idx) idx--;
|
|
if (!ISDIGIT(str[idx])) idx++;
|
|
|
|
uint16_t num = 0;
|
|
|
|
// Add up all digits
|
|
while (ISDIGIT(str[idx]))
|
|
{
|
|
num = (num * 10) + (str[idx] - '0');
|
|
// Erase the number to make sure we don't count it twice
|
|
str[idx] = '.';
|
|
idx++;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
// If the character is a * with two adjacent numbers,
|
|
// returns the gear ratio. Returns zero if the symbol
|
|
// is not a gear.
|
|
uint64_t get_gear_ratio( uint8_t idx,
|
|
char *above,
|
|
char *current,
|
|
char *below)
|
|
{
|
|
char *above_copy = malloc(sizeof(char) * STRBUF_SIZE);
|
|
char *current_copy = malloc(sizeof(char) * STRBUF_SIZE);
|
|
char *below_copy = malloc(sizeof(char) * STRBUF_SIZE);
|
|
|
|
memcpy(above_copy, above, STRBUF_SIZE);
|
|
memcpy(current_copy, current, STRBUF_SIZE);
|
|
memcpy(below_copy, below, STRBUF_SIZE);
|
|
|
|
uint16_t nums[8] = {0};
|
|
|
|
if (idx) nums[0] = parse_num_at(above_copy, idx - 1);
|
|
nums[1] = parse_num_at(above_copy, idx);
|
|
nums[2] = parse_num_at(above_copy, idx + 1);
|
|
|
|
nums[3] = parse_num_at(current_copy, idx + 1);
|
|
nums[4] = parse_num_at(current_copy, idx - 1);
|
|
|
|
if (idx) nums[5] = parse_num_at(below_copy, idx - 1);
|
|
nums[6] = parse_num_at(below_copy, idx);
|
|
nums[7] = parse_num_at(below_copy, idx + 1);
|
|
|
|
free(above_copy);
|
|
free(current_copy);
|
|
free(below_copy);
|
|
|
|
uint8_t i = 0;
|
|
uint16_t num1 = 0;
|
|
uint16_t num2 = 0;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
// More than 2 adjacent numbers
|
|
if (nums[i] && num1 && num2) return 0;
|
|
|
|
// The second number
|
|
if (nums[i] && num1) num2 = nums[i];
|
|
|
|
// The first number
|
|
else if (nums[i]) num1 = nums[i];
|
|
}
|
|
|
|
return num1 * num2;
|
|
}
|
|
|
|
// Search through a line of *current for symbols.
|
|
uint64_t parse_line(char *above, char *current, char *below)
|
|
{
|
|
uint64_t sum = 0;
|
|
uint8_t idx;
|
|
char c;
|
|
|
|
for (idx = 0; current[idx] != 0; idx++)
|
|
{
|
|
c = current[idx];
|
|
if (c == '*')
|
|
{
|
|
// We've found a symbol,
|
|
// add up all numbers around it
|
|
sum += get_gear_ratio(idx, above, current, below);
|
|
}
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
// Get filename as an argument, defaults to "input.txt"
|
|
char *path = (argc > 1) ? argv[1] : DEFAULT_FILE;
|
|
|
|
if (!path)
|
|
{
|
|
printf("Requires input file.\n");
|
|
return 1;
|
|
}
|
|
|
|
FILE *file = fopen(path, "r");
|
|
|
|
if (!file)
|
|
{
|
|
printf("Could not find file.");
|
|
return 1;
|
|
}
|
|
|
|
char *above = malloc(sizeof(char) * STRBUF_SIZE);
|
|
char *current = malloc(sizeof(char) * STRBUF_SIZE);
|
|
char *below = malloc(sizeof(char) * STRBUF_SIZE);
|
|
|
|
memset(above, '.', STRBUF_SIZE);
|
|
fgets(current, STRBUF_SIZE, file);
|
|
|
|
uint64_t sum = 0;
|
|
|
|
while (fgets(below, 200, file))
|
|
{
|
|
sum += parse_line(above, current, below);
|
|
|
|
free(above);
|
|
above = current;
|
|
current = below;
|
|
below = malloc(sizeof(char) * STRBUF_SIZE);
|
|
memset(below, '.', STRBUF_SIZE);
|
|
}
|
|
|
|
// Process the last line of the file
|
|
sum += parse_line(above, current, below);
|
|
|
|
printf("Sum: %lud\n", sum);
|
|
|
|
return 0;
|
|
} |