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