#include #include #include #include #include #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; }