Created
March 31, 2018 03:35
-
-
Save KireinaHoro/0f9b74a6bff047c18ea53427ce51e02d to your computer and use it in GitHub Desktop.
This file contains 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 <algorithm> | |
#include <deque> | |
#include <iomanip> | |
#include <iostream> | |
#include <sstream> | |
#include <unordered_map> | |
#include <vector> | |
// useless headers | |
#include <cstring> | |
class Warrior; | |
class Headquarter; | |
using WarriorPtr = Warrior *; | |
using WarriorCreator = WarriorPtr(*)(int, Headquarter const &); | |
class City { | |
protected: | |
WarriorPtr red = nullptr; | |
WarriorPtr blue = nullptr; | |
int id; | |
public: | |
virtual void event0(); | |
virtual void event5(); | |
virtual void event10(); | |
virtual void event35(); | |
virtual void event40(); | |
virtual void event50(); | |
virtual void event55(); | |
virtual void dispatchEvent() { | |
} | |
}; | |
class Headquarter : public City { | |
private: | |
std::string name; | |
int index = 1; | |
std::vector<WarriorCreator> warriorSequence; | |
int currentWarriorIndex = 0; | |
int strength; | |
bool running = true; | |
std::unordered_map<WarriorCreator, int> warriorsCounter; | |
std::vector<WarriorPtr> warriors; | |
public: | |
Headquarter(std::string name, int strength, WarriorCreator seq[]); | |
void spawn(); | |
std::string getName() const { return name; } | |
bool getState() const { return running; } | |
int getStrength() const { return strength; } | |
}; | |
// current time | |
struct { | |
public: | |
unsigned hour; | |
unsigned minute; | |
std::string format() const { | |
std::ostringstream os; | |
os << std::setfill('0') << std::setw(3) << hour | |
<< std::setw(2) << minute; | |
return os.str(); | |
} | |
} epoch; | |
// number of cities | |
int N; | |
Headquarter *redHead, *blueHead; | |
namespace weapon { | |
class Weapon; | |
const char *names[] = { | |
"sword", | |
"bomb", | |
"arrow", | |
}; | |
using WeaponPtr = Weapon *; | |
using WeaponCreator = WeaponPtr(*)(Warrior *); | |
class Weapon { | |
protected: | |
int attackValue; | |
public: | |
int type; | |
virtual bool usable() const = 0; | |
virtual bool attack(WarriorPtr src, WarriorPtr dst) = 0; | |
virtual const char *const getName() const = 0; | |
virtual bool operator<(WeaponPtr const &a) const { | |
return type < a->type; | |
} | |
Weapon(int attack, int type) : attackValue(attack), type(type) {} | |
}; | |
class Sword : public Weapon { | |
private: | |
constexpr static auto startRatio = 0.2; | |
constexpr static auto name = "sword"; | |
// constexpr static auto wearRatio = 0.8; | |
public: | |
const char *const getName() const override { return name; } | |
explicit Sword(WarriorPtr); | |
bool usable() const override { return true; } | |
bool attack(WarriorPtr src, WarriorPtr dst) override; | |
}; | |
bool cmpForLoot(WeaponPtr const &, WeaponPtr const &); | |
class Arrow : public Weapon { | |
private: | |
constexpr static auto name = "arrow"; | |
constexpr static auto startRatio = 0.3; | |
int remaining = 2; | |
public: | |
const char *const getName() const override { return name; } | |
// static int attackValue; | |
explicit Arrow(WarriorPtr); | |
bool usable() const override { return remaining > 0; } | |
bool attack(WarriorPtr src, WarriorPtr dst) override; | |
bool operator<(WeaponPtr const &a) const override { | |
auto newA = dynamic_cast<Arrow *>(a); | |
if (newA) { | |
return remaining < newA->remaining; | |
} else { | |
return this < a; | |
} | |
} | |
friend bool cmpForLoot(WeaponPtr const &, WeaponPtr const &); | |
}; | |
// // TODO: R | |
// int Arrow::attackValue; | |
class Bomb : public Weapon { | |
private: | |
constexpr static auto name = "bomb"; | |
constexpr static auto startRatio = 0.4; | |
bool used = false; | |
public: | |
const char *const getName() const override { return name; } | |
explicit Bomb(WarriorPtr); | |
bool usable() const override { return !used; } | |
bool attack(WarriorPtr src, WarriorPtr dst) override; | |
}; | |
template<typename T> | |
Weapon *make(Warrior *param = nullptr) { | |
return new T{param}; | |
} | |
WeaponCreator WeaponList[] = {make<Sword>, make<Bomb>, make<Arrow>}; | |
bool cmpForLoot(WeaponPtr const &a, WeaponPtr const &b) { | |
auto newA = dynamic_cast<Arrow *>(a); | |
auto newB = dynamic_cast<Arrow *>(b); | |
if (newA != nullptr && newB != nullptr) { | |
return newA->remaining > newB->remaining; | |
} else { | |
return a < b; | |
} | |
} | |
} | |
class Warrior { | |
protected: | |
int id; | |
int strength; | |
int attackValue; | |
int position; | |
bool territory; | |
std::deque<weapon::WeaponPtr> weapons; | |
void sortWeapons() { | |
std::sort(weapons.begin(), weapons.end()); | |
} | |
void sortWeaponsForLoot() { | |
std::sort(weapons.begin(), weapons.end(), weapon::cmpForLoot); | |
} | |
public: | |
friend class Wolf; | |
void takeDamage(int damage) { | |
strength -= damage; | |
} | |
virtual void walk() { | |
if (getTerritory()) { | |
++position; | |
} else { | |
--position; | |
} | |
std::cout << epoch.format() << ' ' | |
<< (territory ? "red" : "blue") << ' ' << getName() << ' ' | |
<< id << " marched to city " << position << ' ' | |
<< "with " << strength << " elements and force " << attackValue | |
<< std::endl; | |
} | |
bool isDead() const { return strength <= 0; } | |
bool getTerritory() const { return territory; } | |
int getID() const { return id; } | |
int getStrength() const { return strength; } | |
int getAttack() const { return attackValue; } | |
int getPosition() const { return position; } | |
virtual const char *const getName() const = 0; | |
virtual void describe() const = 0; | |
virtual void attack(WarriorPtr dst) { | |
sortWeapons(); | |
dst->sortWeapons(); | |
auto iter = weapons.begin(); | |
auto dstIter = dst->weapons.begin(); | |
bool everAttacked = true; | |
bool dstEverAttacked = true; | |
do { | |
if (!isDead()) { | |
while (!(*iter)->attack(this, dst)) { | |
if (++iter == weapons.end()) { | |
iter = weapons.begin(); | |
everAttacked = false; | |
break; | |
} | |
} | |
} else { | |
break; | |
} | |
if (!dst->isDead()) { | |
while (!(*dstIter)->attack(dst, this)) { | |
if (++dstIter == dst->weapons.end()) { | |
dstIter = dst->weapons.begin(); | |
dstEverAttacked = false; | |
break; | |
} | |
} | |
} else { | |
break; | |
} | |
} while (weapons.size() && dst->weapons.size() | |
&& (everAttacked || dstEverAttacked)); | |
} | |
Warrior(int id, Headquarter const &head, int defaultStrength, int defaultAttack) | |
: id(id), territory(head.getName() == "red"), strength(defaultStrength), | |
attackValue(defaultAttack), weapons{} { | |
if (territory) { | |
position = 0; | |
} else { | |
position = N + 1; | |
} | |
} | |
}; | |
class Dragon : public Warrior { | |
private: | |
// double morale; | |
constexpr static auto name = "dragon"; | |
public: | |
static int defaultStrength; | |
static int defaultAttack; | |
const char *const getName() const override { return name; } | |
// double getMorale() const { return morale; } | |
void describe() const override { | |
} | |
Dragon(int id, Headquarter const &head) | |
: Warrior(id, head, defaultStrength, defaultAttack) { | |
// decrement life in castle first before creating warrior! | |
// morale = static_cast<double>(head.getStrength()) / strength; | |
weapons.push_back(weapon::WeaponList[id % 3](this)); | |
} | |
void attack(WarriorPtr dst) override { | |
Warrior::attack(dst); | |
if (!isDead()) { | |
// cheer | |
std::cout << epoch.format() << ' ' | |
<< (territory ? "red" : "blue") << ' ' | |
<< name << ' ' << id << ' ' | |
<< "yelled in city " << position << std::endl; | |
} | |
} | |
}; | |
int Dragon::defaultStrength; | |
int Dragon::defaultAttack; | |
class Ninja : public Warrior { | |
private: | |
constexpr static auto name = "ninja"; | |
public: | |
static int defaultStrength; | |
static int defaultAttack; | |
const char *const getName() const override { return name; } | |
void describe() const override { | |
} | |
Ninja(int id, Headquarter const &head) | |
: Warrior(id, head, defaultStrength, defaultAttack) { | |
weapons.push_back(weapon::WeaponList[id % 3](this)); | |
weapons.push_back(weapon::WeaponList[(id + 1) % 3](this)); | |
} | |
}; | |
int Ninja::defaultStrength; | |
int Ninja::defaultAttack; | |
class Iceman : public Warrior { | |
private: | |
constexpr static auto name = "iceman"; | |
constexpr static auto walkDamageRatio = 0.9; | |
public: | |
static int defaultStrength; | |
static int defaultAttack; | |
const char *const getName() const override { return name; } | |
void describe() const override { | |
} | |
Iceman(int id, Headquarter const &head) | |
: Warrior(id, head, defaultStrength, defaultAttack) { | |
weapons.push_back(weapon::WeaponList[id % 3](this)); | |
} | |
void walk() override { | |
strength *= walkDamageRatio; | |
Warrior::walk(); | |
} | |
}; | |
int Iceman::defaultStrength; | |
int Iceman::defaultAttack; | |
class Lion : public Warrior { | |
private: | |
int loyalty; | |
bool escaped = false; | |
constexpr static auto name = "lion"; | |
public: | |
static int defaultStrength; | |
static int defaultAttack; | |
static unsigned loyaltyDecrease; | |
const char *const getName() const override { return name; } | |
bool isLoyal() const { | |
return loyalty > 0; | |
} | |
void tryEscape() { | |
if (escaped) { return; } | |
if (!isLoyal() && position != (territory ? N + 1 : 0)) { | |
escaped = true; | |
std::cout << epoch.format() << ' ' | |
<< (territory ? "red" : "blue") << ' ' << name << ' ' | |
<< id << ' ' | |
<< "ran away" << std::endl; | |
} | |
} | |
void walk() override { | |
loyalty -= loyaltyDecrease; | |
Warrior::walk(); | |
} | |
void describe() const override { | |
std::cout << "Its loyalty is " << loyalty << std::endl; | |
} | |
Lion(int id, Headquarter const &head) | |
: Warrior(id, head, defaultStrength, defaultAttack) { | |
// decrement life in castle first before creating warrior! | |
weapons.push_back(weapon::WeaponList[id % 3](this)); | |
loyalty = head.getStrength(); | |
} | |
}; | |
int Lion::defaultStrength; | |
int Lion::defaultAttack; | |
unsigned Lion::loyaltyDecrease; | |
class Wolf : public Warrior { | |
private: | |
constexpr static auto name = "wolf"; | |
public: | |
static int defaultStrength; | |
static int defaultAttack; | |
const char *const getName() const override { return name; } | |
void describe() const override {} | |
void loot(WarriorPtr dst, int cityID) { | |
dst->sortWeaponsForLoot(); | |
int counter = 0; | |
int previousType = 0x7f7f7f7f; | |
while (weapons.size() < 10) { | |
auto trophy = dst->weapons.front(); | |
if (previousType == 0x7f7f7f7f) { | |
previousType = trophy->type; | |
} else if (previousType != trophy->type) { | |
break; | |
} | |
++counter; | |
weapons.push_back(dst->weapons.front()); | |
dst->weapons.pop_front(); | |
} | |
std::cout << epoch.format() << ' ' | |
<< (territory ? "red" : "blue") << ' ' << name << ' ' | |
<< id << ' ' | |
<< "took " << counter << ' ' | |
<< weapon::names[previousType] << ' ' | |
<< "from " << (dst->territory ? "red" : "blue") << ' ' | |
<< dst->getName() << ' ' << dst->id << ' ' | |
<< "in city " << cityID << std::endl; | |
} | |
Wolf(int id, Headquarter const &head) | |
: Warrior(id, head, defaultStrength, defaultAttack) { | |
} | |
}; | |
int Wolf::defaultStrength; | |
int Wolf::defaultAttack; | |
bool weapon::Sword::attack(WarriorPtr src, WarriorPtr dst) { | |
if (!usable()) { return false; } | |
dst->takeDamage(attackValue); | |
return true; | |
} | |
bool weapon::Arrow::attack(WarriorPtr src, WarriorPtr dst) { | |
if (!usable()) { return false; } | |
dst->takeDamage(attackValue); | |
--remaining; | |
return true; | |
} | |
bool weapon::Bomb::attack(WarriorPtr src, WarriorPtr dst) { | |
if (!usable()) { return false; } | |
auto newSrc = dynamic_cast<Ninja *>(src); | |
if (newSrc == nullptr) { | |
// not a ninja | |
src->takeDamage(attackValue / 2); | |
} | |
dst->takeDamage(attackValue); | |
used = true; | |
return true; | |
} | |
weapon::Sword::Sword(WarriorPtr warrior) | |
: weapon::Weapon(static_cast<int>(startRatio * warrior->getAttack()), 0) {} | |
weapon::Arrow::Arrow(WarriorPtr warrior) | |
: weapon::Weapon(static_cast<int>(startRatio * warrior->getAttack()), 2) {} | |
weapon::Bomb::Bomb(WarriorPtr warrior) | |
: weapon::Weapon(static_cast<int>(startRatio * warrior->getAttack()), 1) {} | |
template<typename T> | |
Warrior *make(int param1, Headquarter const ¶m2) { | |
return new T{param1, param2}; | |
} | |
WarriorCreator redSequence[] = {make<Iceman>, make<Lion>, make<Wolf>, make<Ninja>, make<Dragon>}; | |
WarriorCreator blueSequence[] = {make<Lion>, make<Dragon>, make<Ninja>, make<Iceman>, make<Wolf>}; | |
Headquarter::Headquarter(std::string name, int strength, WarriorCreator seq[]) | |
: strength(strength), name(std::move(name)), | |
warriorSequence(seq, seq + 5) { | |
for (const auto &a : warriorSequence) { | |
warriorsCounter.insert({a, 0}); | |
} | |
} | |
void City::event0() {} | |
void City::event5() { | |
auto newRed = dynamic_cast<Lion *>(red); | |
if (newRed != nullptr) { | |
newRed->tryEscape(); | |
} | |
auto newBlue = dynamic_cast<Lion *>(blue); | |
if (newBlue != nullptr) { | |
newBlue->tryEscape(); | |
} | |
} | |
void City::event10() { | |
if (red != nullptr) { | |
red->walk(); | |
} | |
if (blue != nullptr) { | |
blue->walk(); | |
} | |
} | |
void City::event35() { | |
auto newRed = dynamic_cast<Wolf *>(red); | |
auto newBlue = dynamic_cast<Wolf *>(blue); | |
if (newRed != nullptr && newBlue != nullptr) { | |
return; | |
} else if (newRed == nullptr && newBlue == nullptr) { | |
return; | |
} else if (newRed != nullptr && blue != nullptr) { | |
newRed->loot(blue, id); | |
} else if (newBlue != nullptr && red != nullptr) { | |
newBlue->loot(red, id); | |
} | |
} | |
void City::event40() { | |
if (id % 2) { | |
red->attack(blue); | |
} else { | |
blue->attack(red); | |
} | |
} | |
void Headquarter::spawn() { | |
if (!running) { | |
return; | |
} | |
// int attempt = 0; | |
// while (strength < warriorSequence[currentWarriorIndex](0, *this)->getStrength()) { | |
// if (attempt++ >= 5) { | |
// std::cout << epoch.format() << ' ' | |
// << name << ' ' | |
// << "headquarter stops making warriors" << std::endl; | |
// running = false; | |
// return; | |
// } | |
// currentWarriorIndex = (currentWarriorIndex + 1) % 5; | |
// } | |
if (strength < warriorSequence[currentWarriorIndex](0, *this)->getStrength()) { | |
running = false; | |
return; | |
} | |
strength -= warriorSequence[currentWarriorIndex](0, *this)->getStrength(); | |
auto newWarrior = warriorSequence[currentWarriorIndex](index++, *this); | |
++warriorsCounter[warriorSequence[currentWarriorIndex]]; | |
std::cout << epoch.format() << ' ' | |
<< name << ' ' | |
<< newWarrior->getName() << ' ' | |
<< newWarrior->getID() << ' ' | |
<< "born with strength " << newWarrior->getStrength() << ',' | |
<< warriorsCounter[warriorSequence[currentWarriorIndex]] << ' ' | |
<< newWarrior->getName() << ' ' | |
<< "in " << name << " headquarter" << std::endl; | |
newWarrior->describe(); | |
warriors.push_back(newWarrior); | |
currentWarriorIndex = (currentWarriorIndex + 1) % 5; | |
} | |
void tick() { | |
redHead->spawn(); | |
blueHead->spawn(); | |
epoch.minute = 5; | |
// ++epoch; | |
} | |
void runEval(int caseNo) { | |
std::cout << "Case " << caseNo << ":" << std::endl; | |
// strength of headquarters | |
int M; | |
// end time | |
int T; | |
std::cin >> M; | |
std::cin >> N; | |
std::cin >> Lion::loyaltyDecrease; | |
std::cin >> T; | |
std::cin >> Dragon::defaultStrength; | |
std::cin >> Ninja::defaultStrength; | |
std::cin >> Iceman::defaultStrength; | |
std::cin >> Lion::defaultStrength; | |
std::cin >> Wolf::defaultStrength; | |
std::cin >> Dragon::defaultAttack; | |
std::cin >> Ninja::defaultAttack; | |
std::cin >> Iceman::defaultAttack; | |
std::cin >> Lion::defaultAttack; | |
std::cin >> Wolf::defaultAttack; | |
redHead = new Headquarter("red", M, redSequence); | |
blueHead = new Headquarter("blue", M, blueSequence); | |
while (redHead->getState() || blueHead->getState()) { | |
tick(); | |
} | |
delete redHead; | |
delete blueHead; | |
} | |
int main() { | |
int n; | |
std::cin >> n; | |
for (int i = 1; i <= n; i++) { | |
runEval(i); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment