Last active
June 16, 2024 20:30
-
-
Save iglesias/b5ac2e97f68a773fb8da1f81f675023d to your computer and use it in GitHub Desktop.
codeweekend.dev
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def Settings( **kwargs ): | |
| return { | |
| 'flags': [ '-x', 'c++', '-Wall', '-Wextra', '-Werror', '-std=c++23'], | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <cmath> | |
| #include <exception> | |
| #include <iostream> | |
| #include <fstream> | |
| #include <stdexcept> | |
| #include <string> | |
| #include <string_view> | |
| #include <variant> | |
| #include <vector> | |
| constexpr auto squared(auto x) { | |
| return x * x; | |
| } | |
| struct unimplemented : public std::exception { | |
| private: | |
| std::string message_; | |
| public: | |
| explicit unimplemented(std::string_view message) : message_(message) {} | |
| const char* what() const noexcept override { | |
| return message_.c_str(); | |
| } | |
| }; | |
| #include <nlohmann/json.hpp> | |
| using json = nlohmann::json; | |
| namespace { | |
| struct hero { | |
| int base_speed; | |
| int base_power; | |
| int base_range; | |
| int level_speed_coeff; | |
| int level_power_coeff; | |
| int level_range_coeff; | |
| }; | |
| [[maybe_unused]] void to_json(json& j, const hero& h) { | |
| j = json{{"base_speed", h.base_speed}, {"base_power", h.base_power}, {"base_range", h.base_range}, | |
| {"level_speed_coeff", h.level_speed_coeff}, {"level_power_coeff", h.level_power_coeff}, | |
| {"level_range_coeff", h.level_range_coeff}}; | |
| } | |
| [[maybe_unused]] void from_json(const json& j, hero& h) { | |
| j.at("base_speed").get_to(h.base_speed); | |
| j.at("base_power").get_to(h.base_power); | |
| j.at("base_range").get_to(h.base_range); | |
| j.at("level_speed_coeff").get_to(h.level_speed_coeff); | |
| j.at("level_power_coeff").get_to(h.level_power_coeff); | |
| j.at("level_range_coeff").get_to(h.level_range_coeff); | |
| } | |
| struct monster { | |
| int x; | |
| int y; | |
| int hp; | |
| int gold; | |
| int exp; | |
| }; | |
| [[maybe_unused]] void to_json(json& j, const monster& m) { | |
| j = json{{"x", m.x}, {"y", m.y}, {"hp", m.hp}, {"gold", m.gold}, {"exp", m.exp}}; | |
| } | |
| [[maybe_unused]] void from_json(const json& j, monster& m) { | |
| j.at("x").get_to(m.x); | |
| j.at("y").get_to(m.y); | |
| j.at("hp").get_to(m.hp); | |
| j.at("gold").get_to(m.gold); | |
| j.at("exp").get_to(m.exp); | |
| } | |
| using monsters_t = std::vector<::monster>; | |
| struct move { | |
| std::string type; | |
| int target_x, target_y; | |
| }; | |
| [[maybe_unused]] void to_json(json& j, const move& m) { | |
| j = json{{"type", m.type}, {"target_x", m.target_x}, {"target_y", m.target_y}}; | |
| } | |
| [[maybe_unused]] void from_json(const json& j, move& m) { | |
| j.at("type").get_to(m.type); | |
| j.at("target_x").get_to(m.target_x); | |
| j.at("target_y").get_to(m.target_y); | |
| } | |
| struct attack { | |
| std::string type; | |
| int target_id; | |
| }; | |
| [[maybe_unused]] void to_json(json& j, const attack& a) { | |
| j = json{{"type", a.type}, {"target_id", a.target_id}}; | |
| } | |
| [[maybe_unused]] void from_json(const json& j, attack& a) { | |
| j.at("type").get_to(a.type); | |
| j.at("target_id").get_to(a.target_id); | |
| } | |
| [[maybe_unused]] void to_json(json& j, const std::variant<move, attack>& v) { | |
| switch (v.index()) { | |
| case 0: | |
| j = {{"type", "move"}, | |
| {"target_x", std::get<move>(v).target_x}, | |
| {"target_y", std::get<move>(v).target_y}}; | |
| return; | |
| case 1: | |
| j = {{"type", "attack"}, {"target_id", std::get<attack>(v).target_id}}; | |
| return; | |
| default: | |
| throw std::runtime_error("Unexpected case in moves variant."); | |
| } | |
| } | |
| struct moves_t { | |
| using move_t = std::variant<move, attack>; | |
| std::vector<move_t> moves; | |
| void push_back(move_t&& move) { moves.push_back(move); }; | |
| }; | |
| [[maybe_unused]] void to_json(json& j, const moves_t& moves) { | |
| j = {{"moves", moves.moves}}; | |
| } | |
| } // anonymous | |
| [[maybe_unused]] std::ostream& operator<<(std::ostream& os, const ::monster& m) { | |
| os << "xxxxxxxxxxx\n" | |
| << "x (" << m.x << ", " << m.y << ")\n" | |
| << "x " << std::setw(4) << m.hp << " HP\n" | |
| << "x " << std::setw(4) << m.exp << " EXP\n" | |
| << "x " << std::setw(8) << m.gold << "g\n" | |
| << "xxxxxxxxxxx\n\n"; | |
| return os; | |
| } | |
| struct levelling_t { | |
| int level; | |
| int experience; // relative to each level | |
| }; | |
| int find_closest_alive_monster_in_range(const int x, const int y, const ::hero& h, | |
| const ::monsters_t& ms, const levelling_t& levelling) { | |
| int closest_monster_id = -1, shortest_distance = 1000000000; | |
| const int resulting_range = static_cast<int>(std::floor( | |
| h.base_range * (1 + levelling.level * ((1. * h.level_range_coeff) / 100)))); | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| const int d2 = squared(x - ms[i].x) + squared(y - ms[i].y); | |
| if (d2 <= squared(resulting_range) and d2 < shortest_distance) { | |
| closest_monster_id = i; | |
| shortest_distance = d2; | |
| } | |
| } | |
| return closest_monster_id; | |
| } | |
| [[maybe_unused]] int find_closest_alive_monster(const int x, const int y, const ::monsters_t ms) { | |
| int closest_monster_id = -1, shortest_distance = 1000000000; | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| const int d2 = squared(x - ms[i].x) + squared(y - ms[i].y); | |
| if (d2 < shortest_distance) { | |
| closest_monster_id = i; | |
| shortest_distance = d2; | |
| } | |
| } | |
| return closest_monster_id; | |
| } | |
| [[maybe_unused]] int find_richest_alive_monster(const ::monsters_t ms) { | |
| int richest_monster_id = -1, richest_value = 0; | |
| for (int i = 1; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| if (ms[i].gold > richest_value) { | |
| richest_monster_id = i; | |
| richest_value = ms[i].gold; | |
| } | |
| } | |
| if (richest_monster_id == -1) throw unimplemented("Hoped this wouldn't happen."); | |
| return richest_monster_id; | |
| } | |
| [[maybe_unused]] int find_weakest_alive_monster(const ::monsters_t ms) { | |
| int weakest_monster_id = -1, weakest_value = 1000000000; | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| if (ms[i].hp < weakest_value) { | |
| weakest_monster_id = i; | |
| weakest_value = ms[i].hp; | |
| } | |
| } | |
| if (weakest_monster_id == -1) throw unimplemented("Hoped this wouldn't happen."); | |
| return weakest_monster_id; | |
| } | |
| [[maybe_unused]] int find_expest_alive_monster(const ::monsters_t ms) { | |
| int best_monster_id = -1, best_value = 0; | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| if (ms[i].exp > best_value) { | |
| best_monster_id = i; | |
| best_value = ms[i].exp; | |
| } | |
| } | |
| if (best_monster_id == -1) throw unimplemented("Hoped this wouldn't happen."); | |
| return best_monster_id; | |
| } | |
| int width; | |
| int height; | |
| bool within_bounds(int x, int y) { | |
| return 0 <= x and x <= width and 0 <= y and y <= height; | |
| } | |
| void move_toward(int& x, int& y, const int x_g, const int y_g, const int resulting_speed) { | |
| const double D = std::sqrt(squared(x_g - x) + squared(y_g - y)); | |
| if (resulting_speed >= D) { | |
| x = x_g; | |
| y = y_g; | |
| return; | |
| } | |
| const double ux = resulting_speed * (x_g - x) / D, uy = resulting_speed * (y_g - y) / D; | |
| ///logging | |
| ///std::cout << " resulting_speed: " << resulting_speed << " D: " << D << '\n'; | |
| /// | |
| const int xf = static_cast<int>(std::round(x + ux)), yf = static_cast<int>(std::round(y + uy)); | |
| ///logging | |
| ///std::cout << " (xf, yf): (" << xf << ", " << yf << ")\n"; | |
| /// | |
| if (squared(xf - x) + squared(yf - y) <= squared(resulting_speed) and within_bounds(xf, yf)) { | |
| x = xf; | |
| y = yf; | |
| } else { | |
| const std::vector<std::vector<int>> deltas{{-1, 0}, {+1, 0}, { 0, -1}, {0, +1}, | |
| {-1, -1}, {+1, -1}, {-1, +1}, {+1, +1}};; | |
| const int init_x = x, init_y = y; | |
| int scaling_x = 1, scaling_y = 1; | |
| for(;;) { | |
| bool found = false; | |
| for (const std::vector<int>& delta : deltas) { | |
| const int try_x = xf + scaling_x * delta[0], try_y = yf + scaling_y * delta[1]; | |
| if (squared(try_x - init_x) + squared(try_y - init_y) <= squared(resulting_speed) and | |
| within_bounds(try_x, try_y)) { | |
| if (found) { | |
| if (squared(try_x - x_g) + squared(try_y - x_g) < squared(x - x_g) + squared(y - y_g)) { | |
| x = try_x; | |
| y = try_y; | |
| } | |
| } else { | |
| found = true; | |
| x = try_x; | |
| y = try_y; | |
| } | |
| } | |
| } | |
| if (found) return; | |
| if (scaling_x > scaling_y) scaling_y++; | |
| else { assert(scaling_y == scaling_x); scaling_x++; } | |
| } | |
| } | |
| } | |
| std::variant<::move, ::attack> turn(int& x, int& y, const ::hero& h, ::monsters_t& ms, levelling_t& levelling) { | |
| const int closest_monster_in_range_id = find_closest_alive_monster_in_range(x, y, h, ms, levelling); | |
| static int turn_id = -1; | |
| turn_id++; | |
| if (closest_monster_in_range_id == -1) { | |
| //FIXME efficiency this will iterate through monsters for a second time. | |
| //const int monster_id = find_closest_alive_monster(x, y, ms); | |
| //const int monster_id = find_weakest_alive_monster(ms); | |
| const int monster_id = turn_id < 100 ? find_expest_alive_monster(ms) : find_richest_alive_monster(ms); | |
| const ::monster& closest = ms.at(monster_id); | |
| const int resulting_speed = static_cast<int>(std::floor( | |
| h.base_speed * (1 + levelling.level * ((1. * h.level_speed_coeff) / 100)))); | |
| /// logging | |
| std::cout << "Moving toward monster " << monster_id << " at " << closest.x << " " << closest.y | |
| << " from " << x << " " << y << "\n"; | |
| /// | |
| move_toward(x, y, closest.x, closest.y, resulting_speed); | |
| return ::move("move", x, y); | |
| } else { | |
| /****** Attack the closest monster ******/ | |
| const int resulting_attack = static_cast<int>(std::floor( | |
| h.base_power * (1 + levelling.level * ((1. * h.level_power_coeff) / 100)))); | |
| /// logging | |
| std::cout << "Monster " << closest_monster_in_range_id << " with " << ms[closest_monster_in_range_id].hp | |
| << " HP takes " << resulting_attack << " damage from level " << levelling.level | |
| << " hero with " << levelling.experience << " EXP\n"; | |
| /// | |
| //FIXME remove death monsters, would probably need to store initial index of the monsters since | |
| //when removing from the vector the initial order is lost. | |
| ms.at(closest_monster_in_range_id).hp -= resulting_attack; | |
| if (ms[closest_monster_in_range_id].hp <= 0) { | |
| /// logging | |
| std::cout << "Monster " << closest_monster_in_range_id << " died. It left " | |
| << ms[closest_monster_in_range_id].gold << " gold and " | |
| << ms[closest_monster_in_range_id].exp << " experience.\n"; | |
| /// | |
| levelling.experience += ms.at(closest_monster_in_range_id).exp; | |
| int L = levelling.level + 1; | |
| while (levelling.experience >= 1000 + L * (L - 1) * 50) { | |
| levelling.experience -= 1000 + L * (L - 1) * 50; | |
| levelling.level++; | |
| L++; | |
| } | |
| } | |
| return ::attack("attack", closest_monster_in_range_id); | |
| } | |
| } | |
| int main(int argc, char** argv) { | |
| using std::vector; | |
| using std::cout; | |
| json input; | |
| { | |
| std::string fname = "input.json"; | |
| if (argc > 1) fname = std::string(argv[1]); | |
| std::ifstream ifstream(fname); | |
| ifstream >> input; | |
| } | |
| height = input["height"]; | |
| width = input["width"]; | |
| const ::hero hero = input["hero"]; | |
| vector<::monster> monsters = input["monsters"]; | |
| //for (const ::monster& m : monsters) cout << m << '\n'; | |
| moves_t moves; | |
| levelling_t levelling(0, 0); // experience and level, updated and used inside turn, | |
| // here we are just maintaining of keeping track of its state. | |
| // With better design it would be inside hero, together with its position | |
| // Would it be good for something to separate the parts of the hero read | |
| // from the input json (constant) and the dynamic stats and position? | |
| int x = input["start_x"], y = input["start_y"]; | |
| for (int i = 0; i < input["num_turns"]; i++) { | |
| ///logging | |
| std::cout << "======= TURN " << i << " =======\n"; | |
| moves.push_back(turn(x, y, hero, monsters, levelling)); | |
| } | |
| { | |
| json output(moves); | |
| std::ofstream ofstream("output.json"); | |
| ofstream << output << std::endl; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <bitset> | |
| #include <cmath> | |
| #include <exception> | |
| #include <iostream> | |
| #include <fstream> | |
| #include <optional> | |
| #include <queue> | |
| #include <set> | |
| #include <stdexcept> | |
| #include <string> | |
| #include <string_view> | |
| #include <variant> | |
| #include <vector> | |
| #include <nlohmann/json.hpp> | |
| /* | |
| * SUMMARY | |
| struct hero { int base_speed, base_power, ...; }; | |
| struct monster { int x, y, hp, gold, ...; }; | |
| using monsters_t = std::vector<::monster>; | |
| struct levelling_t { int level, experience; }; | |
| using move_t = std::variant<::move, ::attack>; | |
| move_t turn(int& x, int& y, const ::hero& h, ::monsters_t& ms, levelling_t& levelling) { | |
| */ | |
| constexpr auto squared(auto x) { | |
| return x * x; | |
| } | |
| struct unimplemented : public std::exception { | |
| private: | |
| std::string message_; | |
| public: | |
| explicit unimplemented(std::string_view message) : message_(message) {} | |
| const char* what() const noexcept override { | |
| return message_.c_str(); | |
| } | |
| }; | |
| using json = nlohmann::json; | |
| // TODO name namespace. | |
| namespace { | |
| struct hero { | |
| int base_speed; | |
| int base_power; | |
| int base_range; | |
| int level_speed_coeff; | |
| int level_power_coeff; | |
| int level_range_coeff; | |
| }; | |
| [[maybe_unused]] void from_json(const json& j, hero& h) { | |
| j.at("base_speed").get_to(h.base_speed); | |
| j.at("base_power").get_to(h.base_power); | |
| j.at("base_range").get_to(h.base_range); | |
| j.at("level_speed_coeff").get_to(h.level_speed_coeff); | |
| j.at("level_power_coeff").get_to(h.level_power_coeff); | |
| j.at("level_range_coeff").get_to(h.level_range_coeff); | |
| } | |
| struct monster { | |
| int x; | |
| int y; | |
| int hp; | |
| int gold; | |
| int exp; | |
| std::optional<int> attack = std::nullopt; | |
| std::optional<int> range = std::nullopt; | |
| }; | |
| [[maybe_unused]] void from_json(const json& j, monster& m) { | |
| j.at("x").get_to(m.x); | |
| j.at("y").get_to(m.y); | |
| j.at("hp").get_to(m.hp); | |
| j.at("gold").get_to(m.gold); | |
| j.at("exp").get_to(m.exp); | |
| if (j.count("attack")) m.attack = j.at("attack").get<int>(); | |
| if (j.count("range")) m.range = j.at("range").get<int>(); | |
| } | |
| using monsters_t = std::vector<::monster>; | |
| struct move { | |
| int target_x, target_y; | |
| }; | |
| struct attack { | |
| int target_id; | |
| }; | |
| using move_t = std::variant<move, attack>; | |
| [[maybe_unused]] void to_json(json& j, const move_t& v) { | |
| switch (v.index()) { | |
| case 0: | |
| j = {{"type", "move"}, | |
| {"target_x", std::get<move>(v).target_x}, | |
| {"target_y", std::get<move>(v).target_y}}; | |
| return; | |
| case 1: | |
| j = {{"type", "attack"}, {"target_id", std::get<attack>(v).target_id}}; | |
| return; | |
| default: | |
| throw std::runtime_error("Unexpected case in moves variant."); | |
| } | |
| } | |
| // TODO can this be a using instead? | |
| struct moves_t { | |
| std::vector<move_t> moves; | |
| void push_back(move_t&& move) { moves.push_back(move); }; | |
| }; | |
| [[maybe_unused]] void to_json(json& j, const moves_t& moves) { | |
| j = {{"moves", moves.moves}}; | |
| } | |
| } // anonymous | |
| [[maybe_unused]] std::ostream& operator<<(std::ostream& os, const ::monster& m) { | |
| os << "xxxxxxxxxxx\n" | |
| << "x (" << m.x << ", " << m.y << ")\n" | |
| << "x " << std::setw(4) << m.hp << " HP\n" | |
| << "x " << std::setw(4) << m.exp << " EXP\n" | |
| << "x " << std::setw(8) << m.gold << "g\n" | |
| << "xxxxxxxxxxx\n\n"; | |
| return os; | |
| } | |
| struct levelling_t { | |
| int level; | |
| int experience; // relative to each level | |
| }; | |
| int find_closest_alive_monster_in_range(const int x, const int y, const ::hero& h, | |
| const ::monsters_t& ms, const levelling_t& levelling) { | |
| int closest_monster_id = -1, shortest_distance = 1000000000; | |
| const int resulting_range = static_cast<int>(std::floor( | |
| h.base_range * (1 + levelling.level * ((1. * h.level_range_coeff) / 100)))); | |
| //std::cout << ">>>> resulting_range: " << resulting_range << '\n'; | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| if (ms[i].hp >= 1000) continue; | |
| //if (ms[i].gold == 1) continue; | |
| const int d2 = squared(x - ms[i].x) + squared(y - ms[i].y); | |
| //std::cout << ">>>> d2 to " << i << ": " << d2 << '\n'; | |
| if (d2 <= squared(resulting_range) and d2 < shortest_distance) { | |
| closest_monster_id = i; | |
| shortest_distance = d2; | |
| } | |
| } | |
| return closest_monster_id; | |
| } | |
| //TODO consider reducing code duplication in find_*est_alive_monster functions. | |
| [[maybe_unused]] int find_closest_alive_monster(const int x, const int y, const ::monsters_t ms) { | |
| int closest_monster_id = -1, shortest_distance = 1000000000; | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| const int d2 = squared(x - ms[i].x) + squared(y - ms[i].y); | |
| if (d2 < shortest_distance) { | |
| closest_monster_id = i; | |
| shortest_distance = d2; | |
| } | |
| } | |
| return closest_monster_id; | |
| } | |
| [[maybe_unused]] int find_richest_alive_monster(const ::monsters_t ms) { | |
| int richest_monster_id = -1, richest_value = 0; | |
| for (int i = 1; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| if (ms[i].gold > richest_value) { | |
| richest_monster_id = i; | |
| richest_value = ms[i].gold; | |
| } | |
| } | |
| if (richest_monster_id == -1) throw unimplemented("Hoped this wouldn't happen."); | |
| return richest_monster_id; | |
| } | |
| [[maybe_unused]] int find_weakest_alive_monster(const ::monsters_t ms) { | |
| int weakest_monster_id = -1, weakest_value = 1000000000; | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| if (ms[i].hp < weakest_value) { | |
| weakest_monster_id = i; | |
| weakest_value = ms[i].hp; | |
| } | |
| } | |
| if (weakest_monster_id == -1) throw unimplemented("Hoped this wouldn't happen."); | |
| return weakest_monster_id; | |
| } | |
| [[maybe_unused]] int find_expest_alive_monster(const ::monsters_t ms) { | |
| int best_monster_id = -1, best_value = 0; | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms[i].hp <= 0) continue; | |
| if (ms[i].exp > best_value) { | |
| best_monster_id = i; | |
| best_value = ms[i].exp; | |
| } | |
| } | |
| if (best_monster_id == -1) throw unimplemented("Hoped this wouldn't happen."); | |
| return best_monster_id; | |
| } | |
| //TODO refactor globals in global namespace. | |
| int width; | |
| int height; | |
| std::bitset<1001*1001> map; | |
| bool within_bounds(int x, int y) { | |
| return 0 <= x and x <= width and 0 <= y and y <= height; | |
| } | |
| void setup_map_against_strong(const ::monsters_t& ms) { | |
| map.reset(); | |
| for (int i = 0; i < static_cast<int>(ms.size()); i++) { | |
| if (ms.at(i).attack <= 1000) continue; | |
| std::queue<std::pair<int, int>> Q; | |
| std::set<std::pair<int, int>> Qed; | |
| const std::vector<std::vector<int>> deltas = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; | |
| Q.emplace(ms.at(i).x, ms.at(i).y); | |
| Qed.emplace(ms.at(i).x, ms.at(i).y); | |
| while(!Q.empty()){ | |
| std::pair<int, int> cell = Q.front(); | |
| Q.pop(); | |
| map.set(1001*cell.first + cell.second); | |
| for (const std::vector<int>& delta : deltas) { | |
| std::pair new_cell = cell; | |
| new_cell.first += delta[0]; | |
| new_cell.second += delta[1]; | |
| if (!within_bounds(new_cell.first, new_cell.second)) continue; | |
| if (Qed.contains(new_cell)) continue; | |
| constexpr auto error = "No input range for monster."; | |
| if (squared(new_cell.first - ms.at(i).x) + | |
| squared(new_cell.second - ms.at(i).y) <= | |
| squared(ms.at(i).range.or_else( | |
| []() -> std::optional<int> { | |
| throw std::runtime_error(error); | |
| }).value())) { | |
| Q.push(new_cell); | |
| Qed.insert(new_cell); | |
| } | |
| } | |
| } | |
| } | |
| /* | |
| for (int y = 0; y <= 1000; y++) { | |
| for (int x = 0; x <= 1000; x++) { | |
| std::cout << (map.test(x*1001 + y) ? 'x' : ' '); | |
| } | |
| std::cout << '\n'; | |
| } | |
| */ | |
| } | |
| void move_toward(int& x, int& y, const int x_g, const int y_g, const int resulting_speed) { | |
| const double D = std::sqrt(squared(x_g - x) + squared(y_g - y)); | |
| if (resulting_speed >= D) { | |
| x = x_g; | |
| y = y_g; | |
| return; | |
| } | |
| const double ux = resulting_speed * (x_g - x) / D, uy = resulting_speed * (y_g - y) / D; | |
| ///logging | |
| ///std::cout << " resulting_speed: " << resulting_speed << " D: " << D << '\n'; | |
| /// | |
| const int xf = static_cast<int>(std::round(x + ux)), yf = static_cast<int>(std::round(y + uy)); | |
| ///logging | |
| ///std::cout << " (xf, yf): (" << xf << ", " << yf << ")\n"; | |
| /// | |
| if (squared(xf - x) + squared(yf - y) <= squared(resulting_speed) and within_bounds(xf, yf) and !map.test(xf*1001 + yf)) { | |
| x = xf; | |
| y = yf; | |
| } else { | |
| const std::vector<std::vector<int>> deltas{{-1, 0}, {+1, 0}, { 0, -1}, {0, +1}, | |
| {-1, -1}, {+1, -1}, {-1, +1}, {+1, +1}};; | |
| const int init_x = x, init_y = y; | |
| int scaling_x = 1, scaling_y = 1; | |
| for(;;) { | |
| bool found = false; | |
| for (const std::vector<int>& delta : deltas) { | |
| const int try_x = xf + scaling_x * delta[0], try_y = yf + scaling_y * delta[1]; | |
| //std::cout << "trying coordinates: " << try_x << ' ' << try_y << std::endl; | |
| if (squared(try_x - init_x) + squared(try_y - init_y) <= squared(resulting_speed) and | |
| within_bounds(try_x, try_y) and !map.test(try_x*1001 + try_y)) { | |
| if (found) { | |
| if (squared(try_x - x_g) + squared(try_y - x_g) < squared(x - x_g) + squared(y - y_g)) { | |
| x = try_x; | |
| y = try_y; | |
| } | |
| } else { | |
| found = true; | |
| x = try_x; | |
| y = try_y; | |
| } | |
| } | |
| } | |
| if (found) return; | |
| if (scaling_x > scaling_y) scaling_y++; | |
| else { assert(scaling_y == scaling_x); scaling_x++; } | |
| //std::cout << "scalings: " << scaling_x << ' ' << scaling_y << std::endl; | |
| } | |
| } | |
| } | |
| move_t turn(int& x, int& y, const ::hero& h, ::monsters_t& ms, levelling_t& levelling) { | |
| const int closest_monster_in_range_id = find_closest_alive_monster_in_range(x, y, h, ms, levelling); | |
| [[maybe_unused]] static int turn_id = -1; | |
| turn_id++; | |
| if (closest_monster_in_range_id == -1) { | |
| //const int monster_id = find_closest_alive_monster(x, y, ms); | |
| const int monster_id = find_weakest_alive_monster(ms); | |
| //const int monster_id = turn_id < 100 ? find_expest_alive_monster(ms) : find_richest_alive_monster(ms); | |
| const ::monster& closest = ms.at(monster_id); | |
| const int resulting_speed = static_cast<int>(std::floor( | |
| h.base_speed * (1 + levelling.level * ((1. * h.level_speed_coeff) / 100)))); | |
| /// logging | |
| ///std::cout << "Moving toward monster " << monster_id << " at " << closest.x << " " << closest.y | |
| /// << " from " << x << " " << y << "\n"; | |
| /// | |
| move_toward(x, y, closest.x, closest.y, resulting_speed); | |
| return ::move(x, y); | |
| } else { | |
| const int resulting_attack = static_cast<int>(std::floor( | |
| h.base_power * (1 + levelling.level * ((1. * h.level_power_coeff) / 100)))); | |
| /// logging | |
| ///std::cout << "Monster " << closest_monster_in_range_id << " with " << ms[closest_monster_in_range_id].hp | |
| /// << " HP takes " << resulting_attack << " damage from level " << levelling.level | |
| /// << " hero with " << levelling.experience << " EXP\n"; | |
| /// | |
| ms.at(closest_monster_in_range_id).hp -= resulting_attack; | |
| if (ms[closest_monster_in_range_id].hp <= 0) { | |
| /// logging | |
| ///std::cout << "Monster " << closest_monster_in_range_id << " died. It left " | |
| /// << ms[closest_monster_in_range_id].gold << " gold and " | |
| /// << ms[closest_monster_in_range_id].exp << " experience.\n"; | |
| /// | |
| levelling.experience += ms.at(closest_monster_in_range_id).exp; | |
| int L = levelling.level + 1; | |
| while (levelling.experience >= 1000 + L * (L - 1) * 50) { | |
| levelling.experience -= 1000 + L * (L - 1) * 50; | |
| levelling.level++; | |
| L++; | |
| } | |
| } | |
| return ::attack(closest_monster_in_range_id); | |
| } | |
| } | |
| int main(int argc, char** argv) { | |
| json input; | |
| { | |
| std::string fname = "input.json"; | |
| if (argc > 1) fname = std::string(argv[1]); | |
| std::ifstream ifstream(fname); | |
| ifstream >> input; | |
| } | |
| height = input["height"]; | |
| width = input["width"]; | |
| const ::hero hero = input["hero"]; | |
| std::vector<::monster> monsters = input["monsters"]; | |
| //for (const ::monster& m : monsters) std::cout << m << '\n'; | |
| setup_map_against_strong(monsters); | |
| moves_t moves; | |
| levelling_t levelling(0, 0); // experience and level, in/out param for turn, | |
| int x = input["start_x"], y = input["start_y"]; | |
| for (int i = 0; i < input["num_turns"]; i++) { | |
| ///logging | |
| ///std::cout << "======= TURN " << i << " =======\n"; | |
| /// | |
| moves.push_back(turn(x, y, hero, monsters, levelling)); | |
| } | |
| { | |
| json output(moves); | |
| std::ofstream ofstream("output.json"); | |
| ofstream << output << std::endl; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| api_token = 'ihrpngqrxqepzpnajzvhzwmbqccoqocw' | |
| api_url = 'https://codeweekend.dev:3721/api/' | |
| files_url = 'https://codeweekend.dev:81/' | |
| print('API TOKEN:', api_token) | |
| print('API URL:', api_url) | |
| import requests | |
| import json | |
| import time | |
| import os | |
| headers = { | |
| 'authorization': f'Bearer {api_token}' | |
| } | |
| def show(inner_json): | |
| print(json.dumps(inner_json, indent=2)) | |
| def get_scoreboard(): | |
| return requests.get(api_url + 'scoreboard', headers=headers).json() | |
| def get_team_dashboard(): | |
| return requests.get(api_url + 'team_dashboard', headers=headers).json() | |
| def get_test(task_id): | |
| task_id_padded = '{:03d}'.format(task_id) | |
| url = f'{files_url}{task_id_padded}.json' | |
| return requests.get(url, headers=headers).content | |
| # Returns at most 50 submissions | |
| def get_team_submissions(offset=0, task_id=None): | |
| url = f'{api_url}team_submissions?offset={offset}' | |
| if task_id is not None: | |
| url += f'&task_id={task_id}' | |
| return requests.get(url, headers=headers).json() | |
| def get_submission_info(submission_id, wait=False): | |
| url = f'{api_url}submission_info/{submission_id}' | |
| res = requests.get(url, headers=headers).json() | |
| if 'Pending' in res and wait: | |
| print('Submission is in Pending state, waiting...') | |
| time.sleep(1) | |
| return get_submission_info(submission_id) | |
| return res | |
| # Returns submission_id | |
| def submit(task_id, solution): | |
| res = requests.post(url = f'{api_url}submit/{task_id}', | |
| headers=headers, files={'file': solution}) | |
| if res.status_code == 200: | |
| return res.text | |
| print(f'Error: {res.text}') | |
| return None | |
| def download_submission(submission_id): | |
| import urllib.request | |
| url = f'{api_url}download_submission/{submission_id}' | |
| opener = urllib.request.build_opener() | |
| opener.addheaders = headers.items() | |
| urllib.request.install_opener(opener) | |
| try: | |
| file, _ = urllib.request.urlretrieve(url, "downloaded.txt") | |
| except Exception as e: | |
| print('Failed to download submission: ', e) | |
| return None | |
| content = open(file, "r").read() | |
| os.remove(file) | |
| return content | |
| def update_display_name(new_name): | |
| url = api_url + 'update_user' | |
| data = { | |
| 'display_name': new_name, | |
| 'email': "", | |
| 'team_members': "" | |
| } | |
| return requests.post(url, json=data, headers=headers).content | |
| # show(get_scoreboard()) | |
| # show(get_submission_info(427)) | |
| # show(get_team_dashboard()) | |
| # show(get_team_submissions()) | |
| # download_submission(476) | |
| # update_display_name('Test 123') | |
| for task_id in range(26, 51): | |
| print(f'Task_id: {task_id}') | |
| f = open('input.json', 'w') | |
| f.write(get_test(task_id).decode()) | |
| f.close() | |
| import os | |
| os.system('./hero') | |
| with open('output.json') as f: | |
| d = json.load(f) | |
| submission_id = submit(task_id, json.dumps(d)) | |
| print(f' Submission_id: {submission_id}') | |
| info = get_submission_info(submission_id, wait=True) | |
| print(f' Result: {info}') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment