This commit is contained in:
Eric Mertens 2022-11-27 10:41:24 -08:00
parent 50d7ec0416
commit 7d9692efa4

View File

@ -37,15 +37,24 @@ Coord const reading_order[] {{0,-1}, {-1,0}, {1,0}, {0,1}};
struct Game {
Grid grid_;
std::map<Coord, int> hp_;
std::map<Coord, int> units_;
int e_damage_;
int g_damage_;
bool no_elves_can_die;
std::ostream * debug_out_;
Game(Grid grid)
Game(Grid grid);
auto Simulate() -> std::optional<int>;
auto PickTarget(Coord my_coord, char my_team)
-> std::map<Coord, int>::iterator;
auto Move(Coord my_coord, char my_team) -> std::optional<Coord>;
};
Game::Game(Grid grid)
: grid_(std::move(grid))
, hp_{}
, units_{}
, e_damage_(3)
, g_damage_(3)
, no_elves_can_die{false}
@ -54,17 +63,12 @@ struct Game {
// set each unit to its initial hit points
grid_.each([&](auto k, auto v) {
if (IsUnit(v)) {
hp_[k] = initial_hp;
units_[k] = initial_hp;
}
});
}
auto Simulate() -> std::optional<int>;
auto PickTarget(Coord my_coord, char my_team)
-> std::optional<std::map<Coord, int>::iterator>;
auto Move(Coord my_coord, char my_team) -> std::optional<Coord>;
};
// Render the game as seen in the examples
auto operator<<(std::ostream & out, Game const& game) -> std::ostream & {
for (std::size_t y = 0; y < game.grid_.rows.size(); ++y) {
out << game.grid_.rows[y];
@ -78,8 +82,8 @@ auto operator<<(std::ostream & out, Game const& game) -> std::ostream & {
} else {
out << ", ";
}
Coord coord {std::int64_t(x),std::int64_t(y)};
out << c << "(" << game.hp_.at(coord) << ")";
Coord const coord {std::int64_t(x),std::int64_t(y)};
out << c << "(" << game.units_.at(coord) << ")";
}
}
out << std::endl;
@ -88,18 +92,15 @@ auto operator<<(std::ostream & out, Game const& game) -> std::ostream & {
}
// Determine the best target to attack from the current position, if there is one.
auto Game::PickTarget(Coord my_coord, char my_team)
-> std::optional<std::map<Coord, int>::iterator>
auto Game::PickTarget(Coord my_coord, char my_team) -> std::map<Coord, int>::iterator
{
std::optional<std::map<Coord, int>::iterator> best;
int best_hp;
std::map<Coord, int>::iterator best = units_.end();
for (auto const dir : reading_order) {
auto here = dir + my_coord;
auto const here = dir + my_coord;
if (IsOpposed(my_team, grid_[here])) {
auto it = hp_.find(here);
if (!best || best_hp > it->second) {
auto it = units_.find(here);
if (best == units_.end() || best->second > it->second) {
best = it;
best_hp = it->second;
}
}
}
@ -109,22 +110,21 @@ auto Game::PickTarget(Coord my_coord, char my_team)
// Determine the best location to move to, if there is one.
auto Game::Move(Coord my_coord, char my_team) -> std::optional<Coord>
{
int best_distance;
std::optional<Coord> best_start;
Coord best_end;
Coord best_end; // initialized when best_start is non-empty
int best_distance; // initialized when best_start is non-empty
std::set<Coord> seen;
std::deque<std::tuple<Coord, Coord, int>> todo;
for (auto const dir : reading_order) {
auto start = my_coord + dir;
auto const start = my_coord + dir;
if (grid_[start] == '.') {
todo.push_back({start,start,1});
todo.push_back({start, start, 1});
}
}
std::set<Coord> seen;
while (!todo.empty()) {
auto [start, cursor, steps] = todo.front();
auto const [start, cursor, steps] = todo.front();
todo.pop_front();
if (best_start) {
@ -139,7 +139,7 @@ auto Game::Move(Coord my_coord, char my_team) -> std::optional<Coord>
}
}
}
if (PickTarget(cursor, my_team)) {
if (PickTarget(cursor, my_team) != units_.end()) {
best_start = start;
best_end = cursor;
best_distance = steps;
@ -158,21 +158,21 @@ auto Game::Simulate() -> std::optional<int> {
// determine order of unit updates
std::set<Coord, IsBefore> turn_order;
for (auto const& [k,_] : hp_) {
for (auto const& [k,_] : units_) {
turn_order.insert(k);
}
for (auto active_coord : turn_order) {
char active_team = grid_[active_coord];
auto const active_team = grid_[active_coord];
// Detect when no targets remain for this unit and end the game
bool game_over = true;
auto game_over = true;
grid_.each([&](auto k, auto v) {
if (IsOpposed(active_team, v)) game_over = false;
});
if (game_over) {
int total_hp = 0;
for (auto const& [_,v] : hp_) { total_hp += v; }
for (auto const& [_,v] : units_) { total_hp += v; }
return {rounds * total_hp};
}
@ -180,14 +180,14 @@ auto Game::Simulate() -> std::optional<int> {
auto target = PickTarget(active_coord, active_team);
// only move when necessary
if (!target) {
if (target == units_.end()) {
// move if we can
if (auto const destination = Move(active_coord, active_team)) {
std::swap(grid_[*destination], grid_[active_coord]);
auto const it = hp_.find(active_coord);
hp_[*destination] = it->second;
hp_.erase(it);
auto const it = units_.find(active_coord);
units_[*destination] = it->second;
units_.erase(it);
active_coord = *destination;
}
@ -196,8 +196,8 @@ auto Game::Simulate() -> std::optional<int> {
}
// target in range, do the attack
if (target) {
auto & [target_coord, target_hp] = **target;
if (target != units_.end()) {
auto & [target_coord, target_hp] = *target;
auto damage = active_team == 'G' ? g_damage_ : e_damage_;
// lethal
@ -206,7 +206,7 @@ auto Game::Simulate() -> std::optional<int> {
grid_[target_coord] = '.';
turn_order.erase(target_coord);
hp_.erase(*target); // invalidates target, target_coord, target_hp
units_.erase(target); // invalidates target, target_coord, target_hp
} else {
target_hp -= damage;
}