43 #if defined(BENCHMARK) || defined(CHECK)
56 #ifdef ATTACK_PREDICTION_DEBUG
57 #define debug(x) printf x
62 #ifdef ATTACK_PREDICTION_DEBUG
68 printf(
"==================================\n");
69 printf(
"is_attacker: %d\n", static_cast<int>(stats.
is_attacker));
70 printf(
"is_poisoned: %d\n", static_cast<int>(stats.
is_poisoned));
71 printf(
"is_slowed: %d\n", static_cast<int>(stats.
is_slowed));
72 printf(
"slows: %d\n", static_cast<int>(stats.
slows));
73 printf(
"drains: %d\n", static_cast<int>(stats.
drains));
74 printf(
"petrifies: %d\n", static_cast<int>(stats.
petrifies));
75 printf(
"poisons: %d\n", static_cast<int>(stats.
poisons));
76 printf(
"backstab_pos: %d\n", static_cast<int>(stats.
backstab_pos));
77 printf(
"swarm: %d\n", static_cast<int>(stats.
swarm));
78 printf(
"rounds: %u\n", stats.
rounds);
79 printf(
"firststrike: %d\n", static_cast<int>(stats.
firststrike));
81 printf(
"hp: %u\n", stats.
hp);
82 printf(
"max_hp: %u\n", stats.
max_hp);
84 printf(
"damage: %d\n", stats.
damage);
88 printf(
"num_blows: %u\n", stats.
num_blows);
89 printf(
"swarm_min: %u\n", stats.
swarm_min);
90 printf(
"swarm_max: %u\n", stats.
swarm_max);
103 const T&
limit(
const T&
val,
const T& min,
const T& max)
123 prob_matrix(
unsigned int a_max,
unsigned int b_max,
124 bool need_a_slowed,
bool need_b_slowed,
125 unsigned int a_cur,
unsigned int b_cur,
126 const std::vector<double> a_initial[2],
127 const std::vector<double> b_initial[2]);
132 void shift_cols(
unsigned dst,
unsigned src,
unsigned damage,
133 double prob,
int drain_constant,
int drain_percent);
135 void shift_rows(
unsigned dst,
unsigned src,
unsigned damage,
136 double prob,
int drain_constant,
int drain_percent);
139 void move_column(
unsigned d_plane,
unsigned s_plane,
140 unsigned d_col,
unsigned s_col);
142 void move_row(
unsigned d_plane,
unsigned s_plane,
143 unsigned d_row,
unsigned s_row);
146 void merge_col(
unsigned d_plane,
unsigned s_plane,
unsigned col,
unsigned d_row);
147 void merge_cols(
unsigned d_plane,
unsigned s_plane,
unsigned d_row);
148 void merge_row(
unsigned d_plane,
unsigned s_plane,
unsigned row,
unsigned d_col);
149 void merge_rows(
unsigned d_plane,
unsigned s_plane,
unsigned d_col);
152 double prob_of_zero(
bool check_a,
bool check_b)
const;
154 void sum(
unsigned plane, std::vector<double> & row_sums,
155 std::vector<double> & col_sums)
const;
158 bool plane_used(
unsigned p)
const {
return p < NUM_PLANES && plane_[
p] !=
nullptr; }
160 unsigned int num_rows()
const {
return rows_; }
161 unsigned int num_cols()
const {
return cols_; }
180 void initialize_plane(
unsigned plane,
unsigned a_cur,
unsigned b_cur,
181 const std::vector<double> & a_initial,
182 const std::vector<double> & b_initial);
183 void initialize_row(
unsigned plane,
unsigned row,
double row_prob,
184 unsigned b_cur,
const std::vector<double> & b_initial);
186 double &
val(
unsigned plane,
unsigned row,
unsigned col);
187 const double &
val(
unsigned plane,
unsigned row,
unsigned col)
const;
190 void xfer(
unsigned dst_plane,
unsigned src_plane,
191 unsigned row_dst,
unsigned col_dst,
192 unsigned row_src,
unsigned col_src,
195 void xfer(
unsigned dst_plane,
unsigned src_plane,
196 unsigned row_dst,
unsigned col_dst,
197 unsigned row_src,
unsigned col_src);
199 void shift_cols_in_row(
unsigned dst,
unsigned src,
unsigned row,
200 const std::vector<unsigned> & cols,
201 unsigned damage,
double prob,
int drainmax,
202 int drain_constant,
int drain_percent);
203 void shift_rows_in_col(
unsigned dst,
unsigned src,
unsigned col,
204 const std::vector<unsigned> & rows,
205 unsigned damage,
double prob,
int drainmax,
206 int drain_constant,
int drain_percent);
209 const unsigned int rows_, cols_;
210 std::array<double *, NUM_PLANES> plane_;
214 std::array<std::set<unsigned>, NUM_PLANES> used_rows_, used_cols_;
229 prob_matrix::prob_matrix(
unsigned int a_max,
unsigned int b_max,
230 bool need_a_slowed,
bool need_b_slowed,
231 unsigned int a_cur,
unsigned int b_cur,
232 const std::vector<double> a_initial[2],
233 const std::vector<double> b_initial[2])
241 a_cur = std::min<unsigned int>(a_cur, rows_ - 1);
242 b_cur = std::min<unsigned int>(b_cur, cols_ - 1);
245 for (
unsigned plane = 0; plane != NUM_PLANES; ++plane ) {
246 used_rows_[plane].insert(0u);
247 used_cols_[plane].insert(0u);
251 need_a_slowed = need_a_slowed || !a_initial[1].empty();
252 need_b_slowed = need_b_slowed || !b_initial[1].empty();
255 plane_[NEITHER_SLOWED] = new_plane();
256 plane_[A_SLOWED] = !need_a_slowed ?
nullptr : new_plane();
257 plane_[B_SLOWED] = !need_b_slowed ?
nullptr : new_plane();
258 plane_[BOTH_SLOWED] = !(need_a_slowed && need_b_slowed) ?
nullptr : new_plane();
261 initialize_plane(NEITHER_SLOWED, a_cur, b_cur, a_initial[0], b_initial[0]);
262 if ( !a_initial[1].empty() )
263 initialize_plane(A_SLOWED, a_cur, b_cur, a_initial[1], b_initial[0]);
264 if ( !b_initial[1].empty() )
265 initialize_plane(B_SLOWED, a_cur, b_cur, a_initial[0], b_initial[1]);
266 if ( !a_initial[1].empty() && !b_initial[1].empty() )
268 initialize_plane(BOTH_SLOWED, a_cur, b_cur, a_initial[1], b_initial[1]);
271 if ( !a_initial[0].empty() ) {
272 debug((
"A has fought before.\n"));
275 else if ( !b_initial[0].empty() ) {
276 debug((
"B has fought before.\n"));
281 prob_matrix::~prob_matrix()
283 delete[] plane_[NEITHER_SLOWED];
284 delete[] plane_[A_SLOWED];
285 delete[] plane_[B_SLOWED];
286 delete[] plane_[BOTH_SLOWED];
290 double *prob_matrix::new_plane()
292 unsigned int size = rows_ * cols_;
293 double *arr =
new double[
size];
294 memset(arr, 0,
sizeof(
double) * size);
307 void prob_matrix::initialize_plane(
unsigned plane,
unsigned a_cur,
unsigned b_cur,
308 const std::vector<double> & a_initial,
309 const std::vector<double> & b_initial)
311 if ( !a_initial.empty() ) {
312 unsigned row_count = std::min<unsigned>(a_initial.size(), rows_);
314 for (
unsigned row = 0;
row < row_count; ++
row ) {
315 if ( a_initial[
row] != 0.0 ) {
316 used_rows_[plane].insert(
row);
317 initialize_row(plane,
row, a_initial[
row], b_cur, b_initial);
322 used_rows_[plane].insert(a_cur);
324 initialize_row(plane, a_cur, 1.0, b_cur, b_initial);
337 void prob_matrix::initialize_row(
unsigned plane,
unsigned row,
double row_prob,
338 unsigned b_cur,
const std::vector<double> & b_initial)
340 if ( !b_initial.empty() ) {
341 unsigned col_count = std::min<unsigned>(b_initial.size(), cols_);
343 for (
unsigned col = 0; col < col_count; ++col ) {
344 if ( b_initial[col] != 0.0 ) {
345 used_cols_[plane].insert(col);
346 val(plane, row, col) = row_prob * b_initial[col];
352 used_cols_[plane].insert(b_cur);
353 val(plane, row, b_cur) = row_prob;
361 return plane_[
p][row * cols_ + col];
368 return plane_[
p][row * cols_ + col];
375 void prob_matrix::xfer(
unsigned dst_plane,
unsigned src_plane,
376 unsigned row_dst,
unsigned col_dst,
377 unsigned row_src,
unsigned col_src,
380 double &
src =
val(src_plane, row_src, col_src);
382 double diff = src * prob;
385 double &
dst =
val(dst_plane, row_dst, col_dst);
388 used_rows_[dst_plane].insert(row_dst);
389 used_cols_[dst_plane].insert(col_dst);
393 debug((
"Shifted %4.3g from %s(%u,%u) to %s(%u,%u).\n",
394 diff, src_plane == NEITHER_SLOWED ?
""
395 : src_plane == A_SLOWED ?
"[A_SLOWED]"
396 : src_plane == B_SLOWED ?
"[B_SLOWED]"
397 : src_plane == BOTH_SLOWED ?
"[BOTH_SLOWED]" :
"INVALID",
399 dst_plane == NEITHER_SLOWED ?
""
400 : dst_plane == A_SLOWED ?
"[A_SLOWED]"
401 : dst_plane == B_SLOWED ?
"[B_SLOWED]"
402 : dst_plane == BOTH_SLOWED ?
"[BOTH_SLOWED]" :
"INVALID",
410 void prob_matrix::xfer(
unsigned dst_plane,
unsigned src_plane,
411 unsigned row_dst,
unsigned col_dst,
412 unsigned row_src,
unsigned col_src)
414 if ( dst_plane == src_plane && row_dst == row_src && col_dst == col_src )
418 double &src =
val(src_plane, row_src, col_src);
420 debug((
"Shifting %4.3g from %s(%u,%u) to %s(%u,%u).\n",
421 src, src_plane == NEITHER_SLOWED ?
""
422 : src_plane == A_SLOWED ?
"[A_SLOWED]"
423 : src_plane == B_SLOWED ?
"[B_SLOWED]"
424 : src_plane == BOTH_SLOWED ?
"[BOTH_SLOWED]" :
"INVALID",
426 dst_plane == NEITHER_SLOWED ?
""
427 : dst_plane == A_SLOWED ?
"[A_SLOWED]"
428 : dst_plane == B_SLOWED ?
"[B_SLOWED]"
429 : dst_plane == BOTH_SLOWED ?
"[BOTH_SLOWED]" :
"INVALID",
432 double &dst =
val(dst_plane, row_dst, col_dst);
435 used_rows_[dst_plane].insert(row_dst);
436 used_cols_[dst_plane].insert(col_dst);
447 void prob_matrix::shift_cols_in_row(
unsigned dst,
unsigned src,
unsigned row,
448 const std::vector<unsigned> & cols,
449 unsigned damage,
double prob,
int drainmax,
450 int drain_constant,
int drain_percent)
453 int row_i =
static_cast<int>(
row);
454 int max_row =
static_cast<int>(rows_) - 1;
460 for ( ; col_x < cols.size() && cols[col_x] < damage; ++col_x ) {
463 int col_i =
static_cast<int>(cols[col_x]);
464 int drain_amount = col_i*drain_percent/100 + drain_constant;
465 unsigned newrow =
limit<int>(row_i + drain_amount, 1, max_row);
466 xfer(dst, src, newrow, 0, row, cols[col_x], prob);
470 unsigned newrow =
limit<int>(row_i + drainmax, 1, max_row);
471 for ( ; col_x < cols.size(); ++col_x )
472 xfer(dst, src, newrow, cols[col_x] - damage, row, cols[col_x], prob);
481 void prob_matrix::shift_cols(
unsigned dst,
unsigned src,
unsigned damage,
482 double prob,
int drain_constant,
int drain_percent)
484 int drainmax = (drain_percent*(
static_cast<signed>(damage))/100+drain_constant);
486 if(drain_constant || drain_percent) {
487 debug((
"Drains %i (%i%% of %u plus %i)\n", drainmax, drain_percent, damage, drain_constant));
492 const std::vector<unsigned> rows(used_rows_[src].begin(), used_rows_[src].
end());
493 const std::vector<unsigned> cols(used_cols_[src].begin(), used_cols_[src].
end());
499 for (
unsigned row_x = rows.size()-1; row_x != 0; --row_x )
500 shift_cols_in_row(dst, src, rows[row_x], cols, damage, prob, drainmax,
501 drain_constant, drain_percent);
504 for (
unsigned row_x = 1; row_x != rows.size(); ++row_x )
505 shift_cols_in_row(dst, src, rows[row_x], cols, damage, prob, drainmax,
506 drain_constant, drain_percent);
514 void prob_matrix::shift_rows_in_col(
unsigned dst,
unsigned src,
unsigned col,
515 const std::vector<unsigned> & rows,
516 unsigned damage,
double prob,
int drainmax,
517 int drain_constant,
int drain_percent)
520 int col_i =
static_cast<int>(col);
521 int max_col =
static_cast<int>(cols_) - 1;
527 for ( ; row_x < rows.size() && rows[row_x] < damage; ++row_x ) {
530 int row_i =
static_cast<int>(rows[row_x]);
531 int drain_amount = row_i*drain_percent/100 + drain_constant;
532 unsigned newcol =
limit<int>(col_i + drain_amount, 1, max_col);
533 xfer(dst, src, 0, newcol, rows[row_x], col, prob);
537 unsigned newcol =
limit<int>(col_i + drainmax, 1, max_col);
538 for ( ; row_x < rows.size(); ++row_x )
539 xfer(dst, src, rows[row_x] - damage, newcol, rows[row_x], col, prob);
548 void prob_matrix::shift_rows(
unsigned dst,
unsigned src,
unsigned damage,
549 double prob,
int drain_constant,
int drain_percent)
551 int drainmax = (drain_percent*(
static_cast<signed>(damage))/100+drain_constant);
553 if(drain_constant || drain_percent) {
554 debug((
"Drains %i (%i%% of %u plus %i)\n", drainmax, drain_percent, damage, drain_constant));
559 const std::vector<unsigned> rows(used_rows_[src].begin(), used_rows_[src].
end());
560 const std::vector<unsigned> cols(used_cols_[src].begin(), used_cols_[src].
end());
566 for (
unsigned col_x = cols.size()-1; col_x != 0; --col_x )
567 shift_rows_in_col(dst, src, cols[col_x], rows, damage, prob, drainmax,
568 drain_constant, drain_percent);
571 for (
unsigned col_x = 1; col_x != cols.size(); ++col_x )
572 shift_rows_in_col(dst, src, cols[col_x], rows, damage, prob, drainmax,
573 drain_constant, drain_percent);
580 void prob_matrix::move_column(
unsigned d_plane,
unsigned s_plane,
581 unsigned d_col,
unsigned s_col)
583 std::set<unsigned>::const_iterator rows_end = used_rows_[s_plane].end();
584 std::set<unsigned>::const_iterator row_it = used_rows_[s_plane].begin();
587 for ( ; row_it != rows_end; ++row_it )
588 xfer(d_plane, s_plane, *row_it, d_col, *row_it, s_col);
594 void prob_matrix::move_row(
unsigned d_plane,
unsigned s_plane,
595 unsigned d_row,
unsigned s_row)
597 std::set<unsigned>::const_iterator cols_end = used_cols_[s_plane].end();
598 std::set<unsigned>::const_iterator col_it = used_cols_[s_plane].begin();
601 for ( ; col_it != cols_end; ++col_it )
602 xfer(d_plane, s_plane, d_row, *col_it, s_row, *col_it);
609 void prob_matrix::merge_col(
unsigned d_plane,
unsigned s_plane,
unsigned col,
612 std::set<unsigned>::const_iterator rows_end = used_rows_[s_plane].end();
613 std::set<unsigned>::const_iterator row_it = used_rows_[s_plane].begin();
616 for ( ++row_it; row_it != rows_end; ++row_it )
617 xfer(d_plane, s_plane, d_row, col, *row_it, col);
624 void prob_matrix::merge_cols(
unsigned d_plane,
unsigned s_plane,
unsigned d_row)
626 std::set<unsigned>::const_iterator rows_end = used_rows_[s_plane].end();
627 std::set<unsigned>::const_iterator row_it = used_rows_[s_plane].begin();
628 std::set<unsigned>::const_iterator cols_end = used_cols_[s_plane].end();
629 std::set<unsigned>::const_iterator cols_begin = used_cols_[s_plane].begin();
630 std::set<unsigned>::const_iterator col_it;
633 for ( ++row_it; row_it != rows_end; ++row_it )
634 for ( col_it = cols_begin; col_it != cols_end; ++col_it )
635 xfer(d_plane, s_plane, d_row, *col_it, *row_it, *col_it);
642 void prob_matrix::merge_row(
unsigned d_plane,
unsigned s_plane,
unsigned row,
645 std::set<unsigned>::const_iterator cols_end = used_cols_[s_plane].end();
646 std::set<unsigned>::const_iterator col_it = used_cols_[s_plane].begin();
649 for ( ++col_it; col_it != cols_end; ++col_it )
650 xfer(d_plane, s_plane, row, d_col, row, *col_it);
658 void prob_matrix::merge_rows(
unsigned d_plane,
unsigned s_plane,
unsigned d_col)
660 std::set<unsigned>::const_iterator rows_end = used_rows_[s_plane].end();
661 std::set<unsigned>::const_iterator row_it = used_rows_[s_plane].begin();
662 std::set<unsigned>::const_iterator cols_end = used_cols_[s_plane].end();
663 std::set<unsigned>::const_iterator cols_begin = used_cols_[s_plane].begin();
666 std::set<unsigned>::const_iterator col_it;
669 for ( ; row_it != rows_end; ++row_it )
670 for ( col_it = cols_begin; col_it != cols_end; ++col_it )
671 xfer(d_plane, s_plane, *row_it, d_col, *row_it, *col_it);
677 double prob_matrix::prob_of_zero(
bool check_a,
bool check_b)
const
681 for (
unsigned p = 0;
p < NUM_PLANES; ++
p) {
682 if ( !plane_used(
p) )
687 std::set<unsigned>::const_iterator rows_end = used_rows_[
p].end();
688 std::set<unsigned>::const_iterator row_it = used_rows_[
p].begin();
689 for ( ; row_it != rows_end; ++row_it )
690 prob +=
val(
p, *row_it, 0);
694 std::set<unsigned>::const_iterator cols_end = used_cols_[
p].end();
695 std::set<unsigned>::const_iterator col_it = used_cols_[
p].begin();
696 for ( ; col_it != cols_end; ++col_it )
697 prob +=
val(
p, 0, *col_it);
710 void prob_matrix::sum(
unsigned plane, std::vector<double> & row_sums,
711 std::vector<double> & col_sums)
const
713 std::set<unsigned>::const_iterator rows_end = used_rows_[plane].end();
714 std::set<unsigned>::const_iterator row_it = used_rows_[plane].begin();
715 std::set<unsigned>::const_iterator cols_end = used_cols_[plane].end();
716 std::set<unsigned>::const_iterator cols_begin = used_cols_[plane].begin();
717 std::set<unsigned>::const_iterator col_it;
719 for ( ; row_it != rows_end; ++row_it )
720 for ( col_it = cols_begin; col_it != cols_end; ++col_it ) {
721 const double & prob =
val(plane, *row_it, *col_it);
722 row_sums[*row_it] += prob;
723 col_sums[*col_it] += prob;
727 #if defined(CHECK) && defined(ATTACK_PREDICTION_DEBUG)
728 void prob_matrix::dump()
const
730 unsigned int row, col,
m;
732 = {
"NEITHER_SLOWED",
"A_SLOWED",
"B_SLOWED",
"BOTH_SLOWED" };
734 for (m = 0; m < NUM_PLANES; ++
m) {
735 if ( !plane_used(m) )
737 debug((
"%s:\n", names[m]));
738 for (row = 0; row < rows_; ++
row) {
740 for (col = 0; col < cols_; ++col)
741 debug((
"%4.3g ",
val(m, row, col)*100));
747 void prob_matrix::dump()
const
759 class combat_matrix :
private prob_matrix
762 combat_matrix(
unsigned int a_max_hp,
unsigned int b_max_hp,
763 unsigned int a_hp,
unsigned int b_hp,
764 const std::vector<double> a_summary[2],
765 const std::vector<double> b_summary[2],
766 bool a_slows,
bool b_slows,
767 unsigned int a_damage,
unsigned int b_damage,
768 unsigned int a_slow_damage,
unsigned int b_slow_damage,
769 int a_drain_percent,
int b_drain_percent,
770 int a_drain_constant,
int b_drain_constant);
775 void receive_blow_b(
double hit_chance);
777 void receive_blow_a(
double hit_chance);
780 void remove_petrify_distortion_a(
unsigned damage,
unsigned slow_damage,
unsigned b_hp);
781 void remove_petrify_distortion_b(
unsigned damage,
unsigned slow_damage,
unsigned a_hp);
783 void forced_levelup_a();
784 void conditional_levelup_a();
786 void forced_levelup_b();
787 void conditional_levelup_b();
790 void extract_results(std::vector<double> summary_a[2],
791 std::vector<double> summary_b[2]);
794 double dead_prob()
const {
return prob_of_zero(
true,
true); }
796 double dead_prob_a()
const {
return prob_of_zero(
true,
false); }
798 double dead_prob_b()
const {
return prob_of_zero(
false,
true); }
800 void dump()
const { prob_matrix::dump(); }
806 unsigned a_slow_damage_;
807 int a_drain_percent_;
808 int a_drain_constant_;
813 unsigned b_slow_damage_;
814 int b_drain_percent_;
815 int b_drain_constant_;
830 combat_matrix::combat_matrix(
unsigned int a_max_hp,
unsigned int b_max_hp,
831 unsigned int a_hp,
unsigned int b_hp,
832 const std::vector<double> a_summary[2],
833 const std::vector<double> b_summary[2],
834 bool a_slows,
bool b_slows,
835 unsigned int a_damage,
unsigned int b_damage,
836 unsigned int a_slow_damage,
unsigned int b_slow_damage,
837 int a_drain_percent,
int b_drain_percent,
838 int a_drain_constant,
int b_drain_constant)
840 : prob_matrix(a_max_hp, b_max_hp, b_slows, a_slows, a_hp, b_hp, a_summary, b_summary),
842 a_max_hp_(a_max_hp), a_slows_(a_slows), a_damage_(a_damage), a_slow_damage_(a_slow_damage),
843 a_drain_percent_(a_drain_percent), a_drain_constant_(a_drain_constant),
845 b_max_hp_(b_max_hp), b_slows_(b_slows), b_damage_(b_damage), b_slow_damage_(b_slow_damage),
846 b_drain_percent_(b_drain_percent), b_drain_constant_(b_drain_constant)
852 void combat_matrix::receive_blow_b(
double hit_chance)
855 unsigned src = NUM_PLANES;
856 while ( src-- != 0 ) {
857 if ( !plane_used(src) )
861 int dst = a_slows_ ? src|2 :
src;
864 unsigned damage = src & 1 ? a_slow_damage_ : a_damage_;
866 shift_cols(dst, src, damage, hit_chance, a_drain_constant_, a_drain_percent_);
872 void combat_matrix::receive_blow_a(
double hit_chance)
875 unsigned src = NUM_PLANES;
876 while ( src-- != 0 ) {
877 if ( !plane_used(src) )
881 int dst = b_slows_ ? src|1 :
src;
884 unsigned damage = src & 2 ? b_slow_damage_ : b_damage_;
886 shift_rows(dst, src, damage, hit_chance, b_drain_constant_, b_drain_percent_);
891 void combat_matrix::remove_petrify_distortion_a(
unsigned damage,
unsigned slow_damage,
894 for (
int p = 0;
p < NUM_PLANES; ++
p) {
895 if ( !plane_used(
p) )
899 unsigned actual_damage = (
p & 1) ? slow_damage : damage;
900 if ( b_hp > actual_damage )
902 move_column(
p,
p, b_hp - actual_damage, 0);
906 void combat_matrix::remove_petrify_distortion_b(
unsigned damage,
unsigned slow_damage,
909 for (
int p = 0;
p < NUM_PLANES; ++
p) {
910 if ( !plane_used(
p) )
914 unsigned actual_damage = (
p & 2) ? slow_damage : damage;
915 if ( a_hp > actual_damage )
917 move_row(
p,
p, a_hp - actual_damage, 0);
921 void combat_matrix::forced_levelup_a()
925 for (
int p = 0;
p < NUM_PLANES; ++
p) {
927 merge_cols(
p & -2,
p, a_max_hp_);
931 void combat_matrix::forced_levelup_b()
935 for (
int p = 0;
p < NUM_PLANES; ++
p) {
937 merge_rows(
p & -3,
p, b_max_hp_);
941 void combat_matrix::conditional_levelup_a()
945 for (
int p = 0;
p < NUM_PLANES; ++
p) {
947 merge_col(
p & -2,
p, 0, a_max_hp_);
951 void combat_matrix::conditional_levelup_b()
955 for (
int p = 0;
p < NUM_PLANES; ++
p) {
957 merge_row(
p & -3,
p, 0, b_max_hp_);
961 void combat_matrix::extract_results(std::vector<double> summary_a[2],
962 std::vector<double> summary_b[2])
965 summary_a[0] = std::vector<double>(num_rows());
966 summary_b[0] = std::vector<double>(num_cols());
968 if ( plane_used(A_SLOWED) )
969 summary_a[1] = std::vector<double>(num_rows());
970 if ( plane_used(B_SLOWED) )
971 summary_b[1] = std::vector<double>(num_cols());
973 for (
unsigned p = 0;
p < NUM_PLANES; ++
p) {
974 if ( !plane_used(
p) )
978 unsigned dst_a = (
p & 1) ? 1u : 0u;
980 unsigned dst_b = (
p & 2) ? 1u : 0u;
981 sum(
p, summary_a[dst_a], summary_b[dst_b]);
1006 unsigned begin,
unsigned end,
unsigned num_strikes);
1007 combat_slice(
const std::vector<double> src_summary[2],
unsigned num_strikes);
1014 unsigned begin,
unsigned end,
1015 unsigned num_strikes) :
1019 strikes(num_strikes)
1021 if ( src_summary[0].empty() ) {
1028 if ( end > src_summary[0].
size() )
1029 end = src_summary[0].size();
1032 for (
unsigned i = begin;
i <
end; ++
i )
1033 prob += src_summary[0][
i];
1034 if ( !src_summary[1].empty() )
1035 for (
unsigned i = begin;
i <
end; ++
i )
1036 prob += src_summary[1][
i];
1045 unsigned num_strikes) :
1047 end_hp(src_summary[0].size()),
1049 strikes(num_strikes)
1093 unsigned hp_for_next_attack(
unsigned cur_hp,
1096 unsigned old_strikes = stats.
calc_blows(cur_hp);
1100 while ( ++cur_hp <= stats.
max_hp )
1101 if ( stats.
calc_blows(cur_hp) != old_strikes )
1114 std::vector<combat_slice>
result;
1123 debug((
"Slicing:\n"));
1125 unsigned cur_end = 0;
1128 const unsigned cur_begin = cur_end;
1129 cur_end = hp_for_next_attack(cur_begin,
u_);
1133 if ( slice.prob != 0.0 ) {
1134 result.push_back(slice);
1135 debug((
"\t%2u-%2u hp; strikes: %u; probability: %6.2f\n",
1136 cur_begin, cur_end, slice.strikes, slice.prob*100.0));
1146 void forced_levelup(std::vector<double> &hp_dist)
1153 for (; i != hp_dist.end(); ++
i) *i = 0;
1155 hp_dist.back() = 1 - hp_dist.front();
1158 void conditional_levelup(std::vector<double> &hp_dist,
double kill_prob)
1163 double scalefactor = 0;
1164 const double chance_to_survive = 1 - hp_dist.front();
1165 if(chance_to_survive > DBL_MIN) scalefactor = 1 - kill_prob / chance_to_survive;
1168 for (; i != hp_dist.end(); ++
i) *i *= scalefactor;
1170 hp_dist.back() += kill_prob;
1177 unsigned min_hp(
const std::vector<double> & hp_dist,
unsigned def)
1179 const unsigned size = hp_dist.size();
1182 for (
unsigned i = 0; i !=
size; ++
i )
1183 if ( hp_dist[i] != 0.0 )
1194 unsigned strikes,
unsigned opp_strikes,
1195 std::vector<double> & hp_dist,
1196 std::vector<double> & opp_hp_dist,
1197 double & self_not_hit,
double & opp_not_hit,
1198 bool levelup_considered)
1204 const double alive_prob = hp_dist.empty() ? 1.0 : 1.0 - hp_dist[0];
1205 const double hit_chance = (stats.
chance_to_hit/100.0) * alive_prob;
1206 if ( opp_hp_dist.empty() ) {
1208 opp_hp_dist = std::vector<double>(opp_stats.
max_hp+1);
1209 opp_hp_dist[opp_stats.
hp] = 1.0;
1210 for (
unsigned int i = 0; i < strikes; ++
i) {
1211 for (
int j = i; j >= 0; j--) {
1212 unsigned src_index = opp_stats.
hp - j*stats.
damage;
1213 double move = opp_hp_dist[src_index] * hit_chance;
1214 opp_hp_dist[src_index] -= move;
1215 opp_hp_dist[src_index - stats.
damage] += move;
1217 opp_not_hit *= 1.0 - hit_chance;
1221 for (
unsigned int i = 0; i < strikes; ++
i) {
1222 for (
unsigned int j = stats.
damage; j < opp_hp_dist.size(); ++j) {
1223 double move = opp_hp_dist[j] * hit_chance;
1224 opp_hp_dist[j] -= move;
1225 opp_hp_dist[j - stats.
damage] += move;
1227 opp_not_hit *= 1.0 - hit_chance;
1233 const double opp_alive_prob = opp_hp_dist.empty() ? 1.0 : 1.0 - opp_hp_dist[0];
1234 const double opp_hit_chance = (opp_stats.
chance_to_hit/100.0) * opp_alive_prob;
1235 if ( hp_dist.empty() ) {
1237 hp_dist = std::vector<double>(stats.
max_hp+1);
1238 hp_dist[stats.
hp] = 1.0;
1239 for (
unsigned int i = 0; i < opp_strikes; ++
i) {
1240 for (
int j = i; j >= 0; j--) {
1241 unsigned src_index = stats.
hp - j*opp_stats.
damage;
1242 double move = hp_dist[src_index] * opp_hit_chance;
1243 hp_dist[src_index] -= move;
1244 hp_dist[src_index - opp_stats.
damage] += move;
1246 self_not_hit *= 1.0 - opp_hit_chance;
1250 for (
unsigned int i = 0; i < opp_strikes; ++
i) {
1251 for (
unsigned int j = opp_stats.
damage; j < hp_dist.size(); ++j) {
1252 double move = hp_dist[j] * opp_hit_chance;
1254 hp_dist[j - opp_stats.
damage] += move;
1256 self_not_hit *= 1.0 - opp_hit_chance;
1260 if (!levelup_considered)
return;
1263 forced_levelup(hp_dist);
1267 forced_levelup(opp_hp_dist);
1274 unsigned strikes,
unsigned opp_strikes,
1275 std::vector<double> & hp_dist,
1276 std::vector<double> & opp_hp_dist,
1277 double & self_not_hit,
double & opp_not_hit,
1278 bool levelup_considered)
1283 const double alive_prob = hp_dist.empty() ? 1.0 : 1.0 - hp_dist[0];
1284 const double hit_chance = (stats.
chance_to_hit/100.0) * alive_prob;
1285 if ( opp_hp_dist.empty() ) {
1286 opp_hp_dist = std::vector<double>(opp_stats.
max_hp+1);
1287 if ( strikes == 1 ) {
1288 opp_hp_dist[opp_stats.
hp] = 1.0 - hit_chance;
1289 opp_hp_dist[std::max<int>(opp_stats.
hp - stats.
damage, 0)] = hit_chance;
1290 opp_not_hit *= 1.0 - hit_chance;
1292 assert(strikes == 0);
1293 opp_hp_dist[opp_stats.
hp] = 1.0;
1296 if ( strikes == 1 ) {
1297 for (
unsigned int i = 1; i < opp_hp_dist.size(); ++
i) {
1298 double move = opp_hp_dist[
i] * hit_chance;
1299 opp_hp_dist[
i] -= move;
1300 opp_hp_dist[std::max<int>(i - stats.
damage, 0)] += move;
1302 opp_not_hit *= 1.0 - hit_chance;
1307 const double opp_alive_prob = 1.0 - opp_hp_dist[0] / alive_prob;
1308 const double opp_hit_chance = (opp_stats.
chance_to_hit/100.0) * opp_alive_prob;
1309 if ( hp_dist.empty() ) {
1310 hp_dist = std::vector<double>(stats.
max_hp+1);
1311 if ( opp_strikes == 1 ) {
1312 hp_dist[stats.
hp] = 1.0 - opp_hit_chance;
1313 hp_dist[std::max<int>(stats.
hp - opp_stats.
damage, 0)] = opp_hit_chance;
1314 self_not_hit *= 1.0 - opp_hit_chance;
1316 assert(opp_strikes == 0);
1317 hp_dist[stats.
hp] = 1.0;
1320 if ( opp_strikes == 1) {
1321 for (
unsigned int i = 1; i < hp_dist.size(); ++
i) {
1322 double move = hp_dist[
i] * opp_hit_chance;
1324 hp_dist[std::max<int>(i - opp_stats.
damage, 0)] += move;
1326 self_not_hit *= 1.0 - opp_hit_chance;
1330 if (!levelup_considered)
return;
1333 forced_levelup(hp_dist);
1335 conditional_levelup(hp_dist, opp_hp_dist[0]);
1339 forced_levelup(opp_hp_dist);
1341 conditional_levelup(opp_hp_dist, hp_dist[0]);
1347 unsigned strikes,
unsigned opp_strikes,
1348 std::vector<double> summary[2],
1349 std::vector<double> opp_summary[2],
1350 double & self_not_hit,
double & opp_not_hit,
1351 bool levelup_considered)
1353 unsigned int rounds = std::max<unsigned int>(stats.
rounds, opp_stats.
rounds);
1354 unsigned max_attacks = std::max(strikes, opp_strikes);
1356 debug((
"A gets %u attacks, B %u.\n", strikes, opp_strikes));
1359 unsigned int b_damage = opp_stats.
damage, b_slow_damage = opp_stats.
slow_damage;
1365 a_damage = a_slow_damage = opp_stats.
max_hp;
1367 b_damage = b_slow_damage = stats.
max_hp;
1371 stats.
hp, opp_stats.
hp, summary, opp_summary,
1374 a_damage, b_damage, a_slow_damage, b_slow_damage,
1378 const double opp_hit_chance = opp_stats.
chance_to_hit / 100.0;
1381 for (
unsigned int i = 0; i < max_attacks; ++
i) {
1382 if ( i < strikes ) {
1383 debug((
"A strikes\n"));
1384 opp_not_hit *= 1.0 - hit_chance*(1.0-m.dead_prob_a());
1385 m.receive_blow_b(hit_chance);
1388 if ( i < opp_strikes ) {
1389 debug((
"B strikes\n"));
1390 self_not_hit *= 1.0 - opp_hit_chance*(1.0-m.dead_prob_b());
1391 m.receive_blow_a(opp_hit_chance);
1396 debug((
"Combat ends:\n"));
1398 }
while (--rounds && m.dead_prob() < 0.99);
1405 if (levelup_considered) {
1407 m.forced_levelup_a();
1409 m.conditional_levelup_a();
1413 m.forced_levelup_b();
1415 m.conditional_levelup_b();
1420 m.extract_results(summary, opp_summary);
1430 unsigned strikes,
unsigned opp_strikes,
1431 std::vector<double> summary[2], std::vector<double> opp_summary[2],
1432 double & self_not_hit,
double & opp_not_hit,
1433 bool levelup_considered)
1441 summary[1].empty() && opp_summary[1].empty() )
1443 if ( strikes <= 1 && opp_strikes <= 1 )
1444 one_strike_fight(stats, opp_stats, strikes, opp_strikes,
1445 summary[0], opp_summary[0], self_not_hit, opp_not_hit,
1446 levelup_considered);
1447 else if ( strikes*stats.
damage < min_hp(opp_summary[0], opp_stats.
hp) &&
1448 opp_strikes*opp_stats.
damage < min_hp(summary[0], stats.
hp) )
1449 no_death_fight(stats, opp_stats, strikes, opp_strikes,
1450 summary[0], opp_summary[0], self_not_hit, opp_not_hit,
1451 levelup_considered);
1453 complex_fight(stats, opp_stats, strikes, opp_strikes,
1454 summary, opp_summary, self_not_hit, opp_not_hit,
1455 levelup_considered);
1458 complex_fight(stats, opp_stats, strikes, opp_strikes,
1459 summary, opp_summary, self_not_hit, opp_not_hit,
1460 levelup_considered);
1468 void init_slice_summary(std::vector<double> & dst,
const std::vector<double> & src,
1469 unsigned begin_hp,
unsigned end_hp,
double prob)
1475 const unsigned size = src.size();
1477 if ( end_hp > size )
1481 dst.resize(size, 0.0);
1482 for (
unsigned i = begin_hp; i < end_hp; ++
i )
1483 dst[i] = src[i] / prob;
1490 void merge_slice_summary(std::vector<double> & dst,
const std::vector<double> & src,
1493 const unsigned size = src.size();
1496 if ( dst.size() <
size )
1497 dst.resize(size, 0.0);
1500 for (
unsigned i = 0; i !=
size; ++
i )
1501 dst[i] += src[i] * prob;
1515 opp.
fight(*
this, levelup_considered);
1519 #ifdef ATTACK_PREDICTION_DEBUG
1527 std::vector<double>
prev = summary[0], opp_prev = opp.
summary[0];
1528 complex_fight(opp, 1);
1529 std::vector<double>
res = summary[0], opp_res = opp.
summary[0];
1535 double self_not_hit = 1.0;
1536 double opp_not_hit = 1.0;
1541 const std::vector<combat_slice> opp_split = opp.
split_summary();
1543 if ( split.size() == 1 && opp_split.size() == 1 )
1546 summary, opp.
summary, self_not_hit, opp_not_hit,
1547 levelup_considered);
1551 std::vector<double> summary_result[2], opp_summary_result[2];
1557 for (
unsigned s = 0;
s != split.size(); ++
s )
1558 for (
unsigned t = 0;
t != opp_split.size(); ++
t ) {
1559 const double sit_prob = split[
s].prob * opp_split[
t].prob;
1562 std::vector<double> sit_summary[2], sit_opp_summary[2];
1563 init_slice_summary(sit_summary[0], summary[0], split[
s].begin_hp,
1564 split[
s].end_hp, split[
s].prob);
1565 init_slice_summary(sit_summary[1], summary[1], split[
s].begin_hp,
1566 split[
s].end_hp, split[
s].prob);
1567 init_slice_summary(sit_opp_summary[0], opp.
summary[0],
1568 opp_split[
t].begin_hp, opp_split[t].end_hp,
1570 init_slice_summary(sit_opp_summary[1], opp.
summary[1],
1571 opp_split[t].begin_hp, opp_split[t].end_hp,
1576 double sit_self_not_hit = sit_prob;
1577 double sit_opp_not_hit = sit_prob;
1579 do_fight(
u_, opp.
u_, split[
s].strikes, opp_split[t].strikes,
1580 sit_summary, sit_opp_summary, sit_self_not_hit,
1581 sit_opp_not_hit, levelup_considered);
1584 self_not_hit += sit_self_not_hit;
1585 opp_not_hit += sit_opp_not_hit;
1586 merge_slice_summary(summary_result[0], sit_summary[0], sit_prob);
1587 merge_slice_summary(summary_result[1], sit_summary[1], sit_prob);
1588 merge_slice_summary(opp_summary_result[0], sit_opp_summary[0], sit_prob);
1589 merge_slice_summary(opp_summary_result[1], sit_opp_summary[1], sit_prob);
1593 summary[0].swap(summary_result[0]);
1594 summary[1].swap(summary_result[1]);
1595 opp.
summary[0].swap(opp_summary_result[0]);
1596 opp.
summary[1].swap(opp_summary_result[1]);
1600 assert(summary[0].
size() == res.size());
1601 assert(opp.
summary[0].size() == opp_res.size());
1602 for (
unsigned int i = 0; i < summary[0].size(); ++
i) {
1603 if (fabs(summary[0][i] - res[i]) > 0.000001) {
1604 std::cerr <<
"Mismatch for " << i <<
" hp: " << summary[0][
i] <<
" should have been " << res[
i] <<
"\n";
1608 for (
unsigned int i = 0; i < opp.
summary[0].size(); ++
i) {
1609 if (fabs(opp.
summary[0][i] - opp_res[i])> 0.000001) {
1610 std::cerr <<
"Mismatch for " << i <<
" hp: " << opp.
summary[0][
i] <<
" should have been " << opp_res[
i] <<
"\n";
1617 if (summary[1].empty())
1618 hp_dist = summary[0];
1620 const unsigned size = summary[0].size();
1621 hp_dist.resize(size);
1622 for (
unsigned int i = 0; i <
size; ++
i)
1623 hp_dist[i] = summary[0][i] + summary[1][i];
1628 const unsigned size = opp.
summary[0].size();
1630 for (
unsigned int i = 0; i <
size; ++
i)
1635 double touched = 1.0 - self_not_hit;
1636 double opp_touched = 1.0 - opp_not_hit;
1640 opp.poisoned += (1 - opp.poisoned) * opp_touched;
1645 opp.slowed += (1 - opp.slowed) * opp_touched;
1647 untouched *= self_not_hit;
1648 opp.untouched *= opp_not_hit;
1656 for (
unsigned int i = 1; i < hp_dist.size(); ++
i) {
1657 total += hp_dist[
i] * std::min<unsigned int>(i + healing,
u_.
max_hp);
1664 #if defined(BENCHMARK) || defined(CHECK)
1667 #define NUM_UNITS 50
1670 #define timer_sub(a, b, result) \
1672 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
1673 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
1674 if ((result)->tv_usec < 0) { \
1675 --(result)->tv_sec; \
1676 (result)->tv_usec += 1000000; \
1681 #ifdef ATTACK_PREDICTION_DEBUG
1684 printf(
"#%02u: %u-%d; %2uhp; %02u%% to hit; ",
1695 printf(
"firststrike,");
1696 printf(
"maxhp=%u\n", stats.
max_hp);
1704 #ifdef HUMAN_READABLE
1705 void combatant::print(
const char label[],
unsigned int battle,
unsigned int fighter)
const
1707 printf(
"#%06u: (%02u) %s%*c %u-%d; %uhp; %02u%% to hit; %.2f%% unscathed; ",
1708 battle, fighter, label,
int(strlen(label))-12,
':',
1719 printf(
"firststrike,");
1720 printf(
"maxhp=%zu ", hp_dist.size()-1);
1722 int num_outputs = 0;
1723 for (
unsigned int i = 0; i < hp_dist.size(); ++
i )
1724 if ( hp_dist[i] != 0.0 )
1726 if ( num_outputs++ % 6 == 0 )
1730 printf(
"%2u: %5.2f", i, hp_dist[i] * 100);
1735 #elif defined(CHECK)
1736 void combatant::print(
const char label[],
unsigned int battle,
unsigned int )
const
1738 printf(
"#%u: %s: %d %u %u %2g%% ", battle, label,
1749 printf(
"firststrike,");
1750 printf(
"maxhp=%zu ", hp_dist.size()-1);
1752 for (
unsigned int i = 0; i < hp_dist.size(); ++
i)
1753 printf(
" %.2f", hp_dist[i] * 100);
1756 #else // ... BENCHMARK
1764 for (
unsigned int i = 0; i < hp_dist.size(); ++
i )
1769 summary[0] = std::vector<double>();
1770 summary[1] = std::vector<double>();
1774 static void run(
unsigned specific_battle)
1779 unsigned int i, j, k, battle = 0;
1782 for (i = 0; i < NUM_UNITS; ++
i) {
1783 unsigned alt = i + 74;
1786 unsigned max_hp = (i*2)%23 + (i*3)%14 + 25;
1787 unsigned hp = (alt*5)%max_hp + 1;
1799 list_combatant(*stats[i], i+1);
1802 gettimeofday(&
start,
nullptr);
1804 for (i = 0; i < NUM_UNITS; ++
i) {
1805 for (j = 0; j < NUM_UNITS; ++j) {
1808 for (k = 0; k < NUM_UNITS; ++k) {
1809 if (i == k || j == k)
1812 if (specific_battle && battle != specific_battle)
1818 u[
i]->print(
"Defender", battle, i+1);
1819 u[j]->print(
"Attacker #1", battle, j+1);
1820 u[k]->print(
"Attacker #2", battle, k+1);
1828 gettimeofday(&
end,
nullptr);
1833 printf(
"Total time for %i combats was %lu.%06lu\n",
1834 NUM_UNITS*(NUM_UNITS-1)*(NUM_UNITS-2), total.tv_sec, total.tv_usec);
1835 printf(
"Time per calc = %li us\n",
1837 / (NUM_UNITS*(NUM_UNITS-1)*(NUM_UNITS-2)));
1839 printf(
"Total combats: %i\n", NUM_UNITS*(NUM_UNITS-1)*(NUM_UNITS-2));
1842 for (i = 0; i < NUM_UNITS; ++
i) {
1853 int add_to_argv = 4;
1854 int damage = atoi((*argv)[1]);
1855 int num_attacks = atoi((*argv)[2]);
1856 int hitpoints = atoi((*argv)[3]), max_hp = hitpoints;
1857 int hit_chance = atoi((*argv)[4]);
1860 bool drains =
false, slows =
false,
slowed =
false, berserk =
false,
1861 firststrike =
false, swarm =
false;
1862 if ((*argv)[5] && atoi((*argv)[5]) == 0) {
1866 char *max = strstr((*argv)[5],
"maxhp=");
1868 max_hp = atoi(max + strlen(
"maxhp="));
1869 if ( max_hp < hitpoints ) {
1870 fprintf(stderr,
"maxhp must be at least hitpoints.");
1874 if (strstr((*argv)[5],
"drain")) {
1876 fprintf(stderr,
"WARNING: drain specified without maxhp; assuming uninjured.\n");
1880 if (strstr((*argv)[5],
"slows"))
1882 if (strstr((*argv)[5],
"slowed"))
1884 if (strstr((*argv)[5],
"berserk"))
1886 if (strstr((*argv)[5],
"firststrike"))
1888 if (strstr((*argv)[5],
"swarm")) {
1890 fprintf(stderr,
"WARNING: swarm specified without maxhp; assuming uninjured.\n");
1897 *argv += add_to_argv;
1901 hit_chance, drains, slows,
slowed,
1902 berserk, firststrike, swarm);
1905 int main(
int argc,
char *argv[])
1912 run(argv[1] ? atoi(argv[1]) : 0);
1915 fprintf(stderr,
"Usage: %s [<battle>]\n"
1916 "\t%s <damage> <attacks> <hp> <hitprob> [drain,slows,slowed,swarm,firststrike,berserk,maxhp=<num>] <damage> <attacks> <hp> <hitprob> [drain,slows,slowed,berserk,firststrike,swarm,maxhp=<num>] ...\n",
1921 def_stats = parse_unit(&argv);
1923 for (i = 0; argv[1] && i < 19; ++
i) {
1924 att_stats[
i] = parse_unit(&argv);
1929 for (i = 0; att[
i]; ++
i) {
1930 debug((
"Fighting next attacker\n"));
1934 def->print(
"Defender", 0, 0);
1935 for (i = 0; att[
i]; ++
i)
1936 att[i]->
print(
"Attacker", 0, i+1);
1938 for (i = 0; att[
i]; ++
i) {
1940 delete att_stats[
i];
1947 #endif // Standalone program
unsigned int calc_blows(unsigned new_hp) const
Calculates the number of blows we would have if we had new_hp.
double untouched
Resulting chance we were not hit by this opponent (important if it poisons)
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
unsigned int hp
Hitpoints of the unit at the beginning of the battle.
std::vector< combat_slice > split_summary() const
Split the combat by number of attacks per combatant (for swarm).
Various functions that implement attacks and attack calculations.
GLuint const GLfloat * val
bool is_slowed
True if the unit is slowed at the beginning of the battle.
bool slows
Attack slows opponent when it hits.
int drain_constant
Base HP drained regardless of damage dealt.
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
bool poisons
Attack poisons opponent when it hits.
bool backstab_pos
True if the attacker is in position to backstab the defender (this is used to determine whether to ap...
int damage
Effective damage of the weapon (all factors accounted for).
const battle_context_unit_stats & u_
unsigned int rounds
Berserk special can force us to fight more than one round.
A struct to describe one possible combat scenario.
unsigned int swarm_min
Minimum number of blows with swarm (equal to num_blows if swarm isn't used).
combatant(const battle_context_unit_stats &u, const combatant *prev=nullptr)
Construct a combatant.
void fight(combatant &opponent, bool levelup_considered=true)
Simulate a fight! Can be called multiple times for cumulative calculations.
Structure describing the statistics of a unit involved in the battle.
GLuint const GLuint * names
double slowed
Resulting chance we are slowed.
bool swarm
Attack has swarm special.
int slow_damage
Effective damage if unit becomes slowed (== damage, if already slowed)
static void print(std::stringstream &sstr, const std::string &queue, const std::string &id)
Game configuration data as global variables.
bool firststrike
Attack has firststrike special.
bool is_poisoned
True if the unit is poisoned at the beginning of the battle.
int main(int argc, char **argv)
int drain_percent
Percentage of damage recovered as health.
GLenum GLenum GLvoid * row
combat_slice(const std::vector< double > src_summary[2], unsigned begin, unsigned end, unsigned num_strikes)
Creates a slice from a summary, and associates a number of strikes.
unsigned int num_blows
Effective number of blows, takes swarm into account.
unsigned int max_hp
Maximum hitpoints of the unit.
double average_hp(unsigned int healing=0) const
What's the average hp (weighted average of hp_dist).
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
bool is_attacker
True if the unit is the attacker.
unsigned int max_experience
bool petrifies
Attack petrifies opponent when it hits.
bool drains
Attack drains opponent when it hits.
double poisoned
Resulting chance we are poisoned.
unsigned int swarm_max
Maximum number of blows with swarm (equal to num_blows if swarm isn't used).
std::vector< double > summary[2]
Summary of matrix used to calculate last battle (unslowed & slowed).