Skip to content

Commit

Permalink
Fix #9: 0 attack minions don't attack, and a board with 0 attack mini…
Browse files Browse the repository at this point in the history
…ons is a tie
  • Loading branch information
twanvl committed Dec 2, 2019
1 parent dd54764 commit a35eb9e
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 20 deletions.
44 changes: 37 additions & 7 deletions src/battle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand All @@ -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; ++tries) {
if (from >= 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
Expand All @@ -38,6 +67,7 @@ void Battle::attack_round() {
}
// switch players
turn = 1 - turn;
return true;
}

void Battle::single_attack_by(int player, int from) {
Expand Down
9 changes: 6 additions & 3 deletions src/battle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
39 changes: 29 additions & 10 deletions src/simulation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,28 @@ 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
int damage_taken[2] = {0};
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];
}
Expand All @@ -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
}
};

Expand Down

0 comments on commit a35eb9e

Please sign in to comment.