diff --git a/cpp/.ccls b/cpp/.ccls new file mode 100644 index 0000000..015deac --- /dev/null +++ b/cpp/.ccls @@ -0,0 +1,2 @@ +clang +-std=c++20 diff --git a/cpp/Makefile b/cpp/Makefile index 504f358..27c5b3b 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -1,4 +1,5 @@ EXE=run +EXEPVP=runpvp CXX=g++ FLAGS=-std=c++20 -O2 -static -fdiagnostics-color=always @@ -10,11 +11,14 @@ player_code.o: player_code.h player_code.cpp $(EXE).o: $(EXE).cpp player_code.h $(CXX) -c -o $(EXE).o $(FLAGS) $(EXE).cpp +$(EXEPVP).o: $(EXEPVP).cpp player_code.h + $(CXX) -c -o $(EXEPVP).o $(FLAGS) $(EXEPVP).cpp + main.o: main.cpp player_code.h $(CXX) $(FLAGS) -c -o main.o main.cpp -$(EXE): $(EXE).o player_code.o main.o - g++ -fdiagnostics-color=always -static -o $(EXE) $(EXE).o player_code.o main.o +$(EXE): $(EXE).o $(EXEPVP).o player_code.o main.o + g++ -fdiagnostics-color=always -static -o $(EXE) $(EXE).o $(EXEPVP).o player_code.o main.o clean_objects: rm player_code.o $(EXE).o main.o @@ -22,4 +26,4 @@ clean_objects: clean: rm $(EXE) $(EXE).o -.PHONY: all clean +.PHONY: all clean \ No newline at end of file diff --git a/cpp/compiler.Dockerfile b/cpp/compiler.Dockerfile index 127efd3..2147f8a 100644 --- a/cpp/compiler.Dockerfile +++ b/cpp/compiler.Dockerfile @@ -8,4 +8,4 @@ RUN make all RUN rm run.o -CMD [ "make", "all" ] +CMD [ "make", "all" ] \ No newline at end of file diff --git a/cpp/main.cpp b/cpp/main.cpp index a93e4e8..986c176 100644 --- a/cpp/main.cpp +++ b/cpp/main.cpp @@ -1,18 +1,19 @@ #include "player_code.h" -void init_constants() { +std::ostringstream all_logs; - std::cin >> Constants::NO_OF_TURNS >> Constants::MAX_NO_OF_COINS; +//make global object for holding all ids of attackers whose abilities activated +void init_constants() { // All the attacker types std::cin >> Constants::NO_OF_ATTACKER_TYPES; std::unordered_map attacker_type_to_attributes; for (size_t i = 1; i <= Constants::NO_OF_ATTACKER_TYPES; i++) { - unsigned hp, range, attack_power, speed, price, is_aerial; - std::cin >> hp >> range >> attack_power >> speed >> price >> is_aerial; + unsigned hp, range, attack_power, speed, price, is_aerial , weight, num_ability_turns, ability_activation_cost; + std::cin >> hp >> range >> attack_power >> speed >> price >> is_aerial >> weight >> num_ability_turns >> ability_activation_cost; attacker_type_to_attributes.insert( - std::make_pair(i, Attributes(hp, range, attack_power, speed, price, is_aerial))); + std::make_pair(i, Attributes(hp, range, attack_power, speed, price, is_aerial,weight, num_ability_turns,ability_activation_cost))); } Constants::ATTACKER_TYPE_ATTRIBUTES = attacker_type_to_attributes; @@ -24,7 +25,7 @@ void init_constants() { unsigned hp, range, attack_power, speed, price, is_aerial; std::cin >> hp >> range >> attack_power >> speed >> price >> is_aerial; defender_type_to_attributes.insert( - std::make_pair(i, Attributes(hp, range, attack_power, speed, price, is_aerial))); + std::make_pair(i, Attributes(hp, range, attack_power, speed, price, is_aerial,0,0,0))); } Constants::DEFENDER_TYPE_ATTRIBUTES = defender_type_to_attributes; } @@ -36,19 +37,19 @@ Map get_initial_map() { return map; } -void output(State &state, Game &game) { - - // Player logs are logged to cerr, so that driver will collect it +void output(size_t turn_no, Game &game) { game.logr().flush(); + if (!game.logr().view().empty()) { - std::cerr << "TURN " << state.get_turn_no() << '\n'; - std::cerr << game.logr().view() << '\n'; - std::cerr << "ENDLOG" << std::endl; + all_logs << "TURN " << turn_no << std::endl; + all_logs << game.logr().view() << std::endl; + all_logs << "ENDLOG" << std::endl; } // Game details logged const auto &spawn_positions = game.get_spawn_positions(); const auto &player_set_targets = game.get_player_set_targets(); + const auto &ability_activations = game.get_ability_activations(); std::cout << spawn_positions.size() << std::endl; for (const auto &entry : spawn_positions) { @@ -60,10 +61,17 @@ void output(State &state, Game &game) { for (const auto &entry : player_set_targets) { std::cout << entry.first << " " << entry.second << std::endl; } + + std::cout << ability_activations.size() << std::endl; + for (const auto &attacker_id : ability_activations) { + std::cout << attacker_id << std::endl; + } } void dump_logs(State &state, Game &game) {} +void dump_logs(PvPState& state, Game &game) {} + State next_state(size_t cur_turn_no) { size_t no_of_active_defenders; size_t no_of_active_attackers; @@ -71,9 +79,9 @@ State next_state(size_t cur_turn_no) { std::cin >> no_of_active_attackers; std::vector attackers; for (size_t i = 0; i < no_of_active_attackers; i++) { - size_t id, hp, x, y, type; - std::cin >> id >> x >> y >> type >> hp; - attackers.push_back(Attacker(id, hp, type, Position(x, y))); + size_t id, hp, x, y, type, is_ability_active; + std::cin >> id >> x >> y >> type >> hp >> is_ability_active; + attackers.push_back(Attacker(id, hp, type, Position(x, y), is_ability_active)); } std::cin >> no_of_active_defenders; @@ -90,19 +98,87 @@ State next_state(size_t cur_turn_no) { return {move(attackers), move(defenders), coins_left, cur_turn_no + 1}; } -int main() { - init_constants(); - Map initial_map = get_initial_map(); +PvPState pvp_next_state(size_t cur_turn_no) { + size_t no_of_active_attackers; + size_t no_of_opponent_attackers; - State state({}, initial_map.spawn_defenders(), Constants::MAX_NO_OF_COINS, 0); + std::cin >> no_of_active_attackers; + std::vector attackers; + for (size_t i = 0; i < no_of_active_attackers; i++) { + size_t id, hp, x, y, type, is_ability_active; + std::cin >> id >> x >> y >> type >> hp >> is_ability_active; + attackers.push_back(Attacker(id, hp, type, Position(x, y), is_ability_active)); + } + + std::cin >> no_of_opponent_attackers; + std::vector opponent_attackers; + for (size_t i = 0; i < no_of_opponent_attackers; i++) { + size_t id, hp, x, y, type, is_ability_active; + std::cin >> id >> x >> y >> type >> hp >> is_ability_active; + opponent_attackers.push_back(Attacker(id, hp, type, Position(x, y), is_ability_active)); + } + + return {move(attackers), move(opponent_attackers), Constants::PVP_FIXED_COINS, cur_turn_no + 1}; +} + +enum class GameType { + NORMAL, + PVP +}; - auto game = run(state); +GameType string_to_game_type(std::string type) { + if (type == std::string("normal")) { + return GameType::NORMAL; + } else { + return GameType::PVP; + } +} - output(state, game); +int main(int argc, char** argv) { + + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " [game-type]\n"; + exit(1); + } - for (size_t i = 0; i < Constants::NO_OF_TURNS; i++) { - state = next_state(state.get_turn_no()); - game = run(state); - output(state, game); + auto gameType = string_to_game_type(std::string(argv[1])); + + switch (gameType) { + case GameType::NORMAL: { + std::cin >> Constants::NO_OF_TURNS >> Constants::MAX_NO_OF_COINS; + init_constants(); + Map initial_map = get_initial_map(); + State state({}, initial_map.spawn_defenders(), Constants::MAX_NO_OF_COINS, 0); + + auto game = run(state); + output(state.get_turn_no(), game); + + for (size_t i = 0; i < Constants::NO_OF_TURNS; i++) { + state = next_state(state.get_turn_no()); + game = run(state); + output(state.get_turn_no(), game); + } + + break; + } + + case GameType::PVP: { + std::cin >> Constants::NO_OF_TURNS >> Constants::PVP_FIXED_COINS; + init_constants(); + PvPState state({},{},Constants::PVP_FIXED_COINS, 0); + + auto game = run(state); + output(state.get_turn_no(), game); + + for (size_t i = 0; i < Constants::NO_OF_TURNS; i++) { + state = pvp_next_state(state.get_turn_no()); + game = run(state); + output(state.get_turn_no(), game); + } + + break; + } } + + std::cerr << all_logs.str() << std::endl; } diff --git a/cpp/player_code.cpp b/cpp/player_code.cpp index b091f9d..568f2bd 100644 --- a/cpp/player_code.cpp +++ b/cpp/player_code.cpp @@ -10,9 +10,9 @@ #include Attributes::Attributes(unsigned hp, unsigned range, unsigned attack_power, - unsigned speed, unsigned price, unsigned is_aerial) + unsigned speed, unsigned price, unsigned is_aerial, unsigned weight, unsigned num_ability_turns, unsigned ability_activation_cost) : hp(hp), range(range), attack_power(attack_power), speed(speed), - price(price), is_aerial(is_aerial) {} + price(price), is_aerial(is_aerial), weight(weight), num_ability_turns(num_ability_turns), ability_activation_cost(ability_activation_cost) {} Position::Position(int x, int y) : _x(x), _y(y) {} @@ -73,8 +73,8 @@ size_t Actor::get_hp() const { return _hp; } size_t Actor::get_type() const { return _type; } Position Actor::get_position() const { return _position; } -Attacker::Attacker(size_t id, size_t hp, size_t type, Position pos) - : Actor(id, hp, type, pos) {} +Attacker::Attacker(size_t id, size_t hp, size_t type, Position pos, size_t is_ability_active) + : Actor(id, hp, type, pos), is_ability_active(is_ability_active) {} Defender::Defender(size_t id, size_t hp, size_t type, Position pos) : Actor(id, hp, type, pos) {} @@ -93,6 +93,21 @@ const std::vector &State::get_defenders() const { size_t State::get_turn_no() const { return this->_turn_no; } size_t State::get_coins_left() const { return this->_no_of_coins_left; } +PvPState::PvPState(std::vector attackers, std::vector opponent_attackers, + size_t no_of_coins_left, size_t turn_no) + : _turn_no(turn_no), _no_of_coins_left(no_of_coins_left), + _attackers(std::move(attackers)) {} + +const std::vector &PvPState::get_attackers() const { + return this->_attackers; +} + +const std::vector &PvPState::get_opponent_attackers() const { + return this->_opponent_attackers; +} +size_t PvPState::get_turn_no() const { return this->_turn_no; } +size_t PvPState::get_coins_left() const { return this->_no_of_coins_left; } + Game::Game() {} void Game::spawn_attacker(size_t id, Position pos) { @@ -109,6 +124,15 @@ void Game::set_target(const Attacker &attacker, const Defender &defender) { this->_player_set_targets.insert({attacker.get_id(), defender.get_id()}); } +void Game::set_target(const Attacker &attacker, const Attacker &opponent) { + this->_player_set_targets.insert({attacker.get_id(), opponent.get_id()}); +} + +void Game::activate_ability(size_t attacker_id) { + this->_ability_activations.push_back(attacker_id); + this-> already_activated_attacker_ids.insert(attacker_id); +} + std::ostringstream &Game::logr() { return this->_logr; } const std::unordered_map &Game::get_player_set_targets() const { @@ -122,6 +146,10 @@ const std::set &Game::get_already_spawned_positions() const { return this->_already_spawned_positions; } +const std::vector &Game::get_ability_activations() const { + return this->_ability_activations; +} + Map::Map(std::vector> map_as_grid) : _grid(move(map_as_grid)) {} @@ -171,4 +199,4 @@ std::vector Map::spawn_defenders() const { } } return defenders; -} +} \ No newline at end of file diff --git a/cpp/player_code.h b/cpp/player_code.h index 61dbd7a..dee7757 100644 --- a/cpp/player_code.h +++ b/cpp/player_code.h @@ -14,17 +14,21 @@ struct Attributes { const unsigned speed; const unsigned price; const unsigned is_aerial; + const unsigned weight; + const unsigned num_ability_turns; + const unsigned ability_activation_cost; Attributes(unsigned hp, unsigned range, unsigned attack_power, unsigned speed, - unsigned price, unsigned is_aerial); + unsigned price, unsigned is_aerial, unsigned weight, unsigned num_ability_turns, unsigned ability_activation_cost); }; struct Constants { -static inline size_t MAP_NO_OF_ROWS; -static inline size_t MAP_NO_OF_COLS; +static inline size_t MAP_NO_OF_ROWS = 64; +static inline size_t MAP_NO_OF_COLS = 64; static inline size_t NO_OF_DEFENDER_TYPES; static inline size_t NO_OF_ATTACKER_TYPES; static inline size_t NO_OF_TURNS; static inline size_t MAX_NO_OF_COINS; +static inline size_t PVP_FIXED_COINS = 100; static inline std::unordered_map ATTACKER_TYPE_ATTRIBUTES; static inline std::unordered_map DEFENDER_TYPE_ATTRIBUTES; @@ -69,7 +73,8 @@ class Actor { class Attacker : public Actor { public: - Attacker(size_t id, size_t hp, size_t type, Position pos); + Attacker(size_t id, size_t hp, size_t type, Position pos, size_t is_ability_active); + size_t is_ability_active; }; class Defender : public Actor { @@ -94,23 +99,44 @@ class State { std::vector _defenders; }; -class Game { - std::unordered_map _player_set_targets; - std::vector> _spawn_postions; - std::set _already_spawned_positions; - std::ostringstream _logr; +class PvPState { +public: + PvPState(std::vector attackers,std::vector opponent_attackers, size_t no_of_coins_left, size_t turn_no); + + const std::vector &get_attackers() const; + const std::vector &get_opponent_attackers() const; + size_t get_turn_no() const; + size_t get_coins_left() const; +private: + size_t _turn_no; + size_t _no_of_coins_left; + std::vector _attackers; + std::vector _opponent_attackers; +}; +class Game { public: Game(); void spawn_attacker(size_t id, Position pos); bool already_spawned_at_position(Position pos); void set_target(size_t attacker_id, size_t defender_id); void set_target(const Attacker &attacker, const Defender &defender); + void set_target(const Attacker &attacker, const Attacker &opponent); + void activate_ability(size_t attacker_id); std::ostringstream &logr(); const std::unordered_map &get_player_set_targets() const; const std::vector> &get_spawn_positions() const; const std::set &get_already_spawned_positions() const; + const std::vector &get_ability_activations() const; + static inline std::set already_activated_attacker_ids; +private: + std::unordered_map _player_set_targets; + std::vector> _spawn_postions; + std::set _already_spawned_positions; + std::vector _ability_activations; + + std::ostringstream _logr; }; class Map { @@ -121,13 +147,14 @@ class Map { [[nodiscard]] std::vector spawn_defenders() const; - static inline size_t no_of_rows; - static inline size_t no_of_cols; + static inline size_t no_of_rows = 64; + static inline size_t no_of_cols = 64; private: std::vector> _grid; }; Game run(const State &state); +Game run(const PvPState &state); -#define logger game.logr() +#define logger game.logr() \ No newline at end of file diff --git a/cpp/run b/cpp/run index e69de29..c20b32f 100755 Binary files a/cpp/run and b/cpp/run differ diff --git a/cpp/run.cpp b/cpp/run.cpp index 6bb4209..17492f0 100644 --- a/cpp/run.cpp +++ b/cpp/run.cpp @@ -85,6 +85,11 @@ Game run(const State &state) { if (!attackers.empty() && !defenders.empty()) { // check if they are empty beforehand to be safe from unexpected errors game.set_target(attackers.front(), defenders.front()); + //lets say i want to activate the ability of the first attacker + //check if ability wasnt activated before to avoid getting penalized + if (!Game::already_activated_attacker_ids.contains(attackers.front().get_id())){ + game.activate_ability(attackers.front().get_id()); + } } // Lets log all the spawned positions for this turn @@ -96,4 +101,4 @@ Game run(const State &state) { // always return the game object return game; -} +} \ No newline at end of file diff --git a/cpp/runner.Dockerfile b/cpp/runner.Dockerfile index f3a091c..63da2a3 100644 --- a/cpp/runner.Dockerfile +++ b/cpp/runner.Dockerfile @@ -1,3 +1,3 @@ -FROM gcr.io/distroless/cc-debian11 +FROM gcr.io/distroless/cc-debian12 ENTRYPOINT [ "./player_code" ] diff --git a/cpp/runpvp.cpp b/cpp/runpvp.cpp new file mode 100644 index 0000000..e540c2c --- /dev/null +++ b/cpp/runpvp.cpp @@ -0,0 +1,99 @@ +#include "player_code.h" + +// This initial code is well commented and serves as a small tutorial for game +// APIs, for more information you can refer to the documentation + +// This is the function player has to fill +// You can define any new functions here that you want +Game run(const PvPState &state) { + + // Always start by instantiating a Game class object + Game game; + + size_t remaining_coins = state.get_coins_left(); + + game.logr() << "TURN " << state.get_turn_no() << " LOGS:"; + + // Get all the attackers and opponent in the game and store it + const std::vector &attackers = state.get_attackers(); + const std::vector &opponent_attackers = state.get_opponent_attackers(); + + // The function get_all_valid_spawn_positions() is a helper which will give us + // the list of valid spawn positions in map. + // If the position we're spawning is not one of these, the player will be + // penalized by deducting the spawn cost but not spawning the attacker + std::vector all_valid_spawn_positions = + get_all_valid_spawn_positions(); + + // Lets say I want to spawn an attacker of each of the type in one turn + // and I want to use the all_valid_spawn_positions list as well. In order to + // keep traack of the last index in the list that we spawned at, we can use a + // static variable in c++ + + static int last_spawned = 0; + for (size_t type_id = 1; type_id <= Constants::NO_OF_ATTACKER_TYPES; + type_id++) { + // Spawn the attacker of type_id at position + // all_valid_spawn_positions[last_spawned] + + // There are two cases when you might be panalized + // - Spawning at invalid position + // - Spawning at position where you have already spawned one attacker + // in the same turn + // + // We have provided helpers to check just that + + // game class will keep track of all your spawned positions for you and + // provides a helper method called already_spawned_at_position(Position) + // to check if you already spawned in the position + + // Mostly a good practice to check with these two helpers before spawning, + // to save up on accidental penalties + if (is_valid_spawn_position(all_valid_spawn_positions[last_spawned]) && + !game.already_spawned_at_position( + all_valid_spawn_positions[last_spawned])) { + // If lets say you had run out of coins left, the game will just ignore + // the spawn + game.spawn_attacker(type_id, all_valid_spawn_positions[last_spawned]); + + // This has the starting attributes for the attacker we are about to + // spawn + // For full information about the Attributes class refer the + // documentation + // This can be used for strategizing + Attributes attackers_attributes = + Constants::ATTACKER_TYPE_ATTRIBUTES.at(type_id); + + // You can use the logger we provide to show log messages in the + // rendered game + game.logr() << "To to be spawned at Position(" + << all_valid_spawn_positions[last_spawned].get_x() << "," + << all_valid_spawn_positions[last_spawned].get_y() << ")" + << '\n'; + (last_spawned += 1) %= all_valid_spawn_positions.size(); + } + } + + // Now lets say you always want to set the target for the attackers[0] to + // defenders[0] + // To do that you do + if (!attackers.empty() && !opponent_attackers.empty()) { + // check if they are empty beforehand to be safe from unexpected errors + game.set_target(attackers.front(), opponent_attackers.front()); + //lets say i want to activate the ability of the first attacker + //check if ability wasnt activated before to avoid getting penalized + if (!Game::already_activated_attacker_ids.contains(attackers.front().get_id())){ + game.activate_ability(attackers.front().get_id()); + } + } + + // Lets log all the spawned positions for this turn + for (auto &[type_id, pos] : game.get_spawn_positions()) { + // you can use logger macro as well, which is an alias for game.logr() + logger << "Type " << type_id << " at Position (" << pos.get_x() << "," + << pos.get_y() << ")\n"; + } + + // always return the game object + return game; +} \ No newline at end of file diff --git a/java/Attacker.java b/java/Attacker.java index 7788211..596e444 100644 --- a/java/Attacker.java +++ b/java/Attacker.java @@ -1,5 +1,7 @@ public class Attacker extends Actor { - public Attacker(int id, int hp, int type, Position pos) { + public final int is_ability_active; + public Attacker(int id, int hp, int type, Position pos, int is_ability_active) { super(id, hp, type, pos); + this.is_ability_active = is_ability_active; } } diff --git a/java/Attributes.java b/java/Attributes.java index 37bc3e1..3adaa28 100644 --- a/java/Attributes.java +++ b/java/Attributes.java @@ -5,13 +5,19 @@ public class Attributes { public final int speed; public final int price; public final int is_aerial; + public final int weight; + public final int num_ability_turns; + public final int ability_activation_cost; - public Attributes(int hp, int range, int attackPower, int speed, int price, int is_aerial) { + public Attributes(int hp, int range, int attackPower, int speed, int price, int is_aerial, int weight, int num_ability_turns, int ability_activation_cost) { this.hp = hp; this.range = range; this.attackPower = attackPower; this.speed = speed; this.price = price; this.is_aerial = is_aerial; + this.weight = weight; + this.num_ability_turns = num_ability_turns; + this.ability_activation_cost = ability_activation_cost; } } diff --git a/java/Constants.java b/java/Constants.java index f2b5583..de7e5bd 100644 --- a/java/Constants.java +++ b/java/Constants.java @@ -1,12 +1,13 @@ import java.util.Map; public class Constants { - public static int MAP_NO_OF_ROWS; - public static int MAP_NO_OF_COLS; + public static int MAP_NO_OF_ROWS = 64; + public static int MAP_NO_OF_COLS = 64; public static int NO_OF_DEFENDER_TYPES; public static int NO_OF_ATTACKER_TYPES; public static int NO_OF_TURNS; public static int MAX_NO_OF_COINS; + public static int PVP_FIXED_COINS; public static Map ATTACKER_TYPE_ATTRIBUTES; public static Map DEFENDER_TYPE_ATTRIBUTES; diff --git a/java/Game.java b/java/Game.java index 2cc2703..30d7811 100644 --- a/java/Game.java +++ b/java/Game.java @@ -10,12 +10,15 @@ public class Game { private final List _spawnPositions; private final Set _alreadySpawnedPositions; private final StringBuilder _logr; + private final List _ability_activations; + public static Set already_activated_attacker_ids = new HashSet<>(); public Game() { _playerSetTargets = new HashMap<>(); _spawnPositions = new ArrayList<>(); _alreadySpawnedPositions = new HashSet<>(); _logr = new StringBuilder(); + _ability_activations = new ArrayList<>(); } public void spawnAttacker(int id, Position pos) { @@ -23,6 +26,11 @@ public void spawnAttacker(int id, Position pos) { _alreadySpawnedPositions.add(pos); } + public void activateAbility(int attacker_id) { + already_activated_attacker_ids.add(attacker_id); + _ability_activations.add(attacker_id); + } + public List getSpawnPositions() { return _spawnPositions; } @@ -31,6 +39,10 @@ public Map getPlayerSetTargets() { return _playerSetTargets; } + public List getAbilityActivations() { + return _ability_activations; + } + public boolean alreadySpawnedAtPosition(Position pos) { return _alreadySpawnedPositions.contains(pos); } @@ -43,6 +55,10 @@ public void setTarget(Attacker attacker, Defender defender) { setTarget(attacker.getId(), defender.getId()); } + public void setTarget(Attacker attacker, Attacker opponent_attacker) { + setTarget(attacker.getId(), opponent_attacker.getId()); + } + public void log(String s) { _logr.append(s + "\n"); } diff --git a/java/Main.java b/java/Main.java index df9d162..5139095 100644 --- a/java/Main.java +++ b/java/Main.java @@ -7,13 +7,32 @@ public class Main { private static final Scanner in = new Scanner(System.in); + + private static final StringBuilder allLogs = new StringBuilder(); + + private enum GameType { + NORMAL,PVP + } + + private static GameType stringToGameType(String gameType) { + switch (gameType.toLowerCase()) { + case "normal": + return GameType.NORMAL; + case "pvp": + return GameType.PVP; + default: + System.err.println("Usage: java Main [game-type]"); + System.exit(1); + } + return GameType.NORMAL; + } private static State nextState(int currentTurnNo) { int noOfActiveAttackers = in.nextInt(); List attackers = new ArrayList<>(); for (int i = 0; i < noOfActiveAttackers; i++) { attackers.add( - new Attacker(in.nextInt(), in.nextInt(), in.nextInt(), new Position(in.nextInt(), in.nextInt()))); + new Attacker(in.nextInt(), in.nextInt(), in.nextInt(), new Position(in.nextInt(), in.nextInt()), in.nextInt())); } int noOfActiveDefenders = in.nextInt(); @@ -28,6 +47,24 @@ private static State nextState(int currentTurnNo) { return new State(attackers, defenders, coinsLeft, currentTurnNo + 1); } + private static PvPState nextPvPState(int currentTurnNo) { + int noOfActiveAttackers = in.nextInt(); + List attackers = new ArrayList<>(); + for (int i = 0; i < noOfActiveAttackers; i++) { + attackers.add( + new Attacker(in.nextInt(), in.nextInt(), in.nextInt(), new Position(in.nextInt(), in.nextInt()), in.nextInt())); + } + + int noOfActiveOpponentAttackers = in.nextInt(); + List opponentAttackers = new ArrayList<>(); + for (int i = 0; i < noOfActiveOpponentAttackers; i++) { + opponentAttackers.add( + new Attacker(in.nextInt(), in.nextInt(), in.nextInt(), new Position(in.nextInt(), in.nextInt()),in.nextInt())); + } + + return new PvPState(attackers, opponentAttackers, Constants.PVP_FIXED_COINS, currentTurnNo + 1); + } + private static GameMap getInitialMap() { Constants.MAP_NO_OF_ROWS = in.nextInt(); Constants.MAP_NO_OF_COLS = in.nextInt(); @@ -42,17 +79,18 @@ private static GameMap getInitialMap() { return new GameMap(grid); } - private static void output(State state, Game game) { + private static void output(int turnNo, Game game) { String log = game.getLog(); if (!log.isEmpty()) { - System.err.println("TURN " + state.getTurnNo()); - System.err.println(log); - System.err.println("ENDLOG"); + allLogs.append("TURN " + turnNo + "\n"); + allLogs.append(log + "\n"); + allLogs.append("ENDLOG\n"); } List spawnPositions = game.getSpawnPositions(); + List abilityActivations = game.getAbilityActivations(); System.out.println(spawnPositions.size()); for (SpawnDetail entry : spawnPositions) { @@ -64,41 +102,80 @@ private static void output(State state, Game game) { for (Map.Entry entry : playerSetTargets.entrySet()) { System.out.println(entry.getKey() + " " + entry.getValue()); } + + System.out.println(abilityActivations.size()); + for (Integer attacker_id : abilityActivations) { + System.out.println(attacker_id); + } } public static void main(String[] args) { - Constants.NO_OF_TURNS = in.nextInt(); - Constants.MAX_NO_OF_COINS = in.nextInt(); + if(args.length < 1) { + System.err.println("Usage: java Main [game-type]"); + System.exit(1); + } + + GameType gameType = stringToGameType(args[0]); + + Constants.NO_OF_TURNS = in.nextInt(); + if(gameType == GameType.PVP) { + Constants.PVP_FIXED_COINS = in.nextInt(); + } + else{ + Constants.MAX_NO_OF_COINS = in.nextInt(); + } Constants.NO_OF_ATTACKER_TYPES = in.nextInt(); Constants.ATTACKER_TYPE_ATTRIBUTES = new HashMap(); for (int i = 1; i <= Constants.NO_OF_ATTACKER_TYPES; i++) { Constants.ATTACKER_TYPE_ATTRIBUTES.put(i, - new Attributes(in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt())); + new Attributes(in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt())); } Constants.NO_OF_DEFENDER_TYPES = in.nextInt(); Constants.DEFENDER_TYPE_ATTRIBUTES = new HashMap(); for (int i = 1; i <= Constants.NO_OF_DEFENDER_TYPES; i++) { Constants.DEFENDER_TYPE_ATTRIBUTES.put(i, - new Attributes(in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt())); + new Attributes(in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(), in.nextInt(),0,0,0)); } - GameMap map = getInitialMap(); - List defenders = map.spawnDefenders(); - - State state = new State(new ArrayList<>(), defenders, Constants.MAX_NO_OF_COINS, 0); - - Run run = new Run(); - Game game = run.run(state); - output(state, game); - - for (int i = 0; i < Constants.NO_OF_TURNS; i++) { - state = nextState(state.getTurnNo()); - game = run.run(state); - output(state, game); + switch (gameType) { + + case NORMAL: + + GameMap map = getInitialMap(); + List defenders = map.spawnDefenders(); + + State state = new State(new ArrayList<>(), defenders, Constants.MAX_NO_OF_COINS, 0); + + Run run = new Run(); + Game game = run.run(state); + output(state.getTurnNo(), game); + + for (int i = 0; i < Constants.NO_OF_TURNS; i++) { + state = nextState(state.getTurnNo()); + game = run.run(state); + output(state.getTurnNo(), game); + } + + in.close(); + break; + + case PVP: + + PvPState pvpState = new PvPState(new ArrayList<>(), new ArrayList<>(), Constants.PVP_FIXED_COINS, 0); + RunPvP runPvP = new RunPvP(); + Game pvpGame = runPvP.run(pvpState); + output(pvpState.getTurnNo(), pvpGame); + + for (int i = 0; i < Constants.NO_OF_TURNS; i++) { + pvpState = nextPvPState(pvpState.getTurnNo()); + pvpGame = runPvP.run(pvpState); + output(pvpState.getTurnNo(), pvpGame); + } + break; } - in.close(); + System.err.println(allLogs.toString()); } } diff --git a/java/PvPState.java b/java/PvPState.java new file mode 100644 index 0000000..5c0b9f4 --- /dev/null +++ b/java/PvPState.java @@ -0,0 +1,32 @@ +import java.util.List; + +public class PvPState { + private final List _attackers; + private final List _opponentAttackers; + private final int _noOfCoinsLeft; + private final int _turnNo; + + public PvPState(List attackers, List opponentAttackers, + int noOfCoinsLeft, int turnNo) { + _attackers = attackers; + _opponentAttackers = opponentAttackers; + _noOfCoinsLeft = noOfCoinsLeft; + _turnNo = turnNo; + } + + public List getAttackers() { + return _attackers; + } + + public List getOpponentAttackers() { + return _opponentAttackers; + } + + public int getTurnNo() { + return _turnNo; + } + + public int getCoinsLeft() { + return _noOfCoinsLeft; + } +} diff --git a/java/Run.java b/java/Run.java index 208a6f8..ae1afc4 100644 --- a/java/Run.java +++ b/java/Run.java @@ -1,5 +1,6 @@ import java.util.ArrayList; import java.util.List; +import java.util.*; // This initial code is well commented and serves as a small tutorial for game // APIs, for more information you can refer to the documentation @@ -95,6 +96,11 @@ public Game run(State state) { if (!attackers.isEmpty() && !defenders.isEmpty()) { // check if they are empty beforehand to be safe from unexpected errors game.setTarget(attackers.get(0).getId(), defenders.get(0).getId()); + //lets say i want to activate the ability of the first attacker + //check if ability wasnt activated before to avoid getting penalized + if (!Game.already_activated_attacker_ids.contains(attackers.get(0).getId())) { + game.activateAbility(attackers.get(0).getId()); + } } // Lets log all the spawned positions for this turn diff --git a/java/RunPvP.java b/java/RunPvP.java new file mode 100644 index 0000000..cc2bef0 --- /dev/null +++ b/java/RunPvP.java @@ -0,0 +1,112 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.*; + +// This initial code is well commented and serves as a small tutorial for game +// APIs, for more information you can refer to the documentation + +// This is the function player has to fill +// You can define any new functions here that you want +public class RunPvP { + + // Lets say I want to spawn an attacker of each of the type in one turn + // and I want to use the Helpers.getAllValidSpawnPositions list as well. In + // order to keep track of the last index in the list that we spawned at, we can + // use a private variable. We'll initialize it in the constructor for this + // class. This class is only instantiated once so it's safe to use a private + // variable. + private int lastSpawned; + + public RunPvP() { + this.lastSpawned = 0; + } + + public Game run(PvPState state) { + + // Always start by instantiating a Game class object + Game game = new Game(); + + int remainingCoins = state.getCoinsLeft(); + game.log("TURN " + state.getTurnNo() + " LOGS:"); + + // Get all the attackers and opponent in the game and store it + List attackers = state.getAttackers(); + List opponentAttackers = state.getOpponentAttackers(); + + // The function get_all_valid_spawn_positions() is a helper which will give us + // the list of valid spawn positions in map. + // If the position we're spawning is not one of these, the player will be + // penalized by deducting the spawn cost but not spawning the attacker + + // The Helpers.getAllValidSpawnPositions() function + // returns a Set, if we want to index on it, + // we must convert to an indexable collection + List allValidSpawnPositons = new ArrayList(Helpers.getAllValidSpawnPositions()); + + for (int typeId = 1; typeId <= Constants.NO_OF_ATTACKER_TYPES; typeId++) { + // Spawn the attacker of typeId at position + // allValidSpawnPositions[last_spawned] + + // There are two cases when you might be panalized + // - Spawning at invalid position + // - Spawning at position where you have already spawned one attacker + // in the same turn + // + // We have provided helpers to check just that + + // game class will keep track of all your spawned positions for you and + // provides a helper method called already_spawned_at_position(Position) + // to check if you already spawned in the position + + // Mostly a good practice to check with these two helpers before spawning, + // to save up on accidental penalties + // + + if (Helpers.isValidSpawnPosition(allValidSpawnPositons.get(lastSpawned)) && + !game.alreadySpawnedAtPosition( + allValidSpawnPositons.get(lastSpawned))) { + + // If lets say you had run out of coins left, the game will just ignore + // the spawn + game.spawnAttacker(typeId, allValidSpawnPositons.get(lastSpawned)); + + // This has the starting attributes for the attacker we are about to + // spawn + // For full information about the Attributes class refer the + // documentation + // This information can be used for strategizing + Attributes attackersAttributes = Constants.ATTACKER_TYPE_ATTRIBUTES.get(typeId); + + Position pos = allValidSpawnPositons.get(lastSpawned); + game.log(String.format("To be spawned at Position(%d,%d)\n", pos.getX(), pos.getY())); + + lastSpawned += 1; + lastSpawned %= allValidSpawnPositons.size(); + } + + } + + // Now lets say you always want to set the target for the attackers[0] to + // defenders[0] + // To do that you do + if (!attackers.isEmpty() && !opponentAttackers.isEmpty()) { + // check if they are empty beforehand to be safe from unexpected errors + game.setTarget(attackers.get(0).getId(), opponentAttackers.get(0).getId()); + //lets say i want to activate the ability of the first attacker + //check if ability wasnt activated before to avoid getting penalized + if (!Game.already_activated_attacker_ids.contains(attackers.get(0).getId())) { + game.activateAbility(attackers.get(0).getId()); + } + } + + // Lets log all the spawned positions for this turn + for (var entry : game.getSpawnPositions()) { + game.log(String.format("Type %d at Position(%d, %d)", + entry.getTypeId(), entry.getPos().getX(), + entry.getPos().getY())); + } + + // always return the game object + return game; + } +} diff --git a/python/main.py b/python/main.py index f10d4dd..440d358 100644 --- a/python/main.py +++ b/python/main.py @@ -1,13 +1,26 @@ import sys -from run import Position, Attacker, Defender, Constants, Map, State, Game, run +from player_code import Position, Attacker, Defender, Constants, Map, State, Game, PvPState, GameType +from run import run +from runpvp import run_pvp -def output(state: State, game: Game): +all_logs: str = "" + + +def string_to_game_type(game_type_str) -> GameType: + if game_type_str.lower() == "normal": + return GameType.NORMAL + else: + return GameType.PVP + + +def output(turn_no: int, game: Game): log_line = game.get_log() if log_line and len(log_line)!=0: - sys.stderr.write(f"TURN {state.turn_no}\n") - sys.stderr.write(log_line) - sys.stderr.write(f"ENDLOG\n") + global all_logs + all_logs += "TURN " + str(turn_no) + "\n" + all_logs += log_line + all_logs += "ENDLOG\n" sys.stdout.write(f"{len(game.spawn_positions)}\n") for id, position in game.spawn_positions: @@ -16,6 +29,10 @@ def output(state: State, game: Game): sys.stdout.write(f"{len(game.player_set_targets)}\n") for (attacker_id, defender_id) in game.player_set_targets.items(): sys.stdout.write(f"{attacker_id} {defender_id}\n") + + sys.stdout.write(f"{len(game.ability_activations)}\n") + for attacker_id in game.ability_activations: + sys.stdout.write(f"{attacker_id}\n") def next_state(cur_turn_no: int) -> State: @@ -23,9 +40,9 @@ def next_state(cur_turn_no: int) -> State: attackers = [] for _ in range(no_of_active_attackers): - id, x, y, a_type, hp = map(int, sys.stdin.readline().split()) + id, x, y, a_type, hp, is_ability_active = map(int, sys.stdin.readline().split()) attackers.append( - Attacker(id, hp, Constants.ATTACKER_TYPE_ATTRIBUTES[a_type], Position(x, y)) + Attacker(id, hp, Constants.ATTACKER_TYPE_ATTRIBUTES[a_type], Position(x, y), is_ability_active) ) no_of_active_defenders = int(sys.stdin.readline()) @@ -41,17 +58,60 @@ def next_state(cur_turn_no: int) -> State: return State(attackers, defenders, no_of_coins_left, cur_turn_no + 1) +def next_pvp_state(cur_turn_no: int) -> PvPState: + no_of_active_attackers = int(sys.stdin.readline()) + attackers = [] + + for _ in range(no_of_active_attackers): + id, x, y, a_type, hp, is_ability_active = map(int, sys.stdin.readline().split()) + attackers.append( + Attacker(id, hp, Constants.ATTACKER_TYPE_ATTRIBUTES[a_type], Position(x, y), is_ability_active) + ) + + no_of_opponent_attackers = int(sys.stdin.readline()) + opponent_attackers = [] -Constants.initialize() -Map.initialize() + for _ in range(no_of_opponent_attackers): + id, x, y, d_type, hp, is_ability_active = map(int, sys.stdin.readline().split()) + opponent_attackers.append( + Attacker(id, hp, Constants.DEFENDER_TYPE_ATTRIBUTES[d_type], Position(x, y), is_ability_active) + ) -state = State([], Map.spawn_defenders(), Constants.MAX_NO_OF_COINS, 0) + return PvPState(attackers, opponent_attackers, Constants.PVP_FIXED_COINS, cur_turn_no + 1) -game = run(state) -output(state, game) +if len(sys.argv) < 2: + sys.stderr.write(f"Usage: {sys.argv[0]} [game-type]") + sys.exit(1) + +game_type = string_to_game_type(sys.argv[1]) + +Constants.initialize(game_type) + +if game_type == GameType.NORMAL: + Map.initialize() + + state = State([], Map.spawn_defenders(), Constants.MAX_NO_OF_COINS, 0) -for i in range(Constants.NO_OF_TURNS): - state = next_state(state.turn_no) game = run(state) - output(state, game) + + output(state.turn_no, game) + + for i in range(Constants.NO_OF_TURNS): + state = next_state(state.turn_no) + game = run(state) + output(state.turn_no, game) + +elif game_type == GameType.PVP: + state = PvPState([], [], Constants.PVP_FIXED_COINS, 0) + + game = run_pvp(state) + + output(state.turn_no, game) + + for i in range(Constants.NO_OF_TURNS): + state = next_pvp_state(state.turn_no) + game = run_pvp(state) + output(state.turn_no, game) + +sys.stderr.write(all_logs) diff --git a/python/player_code.py b/python/player_code.py index bd06341..b0f69de 100644 --- a/python/player_code.py +++ b/python/player_code.py @@ -1,5 +1,10 @@ from dataclasses import dataclass import sys +from enum import Enum + +class GameType(Enum): + NORMAL = 1 + PVP = 2 @dataclass(eq=True, frozen=True, order=True) @@ -23,6 +28,9 @@ class ActorType: @dataclass(frozen=True) class AttackerType(ActorType): speed: int + weight: int + num_ability_turns: int + ability_activation_cost: int @dataclass(frozen=True) @@ -36,6 +44,7 @@ class Attacker: hp: int type: AttackerType position: Position + is_ability_active: int @dataclass(frozen=True) @@ -53,13 +62,21 @@ class State: no_of_coins_left: int turn_no: int +@dataclass(frozen=True) +class PvPState: + attackers: list[Attacker] + opponent_attackers: list[Attacker] + no_of_coins_left: int + turn_no: int class Game: + already_activated_attacker_ids: set[int] = set() def __init__(self): self._log = "" self.player_set_targets: dict[int, int] = {} self.spawn_positions: list[tuple[int, Position]] = [] self.already_spawned_positions: set[Position] = set() + self.ability_activations: list[int] = [] def spawn_attacker(self, id: int, position: Position): self.spawn_positions.append((id, position)) @@ -73,6 +90,11 @@ def set_target(self, attacker_id: int, defender_id: int): assert (type(defender_id)== int) self.player_set_targets[attacker_id] = defender_id + def activate_ability(self, attacker_id: int): + assert (type(attacker_id)== int) + self.ability_activations.append(attacker_id) + Game.already_activated_attacker_ids.add(attacker_id) + def log(self, line: str): self._log += line + "\n" @@ -92,16 +114,22 @@ class Constants: DEFENDER_TYPE_ATTRIBUTES: dict[int, DefenderType] MAP_NO_OF_ROWS: int MAP_NO_OF_COLS: int + PVP_FIXED_COINS: int = 1000 @classmethod - def initialize(cls): - cls.NO_OF_TURNS, cls.MAX_NO_OF_COINS = map(int, input().split()) + def initialize(cls,game_type:GameType): + cls.MAP_NO_OF_ROWS = 64 + cls.MAP_NO_OF_COLS = 64 + if game_type == GameType.PVP: + cls.NO_OF_TURNS, cls.PVP_FIXED_COINS = map(int, input().split()) + else: + cls.NO_OF_TURNS, cls.MAX_NO_OF_COINS = map(int, input().split()) cls.NO_OF_ATTACKER_TYPES = int(input()) cls.ATTACKER_TYPE_ATTRIBUTES = {} for i in range(1, cls.NO_OF_ATTACKER_TYPES + 1): - hp, a_range, attack_power, speed, price, is_aerial = map(int, input().split()) + hp, a_range, attack_power, speed, price, is_aerial, weight, num_ability_turns, ability_activation_cost = map(int, input().split()) cls.ATTACKER_TYPE_ATTRIBUTES[i] = AttackerType( - hp, a_range, attack_power, price, is_aerial, speed + hp, a_range, attack_power, price, is_aerial, speed, weight, num_ability_turns, ability_activation_cost ) cls.NO_OF_DEFENDER_TYPES = int(input()) diff --git a/python/run.py b/python/run.py index 0eafba4..da4c2ec 100644 --- a/python/run.py +++ b/python/run.py @@ -89,7 +89,11 @@ def run(state: State) -> Game: #To do that you do if len(attackers)!=0 and len(defenders)!=0: game.set_target(attackers[0].id,defenders[0].id) - + #lets say i want to activate the ability of the first attacker + #check if ability wasnt activated before to avoid getting penalized + if attackers[0].id not in Game.already_activated_attacker_ids: + game.activate_ability(attackers[0].id) + #Lets log all the spawned positions for this turn for type_id, pos in game.spawn_positions: game.log("Type {} at Position ({},{})". diff --git a/python/runpvp.py b/python/runpvp.py new file mode 100644 index 0000000..be88ca9 --- /dev/null +++ b/python/runpvp.py @@ -0,0 +1,100 @@ +from player_code import ( + Position, + Attacker, + AttackerType, + Constants, + PvPState, + Game, + is_valid_spawn_position, + get_all_valid_spawn_positions, +) + +# This initial code is well commented and serves as a small tutorial for game +# APIs, for more information you can refer to the documentation + +# This is the function player has to fill +# You can define any new functions here that you want + + +last_spawned = 0 + +def run_pvp(state: PvPState) -> Game: + global last_spawned + # Always start by instantiating a Game class object + game = Game() + + remaining_coins = state.no_of_coins_left + + game.log("TURN {} LOGS:\n".format(state.turn_no)) + + # Get all the attackers and opponent attackers in the game and store it + attackers = state.attackers + opponent_attackers = state.opponent_attackers + + # The function get_all_valid_spawn_positions() is a helper which will give us + # the list of valid spawn positions in map. + # If the position we're spawning is not one of these, the player will be + # penalized by deducting the spawn cost but not spawning the attacker + all_valid_spawn_positions = get_all_valid_spawn_positions() + + # If there's no defenders left,we can stop spawning and save up on coins, + # which are important for boosting game score + for type_id in range(1,Constants.NO_OF_ATTACKER_TYPES+1): + + # Spawn the attacker of type_id at position + # all_valid_spawn_positions[last_spawned] + + # There are two cases when you might be panalized + # - Spawning at invalid position + # - Spawning at position where you have already spawned one attacker + # in the same turn + + # We have provided helpers to check just that + + # game class will keep track of all your spawned positions for you and + # provides a helper method called is_already_spawned_at_position(Position) + # to check if you already spawned in the position + + # Mostly a good practice to check with these two helpers before spawning, + # to save up on accidental penalties + + if is_valid_spawn_position(all_valid_spawn_positions[last_spawned]) and\ + not game.is_already_spawned_at_position(all_valid_spawn_positions[last_spawned]): + + # If lets say you had run out of coins left, the game will just ignore + # the spawn + game.spawn_attacker(type_id, all_valid_spawn_positions[last_spawned]) + + # You can use the logger we provide to show log messages in the + # rendered game + # For full information about the AttackerType class refer the + # documentation + # This information can be used for strategizing + attackers_attributes: AttackerType = Constants.ATTACKER_TYPE_ATTRIBUTES[type_id] + + # You can use the logger we provide to show log messages in the + # rendered game + pos = all_valid_spawn_positions[last_spawned] + game.log("To be spawned at Position({},{})\n".format(pos.x,pos.y)) + + last_spawned = last_spawned + 1 + last_spawned = last_spawned % len(all_valid_spawn_positions) + + #Now lets say you always want to set the target for the attackers[0] to + #defenders[0] + #To do that you do + if len(attackers)!=0 and len(opponent_attackers)!=0: + game.set_target(attackers[0].id,opponent_attackers[0].id) + #lets say i want to activate the ability of the first attacker + #check if ability wasnt activated before to avoid getting penalized + if attackers[0].id not in Game.already_activated_attacker_ids: + game.activate_ability(attackers[0].id) + + #Lets log all the spawned positions for this turn + for type_id, pos in game.spawn_positions: + game.log("Type {} at Position ({},{})". + format(type_id, pos.x, pos.y)) + + #always return the game object + return game +