diff --git a/res/2016/day11/test_input.txt b/res/2016/day11/test_input.txt index e69de29..0a01c5e 100644 --- a/res/2016/day11/test_input.txt +++ b/res/2016/day11/test_input.txt @@ -0,0 +1,4 @@ +The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip. +The second floor contains a hydrogen generator. +The third floor contains a lithium generator. +The fourth floor contains nothing relevant. diff --git a/src/2016/Day11.cpp b/src/2016/Day11.cpp index 4fc904a..2e59243 100644 --- a/src/2016/Day11.cpp +++ b/src/2016/Day11.cpp @@ -2,30 +2,219 @@ namespace y2016::day11 { - REGISTER_DAY(2016, Day11, std::vector, int); + struct Item + { + int floorPos; + bool generator; + int id; - REGISTER_TEST_EXAMPLE(2016, Day11, ExampleInput, 1, 0); - REGISTER_TEST(2016, Day11, Input, 1, 0); - REGISTER_TEST_EXAMPLE(2016, Day11, ExampleInput, 2, 0); - REGISTER_TEST(2016, Day11, Input, 2, 0); + bool operator==(const Item& item) const + { + return floorPos == item.floorPos && id == item.id && generator == item.generator; + } + }; + + static int FLOORS = 4; + + REGISTER_DAY(2016, Day11, std::vector, int); + + REGISTER_TEST_EXAMPLE(2016, Day11, ExampleInput, 1, 11); + REGISTER_TEST(2016, Day11, Input, 1, 37); + REGISTER_TEST(2016, Day11, Input, 2, 61); + + Item ParseItem(const std::string_view& str, int floorPos, std::map& nameToId) + { + size_t posDash = str.find('-'); + std::string name; + bool generator; + if (posDash == std::string::npos) + { + name = str.substr(0, str.find(' ')); + generator = true; + } + else + { + name = str.substr(0, posDash); + generator = false; + } + auto it = nameToId.find(name); + if (it != nameToId.end()) + return Item{floorPos, generator, it->second}; + int size = nameToId.size(); + nameToId[name] = size; + + return Item{floorPos, generator, size}; + } READ_INPUT(input) { - std::vector vec; + std::vector vec; std::string str; - while (getline(input, str)) + std::map nameToId; + for (int i = 0; i < FLOORS; i++) { + getline(input, str); + std::vector split = Helper::Split(str, " a "); + for (size_t j = 1; j < split.size(); j++) + { + vec.emplace_back(ParseItem(split[j], i, nameToId)); + } } return vec; } + struct State + { + std::vector items; + int elevatorPos; + + int amountOfPairs; + + std::vector> GetPairs() const + { + // Convert the data to a format where the pairs of generator/microchip don't matter. All that matters are their + // positions + std::vector> pairs(amountOfPairs); + for (const auto& item : items) + { + if (item.generator) + pairs[item.id].first = item.floorPos; + else + pairs[item.id].second = item.floorPos; + } + std::sort(pairs.begin(), pairs.end()); + return pairs; + } + + bool operator<(const State& state) const + { + if (elevatorPos != state.elevatorPos) + return elevatorPos < state.elevatorPos; + return GetPairs() < state.GetPairs(); + } + }; + + bool HasItem(const State& state, int floor, bool generator, int id) + { + return std::find(state.items.begin(), state.items.end(), Item{floor, generator, id}) != state.items.end(); + } + + bool IsValidState(const State& state) + { + for (int i = 0; i < FLOORS; i++) + { + bool hasChipNotConnected = false; + int generators = 0; + for (const auto& item : state.items) + { + if (item.floorPos != i) + continue; + if (item.generator) + { + generators++; + continue; + } + + if (HasItem(state, i, true, item.id)) + continue; + hasChipNotConnected = true; + } + if (hasChipNotConnected && generators > 0) + { + return false; + } + } + return true; + } + + std::vector> Branch(const std::vector& input, const State& state) + { + std::vector> branches; + for (int i = -1; i <= 1; i += 2) + { + if (state.elevatorPos + i < 0 || state.elevatorPos + i > 3) + continue; + + State newState = state; + newState.elevatorPos += i; + for (auto& item : newState.items) + { + if (item.floorPos != state.elevatorPos) + continue; + + item.floorPos = newState.elevatorPos; + if (IsValidState(newState)) + branches.emplace_back(1, newState); + item.floorPos = state.elevatorPos; + } + + for (size_t i = 0; i < newState.items.size(); i++) + { + if (newState.items[i].floorPos != state.elevatorPos) + continue; + newState.items[i].floorPos = newState.elevatorPos; + + for (size_t j = i + 1; j < newState.items.size(); j++) + { + if (newState.items[j].floorPos != state.elevatorPos) + continue; + newState.items[j].floorPos = newState.elevatorPos; + if (IsValidState(newState)) + branches.emplace_back(1, newState); + newState.items[j].floorPos = state.elevatorPos; + } + newState.items[i].floorPos = state.elevatorPos; + } + } + return branches; + } + + bool Goal(const std::vector& input, const State& state) + { + for (size_t i = 0; i < state.items.size(); i++) + { + if (state.items[i].floorPos != 3) + return false; + } + return true; + } + + int Heuristic(const std::vector& input, const State& state) + { + int heuristic = 0; + int moves = 0; + for (size_t i = 0; i < state.items.size(); i++) + { + moves += (3 - state.items[i].floorPos); + } + heuristic += moves / 2; + return heuristic; + } + OUTPUT1(input) { - return 0; + int max = 0; + for (const auto& item : input) + { + max = std::max(max, item.id); + } + State state{input, 0, max + 1}; + return Helper::AStar(input, state, Heuristic, Branch, Goal); } OUTPUT2(input) { - return 0; + std::vector items = input; + int max = 0; + for (const auto& item : items) + { + max = std::max(max, item.id); + } + items.emplace_back(Item{0, true, max + 1}); + items.emplace_back(Item{0, false, max + 1}); + items.emplace_back(Item{0, true, max + 2}); + items.emplace_back(Item{0, false, max + 2}); + State state{items, 0, max + 3}; + return Helper::AStar(input, state, Heuristic, Branch, Goal); } } diff --git a/src/common/lib/Helper.h b/src/common/lib/Helper.h index c4db010..f131214 100644 --- a/src/common/lib/Helper.h +++ b/src/common/lib/Helper.h @@ -51,6 +51,21 @@ struct Helper t.erase(std::remove_if(t.begin(), t.end(), func), t.end()); } + static std::vector Split(const std::string& str, const std::string& delim) + { + std::vector split; + size_t pos = str.find(delim); + size_t lastPos = 0; + while (pos != std::string::npos) + { + split.emplace_back(std::string_view{str.c_str() + lastPos, pos - lastPos}); + lastPos = pos + delim.size(); + pos = str.find(delim, pos + delim.size()); + } + split.emplace_back(std::string_view{str.c_str() + lastPos, str.size() - lastPos}); + return split; + } + static bool StartsWith(const std::string_view& str, const std::string& prefix, size_t offset = 0) { return str.substr(offset, prefix.size()) == prefix;