Advertisement
Guest User

Untitled

a guest
Nov 18th, 2017
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 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. InfiniteIncrementalCost = 1.0 / D_Succ - 1.0;
  163.  
  164. // Since we're mathematically solving the entire problem henceforth, just say it has a success
  165. // rate of 100%, and set a special variable indicating the solved answer.
  166. N_Succ = 100;
  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
Advertisement