2025 day 1, splitbyindex & day timings

This commit is contained in:
2025-12-01 14:05:54 +00:00
parent d2efdef91f
commit ea97c808e5
10 changed files with 4931 additions and 0 deletions

4392
2025/1.txt Normal file

File diff suppressed because it is too large Load Diff

15
2025/Makefile Normal file
View File

@@ -0,0 +1,15 @@
CXX := clang++
CXXFLAGS := -std=c++17 -Wall -Wextra -I.
TARGET := aoc
SRC := aoc.cpp
all: $(TARGET)
$(TARGET): $(SRC)
$(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET)
clean:
rm -f $(TARGET)
.PHONY: all clean

BIN
2025/aoc Executable file

Binary file not shown.

118
2025/aoc.cpp Normal file
View File

@@ -0,0 +1,118 @@
#include <iostream>
#include <chrono>
#include "aoc.hpp"
#include "day1.hpp"
int main(int argc, char** argv)
{
std::cout << "Advent of Code 2025 runner" << std::endl;
std::cout << "©Ben Kyd 2025, All Rights Reserved" << std::endl;
// Very shit command-line parsing :tm:
int run_day = 0;
std::filesystem::path base = "./";
for (int i = 1; i < argc; ++i)
{
std::string arg = argv[i];
// Day flag
if (arg == "-d")
{
if (i + 1 >= argc)
{
std::cerr << "Error: -d requires a day number\n";
return 1;
}
++i;
run_day = std::atoi(argv[i]);
std::cout << "Selected day to run: " << run_day << "\n";
}
// Path flag
if (arg == "-p" || arg == "--path")
{
if (i + 1 >= argc)
{
std::cerr << "Error: " << arg << " requires a path\n";
return 1;
}
++i;
base /= argv[i];
std::cout << "Selected base path: " << base.string() << "\n";
}
// Help flag
if (arg == "help" || arg == "--help" || arg == "-h")
{
std::cout << "\nUsage:\n"
<< " -d [day] Run a specific day\n"
<< " -p [path] Set a base path for input\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")
{
std::cerr << "Unknown argument: " << arg << "\n"
<< "Use 'help' for usage information.\n";
return 1;
}
}
//
// Run days
if (run_day == 0)
{
for (auto& [num, day] : GetRegisteredDays())
{
std::cout << "Running Day " << num << ":\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};
int partOne = day->PartOne(file);
int partTwo = day->PartTwo(file1);
std::cout << "Part 1: " << partOne << "\n";
std::cout << "Part 2: " << partTwo << "\n";
}
} else
{
for (auto& [num, day] : GetRegisteredDays(run_day))
{
std::cout << "Running only Day " << num << ":\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();
int partOne = day->PartOne(file);
auto endpart1 = std::chrono::high_resolution_clock::now();
auto startpart2 = std::chrono::high_resolution_clock::now();
int 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;
}
}
}

221
2025/aoc.hpp Normal file
View File

