diff --git a/src/battle.cpp b/src/battle.cpp index f155bc6..c8289f7 100644 --- a/src/battle.cpp +++ b/src/battle.cpp @@ -7,8 +7,22 @@ using std::endl; void Battle::run() { start(); + int round = 0; + bool missed_prev = 0; while (!done()) { - attack_round(); + round++; + bool ok = attack_round(); + if (missed_prev && !ok) { + turn = 2; // indicate battle is done + return; + } + missed_prev = !ok; + // track battles that never end + if (round > 1000000) { + std::cerr << "ERROR: infinite loop?" << endl; + std::cerr << *this; + break; + } } } @@ -19,14 +33,29 @@ void Battle::start() { do_hero_powers(); } -void Battle::attack_round() { - // attacker +int find_attacker(Board const& board) { + int from = board.next_attacker; + for (int tries=0; tries= BOARDSIZE || !board.minions[from].exists()) { + from = 0; + } + if (board.minions[from].exists() && board.minions[from].attack > 0) { + return from; + } + } + return -1; +} + +bool Battle::attack_round() { + // find attacker Board& active = board[turn]; - if (active.next_attacker >= BOARDSIZE || !active.minions[active.next_attacker].exists()) { - active.next_attacker = 0; + int from = find_attacker(active); + if (from == -1) { + // no minions that can attack, switch players + turn = 1 - turn; + return false; } - int from = active.next_attacker; - active.next_attacker++; + // info on attacker bool windfury = active.minions[from].windfury; active.track_pos[0] = from; // track if this minion stays alive // do attack @@ -38,6 +67,7 @@ void Battle::attack_round() { } // switch players turn = 1 - turn; + return true; } void Battle::single_attack_by(int player, int from) { diff --git a/src/battle.hpp b/src/battle.hpp index 1119573..da9bb52 100644 --- a/src/battle.hpp +++ b/src/battle.hpp @@ -36,12 +36,15 @@ struct Battle { return turn >= 0; } bool done() const { - return board[0].empty() || board[1].empty(); + return board[0].empty() || board[1].empty() || turn >= 2; } // Positive: player 0 won, negative, player 1 won, score is total stars remaining int score() const { - return board[0].total_stars() - board[1].total_stars(); + int stars0 = board[0].total_stars(); + int stars1 = board[1].total_stars(); + if (stars0 > 0 && stars1 > 0) return 0; + return stars0 - stars1; } // Simulate a battle @@ -50,7 +53,7 @@ struct Battle { void start(); // Attacking - void attack_round(); + bool attack_round(); // return true if an attack happened void single_attack_by(int player, int pos); // Summon minions diff --git a/src/simulation.hpp b/src/simulation.hpp index b37d4b3..b4968b6 100644 --- a/src/simulation.hpp +++ b/src/simulation.hpp @@ -12,6 +12,8 @@ using std::vector; const int DEFAULT_NUM_RUNS = 1000; +enum class Flipped { Flipped }; + struct ScoreSummary { int num_runs = 0; int total_stars[2] = {0}; // #stars by which player i has won @@ -19,6 +21,19 @@ struct ScoreSummary { int num_wins[2] = {0}; int num_deaths[2] = {0}; + ScoreSummary() {} + ScoreSummary(ScoreSummary const& stats, Flipped flipped) + : num_runs(stats.num_runs) + , total_stars{stats.total_stars[1],stats.total_stars[0]} + , damage_taken{stats.damage_taken[1],stats.damage_taken[0]} + , num_wins{stats.num_wins[1],stats.num_wins[0]} + , num_deaths{stats.num_deaths[1],stats.num_deaths[0]} + {} + + ScoreSummary flipped() const { + return ScoreSummary(*this,Flipped::Flipped); + } + int num_draws() const { return num_runs - num_wins[0] - num_wins[1]; } @@ -40,22 +55,26 @@ struct ScoreSummary { double mean_score() const { return (double)(total_stars[0] - total_stars[1]) / num_runs; } + double damage_score() const { + return mean_damage_taken(1) / 7.0 - mean_damage_taken(0); + } void add_run(Battle const& b) { num_runs++; int stars[2] = {b.board[0].total_stars(), b.board[1].total_stars()}; - for (int player=0; player<2; ++player) { - // if player has stars remaining on the board, they have won - if (stars[player] > 0) { - num_wins[player]++; - total_stars[player] += stars[player]; - int dmg = stars[player] + b.board[player].level; - damage_taken[1-player] += dmg; - if (dmg >= b.board[1-player].health) { - num_deaths[1-player]++; - } + if ((stars[0] > 0) != (stars[1] > 0)) { + // only one player has minions remaining (and therefore stars) + int winner = stars[0] > 0 ? 0 : 1; + int loser = 1-winner; + num_wins[winner]++; + total_stars[winner] += stars[winner]; + int dmg = stars[winner] + b.board[winner].level; + damage_taken[loser] += dmg; + if (dmg >= b.board[loser].health) { + num_deaths[loser]++; } } + // otherwise, both players have minions remaining, or neither has minions, game is a draw } };