diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 513e4b2..8409cfc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -73,19 +73,6 @@ jobs: for file in cpp/*.cc; do g++ "$file" -o "${file%.cc}" done - cpp-clang-build: - name: Build C++ with clang - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Build - run: | - for file in cpp/*.cc; do - clang++ "$file" -o "${file%.cc}" - done gcc-build: name: Build C with gcc @@ -100,17 +87,3 @@ jobs: for file in c/*.c; do gcc "$file" -o "${file%.c}" done - - clang-build: - name: Build C with clang - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Build - run: | - for file in c/*.c; do - clang "$file" -o "${file%.c}" - done diff --git a/Cargo.toml b/Cargo.toml index 83c027e..05c3d0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = ["crates/day01", "crates/day02", "crates/day03"] regex = "1.11.1" [profile.release] -opt-level = 'z' +opt-level = 3 lto = true codegen-units = 1 panic = 'abort' diff --git a/README.md b/README.md index 95cf7ca..1228aa5 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,4 @@ Ideally, the solution does not use external crates/dependencies. | | Part 1 | Part 2 | Part 1 | Part 2 | Part 1 | Part 2 | | 1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | 2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| 3 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| 3 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | diff --git a/c/day03.c b/c/day03.c new file mode 100644 index 0000000..dc4eaff --- /dev/null +++ b/c/day03.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include + +typedef struct { + const char *iterator; + const char *end; +} ProgramParser; + +static inline char peek(const ProgramParser *parser) { + return (parser->iterator < parser->end) ? *parser->iterator : '\0'; +} + +static inline void next(ProgramParser *parser) { + if (parser->iterator < parser->end) + parser->iterator++; +} + +static bool parse_mul(ProgramParser *parser) { + if (parser->end - parser->iterator < 3) + return false; + if (strncmp(parser->iterator, "mul", 3) == 0) { + parser->iterator += 3; + return true; + } + return false; +} + +static int parse_number(ProgramParser *parser) { + int value = 0, count = 0; + + while (isdigit(peek(parser)) && count < 3) { + value = value * 10 + (peek(parser) - '0'); + next(parser); + count++; + } + + return count > 0 ? value : -1; +} + +static int parse(ProgramParser *parser) { + int result = 0; + + while (parser->iterator < parser->end) { + if (parse_mul(parser) && peek(parser) == '(') { + next(parser); + int a = parse_number(parser); + + if (a != -1 && peek(parser) == ',') { + next(parser); + int b = parse_number(parser); + + if (b != -1 && peek(parser) == ')') { + next(parser); + result += a * b; + } + } + } else { + next(parser); + } + } + + return result; +} + +typedef struct { + size_t *data; + size_t size; + size_t capacity; +} SizeVector; + +static SizeVector create_size_vector(size_t initial_capacity) { + SizeVector vec = {.data = malloc(initial_capacity * sizeof(size_t)), + .size = 0, + .capacity = initial_capacity}; + return vec; +} + +static void free_size_vector(SizeVector *vec) { free(vec->data); } + +static void push_back(SizeVector *vec, size_t value) { + if (vec->size == vec->capacity) { + vec->capacity = (vec->capacity * 3) / 2; // 1.5x growth + vec->data = realloc(vec->data, vec->capacity * sizeof(size_t)); + } + vec->data[vec->size++] = value; +} + +static SizeVector find_all_indexes(const char *haystack, const char *needle) { + SizeVector indexes = create_size_vector(16); + const size_t needle_len = strlen(needle); + const char *pos = haystack; + + while ((pos = strstr(pos, needle)) != NULL) { + push_back(&indexes, pos - haystack); + pos += needle_len; + } + + return indexes; +} + +int closest_greater_than(size_t x, const SizeVector *values) { + int closest = -1; + for (size_t i = 0; i < values->size; i++) { + if (values->data[i] > x && (closest == -1 || values->data[i] < closest)) { + + closest = values->data[i]; + } + } + return closest; +} + +static int count_mul(const char *message) { + ProgramParser parser = {.iterator = message, + .end = message + strlen(message)}; + return parse(&parser); +} + +static int parse_with_rules(const char *message) { + const char *do_substring = "do()"; + const char *dont_substring = "don't()"; + + SizeVector do_positions = find_all_indexes(message, do_substring); + SizeVector dont_positions = find_all_indexes(message, dont_substring); + + if (do_positions.size == 0 && dont_positions.size == 0) { + int result = count_mul(message); + free_size_vector(&do_positions); + free_size_vector(&dont_positions); + return result; + } + + size_t len = do_positions.data[0] + strlen(do_substring); + char *first_substring = strndup(message, len); + int count = count_mul(first_substring); + free(first_substring); + + int next_dont = -1; + + for (size_t i = 0; i < do_positions.size; i++) { + size_t pos = do_positions.data[i]; + if (next_dont == -1 || pos > next_dont) { + int next_dont_opt = closest_greater_than(pos, &dont_positions); + if (next_dont_opt != -1) { + size_t substring_len = next_dont_opt - pos; + char *substring = strndup(message + pos, substring_len); + count += count_mul(substring); + free(substring); + next_dont = next_dont_opt; + } + } + } + + free_size_vector(&do_positions); + free_size_vector(&dont_positions); + + return count; +} + +static char *read_file(const char *file_path) { + FILE *file = fopen(file_path, "r"); + if (!file) { + perror("Unable to open file"); + exit(EXIT_FAILURE); + } + + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + rewind(file); + + char *content = malloc(file_size + 1); + fread(content, 1, file_size, file); + content[file_size] = '\0'; + fclose(file); + return content; +} + +int main() { + char *message = read_file("crates/day03/input.txt"); + + printf("Part One solution: sum is %d\n", count_mul(message)); + printf("Part Two solution: sum is %d\n", parse_with_rules(message)); + + free(message); + return 0; +} diff --git a/cpp/day03.cc b/cpp/day03.cc new file mode 100644 index 0000000..4335729 --- /dev/null +++ b/cpp/day03.cc @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct Problem { + std::string program; + + explicit Problem(const std::string &input) : program(input) {} + + static Problem fromString(const std::string &input) { return Problem(input); } +}; + +class ProgramParser { +public: + explicit ProgramParser(const std::string &program) + : iterator(program.begin()), end(program.end()) {} + + int parse() { + int result = 0; + + while (iterator != end) { + if (parse_mul()) { + if (peek() == '(') { + next(); + + auto a = parseNumber(); + if (a && peek() == ',') { + next(); + + auto b = parseNumber(); + if (b && peek() == ')') { + next(); + result += (*a) * (*b); + } + } + } + } else { + next(); + } + } + + return result; + } + +private: + std::string::const_iterator iterator; + std::string::const_iterator end; + + char peek() const { return iterator != end ? *iterator : '\0'; } + + void next() { + if (iterator != end) + ++iterator; + } + + bool parse_mul() { + if (peek() == 'm') { + next(); + if (peek() == 'u') { + next(); + if (peek() == 'l') { + next(); + return true; + } + } + } + return false; + } + + std::optional parseNumber() { + std::string digits; + + for (int i = 0; i < 3; ++i) { + if (std::isdigit(peek())) { + digits.push_back(peek()); + next(); + } else { + break; + } + } + + if (!digits.empty()) { + return std::stoi(digits); + } + return std::nullopt; + } +}; + +std::vector findAllIndexes(const std::string &haystack, + const std::string &needle) { + std::vector indexes; + size_t start = 0; + + while ((start = haystack.find(needle, start)) != std::string::npos) { + indexes.push_back(start); + start += needle.size(); + } + + return indexes; +} + +std::optional closest_greater_than(size_t x, + const std::vector &values) { + std::optional closest; + for (size_t value : values) { + if (value > x && (!closest || value < *closest)) { + closest = value; + } + } + return closest; +} + +int count_mul(const std::string &message) { + Problem problem = Problem::fromString(message); + ProgramParser parser(problem.program); + return parser.parse(); +} + +int parseWithRules(const std::string &message) { + const std::string doSubstring = "do()"; + const std::string dontSubstring = "don't()"; + + auto doPositions = findAllIndexes(message, doSubstring); + auto dontPositions = findAllIndexes(message, dontSubstring); + + if (doPositions.empty() && dontPositions.empty()) { + return count_mul(message); + } + + std::string firstSubstring = + message.substr(0, doPositions[0] + doSubstring.size()); + int count = count_mul(firstSubstring); + std::optional nextDont; + + for (size_t i : doPositions) { + if (!nextDont || i > *nextDont) { + auto nextDontOpt = closest_greater_than(i, dontPositions); + if (nextDontOpt) { + size_t nextDontValue = *nextDontOpt; + std::string substring = message.substr(i, nextDontValue - i); + count += count_mul(substring); + nextDont = nextDontValue; + } + } + } + + return count; +} + +std::string readFile(const std::string &filePath) { + std::ifstream file(filePath); + if (!file.is_open()) { + throw std::runtime_error("Unable to open file: " + filePath); + } + + std::ostringstream content; + content << file.rdbuf(); + return content.str(); +} + +int main() { + + std::string message = readFile("crates/day03/input.txt"); + + // --- Part One --- + int count = count_mul(message); + std::cout << "Part One solution: sum is " << count << std::endl; + + // --- Part Two --- + count = parseWithRules(message); + std::cout << "Part Two solution: sum is " << count << std::endl; + + return 0; +} diff --git a/crates/day02/src/main.rs b/crates/day02/src/main.rs index 1869a38..c96bcc6 100644 --- a/crates/day02/src/main.rs +++ b/crates/day02/src/main.rs @@ -27,6 +27,8 @@ fn main() { // --- Part Two --- count = 0; + + // not really optimal, ideally the first pass give hints on what element to remove for e in &reports { if is_safe(e) { count += 1;