瀏覽代碼

Uploading day21

Alexey Dorokhov 1 年之前
當前提交
ce69eb82ae

+ 1 - 0
day21/.gitignore

@@ -0,0 +1 @@
+build

+ 13 - 0
day21/CMakeLists.txt

@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.20)
+project(day21)
+
+add_executable(day21 main.cc
+                     try_split_sequence.cc
+                     find_shortest_sequences.cc
+                     find_shortest_sequences_from_to.cc
+										 cached_shortest_sequences.cc
+                     input.cc
+                     keypad_funcs.cc
+                     convert_code.cc
+                     segmented_sequences.cc)
+set_property(TARGET day21 PROPERTY CXX_STANDARD 20)

+ 80 - 0
day21/cached_shortest_sequences.cc

@@ -0,0 +1,80 @@
+#include "cached_shortest_sequences.h"
+
+#include "find_shortest_sequences.h"
+#include "try_split_sequence.h"
+#include "segmented_sequences.h"
+
+#include <iostream>
+
+std::shared_ptr<SegmentedSequences> CachedShortestSequences::Find(
+    std::string_view code,
+    std::function<std::optional<char>(char, char)> apply_cmd) {
+  std::string cache_key(code.data(), code.size());
+  auto cache_iter = cache_.find(cache_key);
+  if (cache_iter != cache_.end()) {
+    return cache_iter->second;
+  }
+
+  auto maybe_split = TrySplitSequence(code);
+  if (!maybe_split) {
+    auto result_strs = FindShortestSequences(code, apply_cmd);
+    std::vector<std::shared_ptr<SegmentedSequences>> segmented_sequences_leaves;
+    for (auto &result_str : result_strs) {
+      segmented_sequences_leaves.emplace_back(
+          std::make_shared<SegmentedSequencesLeaf>(
+              std::make_shared<std::string>(std::move(result_str))));
+    }
+
+    auto segmented_sequences_alts =
+        std::make_shared<SegmentedSequencesAlternatives>(
+            segmented_sequences_leaves);
+    cache_.emplace(cache_key, segmented_sequences_alts);
+    return segmented_sequences_alts;
+  }
+
+  auto segmented_sequences_product =
+      std::make_shared<SegmentedSequencesProduct>(
+          Find(maybe_split->first, apply_cmd),
+          Find(maybe_split->second, apply_cmd));
+  cache_.emplace(cache_key, segmented_sequences_product);
+
+  return segmented_sequences_product;
+}
+
+std::shared_ptr<SegmentedSequences> CachedShortestSequences::Find(
+    std::shared_ptr<SegmentedSequences> codes,
+    std::function<std::optional<char>(char, char)> apply_cmd) {
+  auto segmented_cache_iter = segmented_cache_.find(codes.get());
+  if (segmented_cache_iter != segmented_cache_.end()) {
+    return segmented_cache_iter->second;
+  }
+
+  auto leaf = std::dynamic_pointer_cast<SegmentedSequencesLeaf>(codes);
+  if (leaf != nullptr) {
+    auto leaf_result = Find(leaf->sequence(), apply_cmd);
+    segmented_cache_.emplace(codes.get(), leaf_result);
+    return leaf_result;
+  }
+
+  auto alts = std::dynamic_pointer_cast<SegmentedSequencesAlternatives>(codes);
+  if (alts != nullptr) {
+    std::vector<std::shared_ptr<SegmentedSequences>> alts_results;
+    for (auto alt : alts->alternatives()) {
+      alts_results.emplace_back(Find(alt, apply_cmd));
+    }
+    auto alt_result = std::make_shared<SegmentedSequencesAlternatives>(
+        std::move(alts_results));
+    segmented_cache_.emplace(codes.get(), alt_result);
+    return alt_result;
+  }
+
+  auto product = std::dynamic_pointer_cast<SegmentedSequencesProduct>(codes);
+  if (product != nullptr) {
+    auto product_result = std::make_shared<SegmentedSequencesProduct>(
+        Find(product->a(), apply_cmd), Find(product->b(), apply_cmd));
+    segmented_cache_.emplace(codes.get(), product_result);
+    return product_result;
+  }
+
+  return nullptr;
+}

+ 29 - 0
day21/cached_shortest_sequences.h

