Update the stand-alone version of attack_prediciton.cpp so it compiles.

This commit is contained in:
J. Tyne 2012-09-27 11:16:22 +00:00
parent ebd8c916c5
commit 17434074e8
4 changed files with 142 additions and 118 deletions

View File

@ -207,10 +207,6 @@ battle_context_unit_stats::battle_context_unit_stats(const unit &u,
}
}
battle_context_unit_stats::~battle_context_unit_stats()
{
}
void battle_context_unit_stats::dump() const
{
printf("==================================\n");

View File

@ -75,7 +75,35 @@ struct battle_context_unit_stats
const unit &opp, const map_location& opp_loc,
const attack_type *opp_weapon,
const unit_map& units);
~battle_context_unit_stats();
~battle_context_unit_stats()
{}
#if defined(BENCHMARK) || defined(CHECK)
/// Special constructor for the stand-alone version of attack_prediction.cpp.
/// (This hardcodes some standard abilities for testing purposes.)
battle_context_unit_stats(int dmg, int blows, int hitpoints, int maximum_hp,
int hit_chance, bool drain, bool slows, bool slowed,
bool berserk, bool first, bool do_swarm) :
weapon(NULL), attack_num(0), is_attacker(true), // These are not used in attack prediction.
is_poisoned(false), is_slowed(slowed),
slows(slows), drains(drain), petrifies(false), plagues(false),
poisons(false), backstab_pos(false), swarm(do_swarm), firststrike(first),
experience(0), max_experience(50), level(1), // No units should advance in the attack prediction tests.
rounds(berserk ? 30 : 1),
hp(std::max<int>(0, hitpoints)), max_hp(std::max<int>(1, maximum_hp)),
chance_to_hit(hit_chance),
damage(std::max(0, dmg)), slow_damage(round_damage(damage, 1, 2)),
drain_percent(drain ? 50 : 0), drain_constant(0),
num_blows(do_swarm ? blows * hp / max_hp : blows),
swarm_min(do_swarm ? 0 : blows), swarm_max(blows),
plague_type()
{
if ( slowed )
damage = slow_damage;
if ( hp > max_hp )
hp = max_hp; // Keeps the prob_matrix from going out of bounds.
}
#endif
/** Dumps the statistics of a unit on stdout. Remove it eventually. */
void dump() const;

View File