@@ -0,0 +1,221 @@
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <filesystem>
struct FileFragment
{
int Line, Col;
std::string Data;
};
class File
{
public:
File() = default;
explicit File(const std::filesystem::path& path)
{
Load(path);
}
void Load(const std::filesystem::path& path)
{
if (!std::filesystem::exists(path))
{
throw std::runtime_error("File " + path.string() + " does not exist");
}
std::ifstream file(path.string());
if (!file.is_open())
{
throw std::runtime_error("Failed to open file " + path.string());
}
_lines.clear();
std::string line;
while (std::getline(file, line))
{
_lines.push_back(line);
}
}
/// Split each line by a multi-character delimiter
void SplitBy(const std::string& delim)
{
_tokens.clear();
for (size_t lineIndex = 0; lineIndex < _lines.size(); ++lineIndex)
{
const auto& line = _lines[lineIndex];
std::vector<FileFragment> lineTokens;
size_t start = 0;
int colIndex = 0;
while (true) {
size_t pos = line.find(delim, start);
if (pos == std::string::npos)
{
lineTokens.push_back({
static_cast<int>(lineIndex + 1),
colIndex,
line.substr(start)
});
break;
}
lineTokens.push_back({
static_cast<int>(lineIndex + 1),
colIndex,
line.substr(start, pos - start)
});
start = pos + delim.size();
++colIndex;
}
_tokens.push_back(std::move(lineTokens));
}
}
void SplitByIndex(const int index)
{
_tokens.clear();
_tokens.reserve(_lines.size());
for (size_t lineIndex = 0; lineIndex < _lines.size(); ++lineIndex)
{
const auto& line = _lines[lineIndex];
std::vector<FileFragment> lineTokens;
// Bounds check
if (index < 0 || static_cast<size_t>(index) >= line.size())
{
// Just return whole line as single token if index is out of range
lineTokens.push_back({
static_cast<int>(lineIndex + 1),
0,
line
});
}
else
{
// Left part (may be empty, e.g. index = 0)
lineTokens.push_back({
static_cast<int>(lineIndex + 1),
0,
line.substr(0, index)
});
// Right part (may be empty)
lineTokens.push_back({
static_cast<int>(lineIndex + 1),
1,
line.substr(index)
});
}
_tokens.push_back(std::move(lineTokens));
}
}
/// Access tokens for a given line (1-based)
const std::vector<FileFragment>& TokensForLine(int line) const
{
if (line < 0 || line > static_cast<int>(_tokens.size()))
{
static const std::vector<FileFragment> empty;
return empty;
}
return _tokens[line];
}
// Access a chunk of tokens for a given line
// E.g. Iterate [1,2], [2,3], [3,4]
std::vector<std::vector<FileFragment>> ChunkView(int line, size_t size, size_t stride)
{
const auto& v = _tokens[line];
std::vector<std::vector<FileFragment>> chunks;
if (v.empty() || stride == 0)
return chunks;
for (size_t i = 0; i + size <= v.size(); i += stride)
{
std::cout << "max " << i + size << " tokens " << v.size() << " index " << i << " size " << size << " stride " << stride << std::endl;
chunks.emplace_back(v.begin() + i, v.begin() + i + size);
}
return chunks;
}
/// Iterate through all lines and their tokens
auto begin() const { return _tokens.begin(); }
auto end() const { return _tokens.end(); }
/// Access raw lines (before splitting)
const std::vector<std::string>& Lines() const { return _lines; }
private:
std::vector<std::string> _lines;
std::vector<std::vector<FileFragment>> _tokens; // [line][token]
};
class AOCDay
{
public:
// The "driver" will expect the Name
// to have the same string as the input
// return 1 -> 1.txt
virtual int Day() = 0;
virtual int PartOne(File&) = 0;
virtual int PartTwo(File&) = 0;
};
inline std::unordered_map<int, AOCDay*>& GetRegisteredDays(int day = 0)
{
static std::unordered_map<int, AOCDay*> days;
if (day != 0)
{
static std::unordered_map<int, AOCDay*> single;
single.clear();
auto it = days.find(day);
if (it != days.end())
single[day] = it->second;
else
std::cerr << "Warning: requested day " << day << " not found.\n";
return single;
}
return days;
}
template<typename T>
struct AOCDayRegistrar
{
AOCDayRegistrar()
{
auto* instance = new T();
int day = instance->Day();
auto& days = GetRegisteredDays();
if (days.count(day))
{
std::cerr << "Duplicate registration for day " << day << "\n";
delete instance;
return;
}
days[day] = instance;
}
};
#define ADD_AOC_DAY(DAYCLASS) \
static AOCDayRegistrar<DAYCLASS> DAYCLASS##_registrar_instance;

74
2025/day1.hpp Normal file
View File

@@ -0,0 +1,74 @@
#include "aoc.hpp"
#include <algorithm>
#include <unordered_map>
class Day01 : public AOCDay
{
public:
Day01() {}
~Day01() {}
int Day() override {return 1;}
int dial = 50;
void RotateDial(int diff)
{
dial = (dial + diff) % 100;
if (dial < 0) dial += 100;
}
int PartOne(File& f) override
{
f.SplitByIndex(1);
dial = 50;
int res = 0;
for (const auto& lineTokens : f)
{
char direction = (char)lineTokens[0].Data[0];
int amount = std::stoi(lineTokens[1].Data);
if (direction == 'L')
{
amount = -amount;
}
RotateDial(amount);
if (dial == 0) res++;
}
return res;
}
int PartTwo(File& f) override
{
f.SplitByIndex(1);
dial = 50;
int res = 0;
for (const auto& lineTokens : f)
{
char direction = (char)lineTokens[0].Data[0];
int amount = std::stoi(lineTokens[1].Data);
int dir = 1;
if (direction == 'L')
{
dir = -1;
}
while (amount > 0)
{
RotateDial(dir);
if (dial == 0) res++;
amount--;
}
}
return res;
}
};
ADD_AOC_DAY(Day01);