Merge branch 'monte-carlo-damage-calculation' of https://github.com/jyrkive/wesnoth into jyrkive-monte-carlo-damage-calculation
This commit is contained in:
commit
2360fb6260
9 changed files with 830 additions and 273 deletions
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue