Skip to content

Commit

Permalink
Adventofcode (#25)
Browse files Browse the repository at this point in the history
Finish 2023 with days 21 and 24.
Add benchmark of map, unordered_map, and a custom BST "splay tree" in day 14.

Summary of commits while squashing:

Day 21:

* Add alternative for part two that turned out to be slower.

I am guessing the unordered_set gets very large.

* Improve bookeeping in searches. Update print making nice visualization.

Visualization using escape code.
Refactor.
For part two, printing number garden plots at each step. Checking the
sequence and difference sequence in Python shows kind of exponential
and a periodic-like function with an exponential trend, respectively.

* Python analysis WIP.

* Part two answer using the Lagrange polynomial using pybind11.

* Coming back to day 24 with basic Python visualization and warning fix.

* Update nob.yml

* Update nob.yml

* Add benchmark of a problem with 3rd party. Refactor.

* Add minimal splay BST (self-balancing) and parameterize benchmark.

* Update build files.

* 🙈

Then:

* Updates to nob.yml

* Day 24 part two using sympy. Last star! 🌟

* CI fixes.

Remove building of step counter (day 21) since with pybind I set up CMake.
The build with CMake manually, without nob, using tmate, didn't
work due to some errors that seem to do with either the version
of pybind or the compiler / compilation flags.

* Revert python version bump to fix nb CI

* Update github-actions-duplicate-code-check.yml
  • Loading branch information
iglesias authored Jul 6, 2024
1 parent 6a00696 commit c442540
Show file tree
Hide file tree
Showing 12 changed files with 510 additions and 126 deletions.
6 changes: 2 additions & 4 deletions .github/workflows/github-actions-duplicate-code-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ name: Duplicate code
on: pull_request

jobs:
duplicate-code-check:
name: Check for duplicate code
run_if:
if: startsWith(github.head_ref, 'adventofcode')
run_if:
if: startsWith(github.head_ref, 'adventofcode')
runs-on: ubuntu-latest
steps:
- name: Check for duplicate code
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/nob.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install GoogleTest
run: sudo apt-get install libgtest-dev

- name: Python setup
uses: actions/setup-python@v5
with:
python-version: '3'

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install libboost-all-dev libgtest-dev
- name: Build & Test
run: |
g++ -std=c++20 -o nob nob.cpp
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ gcm.cache/
__pycache__/
nobuild/
venv/
build/
logs/
*.png
111 changes: 72 additions & 39 deletions adventofcode/2023/day/14/parabolic_reflector_dish.cpp
Original file line number Diff line number Diff line change
@@ -1,50 +1,77 @@
#include <bits/stdc++.h>
#include <exception>
#include <fstream>
#include <functional>
#include <istream>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

void read_input();
void solve();
#include <benchmark/benchmark.h>

int main()
#include "splay_tree_map.hpp"

std::tuple<std::vector<std::string>, int, int> read_input(std::istream& input);
template<template<typename ...> class map_t> void solve(const std::vector<std::string>&, int, int);

namespace {

template<template<typename ...> class map_t> static void main(benchmark::State& state)
{
read_input();
solve();
// Setup
std::ifstream input("./parabolic_reflector_dish_input.txt");
const auto& [m, R, C] = read_input(input);
for (auto _ : state) {
// SUB
solve<map_t>(m, R, C);
}
}

std::vector<std::string> m;
int R, C;
} // anonymous

void read_input()
BENCHMARK(::main<std::map>);
BENCHMARK(::main<std::unordered_map>);
BENCHMARK(::main<dts::splay_tree::map>);
BENCHMARK_MAIN();

std::tuple<std::vector<std::string>, int, int> read_input(std::istream& input)
{
std::vector<std::string> m;
int R = 0, C = 0;
std::string line;
while(std::getline(std::cin, line))
while(std::getline(input, line))
{
m.push_back(line);
R++;
C = (int)line.length();
}
}

long long cycle_number;
std::pair<int, int> ans;
return {m, R, C};
}

void cycle();
void cycle(std::vector<std::string>& m, int R, int C);

void try_wrap()
template<template<typename ...> class map_t>
int try_wrap(map_t<size_t, long long>& cache,
std::vector<std::string>& m, int R, int C,
const long long cycle_number)
{
static std::map<size_t, long long> cache;
size_t h = std::hash<std::string>{}(m[0]);
for(int r=1; r<R; r++) h ^= std::hash<std::string>{}(m[r]);
if(!cache.contains(h))
{
cache.insert(std::make_pair(h, cycle_number));
return;
cache.emplace(h, cycle_number);
return -1;
}
for(int i = 0; i < (1000000000-cycle_number)%cache.at(h); i++) cycle();
for(int r=0;r<R;r++) for(int c=0;c<C;c++) if(m[r][c]=='O') ans.second += (R-r);
std::cout << "Part one: " << ans.first << "\nPart two: " << ans.second << '\n';
std::exit(0);
for(int i = 0; i < (1000000000-cycle_number)%cache.at(h); i++) cycle(m, R, C);
int ans = 0;
for(int r=0;r<R;r++) for(int c=0;c<C;c++) if(m[r][c]=='O') ans += (R-r);
return ans;
}

void tilt_north()
void tilt_north(std::vector<std::string>& m, int R, int C)
{
for(int r=0;r<R;r++) for(int c=0;c<C;c++)
if(m[r][c]=='O' and r>0 and m[r-1][c]=='.')
Expand All @@ -56,7 +83,7 @@ void tilt_north()
}
}

void tilt_west()
void tilt_west(std::vector<std::string>& m, int R, int C)
{
for(int r=0;r<R;r++) for(int c=0;c<C;c++)
if(m[r][c]=='O' and c>0 and m[r][c-1]=='.')
Expand All @@ -68,7 +95,7 @@ void tilt_west()
}
}

