RNG.h 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /*
  2. * RNG.h, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #pragma once
  11. VCMI_LIB_NAMESPACE_BEGIN
  12. namespace vstd
  13. {
  14. class DLL_LINKAGE RNG
  15. {
  16. public:
  17. virtual ~RNG() = default;
  18. /// Returns random number in range [lower, upper]
  19. virtual int nextInt(int lower, int upper) = 0;
  20. /// Returns random number in range [lower, upper]
  21. virtual int64_t nextInt64(int64_t lower, int64_t upper) = 0;
  22. /// Returns random number in range [lower, upper]
  23. virtual double nextDouble(double lower, double upper) = 0;
  24. /// Returns random number in range [0, upper]
  25. virtual int nextInt(int upper) = 0;
  26. /// Returns random number in range [0, upper]
  27. virtual int64_t nextInt64(int64_t upper) = 0;
  28. /// Returns random number in range [0, upper]
  29. virtual double nextDouble(double upper) = 0;
  30. /// Generates an integer between 0 and the maximum value it can hold.
  31. /// Should be only used for seeding other generators
  32. virtual int nextInt() = 0;
  33. /// Returns integer using binomial distribution
  34. /// returned value is number of successfull coin flips with chance 'coinChance' out of 'coinsCount' attempts
  35. virtual int nextBinomialInt(int coinsCount, double coinChance) = 0;
  36. };
  37. }
  38. namespace RandomGeneratorUtil
  39. {
  40. template<typename Container>
  41. auto nextItem(const Container & container, vstd::RNG & rand) -> decltype(std::begin(container))
  42. {
  43. if(container.empty())
  44. throw std::runtime_error("Unable to select random item from empty container!");
  45. return std::next(container.begin(), rand.nextInt64(0, container.size() - 1));
  46. }
  47. template<typename Container>
  48. auto nextItem(Container & container, vstd::RNG & rand) -> decltype(std::begin(container))
  49. {
  50. if(container.empty())
  51. throw std::runtime_error("Unable to select random item from empty container!");
  52. return std::next(container.begin(), rand.nextInt64(0, container.size() - 1));
  53. }
  54. template<typename Container>
  55. size_t nextItemWeighted(Container & container, vstd::RNG & rand)
  56. {
  57. assert(!container.empty());
  58. int64_t totalWeight = std::accumulate(container.begin(), container.end(), 0);
  59. assert(totalWeight > 0);
  60. int64_t roll = rand.nextInt64(0, totalWeight - 1);
  61. for (size_t i = 0; i < container.size(); ++i)
  62. {
  63. int chance = container[i];
  64. if(roll < chance)
  65. return i;
  66. roll -= chance;
  67. }
  68. return container.size() - 1;
  69. }
  70. template<typename Container>
  71. void randomShuffle(Container & container, vstd::RNG & rand)
  72. {
  73. int64_t n = std::distance(container.begin(), container.end());
  74. for(int64_t i = n - 1; i > 0; --i)
  75. {
  76. auto randIndex = rand.nextInt64(0, i);
  77. std::swap(*(container.begin() + i), *(container.begin() + randIndex));
  78. }
  79. }
  80. }
  81. VCMI_LIB_NAMESPACE_END