This commit is contained in:
2025-12-04 15:22:41 +00:00
parent 9ac4656d4d
commit dda4d56f53
4 changed files with 253 additions and 146 deletions

BIN
2025/aoc

Binary file not shown.

View File

@@ -1,8 +1,11 @@
#include <iostream>
#include <chrono>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <numeric>
#include <vector>
#include "aoc.hpp"
#include "day1.hpp"
@@ -12,7 +15,7 @@
void GenerateDay(int day, std::filesystem::path base)
{
std::cout << "Ganerating template for day " << day << std::endl;
std::cout << "Generating template for day " << day << std::endl;
std::ostringstream className;
className << "Day" << std::setw(2) << std::setfill('0') << day; // Day01
std::string classStr = className.str();
@@ -71,6 +74,7 @@ int main(int argc, char** argv)
// Very shit command-line parsing :tm:
int run_day = 0;
int num_runs = 1;
std::filesystem::path base = "./";
for (int i = 1; i < argc; ++i)
@@ -90,6 +94,20 @@ int main(int argc, char** argv)
std::cout << "Selected day to run: " << run_day << "\n";
}
// Runs flag
if (arg == "-r" || arg == "--runs")
{
if (i + 1 >= argc)
{
std::cerr << "Error: -r requires a number of runs\n";
return 1;
}
++i;
num_runs = std::atoi(argv[i]);
if (num_runs < 1) num_runs = 1;
std::cout << "Number of runs for averaging: " << num_runs << "\n";
}
// Path flag
if (arg == "-p" || arg == "--path")
{
@@ -120,23 +138,15 @@ int main(int argc, char** argv)
{
std::cout << "\nUsage:\n"
<< " -d [day] Run a specific day\n"
<< " -r [runs] Number of runs to average timings (default: 1)\n"
<< " -p [path] Set a base path for input\n"
<< " -g [day] Generate a new day! (Stil need to add to compiler)\n"
<< " -g [day] Generate a new day! (Still need to add to compiler)\n"
<< " help Show this help message\n"
<< " (no args) Run all days\n";
return 0;
}
}
// Unknown argument
if (arg != "-d" && arg != "-p" && arg != "--path" &&
arg != "help" && arg != "--help" && arg != "-h" &&
arg != "-g" && arg != "--generate")
{
std::cerr << "Unknown argument: " << arg << "\n"
<< "Use 'help' for usage information.\n";
return 1;
}
}
//
// Run days
if (run_day == 0)
@@ -153,40 +163,74 @@ int main(int argc, char** argv)
File file1{path};
uint64_t partOne = day->PartOne(file);
int partTwo = day->PartTwo(file1);
uint64_t partTwo = day->PartTwo(file1);
std::cout << "Part 1: " << partOne << "\n";
std::cout << "Part 2: " << partTwo << "\n";
}
} else
}
else
{
for (auto& [num, day] : GetRegisteredDays(run_day))
{
std::cout << "Running only Day " << num << ":\n";
std::cout << "Running Day " << num;
if (num_runs > 1) {
std::cout << " (averaging over " << num_runs << " runs)";
}
std::cout << ":\n";
std::string filename = std::to_string(num) + ".txt";
std::filesystem::path path = base / filename;
std::cout << "Reading " << path << "..." << std::endl;
// Storage for timing results
std::vector<double> part1_times;
std::vector<double> part2_times;
std::vector<double> total_times;
part1_times.reserve(num_runs);
part2_times.reserve(num_runs);
total_times.reserve(num_runs);
uint64_t partOne = 0;
uint64_t partTwo = 0;
// Run multiple times
for (int run = 0; run < num_runs; ++run)
{
File file{path};
File file1{path};
auto start = std::chrono::high_resolution_clock::now();
uint64_t partOne = day->PartOne(file);
partOne = day->PartOne(file);
auto endpart1 = std::chrono::high_resolution_clock::now();
auto startpart2 = std::chrono::high_resolution_clock::now();
uint64_t partTwo = day->PartTwo(file1);
partTwo = day->PartTwo(file1);
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Part 1: " << partOne << " - took " << std::chrono::duration<double, std::milli>(endpart1 - start).count() << "ms" << std::endl;
std::cout << "Part 2: " << partTwo << " - took " << std::chrono::duration<double, std::milli>(end - startpart2).count() << "ms" << std::endl;
std::cout << "Day " << run_day << " ran in " << std::chrono::duration<double, std::milli>(end - start).count() << "ms" << std::endl;
}
part1_times.push_back(std::chrono::duration<double, std::milli>(endpart1 - start).count());
part2_times.push_back(std::chrono::duration<double, std::milli>(end - startpart2).count());
total_times.push_back(std::chrono::duration<double, std::milli>(end - start).count());
}
// Calculate averages
double p1_avg = std::accumulate(part1_times.begin(), part1_times.end(), 0.0) / num_runs;
double p2_avg = std::accumulate(part2_times.begin(), part2_times.end(), 0.0) / num_runs;
double total_avg = std::accumulate(total_times.begin(), total_times.end(), 0.0) / num_runs;
// Print results
std::cout << "Part 1: " << partOne << " - took "
<< std::fixed << std::setprecision(3) << p1_avg << "ms" << std::endl;
std::cout << "Part 2: " << partTwo << " - took "
<< std::fixed << std::setprecision(3) << p2_avg << "ms" << std::endl;
std::cout << "Day " << run_day << " ran in "
<< std::fixed << std::setprecision(3) << total_avg << "ms" << std::endl;
}
}
}

