Guest User

Untitled

a guest
Nov 18th, 2017
4,074
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.45 KB | None | 0 0
  1. #include <assert.h>
  2.  
  3. #include <algorithm>
  4. #include <array>
  5. #include <iostream>
  6. #include <optional>
  7. #include <memory>
  8. #include <vector>
  9.  
  10. // Indexed by level difference.
  11. std::array<unsigned, 5> BaseRates = { 100, 50, 25, 10, 1 };
  12. std::array<unsigned, 5> BonusRates = { 0, 16, 8, 3, 0 };
  13.  
  14. // How many 0* materials are required to go from level N to level N+1.
  15. // For example, going from 0* to 1* requires exactly one 0* material.
  16. std::array<double, 5> MatsRequired = { 1.0, -1.0, -1.0, -1.0, -1.0 };
  17.  
  18. // How many 0* items is an N* item equivalent to.  For example, a 0*
  19. // item is equivalent to 1 0* items.  A 1* item is equivalent to 2
  20. // 0* items (one for the base 0*, one to upgrade it).   This is easy
  21. // to compute.  The "value" of an N* item is equal to the value of an
  22. // (N-1)* item plus the number of materials required to go from N->N+1
  23. std::array<double, 6> MatValue = { 1.0, -1.0, -1.0, -1.0, -1.0 };
  24.  
  25.  
  26. struct MaterialNode;
  27.  
  28. struct OutcomeNode {
  29.   explicit OutcomeNode(const MaterialNode &Parent, bool Success);
  30.  
  31.   double matsRequired() const;
  32.  
  33.   // How did we get here?
  34.   const MaterialNode &Parent;
  35.  
  36.   const MaterialNode *cheapestMaterial() const;
  37.  
  38.   // Whether this node represents a successful attmept or a failed attempt.
  39.   bool Success = false;
  40.  
  41.   // From this outcome, what materials did we try?  Empty if and only if
  42.   // Success == true.
  43.   std::vector<std::unique_ptr<MaterialNode>> MaterialsTried;
  44. };
  45.  
  46. struct MaterialNode {
  47.   explicit MaterialNode(unsigned BaseStars, unsigned InitialFailureBonus = 0);
  48.   explicit MaterialNode(OutcomeNode &Parent, unsigned Stars, unsigned FailureBonus);
  49.  
  50.   const MaterialNode &root() const;
  51.  
  52.   unsigned maxMaterial() const;
  53.  
  54.   double matsRequired() const;
  55.  
  56.   unsigned successRate() const;
  57.   unsigned accumulatedFailure() const;
  58.  
  59.   unsigned materialDifference() const;
  60.  
  61.   // How did we get here?
  62.   OutcomeNode *Parent = nullptr;
  63.  
  64.   // The star value (0 - 5) of the material used in this attempt.
  65.   unsigned Stars = 0;
  66.  
  67.   // The sequential number of the attempt this node represents.
  68.   unsigned Attempt = 0;
  69.  
  70.   // The current failure bonus when this material was used.
  71.   unsigned FailureBonus = 0;
  72.  
  73.   // The independent probability that we will succeed on this attempt.
  74.   unsigned N_Succ = 0;
  75.  
  76.   std::optional<double> InfiniteIncrementalCost;
  77.  
  78.   std::unique_ptr<OutcomeNode> Success;
  79.   std::unique_ptr<OutcomeNode> Failure;
  80. };
  81.  
  82. OutcomeNode::OutcomeNode(const MaterialNode &Parent, bool Success)
  83.   : Parent(Parent), Success(Success) {
  84.  
  85.   // If this was a success, we don't need to do anything.  There's no reason to
  86.   // try any more materials.
  87.   if (Success)
  88.     return;
  89.  
  90.   const MaterialNode &Root = Parent.root();
  91.   unsigned Diff = Root.Stars - Parent.Stars;
  92.   unsigned MaxMaterial = Parent.maxMaterial();
  93.   for (unsigned M = 0; M <= MaxMaterial; ++M) {
  94.     unsigned B = BonusRates[Diff];
  95.     unsigned NewFailureBonus = std::min(100U, Parent.FailureBonus + B);
  96.     auto Mat = std::make_unique<MaterialNode>(*this, M, NewFailureBonus);
  97.     MaterialsTried.push_back(std::move(Mat));
  98.   }
  99. }
  100.  
  101. const MaterialNode *OutcomeNode::cheapestMaterial() const {
  102.   const MaterialNode *MC = nullptr;
  103.   for (const auto &M : MaterialsTried) {
  104.     if (!MC || M->matsRequired() < MC->matsRequired())
  105.       MC = M.get();
  106.   }
  107.  
  108.   return MC;
  109. }
  110.  
  111.  
  112. double OutcomeNode::matsRequired() const {
  113.   if (MaterialsTried.empty())
  114.     return 0.0;
  115.   return cheapestMaterial()->matsRequired();
  116. }
  117.  
  118.  
  119. MaterialNode::MaterialNode(unsigned BaseStars, unsigned InitialFailureBonus)
  120.   : Stars(BaseStars), Attempt(0), FailureBonus(InitialFailureBonus) {
  121.   // This is just a dummy node to represent the root.  Consider this a "failure" node
  122.   // so that it's consistent with only descending into failures.
  123.   Failure = std::make_unique<OutcomeNode>(*this, false);
  124. }
  125.  
  126. MaterialNode::MaterialNode(OutcomeNode &Parent, unsigned Stars, unsigned FailureBonus)
  127.   : Parent(&Parent), Stars(Stars), Attempt(Parent.Parent.Attempt + 1), FailureBonus(FailureBonus) {
  128.   assert(Stars <= Parent.Parent.maxMaterial());
  129.   assert(Stars < root().Stars);
  130.   unsigned Difference = root().Stars - Stars;
  131.   N_Succ = BaseRates[Difference];
  132.   N_Succ = std::min(100U, N_Succ + FailureBonus);
  133.  
  134.   if (Difference >= 4) {
  135.     // When the difference is too large, there is no failure bonus, so we can't keep recursing
  136.     // through branches of the decision tree because it would be infinite.  Instead, we have to
  137.     // use some calculus this explicitly.  Specifically, we have the following variables:
  138.     //
  139.     //     N - the cost of materials we've used up until this point.
  140.     //     P - The base success rate with this material + any accumulated failure bonus
  141.     //
  142.     // With these, we have an infinite set of attempts where the absolute chance of succeeding
  143.     // on attempt k is P*(1-P)^k, where k=0 represents the first attempt where Difference >= 4
  144.     // and ignores any attempts that came prior.
  145.     //
  146.     // Furthermore, if the k'th attempt is the attempt that succeeds, then we would have used
  147.     // k additional materials, all 0* so a cost of 1, for a total cost of N+k, and since this
  148.     // would happen with probability P*(1-P)^k the effective cost of this branch of the tree
  149.     // would be:
  150.     //
  151.     //    (N+k)*P*(1-P)^k
  152.     //
  153.     // Thus, we can get the sum of all branches by computing the limit of this sum as K approaches
  154.     // infinity.
  155.     //
  156.     //      ∑[k=0, ∞] (N+k)*P*(1-P)^k
  157.     //
  158.     // I leave it as an exercise to the reader that this is equal to N + 1/P - 1, which means
  159.     // that the *incremental* cost from taking this branch is simply equal to 1/P - 1.
  160.  
  161.     double D_Succ = static_cast<double>(N_Succ) / 100.0;
  162.  
  163.     // Since we're mathematically solving the entire problem henceforth, just say it has a success
  164.     // rate of 100%, and set a special variable indicating the solved answer.
  165.     N_Succ = 100;
  166.     InfiniteIncrementalCost = 1.0 / D_Succ - 1.0;
  167.   } else {
  168.     Success = std::make_unique<OutcomeNode>(*this, true);
  169.     if (N_Succ < 100U) {
  170.       Failure = std::make_unique<OutcomeNode>(*this, false);
  171.     }
  172.   }
  173. }
  174.  
  175. const MaterialNode &MaterialNode::root() const {
  176.   return (Parent) ? Parent->Parent.root() : *this;
  177. }
  178.  
  179. unsigned MaterialNode::maxMaterial() const {
  180.   return (Parent) ? Stars : Stars - 1;
  181. }
  182.  
  183. double MaterialNode::matsRequired() const {
  184.   double M = 0.0;
  185.   if (Parent)
  186.     M = MatValue[Stars];
  187.  
  188.   if (InfiniteIncrementalCost.has_value()) {
  189.     // If this is an infinite branch, just add in the computed cost and
  190.     // exit.
  191.     return M + *InfiniteIncrementalCost;
  192.   }
  193.  
  194.   double P_Succ = static_cast<double>(N_Succ) / 100.0;
  195.   double P_Fail = 1.0 - P_Succ;
  196.   if (Success)
  197.     M += P_Succ * Success->matsRequired();
  198.   if (Failure)
  199.     M += P_Fail * Failure->matsRequired();
  200.   return M;
  201. }
  202.  
  203.  
  204. unsigned MaterialNode::materialDifference() const {
  205.   return root().Stars - Stars;
  206. }
  207.  
  208. unsigned MaterialNode::successRate() const {
  209.   return BaseRates[materialDifference()];
  210. }
  211.  
  212. unsigned MaterialNode::accumulatedFailure() const {
  213.   unsigned N = 0;
  214.   if (Parent)
  215.     N += Parent->Parent.FailureBonus;
  216.  
  217.   return N;
  218. }
  219.  
  220.  
  221. std::array<std::unique_ptr<MaterialNode>, 5> Roots;
  222.  
  223. int main()
  224. {
  225.   for (unsigned I = 0; I < 5; ++I) {
  226.     double M = 1.0;
  227.     if (I > 0) {
  228.       Roots[I] = std::make_unique<MaterialNode>(I);
  229.       M = Roots[I]->matsRequired();
  230.     }
  231.     std::cout << I << "* -> " << (I + 1) << "* : " << M << " 0* item required." << std::endl;
  232.     MatsRequired[I] = M;
  233.     MatValue[I + 1] = MatValue[I] + MatsRequired[I];
  234.   }
  235.  
  236.   for (unsigned I = 2; I < 5; ++I) {
  237.     for (unsigned FB = 0; FB < 100; ++FB) {
  238.       Roots[I] = std::make_unique<MaterialNode>(I, FB);
  239.       const MaterialNode *Cheapest = Roots[I]->Failure->cheapestMaterial();
  240.       std::cout << I << "*, FB = " << FB << ":  Use " << Cheapest->Stars << "* material.\n";
  241.     }
  242.   }
  243.  
  244.   unsigned Base = 0;
  245.   while (true) {
  246.     std::cout << "Enter a base material star value: ";
  247.     std::cin >> Base;
  248.     if (Base < 5)
  249.       break;
  250.  
  251.     std::cout << "Cannot upgrade any item which is 5* or higher" << std::endl;
  252.   }
  253.  
  254.   const MaterialNode *R = Roots[Base].get();
  255.   bool Finished = false;
  256.   double Cost = 0.0;
  257.   double Bonus = 0.0;
  258.   unsigned F = 0;
  259.   std::cout << "Upgrading " << R->Stars << "* base item" << std::endl;
  260.   while (!Finished) {
  261.     std::cout << "Analyzing choices..." << std::endl;
  262.     for (const auto &X : R->Failure->MaterialsTried) {
  263.       std::cout << X->Stars << "* material -> Cost = " << X->matsRequired() << std::endl;
  264.     }
  265.     R = R->Failure->cheapestMaterial();
  266.     F += R->FailureBonus;
  267.     std::cout << "Failure Bonus = " << F << "%" << std::endl;
  268.     std::cout << "Selecting " << R->Stars << "* material" << std::endl;
  269.  
  270.     unsigned S = R->successRate();
  271.     std::cout << "Attempt " << R->Attempt << "[" << (S + F) << "%]: " << S << "% + " << F << "%" << std::endl;
  272.     std::cout << "Did this attempt (S)ucceed or (F)ail?  Or do you want to go (B)ack?";
  273.     char CH;
  274.     std::cin >> CH;
  275.     CH = toupper(CH);
  276.     assert(CH == 'S' || CH == 'F' || CH == 'B');
  277.     if (CH == 'B') {
  278.       F -= R->FailureBonus;
  279.       R = &R->Parent->Parent;
  280.       Cost -= MatsRequired[R->Stars];
  281.       continue;
  282.     }
  283.  
  284.     Cost += MatsRequired[R->Stars];
  285.     Bonus += static_cast<double>(R->FailureBonus) / 100.0;
  286.  
  287.     if (CH == 'S') {
  288.       std::cout << "Done!  Steps = " << R->Attempt << ", Total material cost = " << Cost << std::endl;
  289.       break;
  290.     }
  291.   }
  292.   return 0;
  293. }
Advertisement
Add Comment
Please, Sign In to add comment