diff --git a/2018/15.cpp b/2018/15.cpp index a1d43fc..601d79c 100644 --- a/2018/15.cpp +++ b/2018/15.cpp @@ -37,15 +37,24 @@ Coord const reading_order[] {{0,-1}, {-1,0}, {1,0}, {0,1}}; struct Game { Grid grid_; - std::map hp_; + std::map 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; + auto PickTarget(Coord my_coord, char my_team) + -> std::map::iterator; + auto Move(Coord my_coord, char my_team) -> std::optional; +}; + +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; - auto PickTarget(Coord my_coord, char my_team) - -> std::optional::iterator>; - auto Move(Coord my_coord, char my_team) -> std::optional; -}; - +// 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::iterator> +auto Game::PickTarget(Coord my_coord, char my_team) -> std::map::iterator { - std::optional::iterator> best; - int best_hp; + std::map::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 { - int best_distance; std::optional 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 seen; std::deque> 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 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 } } } - 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 { // determine order of unit updates std::set 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 { 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 { } // 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 { 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; }