2024 Day 1 (and framework). Doing 2025 in zig
This commit is contained in:
1000
2024/1.txt
Normal file
1000
2024/1.txt
Normal file
File diff suppressed because it is too large
Load Diff
4
2024/2.txt
Normal file
4
2024/2.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
this
|
||||
is
|
||||
the
|
||||
inpu,that,has,commast
|
||||
15
2024/Makefile
Normal file
15
2024/Makefile
Normal 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
|
||||
@@ -1 +0,0 @@
|
||||
... oops
|
||||
107
2024/aoc.cpp
Normal file
107
2024/aoc.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "aoc.hpp"
|
||||
#include "day1.hpp"
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout << "Advent of Code 2024 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};
|
||||
|
||||
int partOne = day->PartOne(file);
|
||||
int partTwo = day->PartTwo(file);
|
||||
|
||||
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};
|
||||
|
||||
int partOne = day->PartOne(file);
|
||||
int partTwo = day->PartTwo(file);
|
||||
|
||||
std::cout << "Part 1: " << partOne << "\n";
|
||||
std::cout << "Part 2: " << partTwo << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
161
2024/aoc.hpp
Normal file
161
2024/aoc.hpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Access tokens for a given line (1-based)
|
||||
const std::vector<FileFragment>& TokensForLine(int line) const
|
||||
{
|
||||
if (line < 1 || line > static_cast<int>(_tokens.size()))
|
||||
{
|
||||
static const std::vector<FileFragment> empty;
|
||||
return empty;
|
||||
}
|
||||
return _tokens[line - 1];
|
||||
}
|
||||
|
||||
/// 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;
|
||||
65
2024/day1.hpp
Normal file
65
2024/day1.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "aoc.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
class Day01 : public AOCDay
|
||||
{
|
||||
public:
|
||||
Day01() {}
|
||||
~Day01() {}
|
||||
int Day() override {return 1;}
|
||||
|
||||
std::vector<int> col1;
|
||||
std::vector<int> col2;
|
||||
|
||||
int PartOne(File& f) override
|
||||
{
|
||||
f.SplitBy(" ");
|
||||
|
||||
for (const auto& lineTokens : f)
|
||||
{
|
||||
for (const auto& token : lineTokens)
|
||||
{
|
||||
if (token.Col == 0)
|
||||
col1.push_back(std::atoi(token.Data.c_str()));
|
||||
|
||||
if (token.Col == 1)
|
||||
col2.push_back(std::atoi(token.Data.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(col1.begin(), col1.end());
|
||||
std::sort(col2.begin(), col2.end());
|
||||
|
||||
int res = 0;
|
||||
for (int i = 0; i < col1.size(); i++)
|
||||
{
|
||||
res += std::abs(col1[i] - col2[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int PartTwo(File&) override
|
||||
{
|
||||
// Num of times each number appears in col2
|
||||
std::unordered_map<int, int> hits;
|
||||
|
||||
for (int i = 0; i < col2.size(); i++)
|
||||
{
|
||||
hits[col2[i]] += 1;
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
for (int i = 0; i < col1.size(); i++)
|
||||
{
|
||||
res += (col1[i] * hits[col1[i]]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
ADD_AOC_DAY(Day01);
|
||||
|
||||
Reference in New Issue
Block a user