#pragma once #include #include #include #include #include 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 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(lineIndex + 1), colIndex, line.substr(start) }); break; } lineTokens.push_back({ static_cast(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 lineTokens; // Bounds check if (index < 0 || static_cast(index) >= line.size()) { // Just return whole line as single token if index is out of range lineTokens.push_back({ static_cast(lineIndex + 1), 0, line }); } else { // Left part (may be empty, e.g. index = 0) lineTokens.push_back({ static_cast(lineIndex + 1), 0, line.substr(0, index) }); // Right part (may be empty) lineTokens.push_back({ static_cast(lineIndex + 1), 1, line.substr(index) }); } _tokens.push_back(std::move(lineTokens)); } } /// Access tokens for a given line (1-based) const std::vector& TokensForLine(int line) const { if (line < 0 || line > static_cast(_tokens.size())) { static const std::vector 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> ChunkView(int line, size_t size, size_t stride) { const auto& v = _tokens[line]; std::vector> 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& Lines() const { return _lines; } private: std::vector _lines; std::vector> _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& GetRegisteredDays(int day = 0) { static std::unordered_map days; if (day != 0) { static std::unordered_map 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 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##_registrar_instance;