DEV Community

Neil Gall
Neil Gall

Posted on

Advent of Code 2021 Day 1

No rules this year, I'll pick a language on the day. How about some production quality C? (I really hope there's no bugs!)

If I do more C I'll no doubt factor out the reusable bits.

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ----------------------------------------------
// Abstract reader

struct Reader;
typedef void (*reader_delete_f)(struct Reader *);
typedef char (*reader_next_f)(struct Reader *);

struct Reader {
    reader_next_f next;
    reader_delete_f delete;
};


// ----------------------------------------------
// String reader

struct StringReader {
    struct Reader reader;
    const char *p;
};

char string_reader_f(struct StringReader *sr) {
    return *(sr->p++);
}

struct Reader *string_reader(const char *s) {
    struct StringReader *sr = (struct StringReader *)malloc(sizeof(struct StringReader));
    sr->reader.next = (reader_next_f)&string_reader_f;
    sr->reader.delete = (reader_delete_f)&free;
    sr->p = s;
    return (struct Reader *)sr;
}


// ----------------------------------------------
// File reader

struct FileReader {
    struct Reader reader;
    FILE *fd;
};

char file_reader_f(struct FileReader *fr) {
    int c = fgetc(fr->fd);
    return (c == EOF) ? 0 : (char)c;
}

void file_reader_delete(struct FileReader *fr) {
    fclose(fr->fd);
    free(fr);
}

struct Reader *file_reader(const char *filename) {
    FILE *f = fopen(filename, "rt");
    if (f == NULL) {
        perror("can't open file");
        abort();
    }
    struct FileReader *fr = (struct FileReader *)malloc(sizeof(struct FileReader));
    fr->reader.next = (reader_next_f)&file_reader_f;
    fr->reader.delete = (reader_delete_f)&file_reader_delete;
    fr->fd = f;
    return (struct Reader *)fr;
}


// ----------------------------------------------
// Report structure

struct Report {
    size_t capacity;
    size_t count;
    int entries[];
};

size_t report_size(size_t count) {
    return sizeof(struct Report) + sizeof(int) * count;
}

struct Report *empty_report(size_t capacity) {
    struct Report *report = (struct Report *)malloc(report_size(capacity));
    report->capacity = capacity;
    report->count = 0;
    return report;
}

void append_entry(struct Report **report_p, int entry) {
    struct Report *report = *report_p;
    if (report->count == report->capacity) {
        report->capacity *= 2;
        *report_p = report = (struct Report *)realloc(report, report_size(report->capacity));
    }
    report->entries[report->count++] = entry;

}

struct Report *read_report(struct Reader *reader) {
    struct Report *report = empty_report(100);
    int entry = 0;
    int entry_digits = 0;
    char c;
    while ((c = reader->next(reader)) != 0) {
        if (isdigit(c)) {
            entry = (entry * 10) + (c - '0');
            entry_digits += 1;
        } else {
            append_entry(&report, entry);
            entry = 0;
            entry_digits = 0;
        }
    }
    if (entry_digits > 0) {
        append_entry(&report, entry);
    }
    reader->delete(reader);
    return report;
}


// ----------------------------------------------
// Problems

int part1(struct Report *report) {
    size_t increments = 0;
    for (size_t pos = 1; pos < report->count; ++pos) {
        if (report->entries[pos] > report->entries[pos-1])
            increments++;
    }
    return increments;
}


int window(struct Report *report, int pos) {
    int *es = report->entries + pos;
    return es[0] + es[1] + es[2];
}

int part2(struct Report *report) {
    size_t increments = 0;
    for (size_t pos = 3; pos < report->count; ++pos) {
        int prev = window(report, pos-3);
        int curr = window(report, pos-2);
        if (curr > prev) 
            increments++;
    }
    return increments;
}

// ----------------------------------------------
// tests

void test(const char *input, int (*f)(struct Report *), int expect) {
    struct Report *report = read_report(string_reader(input));
    int actual = f(report);
    free(report);
    if (actual != expect) {
        fprintf(stderr, "test failed input='%s' expect=%d actual=%d\n",
            input, expect, actual);
        abort();
    }
}


// ----------------------------------------------

static const char test_data[] = 
    "199\n200\n208\n210\n200\n207\n240\n269\n260\n263";

int main() {
    test(test_data, part1, 7);
    test(test_data, part2, 5);

    struct Report *report = read_report(file_reader("day1/input.txt"));

    printf("Part 1: %d\n", part1(report));
    printf("Part 2: %d\n", part2(report));

    free(report);
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)