View File

@@ -9,79 +9,103 @@
template<typename T>
class Grid
{
public:
public:
Grid() = default;
Grid(size_t h, size_t w) : data(h, std::vector<T>(w)) {}
Grid(const std::vector<std::vector<T>>& d) : data(d) {}
size_t Height() const { return data.size(); }
size_t Width() const { return data.empty() ? 0 : data[0].size(); }
Grid(size_t h, size_t w) : height(h), width(w), data(h * w) {}
const T& At(size_t r, size_t c) const { return data[r][c]; }
Grid(const std::vector<T> d)
: height(d.size())
, width(d.empty() ? 0 : d[0].size())
, data(d) {}
size_t Height() const { return height; }
size_t Width() const { return width; }
const T& At(size_t r, size_t c) const
{
return data[r * width + c];
}
const T* SafeAt(size_t r, size_t c) const
{
if (r >= Height() || c >= Width()) return nullptr;
return &data[r][c];
if (r >= height || c >= width) return nullptr;
return &data[r * width + c];
}
void ReplaceAt(size_t r, size_t c, const T& value)
{
data[r][c] = value;
}
//
// 4-neighbours (up, down, left, right)
std::vector<const T*> Neighbours4(size_t r, size_t c, bool wrap = false) const
{
std::vector<const T*> out;
auto H = Height();
auto W = Width();
auto idx = [&](size_t x, size_t limit) -> size_t {
return wrap ? ((x + limit) % limit) : x;
};
if (r > 0 || wrap) out.push_back(&data[idx(r - 1, H)][c]);
if (r + 1 < H || wrap) out.push_back(&data[idx(r + 1, H)][c]);
if (c > 0 || wrap) out.push_back(&data[r][idx(c - 1, W)]);
if (c + 1 < W || wrap) out.push_back(&data[r][idx(c + 1, W)]);
return out;
data[r * width + c] = value;
}
// 8-neighbours (diagonals)
std::vector<const T*> Neighbours8(size_t r, size_t c, bool wrap = false) const
size_t Neighbours4(size_t r, size_t c, const T* out[4], bool wrap = false) const
{
std::vector<const T*> out;
auto H = Height();
auto W = Width();
size_t count = 0;
auto idx = [&](size_t x, size_t limit) -> size_t {
return wrap ? ((x + limit) % limit) : x;
};
if (wrap)
{
out[count++] = &data[((r - 1 + height) % height) * width + c];
out[count++] = &data[((r + 1) % height) * width + c];
out[count++] = &data[r * width + ((c - 1 + width) % width)];
out[count++] = &data[r * width + ((c + 1) % width)];
} else
{
if (r > 0)
out[count++] = &data[(r - 1) * width + c];
if (r + 1 < height)
out[count++] = &data[(r + 1) * width + c];
if (c > 0)
out[count++] = &data[r * width + (c - 1)];
if (c + 1 < width)
out[count++] = &data[r * width + (c + 1)];
}
for (int dr = -1; dr <= 1; dr++) {
for (int dc = -1; dc <= 1; dc++) {
return count;
}
size_t Neighbours8(size_t r, size_t c, const T* out[8], bool wrap = false) const
{
size_t count = 0;
if (wrap) {
for (int dr = -1; dr <= 1; ++dr)
{
for (int dc = -1; dc <= 1; ++dc)
{
if (dr == 0 && dc == 0) continue;
size_t rr = (r + dr + height) % height;
size_t cc = (c + dc + width) % width;
out[count++] = &data[rr * width + cc];
}
}
} else {
for (int dr = -1; dr <= 1; ++dr)
{
for (int dc = -1; dc <= 1; ++dc)
{
if (dr == 0 && dc == 0) continue;
size_t rr = r + dr;
size_t cc = c + dc;
if (!wrap) {
if (rr >= H || cc >= W) continue;
}
if (rr >= height || cc >= width) continue;
out.push_back(&data[idx(rr, H)][idx(cc, W)]);
out[count++] = &data[rr * width + cc];
}
}
}
return out;
return count;
}
private:
std::vector<std::vector<T>> data;
private:
size_t height = 0;
size_t width = 0;
std::vector<T> data;
};
struct FileFragment
{
int Line, Col;
@@ -238,28 +262,32 @@ class File
template<typename T>
Grid<T> AsGrid() const
{
std::vector<std::vector<T>> d;
d.reserve(_lines.size());
const size_t height = _lines.size();
if (height == 0) return Grid<T>(0, 0);
for (auto& line : _lines)
{
std::vector<T> row;
row.reserve(line.size());
const size_t width = _lines[0].size();
for (char ch : line)
Grid<T> grid(height, width);
for (size_t r = 0; r < height; ++r)
{
const auto& line = _lines[r];
for (size_t c = 0; c < width; ++c)
{
char ch = line[c];
if constexpr (std::is_constructible_v<T, char>)
{
row.emplace_back(T(ch));
grid.ReplaceAt(r, c, T(ch));
}
else if constexpr (std::is_constructible_v<T, std::string_view>)
{
row.emplace_back(T(std::string_view(&ch, 1)));
grid.ReplaceAt(r, c, T(std::string_view(&ch, 1)));
}
else if constexpr (std::is_arithmetic_v<T>)
{
if (std::isdigit(ch))
row.emplace_back(T(ch - '0'));
grid.ReplaceAt(r, c, T(ch - '0'));
else
throw std::runtime_error("Grid<T>: cannot convert character to numeric T");
}
@@ -268,11 +296,9 @@ class File
static_assert(sizeof(T) == 0, "Grid<T>: T must be constructible from char or string_view");
}
}
d.push_back(std::move(row));
}
return Grid<T>(d);
return grid;
}

View File

@@ -1,3 +1,4 @@
#pragma once
#include "aoc.hpp"
@@ -13,85 +14,121 @@ public:
const auto grid = f.AsGrid<char>();
int res = 0;
const size_t height = grid.Height();
const size_t width = grid.Width();
const char* neighbours[8];
for (int i = 0; i < grid.Width(); i++)
for (size_t i = 0; i < height; i++)
{
for (int j = 0; j < grid.Width(); j++)
for (size_t j = 0; j < width; j++)
{
if (grid.At(i, j) != '@') continue;
auto neighbors = grid.Neighbours8(i, j);
size_t count = grid.Neighbours8(i, j, neighbours);
// Unroll, unroll unroll
int rollsOfPaper = 0;
for (auto* n : neighbors)
{
if (*n == '@')
{
rollsOfPaper++;
if (rollsOfPaper > 4)
{
break;
}
}
}
rollsOfPaper += (count > 0 && *neighbours[0] == '@');
rollsOfPaper += (count > 1 && *neighbours[1] == '@');
rollsOfPaper += (count > 2 && *neighbours[2] == '@');
rollsOfPaper += (count > 3 && *neighbours[3] == '@');
rollsOfPaper += (count > 4 && *neighbours[4] == '@');
rollsOfPaper += (count > 5 && *neighbours[5] == '@');
rollsOfPaper += (count > 6 && *neighbours[6] == '@');
rollsOfPaper += (count > 7 && *neighbours[7] == '@');
if (rollsOfPaper < 4)
{
res++;
}
if (rollsOfPaper < 4) res++;
}
}
return res;
}
int MovePossibleRolls(Grid<char>* grid)
uint64_t PartTwo(File& f) override
{
int removed = 0;
for (int i = 0; i < grid->Width(); i++)
{
for (int j = 0; j < grid->Width(); j++)
{
if (grid->At(i, j) != '@') continue;
auto neighbors = grid->Neighbours8(i, j);
const auto input = f.AsGrid<char>();
int rollsOfPaper = 0;
for (auto* n : neighbors)
const size_t width = input.Width();
const size_t height = input.Height();
const char* neighbours[8];
std::vector<uint8_t> counts(width * height);
for (size_t i = 0; i < height; i++)
{
if (*n == '@')
for (size_t j = 0; j < width; j++)
{
rollsOfPaper++;
if (rollsOfPaper > 4)
const size_t idx = i * width + j;
if (input.At(i, j) == '.')
{
break;
counts[idx] = 255;
continue;
}
size_t count = input.Neighbours8(i, j, neighbours);
uint8_t roll_count = 0;
for (size_t k = 0; k < count; k++)
{
roll_count += (*neighbours[k] != '.');
}
counts[idx] = roll_count;
}
}
if (rollsOfPaper < 4)
int64_t removed = 0;
bool updated = true;
Grid<uint8_t> grid(height, width);
for (size_t i = 0; i < height; i++)
{
grid->ReplaceAt(i, j, '.');
for (size_t j = 0; j < width; j++)
{
grid.ReplaceAt(i, j, counts[i * width + j]);
}
}
while (updated)
{
updated = false;
for (size_t i = 0; i < height; i++)
{
for (size_t j = 0; j < width; j++)
{
uint8_t val = grid.At(i, j);
if (val >= 4) continue;
updated = true;
grid.ReplaceAt(i, j, 255);
removed++;
size_t count = grid.Neighbours8(i, j, (const uint8_t**)neighbours);
for (size_t k = 0; k < count; k++)
{
size_t ni, nj;
// AAAAA
const uint8_t* base = &grid.At(0, 0);
size_t offset = (const uint8_t*)neighbours[k] - base;
ni = offset / width;
nj = offset % width;
uint8_t nval = grid.At(ni, nj);
if (nval < 255 && nval > 0)
{
grid.ReplaceAt(ni, nj, nval - 1);
}
}
}
}
}
return removed;
}
uint64_t PartTwo(File& f) override
{
auto grid = f.AsGrid<char>();
int removedRolls = 0;
int removedLastTime = 0;
do
{
removedLastTime = MovePossibleRolls(&grid);
removedRolls += removedLastTime;
} while (removedLastTime != 0);
return removedRolls;
}
};
ADD_AOC_DAY(Day04);