Unable to load image

ADVENT OF CODE DAY 11: MONKEY BUSINESS :marseymonke:

Those FRICKING monkeys stole your stuff! Get it back! :soycry: :!marseymonke:

(Also, remember to leave a comment if you did something interesting :marseylove:)

35
Jump in the discussion.

No email address required.

Yes i needed to include an external library to split a string in c++ :gigachad2: and no dont ask me why i decided to make things into pointers or i have struct that only stores one value.

Also at first i assume that all throws needed to happen at the same time. So if Monkey 1 throws to Monkey 3, Monkey 3 will not throw that item in the same round again. This was not the case, and Monkey 3 would immediately keep throwing it further in the next throw.

#include <vector>
#include <memory>
#include <fstream>
#include <string>
#include <queue>
#include <scn/scn.h>
#include <fmt/core.h>
#include <boost/algorithm/string.hpp>

struct Operation {
    char operand;
    std::string rhs;
    int32_t rhs_int{ -1 };
};

struct Item {
public:
    int64_t worry;
};

class Monkey {
public:
    int32_t id;
    int32_t divisible_test;
    int32_t items_seen{ 0 };

    std::pair<int32_t, int32_t> next_monkeys;
    std::vector<std::unique_ptr<Item>> items;

    Operation op;

    int64_t do_operation(int64_t worry) {
        int64_t rhs{ 0 };
        if (op.rhs == "old") rhs = worry;
        else {
            if (op.rhs_int < 0) {
                op.rhs_int = std::stoi(op.rhs);
            }
            rhs = op.rhs_int;
        }

        if (op.operand == '*') return worry * rhs;
        if (op.operand == '+') return worry + rhs;
        if (op.operand == '-') return worry - rhs;
        return worry / rhs;
    }

    void simulate_throw(const std::vector<std::unique_ptr<Monkey>>& storage, int32_t mod, int32_t division = 3) {
        for (auto it = items.begin(); it != items.end();) {
            int32_t move_to{ next_monkeys.second };
            this->items_seen++;

            int64_t worry{ do_operation((*it)->worry) };

            if (mod == 1) (*it)->worry = worry / division;
            else (*it)->worry = worry % mod;

            if ((*it)->worry % this->divisible_test == 0) {
                move_to = next_monkeys.first;
            }  

            storage[move_to]->items.push_back(std::move(*it));
            it = items.erase(it);
        }
    }
};

auto load_monkeys()
{
    std::vector<std::unique_ptr<Monkey>> monkey_storage;

    std::ifstream file("input.txt");
    std::string line;

    while (std::getline(file, line)) {
        monkey_storage.push_back(std::make_unique<Monkey>());
        scn::scan(line, "Monkey {}:", monkey_storage.back()->id);
        std::getline(file, line, ':'); // skip until : in Operation: {}, ...
        std::getline(file, line);

        std::vector<std::string> results;
        boost::split(results, line, boost::is_any_of(","));
        for (auto item : results) {
            monkey_storage.back()->items.push_back(std::make_unique<Item>(std::stoi(item)));
        }

        std::getline(file, line);
        scn::scan(line, "  Operation: new = old {} {}", monkey_storage.back()->op.operand, monkey_storage.back()->op.rhs);

        std::getline(file, line);
        scn::scan(line, "  Test: divisible by {}", monkey_storage.back()->divisible_test);

        std::getline(file, line);
        scn::scan(line, "    If true: throw to monkey {}", monkey_storage.back()->next_monkeys.first);

        std::getline(file, line);
        scn::scan(line, "    If false: throw to monkey {}", monkey_storage.back()->next_monkeys.second);

        std::getline(file, line);
    }
    return monkey_storage;
}

std::string run(bool partTwo) {
    auto monkey_storage = load_monkeys();

    int32_t mod{ 1 };
    int32_t runs{ 20 };

    if (partTwo) {
        runs = 10'000;
        for (const auto& monkey : monkey_storage) {
            mod *= monkey->divisible_test;
        }
    } else runs = 20;

    for (int32_t i = { 0 }; i < runs; i++) {
        for (const auto& monkey : monkey_storage) {
            monkey->simulate_throw(monkey_storage, mod);
        }
    }
    std::vector<int64_t> monkey_seen;
    for (const auto& monkey : monkey_storage) {
        monkey_seen.push_back(monkey.get()->items_seen);
    }
    std::sort(monkey_seen.begin(), monkey_seen.end(), std::greater<int64_t>());

    return fmt::format("{}", monkey_seen[0] * monkey_seen[1]);
}

int main() {
    fmt::print("Part One: {}\n", run(false));
    fmt::print("Part Two: {}\n", run(true));
}


Jump in the discussion.

No email address required.

it = items.erase(it);

Actually found a way to optimize this. erase on a vector is slow because it had to rearrange the entire vector, but since we know that a monkey will throw all items, the vector will be empty at the end and we can just reset it with clear.

With that optimization 70% of the cpu time is spent on doing modulo operations

![](/images/1670768188503464.webp)

Jump in the discussion.

No email address required.

es i needed to include an external library to split a string in c++

lmao I just used std::regex :marseysick: (yeah I know) another way would be to use size_t pos; std::stoull(line.substr(18, &pos)) and then recall substr and stoull from position pos + 1 until you're left with an empty substring

https://en.cppreference.com/w/cpp/string/basic_string/stoul

If pos is not a null pointer, then a pointer ptr, internal to the conversion functions, will receive the address of the first unconverted character in str.c_str(), and the index of that character will be calculated and stored in *pos, giving the number of characters that were processed by the conversion.

Jump in the discussion.

No email address required.

Wow, you must be a JP fan.

Jump in the discussion.

No email address required.

Link copied to clipboard
Action successful!
Error, please refresh the page and try again.