Merge branch 'monte-carlo-damage-calculation' of https://github.com/jyrkive/wesnoth into jyrkive-monte-carlo-damage-calculation

This commit is contained in:
Andreas Löf 2016-08-01 22:04:06 +12:00
commit 2360fb6260
9 changed files with 830 additions and 273 deletions

View file

@ -18,6 +18,12 @@ Version 1.13.5+dev:
* Lua API:
* Added new functions wesnoth.fire_event_by_id and fire_event_by_name. The old
function wesnoth.fire_event is now an alias for wesnoth.fire_event_by_name
* Performance:
* When a heuristic determines that it's probably faster, the game predicts battle
outcome by simulating a few thousand fights instead of calculating exact
probabilities. This method is inexact, but in very complex battles (extremely
high HP, drain, slow, berserk, etc.) it's significantly faster than the default
damage calculation method.
Version 1.13.5:
* Campaigns:

View file

@ -188,6 +188,14 @@
type=custom
[/advanced_preference]
[advanced_preference]
field=damage_prediction_allow_monte_carlo_simulation
name= _ "Allow damage calculation with Monte Carlo simulation"
description= _ "Allow the damage calculation window to simulate fights instead of using exact probability calculations"
type=boolean
default=yes
[/advanced_preference]
#ifdef __UNUSED__
[advanced_preference]
field=joystick_support_enabled

View file

@ -41,6 +41,13 @@ Version 1.13.5:
* Added "Registered users only" checkbox to multiplayer configuration dialog which
when checked, only allows registered users to join the game
* Performance:
* When a heuristic determines that it's probably faster, the game predicts battle
outcome by simulating a few thousand fights instead of calculating exact
probabilities. This method is inexact, but in very complex battles (extremely
high HP, drain, slow, berserk, etc.) it's significantly faster than the default
damage calculation method.
Version 1.13.4:
* Language and i18n:
* Updated translations: British English, Russian.

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,9 @@ struct combatant
/** Copy constructor */
combatant(const combatant &that, const battle_context_unit_stats &u);
combatant(const combatant &that) = delete;
combatant& operator=(const combatant &) = delete;
/** Simulate a fight! Can be called multiple times for cumulative calculations. */
void fight(combatant &opponent, bool levelup_considered=true);
@ -60,12 +63,7 @@ struct combatant
#endif
private:
combatant(const combatant &that);
combatant& operator=(const combatant &);
struct combat_slice;
/** Split the combat by number of attacks per combatant (for swarm). */
std::vector<combat_slice> split_summary() const;
static const unsigned int MONTE_CARLO_SIMULATION_THRESHOLD = 5000u;
const battle_context_unit_stats &u_;

View file

@ -1050,5 +1050,15 @@ void set_disable_loadingscreen_animation(bool value)
set("disable_loadingscreen_animation", value);
}
bool damage_prediction_allow_monte_carlo_simulation()
{
return get("damage_prediction_allow_monte_carlo_simulation", true);
}
void set_damage_prediction_allow_monte_carlo_simulation(bool value)
{
set("damage_prediction_allow_monte_carlo_simulation", value);
}
} // end namespace preferences

View file

@ -272,6 +272,9 @@ namespace preferences {
bool disable_loadingscreen_animation();
void set_disable_loadingscreen_animation(bool value);
bool damage_prediction_allow_monte_carlo_simulation();
void set_damage_prediction_allow_monte_carlo_simulation(bool value);
} // end namespace preferences
#endif

View file

@ -99,4 +99,29 @@ namespace random_new
assert(max >= 0);
return static_cast<int> (next_random() % (static_cast<uint32_t>(max)+1));
}
double rng::get_random_double()
{
uint64_t double_as_int = 0u;
/* Exponent. It's set to zero.
Exponent bias is 1023 in double precision, and therefore the value 1023
needs to be encoded. */
double_as_int |= static_cast<uint64_t>(1023) << 52;
/* Significand. A double-precision floating point number stores 52 significand bits.
The underlying RNG only gives us 32 bits, so we need to shift the bits 20 positions
to the left. The last 20 significand bits we can leave at zero, we don't need
the full 52 bits of randomness allowed by the double-precision format. */
double_as_int |= static_cast<uint64_t>(next_random()) << (52 - 32);
/* At this point, the exponent is zero. The significand, taking into account the
implicit leading one bit, is at least exactly one and at most almost two.
In other words, a reinterpret_cast<double*> gives us a number in the range [1, 2[.
Simply subtract one from that value and return it. */
return *reinterpret_cast<double*>(&double_as_int) - 1.0;
}
bool rng::get_random_bool(double probability)
{
assert(probability >= 0.0 && probability <= 1.0);
return get_random_double() < probability;
}
}

View file

@ -16,6 +16,7 @@
#include <cstdlib> //needed for RAND_MAX
#include <cstdint>
#include <iterator> //needed for std::distance
namespace random_new
{
@ -47,6 +48,30 @@ namespace random_new
*/
int get_random_int(int min, int max)
{ return min + get_random_int_in_range_zero_to(max - min); }
/**
* This helper method returns true with the probability supplied as a parameter.
* @param probability The probability of returning true, from 0 to 1.
*/
bool get_random_bool(double probability);
/**
* This helper method returns a floating-point number in the range [0,1[.
*/
double get_random_double();
/**
* This helper method selects a random element from a container of floating-point numbers.
* Every number has a probability to be selected equal to the number itself
* (e.g. a number of 0.1 is selected with a probability of 0.1). The sum of numbers
* should be one.
* @param first Iterator to the beginning of the container
* @param last Iterator to the end of the container
* @ret The index of the selected number
*/
template <typename T>
unsigned int get_random_element(T first, T last);
static rng& default_instance();
protected:
virtual uint32_t next_random_impl() = 0;
@ -66,5 +91,27 @@ namespace random_new
Outside a synced context this has the same effect as rand()
*/
extern rng* generator;
template <typename T>
unsigned int rng::get_random_element(T first, T last)
{
double target = get_random_double();
double sum = 0.0;
T it = first;
sum += *it;
while (sum <= target)
{
++it;
if (it != last)
{
sum += *it;
}
else
{
break;
}
}
return std::distance(first, it);
}
}
#endif