@ -20,7 +20,17 @@
/**
* @file
* Simulate combat to calculate attacks. Standalone program, benchmark.
* Simulate combat to calculate attacks.
* This can be compiled as a stand-alone program to either verify
* correctness or to benchmark performance.
*
* Compile with -O3 -DBENCHMARK for speed testing, and with -DCHECK for
* testing correctness (redirect the output to a file, then compile
* utils/wesnoth-attack-sim.c and run that with the arguments
* --check <file name>).
* For either option, use -DHUMAN_READABLE if you want to see the results
* from each combat displayed in a prettier format (but no longer suitable
* for wesnoth-attack-sim.c).
*/
#include <cfloat>
@ -30,14 +40,17 @@
#include "actions/attack.hpp"
#include "game_config.hpp"
// Compile with -O3 -DBENCHMARK for speed testing,
// -DCHECK for testing correctness
// (run tools/wesnoth-attack-sim.c --check on output)
#if defined(BENCHMARK) || defined(CHECK)
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
// Set some default values so this file can stand alone.
namespace game_config {
int kill_experience = 8;
int tile_size = 72; // Not really needed, but it's used in image.hpp.
}
#endif
#ifdef ATTACK_PREDICTION_DEBUG
@ -229,7 +242,7 @@ const double &prob_matrix::val(unsigned p, unsigned row, unsigned col) const
return plane_[p][row * cols_ + col];
}
#ifdef CHECK
#if defined(CHECK) && defined(ATTACK_PREDICTION_DEBUG)
void prob_matrix::dump() const
{
unsigned int row, col, m;
@ -852,7 +865,7 @@ void combatant::complex_fight(combatant &opp, unsigned rounds, bool levelup_cons
unsigned max_attacks = std::max(hit_chances_.size(), opp.hit_chances_.size());
debug(("A gets %u attacks, B %u\n", hit_chances_.size(), opp.hit_chances_.size()));
debug(("A gets %zu attacks, B %zu\n", hit_chances_.size(), opp.hit_chances_.size()));
unsigned int a_damage = u_.damage, a_slow_damage = u_.slow_damage;
unsigned int b_damage = opp.u_.damage, b_slow_damage = opp.u_.slow_damage;
@ -1041,37 +1054,60 @@ double combatant::average_hp(unsigned int healing) const
} \
} while (0)
unsigned combatant::num_attacks(unsigned int hp) const
// Copied from attack/attack.cpp
void battle_context_unit_stats::dump() const
{
if (swarm_)
return base_num_attacks_ - (base_num_attacks_*(max_hp_-hp)/max_hp_);
else
return base_num_attacks_;
printf("==================================\n");
printf("is_attacker: %d\n", static_cast<int>(is_attacker));
printf("is_poisoned: %d\n", static_cast<int>(is_poisoned));
printf("is_slowed: %d\n", static_cast<int>(is_slowed));
printf("slows: %d\n", static_cast<int>(slows));
printf("drains: %d\n", static_cast<int>(drains));
printf("petrifies: %d\n", static_cast<int>(petrifies));
printf("poisons: %d\n", static_cast<int>(poisons));
printf("backstab_pos: %d\n", static_cast<int>(backstab_pos));
printf("swarm: %d\n", static_cast<int>(swarm));
printf("rounds: %d\n", static_cast<int>(rounds));
printf("firststrike: %d\n", static_cast<int>(firststrike));
printf("\n");
printf("hp: %u\n", hp);
printf("max_hp: %u\n", max_hp);
printf("chance_to_hit: %u\n", chance_to_hit);
printf("damage: %d\n", damage);
printf("slow_damage: %d\n", slow_damage);
printf("drain_percent: %d\n", drain_percent);
printf("drain_constant: %d\n", drain_constant);
printf("num_blows: %u\n", num_blows);
printf("swarm_min: %u\n", swarm_min);
printf("swarm_max: %u\n", swarm_max);
printf("\n");
}
#ifdef CHECK
void combatant::print(const char label[], unsigned int battle) const
{
printf("#%u: %s: %u %u %u %2g%% ", battle,
label, damage_, base_num_attacks_, hp_, base_hit_chance_*100.0);
if (drains_)
printf("#%u: %s: %d %u %u %2g%% ", battle, label,
u_.damage, u_.swarm_max, u_.hp, static_cast<float>(u_.chance_to_hit));
if ( u_.drains )
printf("drains,");
if (slows_)
if ( u_.slows )
printf("slows,");
if (berserk_)
if ( u_.rounds > 1 )
printf("berserk,");
if (swarm_)
if ( u_.swarm_max != u_.swarm_min )
printf("swarm,");
if (firststrike_)
if ( u_.firststrike )
printf("firststrike,");
printf("maxhp=%u ", hp_dist.size()-1);
printf("maxhp=%zu ", hp_dist.size()-1);
printf(" %.2f", untouched);
for (unsigned int i = 0; i < hp_dist.size(); ++i)
printf(" %.2f", hp_dist[i] * 100);
printf("\n");
}
#else // ... BENCHMARK
void combatant::print(const char label[], unsigned int battle) const
void combatant::print(const char /*label*/[], unsigned int /*battle*/) const
{
}
#endif
@ -1080,76 +1116,36 @@ void combatant::reset()
{
for (unsigned int i = 0; i < hp_dist.size(); i++)
hp_dist[i] = 0.0;
untouched = 1.0;
poisoned = u_.is_poisoned ? 1.0 : 0.0;
slowed = u_.is_slowed ? 1.0 : 0.0;
hit_chances_ = std::vector<double>(u_.num_blows, u_.chance_to_hit / 100.0);
summary[0] = std::vector<double>();
summary[1] = std::vector<double>();
hit_chances_ = std::vector<double>(num_attacks(hp_), base_hit_chance_);
}
// Set effect against this particular opponent.
void combatant::set_effectiveness(unsigned damage, double hit_chance,
bool slows)
{
slows_ = slows;
base_hit_chance_ = hit_chance;
if (slowed_)
damage_ = damage / 2;
else
damage_ = damage;
if (!swarm_ || summary[0].empty())
hit_chances_ = std::vector<double>(num_attacks(hp_), hit_chance);
else {
// Whether we get an attack depends on HP distribution from previous
// combat. So we roll this into our P(hitting), since no attack is
// equivalent to missing.
hit_chances_ = std::vector<double>(base_num_attacks_);
double alive_prob;
if (summary[1].empty())
alive_prob = 1 - summary[0][0];
else
alive_prob = 1 - summary[0][0] - summary[1][0];
for (unsigned int i = 1; i <= max_hp_; i++) {
double prob = summary[0][i];
if (!summary[1].empty())
prob += summary[1][i];
for (unsigned int j = 0; j < num_attacks(i); j++)
hit_chances_[j] += prob * hit_chance / alive_prob;
}
}
debug(("\nhit_chances_ (base %u%%):", (unsigned)(hit_chance * 100.0 + 0.5)));
for (unsigned int i = 0; i < base_num_attacks_; i++)
debug((" %.2f", hit_chances_[i]));
debug(("\n"));
}
// Select a weapon.
void combatant::set_weapon(unsigned num_attacks, bool drains, bool berserk,
bool swarm, bool firststrike)
{
base_num_attacks_ = num_attacks;
drains_ = drains;
berserk_ = berserk;
swarm_ = swarm;
firststrike_ = firststrike;
}
static void run(unsigned specific_battle)
{
// N^2 battles
struct battle_context_unit_stats *stats[NUM_UNITS];
struct combatant *u[NUM_UNITS];
unsigned int i, j, k, battle = 0;
struct timeval start, end, total;
for (i = 0; i < NUM_UNITS; ++i) {
unsigned hp = 1 + ((i*3)%23);
u[i] = new combatant(hp, hp + (i+7)%17, false);
u[i]->set_weapon((i % 4) + 1, (i % 9) == 0, (i % 5) == 0,
((i+4) % 4) == 0,
((i+3) % 5) == 0);
u[i]->set_effectiveness((i % 7) + 2, 0.3 + (i % 6)*0.1, (i % 8) == 0);
stats[i] = new battle_context_unit_stats(i%7 + 2, // damage
i%4 + 1, // number of strikes
hp, hp + (i+7)%17,
30 + (i%6)*10, // hit chance
i % 9 == 0, // drains
i % 8 == 0, // slows
false, // slowed
i % 5 == 0, // berserk
(i+3) % 5 == 0, // firststrike
(i+4) % 4 == 0);// swarm
u[i] = new combatant(*stats[i]);
}
gettimeofday(&start, NULL);
@ -1164,10 +1160,6 @@ static void run(unsigned specific_battle)
if (specific_battle && battle != specific_battle)
continue;
u[j]->fight(*u[i]);
// We need this here, because swarm means
// out num hits can change.
u[i]->set_effectiveness((i % 7) + 2, 0.3 + (i % 6)*0.1,
(i % 8) == 0);
u[k]->fight(*u[i]);
u[i]->print("Defender", battle);
u[j]->print("Attacker #1", battle);
@ -1194,24 +1186,27 @@ static void run(unsigned specific_battle)
for (i = 0; i < NUM_UNITS; ++i) {
delete u[i];
delete stats[i];
}
exit(0);
}
static combatant *parse_unit(char ***argv,
unsigned *damagep = NULL,
double *hit_chancep = NULL,
bool *slowsp = NULL)
// Note: The exits in this function are not really necessary, unless they
// are intended as (extreme) warnings.
static battle_context_unit_stats *parse_unit(char ***argv)
{
unsigned damage, num_attacks, hp, max_hp, hit_chance;
// There are four required parameters.
int add_to_argv = 4;
int damage, num_attacks, hp, max_hp, hit_chance;
bool slows, slowed, drains, berserk, swarm, firststrike;
combatant *u;
damage = atoi((*argv)[1]);
num_attacks = atoi((*argv)[2]);
hp = max_hp = atoi((*argv)[3]);
hit_chance = atoi((*argv)[4]);
// Parse the optional (fifth) parameter.
slows = false;
slowed = false;
drains = false;
@ -1219,20 +1214,15 @@ static combatant *parse_unit(char ***argv,
swarm = false;
firststrike = false;
if (damagep)
*damagep = damage;
if (hit_chancep)
*hit_chancep = hit_chance/100.0;
if (slowsp)
*slowsp = slows;
if ((*argv)[5] && atoi((*argv)[5]) == 0) {
// Optional parameter is present.
++add_to_argv;
char *max = strstr((*argv)[5], "maxhp=");
if (max) {
max_hp = atoi(max + strlen("maxhp="));
if (max_hp < hp) {
fprintf(stderr, "maxhp must be > hitpoints");
fprintf(stderr, "maxhp must be at least hitpoints.");
exit(1);
}
}
@ -1258,42 +1248,43 @@ static combatant *parse_unit(char ***argv,
}
swarm = true;
}
*argv += 5;
} else {
*argv += 4;
}
u = new combatant(hp, max_hp, slowed, true);
u->set_weapon(num_attacks, drains, berserk, swarm, firststrike);
u->set_effectiveness(damage, hit_chance/100.0, slows);
return u;
// Update argv.
*argv += add_to_argv;
// Construct the stats and return.
return new battle_context_unit_stats(damage, num_attacks, hp, max_hp,
hit_chance, drains, slows, slowed,
berserk, firststrike, swarm);
}
int main(int argc, char *argv[])
{
battle_context_unit_stats *def_stats, *att_stats[20];
combatant *def, *att[20];
double hit_chance;
unsigned damage;
bool slows;
unsigned int i;
if (argc < 3)
run(argv[1] ? atoi(argv[1]) : 0);
if (argc < 9) {
fprintf(stderr,"Usage: %s <damage> <attacks> <hp> <hitprob> [drain,slows,slowed,swarm,firststrike,berserk,maxhp=<num>] <damage> <attacks> <hp> <hitprob> [drain,slows,slowed,berserk,firststrike,swarm,maxhp=<num>] ...",
argv[0]);
fprintf(stderr, "Usage: %s [<battle>]\n"
"\t%s <damage> <attacks> <hp> <hitprob> [drain,slows,slowed,swarm,firststrike,berserk,maxhp=<num>] <damage> <attacks> <hp> <hitprob> [drain,slows,slowed,berserk,firststrike,swarm,maxhp=<num>] ...\n",
argv[0], argv[0]);
exit(1);
}
def = parse_unit(&argv, &damage, &hit_chance, &slows);
for (i = 0; argv[1]; ++i)
att[i] = parse_unit(&argv);
def_stats = parse_unit(&argv);
def = new combatant(*def_stats);
for (i = 0; argv[1] && i < 19; ++i) {
att_stats[i] = parse_unit(&argv);
att[i] = new combatant(*att_stats[i]);
}
att[i] = NULL;
for (i = 0; att[i]; ++i) {
// In case defender has swarm, effectiveness changes.
debug(("Fighting next attacker\n"));
def->set_effectiveness(damage, hit_chance, slows);
att[i]->fight(*def);
}
@ -1302,8 +1293,11 @@ int main(int argc, char *argv[])
att[i]->print("Attacker", 0);
delete def;
for (i = 0; att[i]; ++i)
delete def_stats;
for (i = 0; att[i]; ++i) {
delete att[i];
delete att_stats[i];
}
return 0;
}

View File

@ -54,6 +54,12 @@ struct combatant
/** What's the average hp (weighted average of hp_dist). */
double average_hp(unsigned int healing = 0) const;
#if defined(BENCHMARK) || defined(CHECK)
// Functions used in the stand-alone version of attack_prediction.cpp
void print(const char label[], unsigned int battle) const;
void reset();
#endif
private:
combatant(const combatant &that);
combatant& operator=(const combatant &);