@@ -0,0 +1,29 @@
+#ifndef CACHED_SHORTEST_SEQUENCES_H
+#define CACHED_SHORTEST_SEQUENCES_H
+
+#include <map>
+#include <vector>
+#include <string_view>
+#include <optional>
+#include <functional>
+
+#include "segmented_sequences.h"
+
+class CachedShortestSequences {
+private:
+  std::map<std::string, std::shared_ptr<SegmentedSequences>> cache_;
+  std::map<const SegmentedSequences *, std::shared_ptr<SegmentedSequences>>
+      segmented_cache_;
+
+public:
+  std::shared_ptr<SegmentedSequences> Find(
+      std::string_view code,
+      std::function<std::optional<char>(char, char)> apply_cmd);
+
+  std::shared_ptr<SegmentedSequences> Find(
+      std::shared_ptr<SegmentedSequences> codes,
+      std::function<std::optional<char>(char, char)> apply_cmd);
+
+};
+
+#endif

+ 14 - 0
day21/convert_code.cc

@@ -0,0 +1,14 @@
+#include "convert_code.h"
+
+size_t ConvertCode(const std::string &code) {
+  size_t result = 0;
+  for (char c : code) {
+    if (c == 'A') {
+      continue;
+    }
+    result *= 10;
+    result += c - '0';
+  }
+
+  return result;
+}

+ 9 - 0
day21/convert_code.h

@@ -0,0 +1,9 @@
+#ifndef CONVERT_CODE_H
+#define CONVERT_CODE_H
+
+#include <string>
+
+size_t ConvertCode(const std::string &code);
+
+
+#endif

+ 44 - 0
day21/find_shortest_sequences.cc

@@ -0,0 +1,44 @@
+#include "find_shortest_sequences.h"
+#include "find_shortest_sequences_from_to.h"
+
+#include <iostream>
+
+std::vector<std::string> FindShortestSequences(
+    std::string_view code,
+    std::function<std::optional<char>(char, char)> apply_cmd) {
+  std::vector<std::string> results;
+  char from = 'A';
+  for (char to : code) {
+    auto sub_results = FindShortestSequencesFromTo(from, to, apply_cmd);
+    from = to;
+
+    if (results.empty()) {
+      results = std::move(sub_results);
+      continue;
+    }
+
+    std::vector<std::string> new_results;
+    for (size_t i = 0; i < results.size(); ++i) {
+      for (size_t j = 0; j < sub_results.size(); ++j) {
+        new_results.emplace_back(results[i] + sub_results[j]);
+      }
+    }
+    results = std::move(new_results);
+  }
+
+  size_t min_size = results[0].size();
+  for (const auto &r : results) {
+    if (r.size() < min_size) {
+      min_size = r.size();
+    }
+  }
+
+  std::vector<std::string> min_results;
+  for (auto &r : results) {
+    if (r.size() == min_size) {
+      min_results.emplace_back(std::move(r));
+    }
+  }
+
+  return min_results;
+}

+ 15 - 0
day21/find_shortest_sequences.h

@@ -0,0 +1,15 @@
+#ifndef FIND_SHORTEST_SEQUENCES_H
+#define FIND_SHORTEST_SEQUENCES_H
+
+#include <string_view>
+#include <vector>
+#include <functional>
+#include <optional>
+#include <string>
+
+std::vector<std::string> FindShortestSequences(
+    std::string_view code,
+    std::function<std::optional<char>(char, char)> apply_cmd);
+
+
+#endif

+ 68 - 0
day21/find_shortest_sequences_from_to.cc

@@ -0,0 +1,68 @@
+#include "find_shortest_sequences_from_to.h"
+
+#include <algorithm>
+#include <iostream>
+#include <queue>
+
+namespace {
+struct NumericSearchState {
+  std::string sequence;
+  int depth = 0;
+  char state = 'A';
+};
+}
+
+std::vector<std::string> FindShortestSequencesFromTo(
+    char from, char to,
+    std::function<std::optional<char>(char, char)> apply_cmd) {
+
+  std::vector<std::string> sequences;
+  std::queue<NumericSearchState> states;
+
+  states.emplace(NumericSearchState{
+      .sequence = "",
+      .depth = 0,
+      .state = from,
+  });
+
+  int max_depth = 100;
+
+  while (!states.empty()) {
+    NumericSearchState state = std::move(states.front());
+    states.pop();
+
+    if (state.depth > max_depth) {
+      continue;
+    }
+
+    if (state.state == to) {
+      if (max_depth > state.sequence.size()) {
+        max_depth = state.sequence.size();
+      }
+      state.sequence.push_back('A');
+      sequences.emplace_back(std::move(state.sequence));
+      continue;
+    }
+
+    for (auto c : {'<', '>', '^', 'v'}) {
+      std::optional<char> maybe_next_state = apply_cmd(c, state.state);
+      if (maybe_next_state) {
+        NumericSearchState move_state = state;
+        move_state.sequence.push_back(c);
+        move_state.state = *maybe_next_state;
+        ++move_state.depth;
+        states.emplace(std::move(move_state));
+      }
+    }
+  }
+
+  std::vector<std::string> min_sequences;
+  for (auto &s : sequences) {
+    if (s.size() > max_depth + 1) {
+      continue;
+    }
+    min_sequences.emplace_back(std::move(s));
+  }
+
+  return min_sequences;
+}

