00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <cfloat>
00027
00028 #include "attack_prediction.hpp"
00029
00030 #include "actions.hpp"
00031 #include "game_config.hpp"
00032
00033
00034
00035
00036 #if defined(BENCHMARK) || defined(CHECK)
00037 #include <time.h>
00038 #include <sys/time.h>
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #endif
00042
00043 #ifdef ATTACK_PREDICTION_DEBUG
00044 #define debug(x) printf x
00045 #else
00046 #define debug(x)
00047 #endif
00048
00049 namespace
00050 {
00051
00052 struct prob_matrix
00053 {
00054
00055 prob_matrix(unsigned int a_max_hp, unsigned int b_max_hp,
00056 bool a_slows, bool b_slows,
00057 unsigned int a_hp, unsigned int b_hp,
00058 const std::vector<double> a_summary[2],
00059 const std::vector<double> b_summary[2]);
00060
00061 ~prob_matrix();
00062
00063
00064 void receive_blow_b(unsigned damage, unsigned slow_damage, double hit_chance,
00065 bool a_slows, int a_drain_constant, int a_drain_percent);
00066
00067
00068 void receive_blow_a(unsigned damage, unsigned slow_damage, double hit_chance,
00069 bool b_slows, int b_drain_constant, int b_drain_percent);
00070
00071 void forced_levelup_a();
00072 void conditional_levelup_a();
00073
00074 void forced_levelup_b();
00075 void conditional_levelup_b();
00076
00077
00078 void remove_petrify_distortion_a(unsigned damage, unsigned slow_damage, unsigned b_hp);
00079 void remove_petrify_distortion_b(unsigned damage, unsigned slow_damage, unsigned a_hp);
00080
00081
00082 void extract_results(std::vector<double> summary_a[2],
00083 std::vector<double> summary_b[2]);
00084
00085
00086 double dead_prob() const;
00087
00088 void dump() const;
00089
00090
00091
00092 enum {
00093 NEITHER_SLOWED,
00094 A_SLOWED,
00095 B_SLOWED,
00096 BOTH_SLOWED
00097 };
00098
00099 private:
00100
00101 double *new_arr(unsigned int size);
00102
00103 double &val(unsigned plane, unsigned row, unsigned col);
00104 const double &val(unsigned plane, unsigned row, unsigned col) const;
00105
00106
00107 void xfer(unsigned dst_plane, unsigned src_plane,
00108 unsigned row_dst, unsigned col_dst,
00109 unsigned row_src, unsigned col_src,
00110 double prob);
00111
00112
00113 void shift_cols(unsigned dst, unsigned src,
00114 unsigned damage, double prob, int drain_constant, int drain_percent);
00115
00116
00117 void shift_rows(unsigned dst, unsigned src,
00118 unsigned damage, double prob, int drain_constant, int drain_percent);
00119
00120
00121 unsigned int rows_, cols_;
00122 double *plane_[4];
00123
00124
00125 unsigned int min_row_[4], min_col_[4];
00126 };
00127
00128 prob_matrix::prob_matrix(unsigned int a_max_hp, unsigned int b_max_hp,
00129 bool a_slows, bool b_slows,
00130 unsigned int a_hp, unsigned int b_hp,
00131 const std::vector<double> a_summary[2],
00132 const std::vector<double> b_summary[2])
00133 : rows_(a_max_hp+1), cols_(b_max_hp+1)
00134 {
00135 if (!a_summary[0].empty()) {
00136
00137 if (!a_summary[1].empty())
00138 b_slows = true;
00139
00140 assert(b_summary[0].empty());
00141 }
00142 if (!b_summary[0].empty()) {
00143
00144 if (!b_summary[1].empty())
00145 a_slows = true;
00146 }
00147
00148 plane_[NEITHER_SLOWED] = new_arr(rows_*cols_);
00149 if (b_slows)
00150 plane_[A_SLOWED] = new_arr(rows_*cols_);
00151 else
00152 plane_[A_SLOWED] = NULL;
00153 if (a_slows)
00154 plane_[B_SLOWED] = new_arr(rows_*cols_);
00155 else
00156 plane_[B_SLOWED] = NULL;
00157 if (a_slows && b_slows)
00158 plane_[BOTH_SLOWED] = new_arr(rows_*cols_);
00159 else
00160 plane_[BOTH_SLOWED] = NULL;
00161
00162 min_row_[NEITHER_SLOWED] = a_hp - 1;
00163 min_col_[NEITHER_SLOWED] = b_hp - 1;
00164 min_row_[A_SLOWED] = min_row_[B_SLOWED] = min_row_[BOTH_SLOWED] = rows_;
00165 min_col_[A_SLOWED] = min_col_[B_SLOWED] = min_col_[BOTH_SLOWED] = cols_;
00166
00167
00168 if (!a_summary[0].empty()) {
00169
00170 min_row_[NEITHER_SLOWED] = 0;
00171 min_row_[A_SLOWED] = 0;
00172 min_col_[A_SLOWED] = b_hp - 1;
00173 for (unsigned int row = 0; row < a_summary[0].size(); ++row)
00174 val(NEITHER_SLOWED, row, b_hp) = a_summary[0][row];
00175 if (!a_summary[1].empty()) {
00176 for (unsigned int row = 0; row < a_summary[1].size(); ++row)
00177 val(A_SLOWED, row, b_hp) = a_summary[1][row];
00178 }
00179 debug(("A has fought before\n"));
00180 dump();
00181 } else if (!b_summary[0].empty()) {
00182 min_col_[NEITHER_SLOWED] = 0;
00183 min_col_[B_SLOWED] = 0;
00184 min_row_[B_SLOWED] = a_hp - 1;
00185 for (unsigned int col = 0; col < b_summary[0].size(); ++col)
00186 val(NEITHER_SLOWED, a_hp, col) = b_summary[0][col];
00187 if (!b_summary[1].empty()) {
00188 for (unsigned int col = 0; col < b_summary[1].size(); ++col)
00189 val(B_SLOWED, a_hp, col) = b_summary[1][col];
00190 }
00191 debug(("B has fought before\n"));
00192 dump();
00193 } else {
00194
00195
00196 a_hp = std::min<unsigned int>(a_hp, rows_ - 1);
00197 b_hp = std::min<unsigned int>(b_hp, cols_ - 1);
00198 val(NEITHER_SLOWED, a_hp, b_hp) = 1.0;
00199 }
00200 }
00201
00202 prob_matrix::~prob_matrix()
00203 {
00204 delete[] plane_[NEITHER_SLOWED];
00205 delete[] plane_[A_SLOWED];
00206 delete[] plane_[B_SLOWED];
00207 delete[] plane_[BOTH_SLOWED];
00208 }
00209
00210
00211 double *prob_matrix::new_arr(unsigned int size)
00212 {
00213 double *arr = new double[size];
00214 memset(arr, 0, sizeof(double) * size);
00215 return arr;
00216 }
00217
00218 double &prob_matrix::val(unsigned p, unsigned row, unsigned col)
00219 {
00220 assert(row < rows_);
00221 assert(col < cols_);
00222 return plane_[p][row * cols_ + col];
00223 }
00224
00225 const double &prob_matrix::val(unsigned p, unsigned row, unsigned col) const
00226 {
00227 assert(row < rows_);
00228 assert(col < cols_);
00229 return plane_[p][row * cols_ + col];
00230 }
00231
00232 #ifdef CHECK
00233 void prob_matrix::dump() const
00234 {
00235 unsigned int row, col, m;
00236 const char *names[]
00237 = { "NEITHER_SLOWED", "A_SLOWED", "B_SLOWED", "BOTH_SLOWED" };
00238
00239 for (m = 0; m < 4; ++m) {
00240 if (!plane_[m])
00241 continue;
00242 debug(("%s:\n", names[m]));
00243 for (row = 0; row < rows_; ++row) {
00244 debug((" "));
00245 for (col = 0; col < cols_; ++col)
00246 debug(("%4.3g ", val(m, row, col)*100));
00247 debug(("\n"));
00248 }
00249 }
00250 }
00251 #else
00252 void prob_matrix::dump() const
00253 {
00254 }
00255 #endif
00256
00257
00258 void prob_matrix::xfer(unsigned dst_plane, unsigned src_plane,
00259 unsigned row_dst, unsigned col_dst,
00260 unsigned row_src, unsigned col_src,
00261 double prob)
00262 {
00263 double &src = val(src_plane, row_src, col_src);
00264 if (src != 0.0) {
00265 double diff = src * prob;
00266 src -= diff;
00267
00268
00269 if (col_dst >= cols_)
00270 col_dst = cols_ - 1;
00271 if (row_dst >= rows_)
00272 row_dst = rows_ - 1;
00273
00274 val(dst_plane, row_dst, col_dst) += diff;
00275
00276 debug(("Shifted %4.3g from %s(%u,%u) to %s(%u,%u)\n",
00277 diff, src_plane == NEITHER_SLOWED ? ""
00278 : src_plane == A_SLOWED ? "[A_SLOWED]"
00279 : src_plane == B_SLOWED ? "[B_SLOWED]"
00280 : src_plane == BOTH_SLOWED ? "[BOTH_SLOWED]" : "INVALID",
00281 row_src, col_src,
00282 dst_plane == NEITHER_SLOWED ? ""
00283 : dst_plane == A_SLOWED ? "[A_SLOWED]"
00284 : dst_plane == B_SLOWED ? "[B_SLOWED]"
00285 : dst_plane == BOTH_SLOWED ? "[BOTH_SLOWED]" : "INVALID",
00286 row_dst, col_dst));
00287 }
00288 }
00289
00290 void prob_matrix::shift_cols(unsigned dst, unsigned src,
00291 unsigned damage, double prob, int drain_constant, int drain_percent)
00292 {
00293 unsigned int row, col;
00294 int drainmax = (drain_percent*(static_cast<signed>(damage))/100+drain_constant);
00295
00296 if(drain_constant || drain_percent) {
00297 debug(("Drains %i (%i%% of %i plus %i)\n", drainmax, drain_percent, damage, drain_constant));
00298 }
00299
00300 if (damage >= cols_)
00301 damage = cols_ - 1;
00302
00303
00304 for (row = min_row_[src] + 1; row < rows_; row++) {
00305
00306 for (col = 1; col <= damage; ++col) {
00307 int newrow = std::max(static_cast<signed>(row)+(drain_percent*static_cast<signed>(col)/100+drain_constant), 1);
00308 xfer(dst, src, static_cast<unsigned>(newrow), 0, row, col, prob);
00309 }
00310 }
00311
00312
00313
00314 if(drainmax > 0) {
00315 for (row = rows_ - 1; row > min_row_[src]; row--) {
00316 int newrow = std::max(static_cast<signed>(row)+drainmax, 1);
00317 for (col = damage+1; col < cols_; ++col)
00318 xfer(dst, src, static_cast<unsigned>(newrow), col - damage, row, col, prob);
00319 }
00320 } else {
00321 for (row = min_row_[src] + 1; row < rows_; row++) {
00322 int newrow = std::max(static_cast<signed>(row)+drainmax, 1);
00323 for (col = damage+1; col < cols_; ++col)
00324 xfer(dst, src, static_cast<unsigned>(newrow), col - damage, row, col, prob);
00325 }
00326 }
00327 }
00328
00329 void prob_matrix::shift_rows(unsigned dst, unsigned src,
00330 unsigned damage, double prob, int drain_constant, int drain_percent)
00331 {
00332 unsigned int row, col;
00333 int drainmax = (drain_percent*(static_cast<signed>(damage))/100+drain_constant);
00334
00335 if(drain_constant || drain_percent) {
00336 debug(("Drains %i (%i%% of %i plus %i)\n", drainmax, drain_percent, damage, drain_constant));
00337 }
00338
00339 if (damage >= rows_)
00340 damage = rows_ - 1;
00341
00342
00343 for (col = min_col_[src] + 1; col < cols_; col++) {
00344
00345 for (row = 1; row <= damage; ++row) {
00346 int newcol = std::max(static_cast<signed>(col)+(drain_percent*static_cast<signed>(row)/100+drain_constant), 1);
00347 xfer(dst, src, 0, static_cast<unsigned>(newcol), row, col, prob);
00348 }
00349 }
00350
00351
00352
00353 if(drainmax > 0) {
00354 for (col = cols_ - 1; col > min_col_[src]; col--) {
00355 int newcol = std::max(static_cast<signed>(col)+drainmax, 1);
00356 for (row = damage+1; row < rows_; ++row)
00357 xfer(dst, src, row - damage, static_cast<unsigned>(newcol), row, col, prob);
00358 }
00359 } else {
00360 for (col = min_col_[src] + 1; col < cols_; col++) {
00361 int newcol = std::max(static_cast<signed>(col)+drainmax, 1);
00362 for (row = damage+1; row < rows_; ++row)
00363 xfer(dst, src, row - damage, static_cast<unsigned>(newcol), row, col, prob);
00364 }
00365 }
00366 }
00367
00368
00369
00370 void prob_matrix::receive_blow_b(unsigned damage, unsigned slow_damage, double hit_chance,
00371 bool a_slows, int a_drain_constant, int a_drain_percent)
00372 {
00373 int src, dst;
00374
00375
00376 for (src = 3; src >=0; src--) {
00377 unsigned int actual_damage;
00378
00379 if (!plane_[src])
00380 continue;
00381
00382
00383 if (a_slows)
00384 dst = (src|2);
00385 else
00386 dst = src;
00387
00388
00389 if (src & 1)
00390 actual_damage = slow_damage;
00391 else
00392 actual_damage = damage;
00393
00394 shift_cols(dst, src, actual_damage, hit_chance, a_drain_constant, a_drain_percent);
00395 if (min_col_[src] < damage)
00396 min_col_[dst] = 0;
00397 else if (min_col_[src] - damage < min_col_[dst])
00398 min_col_[dst] = min_col_[src] - damage;
00399 if (min_row_[src] < min_row_[dst])
00400 min_row_[dst] = min_row_[src];
00401
00402 int drain_worst = std::min<int>(a_drain_percent*(static_cast<signed>(actual_damage))/100+a_drain_constant, a_drain_constant);
00403 if(drain_worst < 0) {
00404 unsigned int max_drain_damage = static_cast<unsigned>(-drain_worst);
00405 if(max_drain_damage >= min_row_[dst])
00406 min_row_[dst] = 0;
00407 else
00408 min_row_[dst] -= max_drain_damage;
00409 }
00410 }
00411 }
00412
00413
00414 void prob_matrix::remove_petrify_distortion_a(unsigned damage, unsigned slow_damage,
00415 unsigned b_hp)
00416 {
00417 for (int p = 0; p < 4; ++p) {
00418 if (!plane_[p])
00419 continue;
00420
00421
00422 if (p & 1) {
00423 if (b_hp > slow_damage)
00424 for (unsigned int row = 0; row < rows_; ++row)
00425 xfer(p, p, row, b_hp - slow_damage, row, 0, 1.0);
00426 } else {
00427 if (b_hp > damage)
00428 for (unsigned int row = 0; row < rows_; ++row)
00429 xfer(p, p, row, b_hp - damage, row, 0, 1.0);
00430 }
00431 }
00432 }
00433
00434 void prob_matrix::remove_petrify_distortion_b(unsigned damage, unsigned slow_damage,
00435 unsigned a_hp)
00436 {
00437 for (int p = 0; p < 4; ++p) {
00438 if (!plane_[p])
00439 continue;
00440
00441
00442 if (p & 2) {
00443 if (a_hp > slow_damage)
00444 for (unsigned int col = 0; col < cols_; ++col)
00445 xfer(p, p, a_hp - slow_damage, col, 0, col, 1.0);
00446 } else {
00447 if (a_hp > damage)
00448 for (unsigned int col = 0; col < cols_; ++col)
00449 xfer(p, p, a_hp - damage, col, 0, col, 1.0);
00450 }
00451 }
00452 }
00453
00454 void prob_matrix::extract_results(std::vector<double> summary_a[2],
00455 std::vector<double> summary_b[2])
00456 {
00457 unsigned int p, row, col;
00458
00459 summary_a[0] = std::vector<double>(rows_);
00460 summary_b[0] = std::vector<double>(cols_);
00461
00462 if (plane_[A_SLOWED])
00463 summary_a[1] = std::vector<double>(rows_);
00464 if (plane_[B_SLOWED])
00465 summary_b[1] = std::vector<double>(cols_);
00466
00467 for (p = 0; p < 4; ++p) {
00468 int dst_a, dst_b;
00469 if (!plane_[p])
00470 continue;
00471
00472
00473 dst_a = (p & 1);
00474
00475 dst_b = !!(p & 2);
00476 for (row = 0; row < rows_; ++row) {
00477 for (col = 0; col < cols_; ++col) {
00478 summary_a[dst_a][row] += val(p, row, col);
00479 summary_b[dst_b][col] += val(p, row, col);
00480 }
00481 }
00482 }
00483 }
00484
00485
00486 double prob_matrix::dead_prob() const
00487 {
00488 unsigned int p, row, col;
00489 double prob = 0.0;
00490
00491 for (p = 0; p < 4; ++p) {
00492 if (!plane_[p])
00493 continue;
00494
00495 for (row = min_row_[p]; row < rows_; ++row)
00496 prob += val(p, row, 0);
00497 for (col = min_col_[p]; col < cols_; ++col)
00498 prob += val(p, 0, col);
00499 }
00500 return prob;
00501 }
00502
00503
00504
00505 void prob_matrix::receive_blow_a(unsigned damage, unsigned slow_damage, double hit_chance,
00506 bool b_slows, int b_drain_constant, int b_drain_percent)
00507 {
00508 int src, dst;
00509
00510
00511 for (src = 3; src >=0; src--) {
00512 unsigned actual_damage;
00513
00514 if (!plane_[src])
00515 continue;
00516
00517
00518 if (b_slows)
00519 dst = (src|1);
00520 else
00521 dst = src;
00522
00523
00524 if (src & 2)
00525 actual_damage = slow_damage;
00526 else
00527 actual_damage = damage;
00528
00529 shift_rows(dst, src, actual_damage, hit_chance, b_drain_constant, b_drain_percent);
00530 if (min_row_[src] < damage)
00531 min_row_[dst] = 0;
00532 else if (min_row_[src] - damage < min_row_[dst])
00533 min_row_[dst] = min_row_[src] - damage;
00534 if (min_col_[src] < min_col_[dst])
00535 min_col_[dst] = min_col_[src];
00536
00537 int drain_worst = std::min<int>(b_drain_percent*(static_cast<signed>(actual_damage))/100+b_drain_constant, b_drain_constant);
00538 if(drain_worst < 0) {
00539 unsigned int max_drain_damage = static_cast<unsigned>(-drain_worst);
00540 if(max_drain_damage >= min_col_[dst])
00541 min_col_[dst] = 0;
00542 else
00543 min_col_[dst] -= max_drain_damage;
00544 }
00545 }
00546 }
00547
00548 void prob_matrix::forced_levelup_a()
00549 {
00550
00551
00552 for (int p = 0; p < 4; ++p) {
00553 if (!plane_[p]) continue;
00554 for (unsigned row = std::max(min_row_[p], 1u); row < rows_; ++row) {
00555 for (unsigned col = min_col_[p]; col < cols_; ++col) {
00556 double v = val(p, row, col);
00557 val(p, row, col) = 0;
00558 val(p & -2, rows_ - 1, col) += v;
00559 }
00560 }
00561 }
00562 }
00563
00564 void prob_matrix::forced_levelup_b()
00565 {
00566
00567
00568 for (int p = 0; p < 4; ++p) {
00569 if (!plane_[p]) continue;
00570 for (unsigned row = min_row_[p]; row < rows_; ++row) {
00571 for (unsigned col = std::max(min_col_[p], 1u); col < cols_; ++col) {
00572 double v = val(p, row, col);
00573 val(p, row, col) = 0;
00574 val(p & -3, row, cols_ - 1) += v;
00575 }
00576 }
00577 }
00578 }
00579
00580 void prob_matrix::conditional_levelup_a()
00581 {
00582
00583
00584 for (int p = 0; p < 4; ++p) {
00585 if (!plane_[p]) continue;
00586 for (unsigned row = std::max(min_row_[p], 1u); row < rows_; ++row) {
00587 double v = val(p, row, 0);
00588 val(p, row, 0) = 0;
00589 val(p & -2, rows_ - 1, 0) += v;
00590 }
00591 }
00592 }
00593
00594 void prob_matrix::conditional_levelup_b()
00595 {
00596
00597
00598 for (int p = 0; p < 4; ++p) {
00599 if (!plane_[p]) continue;
00600 for (unsigned col = std::max(min_col_[p], 1u); col < cols_; ++col) {
00601 double v = val(p, 0, col);
00602 val(p, 0, col) = 0;
00603 val(p & -3, 0, cols_ - 1) += v;
00604 }
00605 }
00606 }
00607
00608 }
00609
00610 unsigned combatant::hp_dist_size(const battle_context_unit_stats &u, const combatant *prev)
00611 {
00612
00613 if (prev) {
00614 return prev->hp_dist.size();
00615 }
00616
00617
00618
00619 return u.max_hp + 1;
00620 }
00621
00622 combatant::combatant(const battle_context_unit_stats &u, const combatant *prev)
00623 : hp_dist(hp_dist_size(u, prev)),
00624 untouched(0.0),
00625 poisoned(0.0),
00626 slowed(0.0),
00627 u_(u),
00628 hit_chances_(u.num_blows, u.chance_to_hit / 100.0)
00629 {
00630
00631 if (prev) {
00632 summary[0] = prev->summary[0];
00633 summary[1] = prev->summary[1];
00634 poisoned = prev->poisoned;
00635 untouched = prev->untouched;
00636 slowed = prev->slowed;
00637 } else {
00638 untouched = 1.0;
00639 poisoned = u.is_poisoned ? 1.0 : 0.0;
00640 slowed = u.is_slowed ? 1.0 : 0.0;
00641 }
00642 }
00643
00644
00645 combatant::combatant(const combatant &that, const battle_context_unit_stats &u)
00646 : hp_dist(that.hp_dist), untouched(that.untouched), poisoned(that.poisoned), slowed(that.slowed), u_(u), hit_chances_(that.hit_chances_)
00647 {
00648 summary[0] = that.summary[0];
00649 summary[1] = that.summary[1];
00650 }
00651
00652
00653
00654
00655
00656
00657 void combatant::adjust_hitchance()
00658 {
00659 if (summary[0].empty() || u_.swarm_min == u_.swarm_max)
00660 return;
00661
00662 hit_chances_ = std::vector<double>(u_.swarm_max);
00663 double alive_prob;
00664
00665 if (summary[1].empty())
00666 alive_prob = 1 - summary[0][0];
00667 else
00668 alive_prob = 1 - summary[0][0] - summary[1][0];
00669
00670 unsigned int i;
00671 for (i = 1; i <= u_.max_hp; ++i) {
00672 double prob = 0.0;
00673 if(i < summary[0].size()) {
00674 prob = summary[0][i];
00675 }
00676 if (!summary[1].empty())
00677 prob += summary[1][i];
00678 for (unsigned int j = 0; j < u_.swarm_min + (u_.swarm_max -
00679 static_cast<double>(u_.swarm_min)) * u_.hp / u_.max_hp; ++j)
00680
00681 hit_chances_[j] += prob * u_.chance_to_hit / 100.0 / alive_prob;
00682 }
00683
00684 debug(("\nhit_chances_ (base %u%%):", u_.chance_to_hit));
00685 for (i = 0; i < u_.swarm_max; ++i)
00686 debug((" %.2f", hit_chances_[i] * 100.0 + 0.5));
00687 debug(("\n"));
00688 }
00689
00690
00691 unsigned combatant::min_hp() const
00692 {
00693 if (summary[0].empty())
00694 return u_.hp;
00695
00696
00697 assert(summary[1].empty());
00698
00699 unsigned int i;
00700 for (i = 0; summary[0][i] == 0; ++i) {};
00701 return i;
00702 }
00703
00704 static void forced_levelup(std::vector<double> &hp_dist)
00705 {
00706
00707
00708
00709 std::vector<double>::iterator i = hp_dist.begin();
00710 ++i;
00711 for (; i != hp_dist.end(); ++i) *i = 0;
00712
00713 hp_dist.back() = 1 - hp_dist.front();
00714 }
00715
00716 static void conditional_levelup(std::vector<double> &hp_dist, double kill_prob)
00717 {
00718
00719
00720
00721 double scalefactor = 0;
00722 const double chance_to_survive = 1 - hp_dist.front();
00723 if(chance_to_survive > DBL_MIN) scalefactor = 1 - kill_prob / chance_to_survive;
00724 std::vector<double>::iterator i = hp_dist.begin();
00725 ++i;
00726 for (; i != hp_dist.end(); ++i) *i *= scalefactor;
00727
00728 hp_dist.back() += kill_prob;
00729 }
00730
00731
00732 void combatant::no_death_fight(combatant &opp, bool levelup_considered)
00733 {
00734 if (summary[0].empty()) {
00735
00736 summary[0] = std::vector<double>(u_.max_hp+1);
00737 summary[0][u_.hp] = 1.0;
00738 for (unsigned int i = 0; i < opp.hit_chances_.size(); ++i) {
00739 for (int j = i; j >= 0; j--) {
00740 double move = summary[0][u_.hp - j * opp.u_.damage] * opp.hit_chances_[i];
00741 summary[0][u_.hp - j * opp.u_.damage] -= move;
00742 summary[0][u_.hp - (j+1) * opp.u_.damage] += move;
00743 }
00744 }
00745 } else {
00746
00747 for (unsigned int i = 0; i < opp.hit_chances_.size(); ++i) {
00748 for (unsigned int j = opp.u_.damage; j <= u_.hp; ++j) {
00749 double move = summary[0][j] * opp.hit_chances_[i];
00750 summary[0][j] -= move;
00751 summary[0][j - opp.u_.damage] += move;
00752 }
00753 }
00754 }
00755
00756 if (opp.summary[0].empty()) {
00757
00758 opp.summary[0] = std::vector<double>(opp.u_.max_hp+1);
00759 opp.summary[0][opp.u_.hp] = 1.0;
00760 for (unsigned int i = 0; i < hit_chances_.size(); ++i) {
00761 for (int j = i; j >= 0; j--) {
00762 double move = opp.summary[0][opp.u_.hp - j * u_.damage] * hit_chances_[i];
00763 opp.summary[0][opp.u_.hp - j * u_.damage] -= move;
00764 opp.summary[0][opp.u_.hp - (j+1) * u_.damage] += move;
00765 }
00766 }
00767 } else {
00768
00769 for (unsigned int i = 0; i < hit_chances_.size(); ++i) {
00770 for (unsigned int j = u_.damage; j <= opp.u_.hp; ++j) {
00771 double move = opp.summary[0][j] * hit_chances_[i];
00772 opp.summary[0][j] -= move;
00773 opp.summary[0][j - u_.damage] += move;
00774 }
00775 }
00776 }
00777
00778 if (!levelup_considered) return;
00779
00780 if (u_.experience + opp.u_.level >= u_.max_experience) {
00781 forced_levelup(summary[0]);
00782 }
00783
00784 if (opp.u_.experience + u_.level >= opp.u_.max_experience) {
00785 forced_levelup(opp.summary[0]);
00786 }
00787 }
00788
00789
00790 void combatant::one_strike_fight(combatant &opp, bool levelup_considered)
00791 {
00792 if (opp.summary[0].empty()) {
00793 opp.summary[0] = std::vector<double>(opp.u_.max_hp+1);
00794 if (hit_chances_.size() == 1) {
00795 opp.summary[0][opp.u_.hp] = 1.0 - hit_chances_[0];
00796 opp.summary[0][std::max<int>(opp.u_.hp - u_.damage, 0)] = hit_chances_[0];
00797 } else {
00798 assert(hit_chances_.empty());
00799 opp.summary[0][opp.u_.hp] = 1.0;
00800 }
00801 } else {
00802 if (hit_chances_.size() == 1) {
00803 for (unsigned int i = 1; i < opp.summary[0].size(); ++i) {
00804 double move = opp.summary[0][i] * hit_chances_[0];
00805 opp.summary[0][i] -= move;
00806 opp.summary[0][std::max<int>(i - u_.damage, 0)] += move;
00807 }
00808 }
00809 }
00810
00811
00812 double opp_alive_prob = 1.0 - opp.summary[0][0];
00813 if (summary[0].empty()) {
00814 summary[0] = std::vector<double>(u_.max_hp+1);
00815 if (opp.hit_chances_.size() == 1) {
00816 summary[0][u_.hp] = 1.0 - opp.hit_chances_[0] * opp_alive_prob;
00817 summary[0][std::max<int>(u_.hp - opp.u_.damage, 0)] = opp.hit_chances_[0] * opp_alive_prob;
00818 } else {
00819 assert(opp.hit_chances_.empty());
00820 summary[0][u_.hp] = 1.0;
00821 }
00822 } else {
00823 if (opp.hit_chances_.size() == 1) {
00824 for (unsigned int i = 1; i < summary[0].size(); ++i) {
00825 double move = summary[0][i] * opp.hit_chances_[0] * opp_alive_prob;
00826 summary[0][i] -= move;
00827 summary[0][std::max<int>(i - opp.u_.damage, 0)] += move;
00828 }
00829 }
00830 }
00831
00832 if (!levelup_considered) return;
00833
00834 if (u_.experience + opp.u_.level >= u_.max_experience) {
00835 forced_levelup(summary[0]);
00836 } else if (u_.experience + game_config::kill_xp(opp.u_.level) >= u_.max_experience) {
00837 conditional_levelup(summary[0], opp.summary[0][0]);
00838 }
00839
00840 if (opp.u_.experience + u_.level >= opp.u_.max_experience) {
00841 forced_levelup(opp.summary[0]);
00842 } else if (opp.u_.experience + game_config::kill_xp(u_.level) >= opp.u_.max_experience) {
00843 conditional_levelup(opp.summary[0], summary[0][0]);
00844 }
00845 }
00846
00847 void combatant::complex_fight(combatant &opp, unsigned rounds, bool levelup_considered)
00848 {
00849 prob_matrix m(hp_dist.size()-1, opp.hp_dist.size()-1,
00850 u_.slows && !opp.u_.is_slowed, opp.u_.slows && !u_.is_slowed,
00851 u_.hp, opp.u_.hp, summary, opp.summary);
00852
00853 unsigned max_attacks = std::max(hit_chances_.size(), opp.hit_chances_.size());
00854
00855 debug(("A gets %u attacks, B %u\n", hit_chances_.size(), opp.hit_chances_.size()));
00856
00857 unsigned int a_damage = u_.damage, a_slow_damage = u_.slow_damage;
00858 unsigned int b_damage = opp.u_.damage, b_slow_damage = opp.u_.slow_damage;
00859
00860
00861
00862 if (u_.petrifies)
00863 a_damage = a_slow_damage = opp.u_.max_hp;
00864 if (opp.u_.petrifies)
00865 b_damage = b_slow_damage = u_.max_hp;
00866
00867 do {
00868 for (unsigned int i = 0; i < max_attacks; ++i) {
00869 if (i < hit_chances_.size()) {
00870 debug(("A strikes\n"));
00871 m.receive_blow_b(a_damage, a_slow_damage, hit_chances_[i],
00872 u_.slows && !opp.u_.is_slowed, u_.drain_constant, u_.drain_percent);
00873 m.dump();
00874 }
00875 if (i < opp.hit_chances_.size()) {
00876 debug(("B strikes\n"));
00877 m.receive_blow_a(b_damage, b_slow_damage, opp.hit_chances_[i],
00878 opp.u_.slows && !u_.is_slowed, opp.u_.drain_constant, opp.u_.drain_percent);
00879 m.dump();
00880 }
00881 }
00882
00883 debug(("Combat ends:\n"));
00884 m.dump();
00885 } while (--rounds && m.dead_prob() < 0.99);
00886
00887 if (u_.petrifies)
00888 m.remove_petrify_distortion_a(u_.damage, u_.slow_damage, opp.u_.hp);
00889 if (opp.u_.petrifies)
00890 m.remove_petrify_distortion_b(opp.u_.damage, opp.u_.slow_damage, u_.hp);
00891
00892 if (levelup_considered) {
00893 if (u_.experience + opp.u_.level >= u_.max_experience) {
00894 m.forced_levelup_a();
00895 } else if (u_.experience + game_config::kill_xp(opp.u_.level) >= u_.max_experience) {
00896 m.conditional_levelup_a();
00897 }
00898
00899 if (opp.u_.experience + u_.level >= opp.u_.max_experience) {
00900 m.forced_levelup_b();
00901 } else if (opp.u_.experience + game_config::kill_xp(u_.level) >= opp.u_.max_experience) {
00902 m.conditional_levelup_b();
00903 }
00904 }
00905
00906
00907 m.extract_results(summary, opp.summary);
00908 }
00909
00910
00911
00912
00913
00914
00915 void combatant::fight(combatant &opp, bool levelup_considered)
00916 {
00917 unsigned int rounds = std::max<unsigned int>(u_.rounds, opp.u_.rounds);
00918
00919
00920 if (opp.u_.firststrike && !u_.firststrike) {
00921 opp.fight(*this, levelup_considered);
00922 return;
00923 }
00924
00925 #ifdef ATTACK_PREDICTION_DEBUG
00926 printf("A:\n");
00927 u_.dump();
00928 printf("B:\n");
00929 opp.u_.dump();
00930 #endif
00931
00932
00933 adjust_hitchance();
00934 opp.adjust_hitchance();
00935
00936 #if 0
00937 std::vector<double> prev = summary[0], opp_prev = opp.summary[0];
00938 complex_fight(opp, 1);
00939 std::vector<double> res = summary[0], opp_res = opp.summary[0];
00940 summary[0] = prev;
00941 opp.summary[0] = opp_prev;
00942 #endif
00943
00944
00945 if (rounds == 1 && !u_.slows && !opp.u_.slows &&
00946 !u_.drains && !opp.u_.drains && !u_.petrifies && !opp.u_.petrifies &&
00947 summary[1].empty() && opp.summary[1].empty()) {
00948 if (hit_chances_.size() <= 1 && opp.hit_chances_.size() <= 1) {
00949 one_strike_fight(opp, levelup_considered);
00950 } else if (hit_chances_.size() * u_.damage < opp.min_hp() &&
00951 opp.hit_chances_.size() * opp.u_.damage < min_hp()) {
00952 no_death_fight(opp, levelup_considered);
00953 } else {
00954 complex_fight(opp, rounds, levelup_considered);
00955 }
00956 } else {
00957 complex_fight(opp, rounds, levelup_considered);
00958 }
00959
00960 #if 0
00961 assert(summary[0].size() == res.size());
00962 assert(opp.summary[0].size() == opp_res.size());
00963 for (unsigned int i = 0; i < summary[0].size(); ++i) {
00964 if (fabs(summary[0][i] - res[i]) > 0.000001) {
00965 std::cerr << "Mismatch for " << i << " hp: " << summary[0][i] << " should have been " << res[i] << "\n";
00966 assert(0);
00967 }
00968 }
00969 for (unsigned int i = 0; i < opp.summary[0].size(); ++i) {
00970 if (fabs(opp.summary[0][i] - opp_res[i])> 0.000001) {
00971 std::cerr << "Mismatch for " << i << " hp: " << opp.summary[0][i] << " should have been " << opp_res[i] << "\n";
00972 assert(0);
00973 }
00974 }
00975 #endif
00976
00977
00978 if (summary[1].empty())
00979 hp_dist = summary[0];
00980 else {
00981 for (unsigned int i = 0; i < hp_dist.size(); ++i)
00982 hp_dist[i] = summary[0][i] + summary[1][i];
00983 }
00984 if (opp.summary[1].empty())
00985 opp.hp_dist = opp.summary[0];
00986 else {
00987 for (unsigned int i = 0; i < opp.hp_dist.size(); ++i)
00988 opp.hp_dist[i] = opp.summary[0][i] + opp.summary[1][i];
00989 }
00990
00991
00992
00993
00994 const unsigned int hp = std::min<unsigned int>(u_.hp, hp_dist.size() - 1);
00995 const unsigned int opp_hp = std::min<unsigned int>(opp.u_.hp, opp.hp_dist.size() - 1);
00996
00997
00998 double touched = untouched - hp_dist[hp];
00999 double opp_touched = opp.untouched - opp.hp_dist[opp_hp];
01000 if (opp.u_.poisons)
01001 poisoned += (1 - poisoned) * touched;
01002 if (u_.poisons)
01003 opp.poisoned += (1 - opp.poisoned) * opp_touched;
01004
01005 if (opp.u_.slows)
01006 slowed += (1 - slowed) * touched;
01007 if (u_.slows)
01008 opp.slowed += (1 - opp.slowed) * opp_touched;
01009
01010
01011 untouched = hp_dist[hp];
01012 opp.untouched = opp.hp_dist[opp_hp];
01013 }
01014
01015 double combatant::average_hp(unsigned int healing) const
01016 {
01017 double total = 0;
01018
01019
01020 for (unsigned int i = 1; i < hp_dist.size(); ++i) {
01021 total += hp_dist[i] * std::min<unsigned int>(i + healing, u_.max_hp);
01022 }
01023 return total;
01024 }
01025
01026 #if defined(BENCHMARK) || defined(CHECK)
01027
01028
01029 #define NUM_UNITS 50
01030
01031
01032 #define timer_sub(a, b, result) \
01033 do { \
01034 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
01035 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
01036 if ((result)->tv_usec < 0) { \
01037 --(result)->tv_sec; \
01038 (result)->tv_usec += 1000000; \
01039 } \
01040 } while (0)
01041
01042 #ifdef CHECK
01043 void combatant::print(const char label[], unsigned int battle) const
01044 {
01045 printf("#%u: %s: %u %u %u %2g%% ", battle,
01046 label, damage_, base_num_attacks_, hp_, base_hit_chance_*100.0);
01047 if (drains_)
01048 printf("drains,");
01049 if (slows_)
01050 printf("slows,");
01051 if (berserk_)
01052 printf("berserk,");
01053 if (swarm_)
01054 printf("swarm,");
01055 if (firststrike_)
01056 printf("firststrike,");
01057 printf("maxhp=%u ", hp_dist.size()-1);
01058 printf(" %.2f", untouched);
01059 for (unsigned int i = 0; i < hp_dist.size(); ++i)
01060 printf(" %.2f", hp_dist[i] * 100);
01061 printf("\n");
01062 }
01063 #else // ... BENCHMARK
01064 void combatant::print(const char label[], unsigned int battle) const
01065 {
01066 }
01067 #endif
01068
01069 static void run(unsigned specific_battle)
01070 {
01071
01072 struct combatant *u[NUM_UNITS];
01073 unsigned int i, j, k, battle = 0;
01074 struct timeval start, end, total;
01075
01076 for (i = 0; i < NUM_UNITS; ++i) {
01077 unsigned hp = 1 + ((i*3)%23);
01078 u[i] = new combatant(hp, hp + (i+7)%17, false);
01079 u[i]->set_weapon((i % 4) + 1, (i % 9) == 0, (i % 5) == 0,
01080 ((i+4) % 4) == 0,
01081 ((i+3) % 5) == 0);
01082 u[i]->set_effectiveness((i % 7) + 2, 0.3 + (i % 6)*0.1, (i % 8) == 0);
01083 }
01084
01085 gettimeofday(&start, NULL);
01086 for (i = 0; i < NUM_UNITS; ++i) {
01087 for (j = 0; j < NUM_UNITS; ++j) {
01088 if (i == j)
01089 continue;
01090 for (k = 0; k < NUM_UNITS; ++k) {
01091 if (i == k || j == k)
01092 continue;
01093 ++battle;
01094 if (specific_battle && battle != specific_battle)
01095 continue;
01096 u[j]->fight(*u[i]);
01097
01098
01099 u[i]->set_effectiveness((i % 7) + 2, 0.3 + (i % 6)*0.1,
01100 (i % 8) == 0);
01101 u[k]->fight(*u[i]);
01102 u[i]->print("Defender", battle);
01103 u[j]->print("Attacker #1", battle);
01104 u[k]->print("Attacker #2", battle);
01105 u[i]->reset();
01106 u[j]->reset();
01107 u[k]->reset();
01108 }
01109 }
01110 }
01111 gettimeofday(&end, NULL);
01112
01113 timer_sub(&end, &start, &total);
01114
01115 #ifdef BENCHMARK
01116 printf("Total time for %i combats was %lu.%06lu\n",
01117 NUM_UNITS*(NUM_UNITS-1)*(NUM_UNITS-2), total.tv_sec, total.tv_usec);
01118 printf("Time per calc = %li us\n",
01119 ((end.tv_sec-start.tv_sec)*1000000 + (end.tv_usec-start.tv_usec))
01120 / (NUM_UNITS*(NUM_UNITS-1)*(NUM_UNITS-2)));
01121 #else
01122 printf("Total combats: %i\n", NUM_UNITS*(NUM_UNITS-1)*(NUM_UNITS-2));
01123 #endif
01124
01125 for (i = 0; i < NUM_UNITS; ++i) {
01126 delete u[i];
01127 }
01128
01129 exit(0);
01130 }
01131
01132 static combatant *parse_unit(char ***argv,
01133 unsigned *damagep = NULL,
01134 double *hit_chancep = NULL,
01135 bool *slowsp = NULL)
01136 {
01137 unsigned damage, num_attacks, hp, max_hp, hit_chance;
01138 bool slows, slowed, drains, berserk, swarm, firststrike;
01139 combatant *u;
01140
01141 damage = atoi((*argv)[1]);
01142 num_attacks = atoi((*argv)[2]);
01143 hp = max_hp = atoi((*argv)[3]);
01144 hit_chance = atoi((*argv)[4]);
01145 slows = false;
01146 slowed = false;
01147 drains = false;
01148 berserk = false;
01149 swarm = false;
01150 firststrike = false;
01151
01152 if (damagep)
01153 *damagep = damage;
01154 if (hit_chancep)
01155 *hit_chancep = hit_chance/100.0;
01156 if (slowsp)
01157 *slowsp = slows;
01158
01159 if ((*argv)[5] && atoi((*argv)[5]) == 0) {
01160 char *max = strstr((*argv)[5], "maxhp=");
01161
01162 if (max) {
01163 max_hp = atoi(max + strlen("maxhp="));
01164 if (max_hp < hp) {
01165 fprintf(stderr, "maxhp must be > hitpoints");
01166 exit(1);
01167 }
01168 }
01169 if (strstr((*argv)[5], "drain")) {
01170 if (!max) {
01171 fprintf(stderr, "drain needs maxhp set");
01172 exit(1);
01173 }
01174 drains = true;
01175 }
01176 if (strstr((*argv)[5], "slows"))
01177 slows = true;
01178 if (strstr((*argv)[5], "slowed"))
01179 slowed = true;
01180 if (strstr((*argv)[5], "berserk"))
01181 berserk = true;
01182 if (strstr((*argv)[5], "firststrike"))
01183 firststrike = true;
01184 if (strstr((*argv)[5], "swarm")) {
01185 if (!max) {
01186 fprintf(stderr, "swarm needs maxhp set");
01187 exit(1);
01188 }
01189 swarm = true;
01190 }
01191 *argv += 5;
01192 } else {
01193 *argv += 4;
01194 }
01195 u = new combatant(hp, max_hp, slowed, true);
01196 u->set_weapon(num_attacks, drains, berserk, swarm, firststrike);
01197 u->set_effectiveness(damage, hit_chance/100.0, slows);
01198 return u;
01199 }
01200
01201 int main(int argc, char *argv[])
01202 {
01203 combatant *def, *att[20];
01204 double hit_chance;
01205 unsigned damage;
01206 bool slows;
01207 unsigned int i;
01208
01209 if (argc < 3)
01210 run(argv[1] ? atoi(argv[1]) : 0);
01211
01212 if (argc < 9) {
01213 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>] ...",
01214 argv[0]);
01215 exit(1);
01216 }
01217
01218 def = parse_unit(&argv, &damage, &hit_chance, &slows);
01219 for (i = 0; argv[1]; ++i)
01220 att[i] = parse_unit(&argv);
01221 att[i] = NULL;
01222
01223 for (i = 0; att[i]; ++i) {
01224
01225 debug(("Fighting next attacker\n"));
01226 def->set_effectiveness(damage, hit_chance, slows);
01227 att[i]->fight(*def);
01228 }
01229
01230 def->print("Defender", 0);
01231 for (i = 0; att[i]; ++i)
01232 att[i]->print("Attacker", 0);
01233
01234 delete def;
01235 for (i = 0; att[i]; ++i)
01236 delete att[i];
01237
01238 return 0;
01239 }
01240 #endif // Standalone program