From 941ee5f329afcf86fffad63a235db171d9d069f2 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Thu, 5 Dec 2024 22:01:27 +0100 Subject: [PATCH] Day 2 part I --- Makefile | 4 ++ README.md | 4 ++ src/02.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/02.c diff --git a/Makefile b/Makefile index eb6fce4..3df2a74 100644 --- a/Makefile +++ b/Makefile @@ -14,5 +14,9 @@ bin/%: src/%.c $(STDLIB_SOURCES) $(STDLIB_HEADERS) gcc $(CFLAGS) $< $(STDLIB_SOURCES) -o $@ strip -R .comment $@ +out/%.txt: in/%.txt bin/% + @mkdir -p $(@D) + <$< bin/$* >$@ + clean: rm -rf bin diff --git a/README.md b/README.md index 7f08bc0..0eeee4f 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,7 @@ Run `make` to build everything. ## Running Every program accepts the input through `stdin` and outputs through `stdout`. + +If you place puzzle inputs in files at `in/01.txt`, `in/02.txt`, etc., then you +can use make to compile and compute the answer by running `make out/01.txt`, +`make out/02.txt`, etc. diff --git a/src/02.c b/src/02.c new file mode 100644 index 0000000..92aea13 --- /dev/null +++ b/src/02.c @@ -0,0 +1,121 @@ +#include +#include +#include + +#define BUFFER_SIZE 4096UL + +typedef enum parser_state { + parser_state_digits_start, + parser_state_digits_rest, + parser_state_whitespace, +} parser_state; + +typedef struct parser { + i8 prev; + i8 current; + i8 diff; + bool unsafe; + parser_state state; +} parser; + +void parser_process(parser* parser, const c8* ptr, usize len); +bool is_digit(c8 c); +void println_u64(u64 value); + +u64 safe_count = 0; + +i32 main(i32 argc, char *argv[]) +{ + c8* buffer = buffer_init(BUFFER_SIZE); + + usize buffer_ptr = 0; + isize bytes_read = 0; + parser parser = {0}; + + while ((bytes_read = read(stdin, &buffer[buffer_ptr], BUFFER_SIZE)) > 0) { + parser_process(&parser, &buffer[buffer_ptr], bytes_read); + buffer_ptr = (buffer_ptr + bytes_read) % BUFFER_SIZE; + } + + println_u64(safe_count); + + return 0; +} + +void parser_process(parser* parser, const c8* ptr, usize len) +{ + const c8* end = &ptr[len]; + while (ptr < end) { + c8 c = *ptr; + switch (parser->state) { + case parser_state_digits_start: + if (is_digit(c)) { + parser->prev = parser->prev * 10 + (c - '0'); + ptr += 1; + } else { + parser->state = parser_state_whitespace; + } + break; + case parser_state_digits_rest: + if (is_digit(c)) { + parser->current = parser->current * 10 + (c - '0'); + ptr += 1; + } else { + i8 diff = parser->current - parser->prev; + if (diff * parser->diff < 0 || diff < -3 || diff == 0 || diff > 3) { + parser->unsafe = true; + } + + parser->prev = parser->current; + parser->current = 0; + parser->diff = diff; + parser->state = parser_state_whitespace; + } + break; + case parser_state_whitespace: + if (c == '\n') { + if (!parser->unsafe) { + safe_count += 1; + } + + parser->prev = 0; + parser->current = 0; + parser->diff = 0; + parser->unsafe = false; + parser->state = parser_state_digits_start; + + ptr += 1; + } else if (is_digit(c)) { + parser->state = parser_state_digits_rest; + } else { + ptr += 1; + } + break; + } + } +} + +bool is_digit(c8 c) +{ + return c >= '0' && c <= '9'; +} + +void println_u64(u64 value) +{ + /* The longest string we produce would be "18446744073709551615\n", which is + * 21 bytes long (we don't care about the null terminator). + */ + c8 buffer[21]; + usize strlen = 1; + + buffer[20] = '\n'; + do + { + c8 digit = (value % 10) + '0'; + buffer[20 - strlen] = digit; + strlen += 1; + value /= 10; + } while (value > 0); + + write(stdout, &buffer[21 - strlen], strlen); +}