+ 14 - 0
day21/find_shortest_sequences_from_to.h

@@ -0,0 +1,14 @@
+#ifndef FIND_SHORTEST_SEQUENCES_FROM_TO_H
+#define FIND_SHORTEST_SEQUENCES_FROM_TO_H
+
+#include <vector>
+#include <functional>
+#include <optional>
+#include <map>
+#include <string>
+
+std::vector<std::string> FindShortestSequencesFromTo(
+    char from, char to,
+    std::function<std::optional<char>(char, char)> apply_cmd);
+
+#endif

+ 39 - 0
day21/input.cc

@@ -0,0 +1,39 @@
+#include "input.h"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <optional>
+
+std::string ReadFile(const std::string &filepath) {
+  std::ifstream file(filepath, std::ios::binary | std::ios::ate);
+  std::streamsize size = file.tellg();
+  file.seekg(0, std::ios::beg);
+  std::string buffer(size, '\0');
+  file.read(buffer.data(), size);
+  return buffer;
+}
+
+std::optional<std::string_view> Lines::Next() {
+  if (text_.empty()) {
+    return std::nullopt;
+  }
+
+  for (size_t i = 0; i < text_.size(); ++i) {
+    if (text_[i] == '\n') {
+      std::string_view result(text_.data(), i);
+      if (text_.size() > i + 1) {
+        text_ = std::string_view(text_.data() + i + 1, text_.size() - i - 1);
+      } else {
+        text_ = std::string_view();
+      }
+      return result;
+    }
+  }
+
+  std::string_view last_result = text_;
+  text_ = std::string_view();
+
+  return last_result;
+}

+ 18 - 0
day21/input.h

@@ -0,0 +1,18 @@
+#ifndef INPUT_H
+#define INPUT_H
+
+#include <string> 
+#include <optional>
+
+std::string ReadFile(const std::string &filepath);
+
+class Lines {
+private:
+  std::string_view text_;
+
+public:
+  explicit Lines(std::string_view text) : text_(text) {}
+  std::optional<std::string_view> Next();
+};
+
+#endif

+ 175 - 0
day21/keypad_funcs.cc

@@ -0,0 +1,175 @@
+#include "keypad_funcs.h"
+
+std::optional<char> ApplyNumericCommand(char command, char state) {
+  switch (command) {
+    case '<':
+      switch (state) {
+        case '9':
+          return '8';
+        
+        case '6':
+          return '5';
+
+        case '3':
+          return '2';
+
+        case 'A':
+          return '0';
+
+        case '8':
+          return '7';
+
+        case '5':
+          return '4';
+
+        case '2':
+          return '1';
+
+        default:
+          return std::nullopt;
+      }
+    case '>':
+      switch (state) {
+        case '7':
+          return '8';
+
+        case '4':
+          return '5';
+
+        case '1':
+          return '2';
+
+        case '8':
+          return '9';
+
+        case '5':
+          return '6';
+
+        case '2':
+          return '3';
+
+        case '0':
+          return 'A';
+
+        default:
+          return std::nullopt;
+      }
+    case '^':
+      switch (state) {
+        case '0':
+          return '2';
+
+        case 'A':
+          return '3';
+
+        case '1':
+          return '4';
+
+        case '2':
+          return '5';
+
+        case '3':
+          return '6';
+
+        case '4':
+          return '7';
+
+        case '5':
+          return '8';
+
+        case '6':
+          return '9';
+
+        default:
+          return std::nullopt;
+      }
+    case 'v':
+      switch (state) {
+        case '7':
+          return '4';
+
+        case '8':
+          return '5';
+
+        case '9':
+          return '6';
+
+        case '4':
+          return '1';
+
+        case '5':
+          return '2';
+
+        case '6':
+          return '3';
+
+        case '2':
+          return '0';
+
+        case '3':
+          return 'A';
+      }
+  }
+
+  return std::nullopt;
+}
+
+std::optional<char> ApplyDirectionalCommand(char command, char state) {
+  switch (command) {
+    case '<':
+      switch (state) {
+        case 'A':
+          return '^';
+
+        case '>':
+          return 'v';
+
+        case 'v':
+          return '<';
+
+        default:
+          return std::nullopt;
+      }
+
+    case '>':
+      switch (state) {
+        case '<':
+          return 'v';
+
+        case '^':
+          return 'A';
+
+        case 'v':
+          return '>';
+
+        default:
+          return std::nullopt;
+      }
+
+    case '^':
+      switch (state) {
+        case 'v':
+          return '^';
+
+        case '>':
+          return 'A';
+
+        default:
+          return std::nullopt;
+      }
+
+    case 'v':
+      switch (state) {
+        case '^':
+          return 'v';
+
+        case 'A':
+          return '>';
+
+        default:
+          return std::nullopt;
+      }
+  }
+
+  return std::nullopt;
+}

