summaryrefslogtreecommitdiff
path: root/2023/19/src/part1.cpp
blob: b63f05d3d5ff2b24c6d978b15cb42d93f61fa9cc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <iostream>
#include <fstream>
#include <array>
#include <vector>
#include <unordered_map>
#include <functional>

#include "util.h"

using Name = std::string;
using Part = std::array<int,4>;
using Cond = std::function<bool(const Part&)>;
using Rule = std::pair<Cond,Name>;
using Workflow = std::vector<Rule>;
using Workflows = std::unordered_map<Name,Workflow>;

const std::unordered_map<char,int> fields{ {'x', 0}, {'m', 1}, {'a', 2}, {'s', 3} };

std::pair<std::vector<Part>,Workflows> parse(const char* file)
{
    using namespace util::separators;

    Workflows wfs;
    std::vector<Part> parts;

    if (std::ifstream input{ file }; input.is_open())
    {
        std::string line;
        std::getline(input,line);
        while (not line.empty())
        {
            auto bw = line.find('{');
            Name name = line.substr(0, bw);

            std::vector<Rule> rules;
            line = line.substr(bw + 1, line.size() - bw - 2);
            for (auto srule : util::split<COMMA>(std::string_view{ line }))
            {
                auto tokens = util::split<COLON>(srule);
                if (tokens.size() > 1)
                {
                    int f = fields.at(tokens[0][0]);
                    int v = std::stoi(std::string(tokens[0].substr(2)));
                    switch (tokens[0][1])
                    {
                        case '<':
                            rules.push_back({
                                [f,v](const Part& p) { return p[f] < v; },
                                Name{ tokens[1] }
                            });
                            break;
                        default:
                            rules.push_back({
                                [f,v](const Part& p) { return p[f] > v; },
                                Name{ tokens[1] }
                            });
                    };
                }
                else
                {
                    rules.push_back({
                        [](const Part& p) { return true; },
                        Name{ tokens[0] }
                    });
                }
            }
            wfs.insert({ name, std::move(rules) });
            std::getline(input,line);
        }

        while (not std::getline(input,line).eof())
        {
            Part part; int i{};
            util::accumulate<COMMA>(std::string_view{ line }.substr(1, line.size() - 2),
                [&part,&i](std::string_view& v)
                {
                    part[i++] = std::stoi(std::string{ v.substr(2) });
                }
            );
            parts.push_back(std::move(part));
        }
    }
    return { std::move(parts), std::move(wfs) };
}

Name apply_workflow(const Workflow& wf, const Part& p)
{
    for (const auto& rule : wf)
    {
        auto [cond, name] = rule;
        if (cond(p)) return name;
    }
    return "R";
}

int value(const Part& p)
{
    return p[0] + p[1] + p[2] + p[3];
}

int process(const Part& p, const Workflows& wfs)
{
    Name wname = "in";
    while (wname != "A")
    {
        if (wname == "R") return 0;
        wname = apply_workflow(wfs.at(wname), p);
    }
    return value(p);
}

int main(int argc, char* argv[])
{
    int answer{};

    auto [parts, workflows] = parse(argv[1]);

    for (const auto& part : parts)
    {
        answer += process(part, workflows);
    }

    std::cout << answer << std::endl;
    return 0;
}