mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-08 09:16:26 +00:00
doxygen, comments
This commit is contained in:
parent
4af4f8ec27
commit
304c1f93fe
@ -13,9 +13,13 @@
|
|||||||
|
|
||||||
Full algorithm by Yogin. Typing and optimization by Rusty.
|
Full algorithm by Yogin. Typing and optimization by Rusty.
|
||||||
|
|
||||||
This code has lots of debugging. It is there for a reason: this
|
This code has lots of debugging. It is there for a reason:
|
||||||
code is kinda tricky. Do not remove it.
|
this code is kinda tricky. Do not remove it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//! @file attack_prediction.cpp
|
||||||
|
//! Simulate combat to calculate attacks. Standalone program, benchmark.
|
||||||
|
|
||||||
#include "attack_prediction.hpp"
|
#include "attack_prediction.hpp"
|
||||||
|
|
||||||
#include <cstring> // For memset
|
#include <cstring> // For memset
|
||||||
@ -23,8 +27,9 @@
|
|||||||
|
|
||||||
#include "wassert.hpp"
|
#include "wassert.hpp"
|
||||||
|
|
||||||
// Compile with -O3 -DBENCHMARK for speed testing, -DCHECK for testing
|
// Compile with -O3 -DBENCHMARK for speed testing,
|
||||||
// correctness (run tools/wesnoth-attack-sim.c --check on output)
|
// -DCHECK for testing correctness
|
||||||
|
// (run tools/wesnoth-attack-sim.c --check on output)
|
||||||
#if !defined(BENCHMARK) && !defined(CHECK)
|
#if !defined(BENCHMARK) && !defined(CHECK)
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#else
|
#else
|
||||||
@ -44,7 +49,7 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// A matrix of A's hitpoints vs B's hitpoints.
|
//! A matrix of A's hitpoints vs B's hitpoints.
|
||||||
struct prob_matrix
|
struct prob_matrix
|
||||||
{
|
{
|
||||||
// Simple matrix, both known HP.
|
// Simple matrix, both known HP.
|
||||||
@ -78,8 +83,7 @@ struct prob_matrix
|
|||||||
void dump() const;
|
void dump() const;
|
||||||
|
|
||||||
// We need four matrices, or "planes", reflecting the possible
|
// We need four matrices, or "planes", reflecting the possible
|
||||||
// "slowed" states (neither slowed, A slowed, B slowed, both
|
// "slowed" states (neither slowed, A slowed, B slowed, both slowed).
|
||||||
// slowed).
|
|
||||||
enum {
|
enum {
|
||||||
NEITHER_SLOWED,
|
NEITHER_SLOWED,
|
||||||
A_SLOWED,
|
A_SLOWED,
|
||||||
@ -108,7 +112,7 @@ private:
|
|||||||
void shift_rows(unsigned dst, unsigned src,
|
void shift_rows(unsigned dst, unsigned src,
|
||||||
unsigned damage, double prob, bool drain);
|
unsigned damage, double prob, bool drain);
|
||||||
|
|
||||||
// FIXME: rename using _ at end.
|
//! @todo FIXME: rename using _ at end.
|
||||||
unsigned int rows, cols;
|
unsigned int rows, cols;
|
||||||
double *plane[4];
|
double *plane[4];
|
||||||
|
|
||||||
@ -157,7 +161,7 @@ prob_matrix::prob_matrix(unsigned int a_max_hp, unsigned int b_max_hp,
|
|||||||
|
|
||||||
// Transfer HP distribution from A?
|
// Transfer HP distribution from A?
|
||||||
if (!a_summary[0].empty()) {
|
if (!a_summary[0].empty()) {
|
||||||
// FIXME: Can optimize here.
|
// @todo FIXME: Can optimize here.
|
||||||
min_row[NEITHER_SLOWED] = 0;
|
min_row[NEITHER_SLOWED] = 0;
|
||||||
min_row[A_SLOWED] = 0;
|
min_row[A_SLOWED] = 0;
|
||||||
min_col[A_SLOWED] = b_hp - 1;
|
min_col[A_SLOWED] = b_hp - 1;
|
||||||
@ -182,8 +186,8 @@ prob_matrix::prob_matrix(unsigned int a_max_hp, unsigned int b_max_hp,
|
|||||||
debug(("B has fought before\n"));
|
debug(("B has fought before\n"));
|
||||||
dump();
|
dump();
|
||||||
} else {
|
} else {
|
||||||
// if a unit has drain it might end with more HP than before
|
// If a unit has drain it might end with more HP than before.
|
||||||
// make sure we don't access the matrix in invalid positions
|
// Make sure we don't access the matrix in invalid positions.
|
||||||
a_hp = minimum<unsigned int>(a_hp, rows - 1);
|
a_hp = minimum<unsigned int>(a_hp, rows - 1);
|
||||||
b_hp = minimum<unsigned int>(b_hp, cols - 1);
|
b_hp = minimum<unsigned int>(b_hp, cols - 1);
|
||||||
val(NEITHER_SLOWED, a_hp, b_hp) = 1.0;
|
val(NEITHER_SLOWED, a_hp, b_hp) = 1.0;
|
||||||
@ -316,8 +320,8 @@ void prob_matrix::shift_rows(unsigned dst, unsigned src,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shift prob_matrix to reflect probability 'hit_chance' that damage (up
|
// Shift prob_matrix to reflect probability 'hit_chance'
|
||||||
// to) 'damage' is done to 'b'.
|
// that damage (up to) 'damage' is done to 'b'.
|
||||||
void prob_matrix::receive_blow_b(unsigned damage, unsigned slow_damage, double hit_chance,
|
void prob_matrix::receive_blow_b(unsigned damage, unsigned slow_damage, double hit_chance,
|
||||||
bool a_slows, bool a_drains)
|
bool a_slows, bool a_drains)
|
||||||
{
|
{
|
||||||
@ -330,7 +334,7 @@ void prob_matrix::receive_blow_b(unsigned damage, unsigned slow_damage, double h
|
|||||||
if (!plane[src])
|
if (!plane[src])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If a slows us we go from 0=>2, 1=>3, 2=>2 3=>3.
|
// If A slows us, we go from 0=>2, 1=>3, 2=>2 3=>3.
|
||||||
if (a_slows)
|
if (a_slows)
|
||||||
dst = (src|2);
|
dst = (src|2);
|
||||||
else
|
else
|
||||||
@ -442,8 +446,8 @@ double prob_matrix::dead_prob() const
|
|||||||
return prob;
|
return prob;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shift matrix to reflect probability 'hit_chance' that damage (up
|
// Shift matrix to reflect probability 'hit_chance'
|
||||||
// to) 'damage' is done to 'a'.
|
// that damage (up to) 'damage' is done to 'a'.
|
||||||
void prob_matrix::receive_blow_a(unsigned damage, unsigned slow_damage, double hit_chance,
|
void prob_matrix::receive_blow_a(unsigned damage, unsigned slow_damage, double hit_chance,
|
||||||
bool b_slows, bool b_drains)
|
bool b_slows, bool b_drains)
|
||||||
{
|
{
|
||||||
@ -456,7 +460,7 @@ void prob_matrix::receive_blow_a(unsigned damage, unsigned slow_damage, double h
|
|||||||
if (!plane[src])
|
if (!plane[src])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If b slows us we go from 0=>1, 1=>1, 2=>3 3=>3.
|
// If B slows us, we go from 0=>1, 1=>1, 2=>3 3=>3.
|
||||||
if (b_slows)
|
if (b_slows)
|
||||||
dst = (src|1);
|
dst = (src|1);
|
||||||
else
|
else
|
||||||
@ -478,7 +482,7 @@ void prob_matrix::receive_blow_a(unsigned damage, unsigned slow_damage, double h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // end anon namespace
|
||||||
|
|
||||||
unsigned combatant::hp_dist_size(const battle_context::unit_stats &u, const combatant *prev)
|
unsigned combatant::hp_dist_size(const battle_context::unit_stats &u, const combatant *prev)
|
||||||
{
|
{
|
||||||
@ -523,9 +527,9 @@ combatant::combatant(const combatant &that, const battle_context::unit_stats &u)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// For swarm, whether we get an attack depends on HP distribution from
|
// For swarm, whether we get an attack depends on HP distribution
|
||||||
// previous combat. So we roll this into our P(hitting), since no
|
// from previous combat. So we roll this into our P(hitting),
|
||||||
// attack is equivalent to missing.
|
// since no attack is equivalent to missing.
|
||||||
void combatant::adjust_hitchance()
|
void combatant::adjust_hitchance()
|
||||||
{
|
{
|
||||||
if (summary[0].empty() || u_.swarm_min == u_.swarm_max)
|
if (summary[0].empty() || u_.swarm_min == u_.swarm_max)
|
||||||
@ -559,7 +563,7 @@ void combatant::adjust_hitchance()
|
|||||||
debug(("\n"));
|
debug(("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimum hp we could possibly have.
|
// Minimum HP we could possibly have.
|
||||||
unsigned combatant::min_hp() const
|
unsigned combatant::min_hp() const
|
||||||
{
|
{
|
||||||
if (summary[0].empty())
|
if (summary[0].empty())
|
||||||
@ -679,7 +683,7 @@ void combatant::complex_fight(combatant &opp, unsigned int rounds)
|
|||||||
unsigned int b_damage = opp.u_.damage, b_slow_damage = opp.u_.slow_damage;
|
unsigned int b_damage = opp.u_.damage, b_slow_damage = opp.u_.slow_damage;
|
||||||
|
|
||||||
// To simulate stoning, we set to amount which kills, and re-adjust after.
|
// To simulate stoning, we set to amount which kills, and re-adjust after.
|
||||||
// FIXME: This doesn't work for rolling calculations, just first battle.
|
//! @todo FIXME: This doesn't work for rolling calculations, just first battle.
|
||||||
if (u_.stones)
|
if (u_.stones)
|
||||||
a_damage = a_slow_damage = opp.u_.max_hp;
|
a_damage = a_slow_damage = opp.u_.max_hp;
|
||||||
if (opp.u_.stones)
|
if (opp.u_.stones)
|
||||||
@ -715,8 +719,9 @@ void combatant::complex_fight(combatant &opp, unsigned int rounds)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Two man enter. One man leave!
|
// Two man enter. One man leave!
|
||||||
// ... Or maybe two. But definitely not three. Of course, one could
|
// ... Or maybe two. But definitely not three.
|
||||||
// be a woman. Or both. And neither could be human, too.
|
// Of course, one could be a woman. Or both.
|
||||||
|
// And neither could be human, too.
|
||||||
// Um, ok, it was a stupid thing to say.
|
// Um, ok, it was a stupid thing to say.
|
||||||
void combatant::fight(combatant &opp)
|
void combatant::fight(combatant &opp)
|
||||||
{
|
{
|
||||||
@ -794,8 +799,9 @@ void combatant::fight(combatant &opp)
|
|||||||
opp.hp_dist[i] = opp.summary[0][i] + opp.summary[1][i];
|
opp.hp_dist[i] = opp.summary[0][i] + opp.summary[1][i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we don't try to access the vectors out of bounds, drain increases hps
|
// Make sure we don't try to access the vectors out of bounds,
|
||||||
// so we determine the number of hp here and make sure it stays within bounds
|
// drain increases HPs so we determine the number of HP here
|
||||||
|
// and make sure it stays within bounds
|
||||||
const unsigned int hp = minimum<unsigned int>(u_.hp, hp_dist.size() - 1);
|
const unsigned int hp = minimum<unsigned int>(u_.hp, hp_dist.size() - 1);
|
||||||
const unsigned int opp_hp = minimum<unsigned int>(opp.u_.hp, opp.hp_dist.size() - 1);
|
const unsigned int opp_hp = minimum<unsigned int>(opp.u_.hp, opp.hp_dist.size() - 1);
|
||||||
|
|
||||||
@ -812,7 +818,7 @@ void combatant::fight(combatant &opp)
|
|||||||
if (u_.slows)
|
if (u_.slows)
|
||||||
opp.slowed += (1 - opp.slowed) * opp_touched;
|
opp.slowed += (1 - opp.slowed) * opp_touched;
|
||||||
|
|
||||||
// FIXME: This is approximate: we could drain, then get hit.
|
//! @todo FIXME: This is approximate: we could drain, then get hit.
|
||||||
untouched = hp_dist[hp];
|
untouched = hp_dist[hp];
|
||||||
opp.untouched = opp.hp_dist[opp_hp];
|
opp.untouched = opp.hp_dist[opp_hp];
|
||||||
}
|
}
|
||||||
@ -829,8 +835,8 @@ double combatant::average_hp(unsigned int healing) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(BENCHMARK) || defined(CHECK)
|
#if defined(BENCHMARK) || defined(CHECK)
|
||||||
// We create a significant number of nasty-to-calculate units, and
|
// We create a significant number of nasty-to-calculate units,
|
||||||
// test each one against the others.
|
// and test each one against the others.
|
||||||
#define NUM_UNITS 50
|
#define NUM_UNITS 50
|
||||||
|
|
||||||
// Stolen from glibc headers sys/time.h
|
// Stolen from glibc headers sys/time.h
|
||||||
@ -900,8 +906,8 @@ static void run(unsigned specific_battle)
|
|||||||
if (specific_battle && battle != specific_battle)
|
if (specific_battle && battle != specific_battle)
|
||||||
continue;
|
continue;
|
||||||
u[j]->fight(*u[i]);
|
u[j]->fight(*u[i]);
|
||||||
// We need this here, because swarm means out num hits
|
// We need this here, because swarm means
|
||||||
// can change.
|
// out num hits can change.
|
||||||
u[i]->set_effectiveness((i % 7) + 2, 0.3 + (i % 6)*0.1,
|
u[i]->set_effectiveness((i % 7) + 2, 0.3 + (i % 6)*0.1,
|
||||||
(i % 8) == 0);
|
(i % 8) == 0);
|
||||||
u[k]->fight(*u[i]);
|
u[k]->fight(*u[i]);
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
See the COPYING file for more details.
|
See the COPYING file for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//! @file attack_prediction.hpp
|
||||||
|
//!
|
||||||
|
|
||||||
#ifndef ATTACK_PREDICTION_H_INCLUDED
|
#ifndef ATTACK_PREDICTION_H_INCLUDED
|
||||||
#define ATTACK_PREDICTION_H_INCLUDED
|
#define ATTACK_PREDICTION_H_INCLUDED
|
||||||
|
|
||||||
@ -21,60 +24,61 @@
|
|||||||
#include "actions.hpp"
|
#include "actions.hpp"
|
||||||
|
|
||||||
// This encapsulates all we need to know for this combat.
|
// This encapsulates all we need to know for this combat.
|
||||||
|
//! All combat-related infos.
|
||||||
struct combatant
|
struct combatant
|
||||||
{
|
{
|
||||||
// Construct a combatant.
|
//! Construct a combatant.
|
||||||
combatant(const battle_context::unit_stats &u, const combatant *prev = NULL);
|
combatant(const battle_context::unit_stats &u, const combatant *prev = NULL);
|
||||||
|
|
||||||
// Copy constructor
|
//! Copy constructor
|
||||||
combatant(const combatant &that, const battle_context::unit_stats &u);
|
combatant(const combatant &that, const battle_context::unit_stats &u);
|
||||||
|
|
||||||
// Simulate a fight! Can be called multiple times for cumulative calculations.
|
//! Simulate a fight! Can be called multiple times for cumulative calculations.
|
||||||
void fight(combatant &opponent);
|
void fight(combatant &opponent);
|
||||||
|
|
||||||
// Resulting probability distribution (may NOT be as large as max_hp)
|
//! Resulting probability distribution (may NOT be as large as max_hp)
|
||||||
std::vector<double> hp_dist;
|
std::vector<double> hp_dist;
|
||||||
|
|
||||||
// Resulting chance we were not hit by this opponent (important if it poisons)
|
//! Resulting chance we were not hit by this opponent (important if it poisons)
|
||||||
double untouched;
|
double untouched;
|
||||||
|
|
||||||
// Resulting chance we are poisoned.
|
//! Resulting chance we are poisoned.
|
||||||
double poisoned;
|
double poisoned;
|
||||||
|
|
||||||
// Resulting chance we are slowed.
|
//! Resulting chance we are slowed.
|
||||||
double slowed;
|
double slowed;
|
||||||
|
|
||||||
// What's the average hp (weighted average of hp_dist).
|
//! What's the average hp (weighted average of hp_dist).
|
||||||
double average_hp(unsigned int healing = 0) const;
|
double average_hp(unsigned int healing = 0) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
combatant(const combatant &that);
|
combatant(const combatant &that);
|
||||||
combatant& operator=(const combatant &);
|
combatant& operator=(const combatant &);
|
||||||
|
|
||||||
// Minimum hp we could possibly have.
|
//! Minimum hp we could possibly have.
|
||||||
unsigned min_hp() const;
|
unsigned min_hp() const;
|
||||||
|
|
||||||
// HP distribution we could end up with.
|
//! HP distribution we could end up with.
|
||||||
static unsigned hp_dist_size(const battle_context::unit_stats &u, const combatant *prev);
|
static unsigned hp_dist_size(const battle_context::unit_stats &u, const combatant *prev);
|
||||||
|
|
||||||
// Combat without chance of death, berserk, slow or drain is simple.
|
//! Combat without chance of death, berserk, slow or drain is simple.
|
||||||
void no_death_fight(combatant &opponent);
|
void no_death_fight(combatant &opponent);
|
||||||
|
|
||||||
// Combat with <= 1 strike each is simple, too.
|
//! Combat with <= 1 strike each is simple, too.
|
||||||
void one_strike_fight(combatant &opponent);
|
void one_strike_fight(combatant &opponent);
|
||||||
|
|
||||||
// All other cases.
|
//! All other cases.
|
||||||
void complex_fight(combatant &opponent, unsigned int rounds);
|
void complex_fight(combatant &opponent, unsigned int rounds);
|
||||||
|
|
||||||
// We must adjust for swarm after every combat.
|
//! We must adjust for swarm after every combat.
|
||||||
void adjust_hitchance();
|
void adjust_hitchance();
|
||||||
|
|
||||||
const battle_context::unit_stats &u_;
|
const battle_context::unit_stats &u_;
|
||||||
|
|
||||||
// Usually uniform, but if we have swarm, then can be different.
|
//! Usually uniform, but if we have swarm, then can be different.
|
||||||
std::vector<double> hit_chances_;
|
std::vector<double> hit_chances_;
|
||||||
|
|
||||||
// Summary of matrix used to calculate last battle (unslowed & slowed).
|
//! Summary of matrix used to calculate last battle (unslowed & slowed).
|
||||||
std::vector<double> summary[2];
|
std::vector<double> summary[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user