244 lines
7.7 KiB
C++
244 lines
7.7 KiB
C++
#include <algorithm>
|
|
#include <array>
|
|
#include <charconv>
|
|
#include <cstddef>
|
|
#include <expected>
|
|
#include <format>
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <list>
|
|
#include <map>
|
|
#include <mdspan>
|
|
#include <numeric>
|
|
#include <print>
|
|
#include <ranges>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
struct ColumnData {
|
|
std::vector<int> numbers;
|
|
char op;
|
|
};
|
|
|
|
struct Diagram {
|
|
std::vector<char> data;
|
|
size_t width;
|
|
size_t height;
|
|
|
|
using GridView = std::mdspan<char, std::dextents<size_t, 2>>;
|
|
|
|
auto grid() { return GridView(data.data(), height, width); }
|
|
};
|
|
|
|
auto printDiagram(Diagram &diagram) {
|
|
auto grid = diagram.grid();
|
|
for (auto row = 0UZ; row != grid.extent(0); row++) {
|
|
for (auto col = 0UZ; col != grid.extent(1); col++) {
|
|
std::print("{}", grid[row, col]);
|
|
}
|
|
std::println();
|
|
}
|
|
// std::println("Map: {}", diagram.data);
|
|
std::println("Width: {}", diagram.width);
|
|
std::println("Height: {}", diagram.height);
|
|
}
|
|
|
|
template <typename T> auto parseLine(std::string_view line) -> std::vector<T> {
|
|
auto to_value = [](auto &&subrange) -> T {
|
|
auto str = std::string_view(subrange);
|
|
T value{};
|
|
if constexpr (std::is_same_v<T, char>) {
|
|
return str[0];
|
|
} else {
|
|
std::from_chars(str.data(), str.data() + str.size(), value);
|
|
return value;
|
|
}
|
|
};
|
|
|
|
return line | std::views::split(' ') | std::views::filter([](auto &&range) -> bool { return !range.empty(); }) |
|
|
std::views::transform(to_value) | std::ranges::to<std::vector<T>>();
|
|
}
|
|
|
|
static auto parseInput(const std::string &filename) -> std::expected<std::vector<ColumnData>, std::string> {
|
|
std::ifstream inputF{filename};
|
|
|
|
if (!inputF) {
|
|
return std::unexpected{"Some file open error."};
|
|
}
|
|
std::vector<ColumnData> puzzleInput{};
|
|
|
|
std::vector<std::string> rawLines;
|
|
std::string puzzleLine;
|
|
while (std::getline(inputF, puzzleLine)) {
|
|
if (!puzzleLine.empty()) {
|
|
rawLines.push_back(std::move(puzzleLine));
|
|
}
|
|
}
|
|
if (rawLines.empty()) {
|
|
return std::unexpected{"No lines."};
|
|
}
|
|
|
|
auto operatorLine = rawLines.back();
|
|
auto num_lines = rawLines | std::views::take(rawLines.size() - 1);
|
|
|
|
std::vector<char> ops = parseLine<char>(operatorLine);
|
|
|
|
auto grid =
|
|
num_lines |
|
|
std::views::transform([](const std::string &string) -> std::vector<int> { return parseLine<int>(string); }) |
|
|
std::ranges::to<std::vector<std::vector<int>>>();
|
|
|
|
size_t width = ops.size();
|
|
bool consistent = std::ranges::all_of(grid, [width](const auto &row) -> bool { return row.size() == width; });
|
|
if (!consistent) {
|
|
return std::unexpected{"Error: Row width mismatch."};
|
|
}
|
|
|
|
auto columns = std::views::iota(0UZ, width) | std::views::transform([&](size_t col_idx) -> ColumnData {
|
|
// Collect numbers for this column from the grid
|
|
auto col_nums = grid |
|
|
std::views::transform([col_idx](const auto &row) { return row[col_idx]; }) |
|
|
std::ranges::to<std::vector<int>>();
|
|
|
|
return ColumnData{.numbers = std::move(col_nums), .op = ops[col_idx]};
|
|
}) |
|
|
std::ranges::to<std::vector<ColumnData>>();
|
|
std::cout << "Parsed " << columns.size() << " columns with " << grid.size() << " operands each:\n";
|
|
|
|
for (const auto &col : columns) {
|
|
std::cout << "[ ";
|
|
for (int n : col.numbers) {
|
|
std::cout << n << " ";
|
|
}
|
|
std::cout << "] Op: " << col.op << "\n";
|
|
}
|
|
return std::move(columns);
|
|
}
|
|
|
|
static auto parseInput_p2(const std::string &filename) -> std::expected<Diagram, std::string> {
|
|
std::ifstream inputF{filename};
|
|
|
|
if (!inputF) {
|
|
return std::unexpected{"Some file open error."};
|
|
}
|
|
Diagram puzzleInput{};
|
|
int maxWidth = 0;
|
|
std::string puzzleLine;
|
|
while (std::getline(inputF, puzzleLine)) {
|
|
if (!puzzleLine.empty()) {
|
|
std::ranges::reverse(puzzleLine);
|
|
for (auto c : puzzleLine) {
|
|
puzzleInput.data.emplace_back(c);
|
|
}
|
|
puzzleInput.width = puzzleLine.length();
|
|
puzzleInput.height++;
|
|
}
|
|
}
|
|
|
|
std::println("{}", puzzleInput.data);
|
|
printDiagram(puzzleInput);
|
|
return puzzleInput;
|
|
}
|
|
|
|
auto calculate_column(const ColumnData &col) -> long long {
|
|
if (col.op == '+') {
|
|
return std::ranges::fold_left(col.numbers, 0LL, std::plus());
|
|
}
|
|
if (col.op == '*') {
|
|
return std::ranges::fold_left(col.numbers, 1LL, std::multiplies());
|
|
}
|
|
return 0; // Fallback
|
|
}
|
|
|
|
auto cephalopodMath(const std::vector<ColumnData> &columns) -> long long {
|
|
long long grand_total = std::ranges::fold_left(columns | std::views::transform(calculate_column), 0LL, std::plus{});
|
|
return grand_total;
|
|
}
|
|
auto cephalopodMath_p2(Diagram &diagram) -> long long {
|
|
long long grandTotal{};
|
|
|
|
long long currentNumber = 0;
|
|
std::vector<long long> columnNumbers{};
|
|
auto grid = diagram.grid();
|
|
for (auto col = 0UZ; col != grid.extent(1); col++) {
|
|
for (auto row = 0UZ; row != grid.extent(0); row++) {
|
|
char cur = grid[row, col];
|
|
|
|
if (cur == ' ' && currentNumber == 0) {
|
|
// std::print("s");
|
|
continue;
|
|
} else if (cur == ' ') {
|
|
columnNumbers.emplace_back(currentNumber);
|
|
currentNumber = 0;
|
|
std::print("_");
|
|
continue;
|
|
} else if (cur == '*') {
|
|
if (currentNumber != 0) {
|
|
columnNumbers.emplace_back(currentNumber);
|
|
}
|
|
std::print("*");
|
|
auto total = std::ranges::fold_left(columnNumbers, 1LL, std::multiplies());
|
|
grandTotal += total;
|
|
std::print("\nMultiplying {}: Result: {}\n", columnNumbers, total);
|
|
currentNumber = 0;
|
|
columnNumbers.clear();
|
|
continue;
|
|
} else if (cur == '+') {
|
|
if (currentNumber != 0) {
|
|
columnNumbers.emplace_back(currentNumber);
|
|
}
|
|
std::print("+");
|
|
auto total = std::ranges::fold_left(columnNumbers, 0LL, std::plus());
|
|
grandTotal += total;
|
|
std::print("\nAdding {}: Result: {}\n", columnNumbers, total);
|
|
|
|
currentNumber = 0;
|
|
columnNumbers.clear();
|
|
continue;
|
|
}
|
|
|
|
// cur must be a digit...
|
|
currentNumber = (currentNumber * 10) + (cur - '0');
|
|
std::print("{}", cur);
|
|
}
|
|
}
|
|
return grandTotal;
|
|
}
|
|
|
|
auto main() -> int {
|
|
// auto testCase = parseInput("test_input");
|
|
// if (testCase) {
|
|
|
|
// auto testResult = cephalopodMath(*testCase);
|
|
// std::println("P1 Testcase result: {}", testResult);
|
|
// } else {
|
|
// std::print("{}\n", testCase.error());
|
|
// }
|
|
|
|
auto testCase_p2 = parseInput_p2("test_input");
|
|
if (testCase_p2) {
|
|
auto testResult_p2 = cephalopodMath_p2(*testCase_p2);
|
|
std::println("P2 Testcase result: {}", testResult_p2);
|
|
} else {
|
|
std::print("{}\n", testCase_p2.error());
|
|
}
|
|
|
|
auto realPuzzle = parseInput_p2("puzzle_input");
|
|
if (realPuzzle) {
|
|
// auto realResult = cephalopodMath(*realPuzzle);
|
|
// std::println("P1 Real result: {}", realResult);
|
|
|
|
auto realResultP2 = cephalopodMath_p2(*realPuzzle);
|
|
std::println("P2 Real result: {}", realResultP2);
|
|
} else {
|
|
std::print("{}\n", realPuzzle.error());
|
|
}
|
|
|
|
return 0;
|
|
}
|