+ 10 - 0
day21/keypad_funcs.h

@@ -0,0 +1,10 @@
+#ifndef KEYPAD_H
+#define KEYPAD_H
+
+#include <optional>
+
+std::optional<char> ApplyNumericCommand(char command, char state);
+std::optional<char> ApplyDirectionalCommand(char command, char state);
+
+#endif
+

+ 45 - 0
day21/main.cc

@@ -0,0 +1,45 @@
+#include <string>
+#include <iostream>
+
+#include "cached_shortest_sequences.h"
+#include "segmented_sequences.h"
+#include "keypad_funcs.h"
+#include "convert_code.h"
+#include "input.h"
+
+int main(int argc, char **argv) {
+  std::string input_text = ReadFile(argv[1]);
+  Lines lines(input_text);
+
+  CachedShortestSequences cached_shortest_sequences;
+
+  std::vector<std::string> codes;
+  for (;;) {
+    auto line = lines.Next();
+    if (!line || line->empty()) {
+      break;
+    }
+
+    codes.emplace_back(line->data(), line->size());
+  }
+
+  std::vector<char> buffer;
+
+  size_t total = 0;
+  for (const auto &code : codes) {
+    size_t converted_code = ConvertCode(code);
+    auto shortest_sequences0 =
+        cached_shortest_sequences.Find(code, ApplyNumericCommand);
+
+    for (size_t layer = 0; layer < 25; ++layer) {
+      auto shortest_sequences1 = cached_shortest_sequences.Find(
+          shortest_sequences0, ApplyDirectionalCommand);
+      shortest_sequences0 = shortest_sequences1;
+    }
+
+    total += shortest_sequences0->MinSize() * converted_code;
+  }
+
+  std::cout << total << std::endl;
+  return 0;
+}

+ 63 - 0
day21/segmented_sequences.cc

@@ -0,0 +1,63 @@
+#include "segmented_sequences.h"
+
+#include <algorithm>
+
+SegmentedSequencesLeaf::SegmentedSequencesLeaf(
+    std::shared_ptr<std::string> sequence)
+    : sequence_(sequence) {}
+
+void SegmentedSequencesLeaf::Visit(
+    std::function<void(SegmentedString sequence)> visitor) const {
+  visitor(SegmentedString{sequence_});
+}
+
+size_t SegmentedSequencesLeaf::MinSize() const {
+  return sequence_->size();
+}
+
+SegmentedSequencesAlternatives::SegmentedSequencesAlternatives(
+    std::vector<std::shared_ptr<SegmentedSequences>> alternatives) {
+  min_size_ = alternatives[0]->MinSize();
+  for (const auto &alt : alternatives) {
+    min_size_ = std::min(min_size_, alt->MinSize());
+  }
+
+  for (const auto &alt : alternatives) {
+    if (alt->MinSize() == min_size_) {
+      alternatives_.push_back(alt);
+    }
+  }
+}
+
+void SegmentedSequencesAlternatives::Visit(
+    std::function<void(SegmentedString sequence)> visitor) const {
+  for (const auto &alt : alternatives_) {
+    alt->Visit(visitor);
+  }
+}
+
+size_t SegmentedSequencesAlternatives::MinSize() const {
+  return min_size_;
+}
+
+SegmentedSequencesProduct::SegmentedSequencesProduct(
+    std::shared_ptr<SegmentedSequences> a,
+    std::shared_ptr<SegmentedSequences> b)
+    : a_(a), b_(b) {
+  min_size_ = a_->MinSize() + b_->MinSize();
+}
+
+void SegmentedSequencesProduct::Visit(
+    std::function<void(SegmentedString sequence)> visitor) const {
+  a_->Visit([this, visitor](SegmentedString seq_a) {
+    b_->Visit([this, visitor, &seq_a](SegmentedString seq_b) {
+      SegmentedString joined_seq = seq_a;
+      joined_seq.insert(joined_seq.end(), seq_b.begin(), seq_b.end());
+      visitor(joined_seq);
+    });
+  });
+}
+
+size_t SegmentedSequencesProduct::MinSize() const {
+  return min_size_;
+}

