d4 fast
This commit is contained in:
98
2025/aoc.cpp
98
2025/aoc.cpp
@@ -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;
|
||||
File file{path};
|
||||
File file1{path};
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
// Storage for timing results
|
||||
std::vector<double> part1_times;
|
||||
std::vector<double> part2_times;
|
||||
std::vector<double> total_times;
|
||||
|
||||
uint64_t partOne = day->PartOne(file);
|
||||
part1_times.reserve(num_runs);
|
||||
part2_times.reserve(num_runs);
|
||||
total_times.reserve(num_runs);
|
||||
|
||||
auto endpart1 = std::chrono::high_resolution_clock::now();
|
||||
auto startpart2 = std::chrono::high_resolution_clock::now();
|
||||
uint64_t partOne = 0;
|
||||
uint64_t partTwo = 0;
|
||||
|
||||
uint64_t partTwo = day->PartTwo(file1);
|
||||
// Run multiple times
|
||||
for (int run = 0; run < num_runs; ++run)
|
||||
{
|
||||
File file{path};
|
||||
File file1{path};
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto start = 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;
|
||||
partOne = day->PartOne(file);
|
||||
|
||||
auto endpart1 = std::chrono::high_resolution_clock::now();
|
||||
auto startpart2 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
partTwo = day->PartTwo(file1);
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
158
2025/aoc.hpp
158
2025/aoc.hpp
@@ -9,79 +9,103 @@
|
||||
template<typename T>
|
||||
class Grid
|
||||
{
|
||||
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) {}
|
||||
public:
|
||||
Grid() = default;
|
||||
|
||||
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) {}
|
||||
|
||||
const T* SafeAt(size_t r, size_t c) const
|
||||
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 * width + c];
|
||||
}
|
||||
|
||||
void ReplaceAt(size_t r, size_t c, const T& value)
|
||||
{
|
||||
data[r * width + c] = value;
|
||||
}
|
||||
|
||||
size_t Neighbours4(size_t r, size_t c, const T* out[4], bool wrap = false) const
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
if (wrap)
|
||||
{
|
||||
if (r >= Height() || c >= Width()) return nullptr;
|
||||
return &data[r][c];
|
||||
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)];
|
||||
}
|
||||
|
||||
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;
|
||||
return count;
|
||||
}
|
||||
|
||||
auto H = Height();
|
||||
auto W = Width();
|
||||
size_t Neighbours8(size_t r, size_t c, const T* out[8], bool wrap = false) const
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
auto idx = [&](size_t x, size_t limit) -> size_t {
|
||||
return wrap ? ((x + limit) % limit) : x;
|
||||
};
|
||||
if (wrap) {
|
||||
for (int dr = -1; dr <= 1; ++dr)
|
||||
{
|
||||
for (int dc = -1; dc <= 1; ++dc)
|
||||
{
|
||||
if (dr == 0 && dc == 0) continue;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 8-neighbours (diagonals)
|
||||
std::vector<const T*> Neighbours8(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;
|
||||
};
|
||||
|
||||
for (int dr = -1; dr <= 1; dr++) {
|
||||
for (int dc = -1; dc <= 1; dc++) {
|
||||
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;
|
||||
}
|
||||
private:
|
||||
std::vector<std::vector<T>> data;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
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)
|
||||
const size_t width = _lines[0].size();
|
||||
|
||||
Grid<T> grid(height, width);
|
||||
|
||||
for (size_t r = 0; r < height; ++r)
|
||||
{
|
||||
std::vector<T> row;
|
||||
row.reserve(line.size());
|
||||
|
||||
for (char ch : line)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
143
2025/day4.hpp
143
2025/day4.hpp
@@ -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++)
|
||||
{
|
||||
for (size_t j = 0; j < width; j++)
|
||||
{
|
||||
const size_t idx = i * width + j;
|
||||
|
||||
if (input.At(i, j) == '.')
|
||||
{
|
||||
if (*n == '@')
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t removed = 0;
|
||||
bool updated = true;
|
||||
|
||||
Grid<uint8_t> grid(height, width);
|
||||
for (size_t i = 0; i < height; i++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
rollsOfPaper++;
|
||||
if (rollsOfPaper > 4)
|
||||
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)
|
||||
{
|
||||
break;
|
||||
grid.ReplaceAt(ni, nj, nval - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rollsOfPaper < 4)
|
||||
{
|
||||
grid->ReplaceAt(i, j, '.');
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user