void tilt_south()
void tilt_south(std::vector<std::string>& m, int R, int C)
{
for(int r=R-1;r>=0;r--) for(int c=0;c<C;c++)
if(m[r][c]=='O' and r<R-1 and m[r+1][c]=='.')
Expand All @@ -80,7 +107,7 @@ void tilt_south()
}
}

void tilt_east()
void tilt_east(std::vector<std::string>& m, int R, int C)
{
for(int r=0;r<R;r++) for(int c=C-1;c>=0;c--)
if(m[r][c]=='O' and c<C-1 and m[r][c+1]=='.')
Expand All @@ -92,27 +119,33 @@ void tilt_east()
}
}

void cycle()
void cycle(std::vector<std::string>& m, int R, int C)
{
tilt_north();
tilt_west();
tilt_south();
tilt_east();
tilt_north(m, R, C);
tilt_west(m, R, C);
tilt_south(m, R, C);
tilt_east(m, R, C);
}

void solve()
template<template<typename ...> class map_t>
void solve(const std::vector<std::string>& input_m, int R, int C)
{
tilt_north();
std::pair<int, int> ans{0, 0};
std::vector<std::string> m = input_m; // copy
tilt_north(m, R, C);
for(int r=0;r<R;r++) for(int c=0;c<C;c++) if(m[r][c]=='O') ans.first += (R-r);
tilt_west();
tilt_south();
tilt_east();
cycle_number = 1;
tilt_west(m, R, C);
tilt_south(m, R, C);
tilt_east(m, R, C);
long long cycle_number = 1;

map_t<size_t, long long> cache;
for(long long i=0; i<1000000000; i++)
{
cycle();
cycle(m, R, C);
cycle_number++;
try_wrap();
if ((ans.second = try_wrap(cache, m, R, C, cycle_number)) > 0) break;
}
//std::cout << "Part one: " << ans.first << "\nPart two: " << ans.second << '\n';
if (ans.first != 106997 or ans.second != 99641) throw std::exception();
}
135 changes: 135 additions & 0 deletions adventofcode/2023/day/14/splay_tree_map.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#include <memory>
#include <functional>
#include <utility>

namespace dts {

//FIXME design
namespace splay_tree {

using std::pair;

// Initial version result of fixing memory and refactoring wikipedia's splay tree ¯\_(ツ)_/¯
template<typename K, typename V> class map {

//FIXME generic
std::less<K> comp;

struct node {
node *left, *right;
node *parent;
pair<K, V> key_value;
K key;
node(const pair<K, V>& init = pair<K, V>()) :
left(nullptr), right(nullptr), parent(nullptr), key_value(init) {
key = key_value.first;
}
~node() { }
} *root;

void left_rotate(node *x) {
node *y = x->right;
if (y) {
x->right = y->left;
if (y->left) y->left->parent = x;
y->parent = x->parent;
}

if (!x->parent) root = y;
else if (x == x->parent->left) x->parent->left = y;
else x->parent->right = y;
if (y) y->left = x;
x->parent = y;
}

void right_rotate(node *x) {
node *y = x->left;
if (y) {
x->left = y->right;
if (y->right) y->right->parent = x;
y->parent = x->parent;
}
if (!x->parent) root = y;
else if (x == x->parent->left) x->parent->left = y;
else x->parent->right = y;
if (y) y->right = x;
x->parent = y;
}

void splay(node *x) {
if (!x->parent) return;
if (!x->parent->parent) {
if (x->parent->left == x) right_rotate(x->parent);
else left_rotate(x->parent);
} else if (x->parent->left == x && x->parent->parent->left == x->parent) {
right_rotate(x->parent->parent);
right_rotate(x->parent);
} else if (x->parent->right == x && x->parent->parent->right == x->parent) {
left_rotate(x->parent->parent);
left_rotate(x->parent);
} else if (x->parent->left == x && x->parent->parent->right == x->parent) {
right_rotate(x->parent);
left_rotate(x->parent);
} else {
left_rotate(x->parent);
right_rotate(x->parent);
}
}

void free(node **z) {
if (!(*z)) return;
if ((*z)->left or (*z)->right) {
free(&((*z)->left));
free(&((*z)->right));
}
delete *z;
*z = nullptr;
}

public:
map() : root(nullptr) { }
~map() {
free(&root);
}

void emplace(const K& key, const V& value) {
node *z = root;
node *p = nullptr;

while (z) {
p = z;
if (comp(z->key, key)) z = z->right;
else z = z->left;
}

// TODO fix leak
z = new node(std::make_pair(key, value));
z->parent = p;

if (!p) root = z;
else if (comp(p->key, z->key)) p->right = z;
else p->left = z;

splay(z);
}

bool contains(const K& key) {
node *z = root;
while (z) {
if (comp(z->key, key)) z = z->right;
else if (comp(key, z->key)) z = z->left;
else { splay(z); return true; }
}
return false;
}

//FIXME precondition: contains(key) (or insert(key)) was called last,
// with no ohter insertion or lookup after it
V at([[maybe_unused]] const K& key) {
return root->key_value.second;
}
};

} // namespace splay_tree

} // namespace dts
8 changes: 8 additions & 0 deletions adventofcode/2023/day/21/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.15..3.27)
project(aoc-2023-21 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
#set(CMAKE_CXX_FLAGS "-Wall -Wconversion -Wextra -pedantic -fsanitize=address,pointer-overflow,signed-integer-overflow,undefined")
find_package(pybind11 REQUIRED)
add_executable(step_counter step_counter.cpp)
target_link_libraries(step_counter PRIVATE pybind11::embed)
set(CMAKE_BUILD_TYPE Release)
Loading

0 comments on commit c442540

Please sign in to comment.