+ 74 - 0
day21/segmented_sequences.h

@@ -0,0 +1,74 @@
+#ifndef SEGMENTED_SEQUENCES_H
+#define SEGMENTED_SEQUENCES_H
+
+#include <string>
+#include <memory>
+#include <vector>
+#include <variant>
+#include <optional>
+#include <functional>
+
+using SegmentedString = std::vector<std::shared_ptr<std::string>>;
+
+class SegmentedSequences {
+public:
+  SegmentedSequences() = default;
+  virtual ~SegmentedSequences() = default;
+
+  virtual void Visit(std::function<void(SegmentedString sequence)> visitor) const = 0;
+  virtual size_t MinSize() const = 0;
+};
+
+class SegmentedSequencesLeaf : public SegmentedSequences {
+private:
+  std::shared_ptr<std::string> sequence_;
+public:
+  SegmentedSequencesLeaf() = default;
+  ~SegmentedSequencesLeaf() override = default;
+  explicit SegmentedSequencesLeaf(std::shared_ptr<std::string> sequence);
+
+  void Visit(std::function<void(SegmentedString sequence)> visitor) const override;
+  size_t MinSize() const override;
+
+  const std::string& sequence() const { return *sequence_; }
+};
+
+class SegmentedSequencesAlternatives : public SegmentedSequences {
+private:
+  std::vector<std::shared_ptr<SegmentedSequences>> alternatives_;
+  size_t min_size_ = 0;
+
+public:
+  SegmentedSequencesAlternatives() = default;
+  ~SegmentedSequencesAlternatives() override = default;
+  explicit SegmentedSequencesAlternatives(
+      std::vector<std::shared_ptr<SegmentedSequences>> alternatives);
+
+  void Visit(std::function<void(SegmentedString sequence)> visitor) const override;
+  size_t MinSize() const override;
+
+  const std::vector<std::shared_ptr<SegmentedSequences>>& alternatives() const {
+    return alternatives_;
+  }
+};
+
+class SegmentedSequencesProduct : public SegmentedSequences {
+private:
+  std::shared_ptr<SegmentedSequences> a_;
+  std::shared_ptr<SegmentedSequences> b_;
+  size_t min_size_ = 0;
+
+public:
+  SegmentedSequencesProduct() = default;
+  ~SegmentedSequencesProduct() override = default;
+  SegmentedSequencesProduct(std::shared_ptr<SegmentedSequences> a,
+                            std::shared_ptr<SegmentedSequences> b);
+
+  void Visit(std::function<void(SegmentedString sequence)> visitor) const override;
+  size_t MinSize() const override;
+
+  std::shared_ptr<SegmentedSequences> a() const { return a_; }
+  std::shared_ptr<SegmentedSequences> b() const { return b_; }
+};
+
+#endif

+ 22 - 0
day21/try_split_sequence.cc

@@ -0,0 +1,22 @@
+#include "try_split_sequence.h"
+
+#include <vector>
+
+std::optional<std::pair<std::string_view, std::string_view>>
+TrySplitSequence(const std::string_view &sequence) {
+  std::vector<size_t> a_keys;
+  for (size_t i = 0; i < sequence.size(); ++i) {
+    if (sequence[i] == 'A') {
+      a_keys.push_back(i);
+    }
+  }
+
+  if (a_keys.size() < 2) {
+    return std::nullopt;
+  }
+
+  size_t midpoint = a_keys.size() / 2 - 1;
+  return std::make_pair(std::string_view(&(sequence[0]), a_keys[midpoint]+1),
+                        std::string_view(&(sequence[a_keys[midpoint]+1]),
+                                         sequence.size() - a_keys[midpoint]-1));
+}

+ 11 - 0
day21/try_split_sequence.h

@@ -0,0 +1,11 @@
+#ifndef TRY_SPLIT_SEQUENCE_H
+#define TRY_SPLIT_SEQUENCE_H
+
+#include <optional>
+#include <string_view>
+#include <utility>
+
+std::optional<std::pair<std::string_view, std::string_view>>
+TrySplitSequence(const std::string_view &sequence);
+
+#endif