attack_prediction.cpp

Go to the documentation of this file.
00001 /* $Id: attack_prediction.cpp 53487 2012-03-10 12:24:37Z mordante $ */
00002 /*
00003    Copyright (C) 2006 - 2012 by Rusty Russell <rusty@rustcorp.com.au>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 
00015    Full algorithm by Yogin.  Typing and optimization by Rusty.
00016 
00017    This code has lots of debugging.  It is there for a reason:
00018    this code is kinda tricky.  Do not remove it.
00019 */
00020 
00021 /**
00022  * @file
00023  * Simulate combat to calculate attacks. Standalone program, benchmark.
00024  */
00025 
00026 #include <cfloat>
00027 
00028 #include "attack_prediction.hpp"
00029 
00030 #include "actions.hpp"
00031 #include "game_config.hpp"
00032 
00033 // Compile with -O3 -DBENCHMARK for speed testing,
00034 // -DCHECK for testing correctness
00035 // (run tools/wesnoth-attack-sim.c --check on output)
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 /** A matrix of A's hitpoints vs B's hitpoints. */
00052 struct prob_matrix
00053 {
00054     // Simple matrix, both known HP.
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     // A hits B.
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     // B hits A.  Why can't they just get along?
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     // We lied: actually did less damage, adjust matrix.
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     // Its over, and here's the bill.
00082     void extract_results(std::vector<double> summary_a[2],
00083                          std::vector<double> summary_b[2]);
00084 
00085     // What's the chance one is dead?
00086     double dead_prob() const;
00087 
00088     void dump() const;
00089 
00090     // We need four matrices, or "planes", reflecting the possible
00091     // "slowed" states (neither slowed, A slowed, B slowed, both slowed).
00092     enum {
00093         NEITHER_SLOWED,
00094         A_SLOWED,
00095         B_SLOWED,
00096         BOTH_SLOWED
00097     };
00098 
00099 private:
00100     // This gives me 10% speed improvement over std::vector<> (g++4.0.3 x86)
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     // Move this much from src to dst.  Returns true if anything transferred.
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     // Shift columns on this plane (b taking damage).  Returns min col.
00113     void shift_cols(unsigned dst, unsigned src,
00114                     unsigned damage, double prob, int drain_constant, int drain_percent);
00115 
00116     // Shift rows on this plane (a taking damage).  Returns new min row.
00117     void shift_rows(unsigned dst, unsigned src,
00118                     unsigned damage, double prob, int drain_constant, int drain_percent);
00119 
00120     /** @todo FIXME: rename using _ at end. */
00121     unsigned int rows_, cols_;
00122     double *plane_[4];
00123 
00124     // For optimization, we keep track of the lower row/col we need to consider
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         // A has fought before.  Do we need a slow plane for it?
00137         if (!a_summary[1].empty())
00138             b_slows = true;
00139         // Don't handle both being reused.
00140         assert(b_summary[0].empty());
00141     }
00142     if (!b_summary[0].empty()) {
00143         // B has fought before.  Do we need a slow plane for it?
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     // Transfer HP distribution from A?
00168     if (!a_summary[0].empty()) {
00169         // @todo FIXME: Can optimize here.
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         // If a unit has drain it might end with more HP than before.
00195         // Make sure we don't access the matrix in invalid positions.
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 // Allocate a new probability array, initialized to 0.
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 // xfer, shift_cols and shift_rows use up most of our time.  Careful!
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         // This is here for drain.
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     // Killing blows can have strange drain amounts, so handle them first
00304     for (row = min_row_[src] + 1; row < rows_; row++) {
00305         // These are all going to die (move to col 0).
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     // Loop downwards if we drain positive, but upwards if we drain negative,
00313     // so we write behind us (for when src == dst).
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     // Killing blows can have strange drain amounts, so handle them first
00343     for (col = min_col_[src] + 1; col < cols_; col++) {
00344         // These are all going to die (move to row 0).
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     // Loop downwards if we drain positive, but upwards if we drain negative,
00352     // so we write behind us (for when src == dst).
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 // Shift prob_matrix to reflect probability 'hit_chance'
00369 // that damage (up to) 'damage' is done to 'b'.
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     // Walk backwards so we don't copy already-copied matrix planes.
00376     for (src = 3; src >=0; src--) {
00377         unsigned int actual_damage;
00378 
00379         if (!plane_[src])
00380             continue;
00381 
00382         // If A slows us, we go from 0=>2, 1=>3, 2=>2 3=>3.
00383         if (a_slows)
00384             dst = (src|2);
00385         else
00386             dst = src;
00387 
00388         // A is slow in planes 1 and 3.
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 // We lied: actually did less damage, adjust matrix.
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         // A is slow in planes 1 and 3.
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         // B is slow in planes 2 and 3.
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         // A is slow in planes 1 and 3.
00473         dst_a = (p & 1);
00474         // B is slow in planes 2 and 3.
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 // What's the chance one is dead?
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         // We might count 0,0 twice, but that is always 0 anyway.
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 // Shift matrix to reflect probability 'hit_chance'
00504 // that damage (up to) 'damage' is done to 'a'.
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     // Walk backwards so we don't copy already-copied matrix planes.
00511     for (src = 3; src >=0; src--) {
00512         unsigned actual_damage;
00513 
00514         if (!plane_[src])
00515             continue;
00516 
00517         // If B slows us, we go from 0=>1, 1=>1, 2=>3 3=>3.
00518         if (b_slows)
00519             dst = (src|1);
00520         else
00521             dst = src;
00522 
00523         // B is slow in planes 2 and 3.
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     /* Move all the values (except 0hp) of all the planes to the last
00551        row of the planes unslowed for A. */
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     /* Move all the values (except 0hp) of all the planes to the last
00567        column of planes unslowed for B. */
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     /* Move the values of the first column (except 0hp) of all the
00583        planes to the last row of the planes unslowed for A. */
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     /* Move the values of the first row (except 0hp) of all the
00597        planes to the last column of the planes unslowed for B. */
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 } // end anon namespace
00609 
00610 unsigned combatant::hp_dist_size(const battle_context_unit_stats &u, const combatant *prev)
00611 {
00612     // Our summary must be as big as previous one.
00613     if (prev) {
00614         return prev->hp_dist.size();
00615     }
00616 
00617     // If this unit drains, HP can increase, so alloc full array.
00618     // Do this anyway in case we level up.
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     // We inherit current state from previous combatant.
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 // Copy constructor (except use this copy of battle_context_unit_stats)
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 // For swarm, whether we get an attack depends on HP distribution
00655 // from previous combat.  So we roll this into our P(hitting),
00656 // since no attack is equivalent to missing.
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 // Minimum HP we could possibly have.
00691 unsigned combatant::min_hp() const
00692 {
00693     if (summary[0].empty())
00694         return u_.hp;
00695 
00696     // We don't handle this (yet).
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     /* If we survive the combat, we will level up. So the probability
00707        of death is unchanged, but all other cases get merged into the
00708        fully healed case. */
00709     std::vector<double>::iterator i = hp_dist.begin();
00710     ++i; // Skip to the second value.
00711     for (; i != hp_dist.end(); ++i) *i = 0;
00712     // Full heal unless dead.
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     /* If we kill, we will level up. So then the damage we had becomes
00719        less probable since it's now conditional on us not levelling up.
00720        This doesn't apply to the probability of us dying, of course. */
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; // Skip to the second value.
00726     for (; i != hp_dist.end(); ++i) *i *= scalefactor;
00727     // Full heal if leveled up.
00728     hp_dist.back() += kill_prob;
00729 }
00730 
00731 // Combat without chance of death, berserk, slow or drain is simple.
00732 void combatant::no_death_fight(combatant &opp, bool levelup_considered)
00733 {
00734     if (summary[0].empty()) {
00735         // Starts with a known HP, so Pascal's triangle.
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         // HP could be spread anywhere, iterate through whole thing.
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         // Starts with a known HP, so Pascal's triangle.
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         // HP could be spread anywhere, iterate through whole thing.
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 // Combat with <= 1 strike each is simple, too.
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     // If we killed opponent, it won't attack us.
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     // To simulate stoning, we set to amount which kills, and re-adjust after.
00861     /** @todo FIXME: This doesn't work for rolling calculations, just first battle. */
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     // We extract results separately, then combine.
00907     m.extract_results(summary, opp.summary);
00908 }
00909 
00910 // Two man enter.  One man leave!
00911 // ... Or maybe two.  But definitely not three.
00912 // Of course, one could be a woman.  Or both.
00913 // And neither could be human, too.
00914 // Um, ok, it was a stupid thing to say.
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     // If defender has firststrike and we don't, reverse.
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     // If we've fought before and we have swarm, we must adjust cth array.
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     // Optimize the simple cases.
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     // Combine summary into distribution.
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     // Make sure we don't try to access the vectors out of bounds,
00992     // drain increases HPs so we determine the number of HP here
00993     // and make sure it stays within bounds
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     // Chance that we / they were touched this time.
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     /** @todo FIXME: This is approximate: we could drain, then get hit. */
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     // Since sum of probabilities is 1.0, we can just tally weights.
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 // We create a significant number of nasty-to-calculate units,
01028 // and test each one against the others.
01029 #define NUM_UNITS 50
01030 
01031 // Stolen from glibc headers sys/time.h
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     // N^2 battles
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                 // We need this here, because swarm means
01098                 // out num hits can change.
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         // In case defender has swarm, effectiveness changes.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:34 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs