Skip to content

Instantly share code, notes, and snippets.

@poseidon4o
Created May 15, 2025 18:03
Show Gist options
  • Save poseidon4o/8d8d5833a3a15d5f911b03e2c01aed9d to your computer and use it in GitHub Desktop.
Save poseidon4o/8d8d5833a3a15d5f911b03e2c01aed9d to your computer and use it in GitHub Desktop.
#include <exception>
#include <iostream>
#include <ostream>
struct Position {
int x;
int y;
};
Position operator-(const Position &a, const Position &b) {
return Position{a.x - b.x, a.y - b.y};
}
bool operator==(const Position &a, const Position &b) {
return a.x == b.x && a.y == b.y;
}
bool operator!=(const Position &a, const Position &b) {
return !(a == b);
}
Position abs(const Position &a) {
return Position{abs(a.x), abs(a.y)};
}
std::ostream &operator<<(std::ostream &out, const Position &p) {
return out << p.x << ' ' << p.y;
}
enum class PlayerType {
Player,
Mage,
Warrior,
Necromancer
};
class Player {
public:
friend std::ostream &operator<<(std::ostream &out, const Player &p);
enum Weapon {
Sword, Wand, Staff, Axe
};
protected:
PlayerType type;
char *name = nullptr;
unsigned health;
Position position;
Weapon weapon;
unsigned attack_damage;
void setName(const char *name) {
if (!name) {
throw std::logic_error("Empty Player name");
}
char *newName = new char[strlen(name) + 1];
strcpy(newName, name);
delete[] this->name;
this->name = newName;
}
void swap(Player &other) {
std::swap(type, other.type);
std::swap(name, other.name);
std::swap(health, other.health);
std::swap(position, other.position);
std::swap(weapon, other.weapon);
std::swap(attack_damage, other.attack_damage);
}
public:
Player(PlayerType type, const char *name, unsigned health, const Position &pos, Weapon weapon, unsigned damage)
: type(type)
, health(health)
, position(pos)
, weapon(weapon)
, attack_damage(damage)
{
setName(name);
}
Player(const Player &other)
: type(other.type)
, health(other.health)
, position(other.position)
, weapon(other.weapon)
, attack_damage(other.attack_damage)
{
setName(other.name);
}
void move(const Position &pos) {
position = pos;
}
bool isDead() const {
return health == 0;
}
const Position &getPosition() const {
return position;
}
const char *getName() const {
return name;
}
PlayerType getType() const {
return type;
}
void handleAttack(Player &other);
unsigned getAttackPower() const {
return attack_damage;
}
Player &operator=(const Player &other) {
if (this == &other) return *this;
type = other.type;
setName(other.name);
health = other.health;
position = other.position;
weapon = other.weapon;
attack_damage = other.attack_damage;
return *this;
}
~Player() {
delete[] name;
}
void print() {
std::cout << *this;
}
Player *clone() const;
void freeMem();
};
std::ostream &operator<<(std::ostream &out, const Player &p) {
return out << p.health << ' ' << p.name << ' ' << p.attack_damage << ' ' << p.position;
}
class Warrior : public Player {
unsigned armor;
public:
Warrior(const char *name, unsigned health, const Position &pos, Weapon weapon, unsigned damage, unsigned armor)
: Player(PlayerType::Warrior, name, health, pos, weapon, damage)
, armor(armor)
{}
void handleAttack(Player &other);
};
class Mage : public Player {
char *ability = nullptr;
float attackMultiplier;
void swap(Mage &other) {
std::swap(ability, other.ability);
std::swap(attackMultiplier, other.attackMultiplier);
Player::swap(other);
}
public:
Mage(const char *name, unsigned health, const Position &pos, Weapon weapon, unsigned damage, const char *ability, float multiplier)
: Player(PlayerType::Mage, name, health, pos, weapon, damage)
, attackMultiplier(multiplier)
{
if (multiplier >= 1 || multiplier <= 0) {
throw std::logic_error("Mage multiplier must be in (0, 1)");
}
setAbility(ability);
}
~Mage() {
delete[] ability;
}
Mage(const Mage &other)
: Player(other)
, attackMultiplier(other.attackMultiplier)
{
setAbility(other.ability);
}
float getAttackMultiplier() const {
return attackMultiplier;
}
Mage &operator=(const Mage &other) {
if (&other == this) return *this;
Mage copy(other);
swap(copy);
return *this;
}
void setAbility(const char *ability) {
if (!ability) {
throw std::logic_error("Empty Player Ability");
}
char *newAbility = new char[strlen(ability) + 1];
strcpy(newAbility, ability);
delete[] this->ability;
this->ability = newAbility;
}
};
class PlayerCollection {
Player **player = nullptr;
int size = 0;
int cap = 0;
public:
PlayerCollection() = default;
PlayerCollection(const PlayerCollection &other)
: size(other.size)
, cap(other.cap)
{
player = new Player*[cap];
for (int c = 0; c < size; c++) {
try {
player[c] = other.player[c]->clone();
} catch (const std::exception &ex) {
for (int r = 0; r < c; r++) {
player[r]->freeMem();
}
delete[] player;
throw;
}
}
}
PlayerCollection &operator=(const PlayerCollection &) = delete;
~PlayerCollection() {
for (int c = 0; c < size; c++) {
player[c]->freeMem();
}
delete[] player;
}
void addPlayer(const Player &other) {
if (size >= cap) {
const int newCap = cap == 0 ? 8 : cap * 1.66;
Player **newArr = new Player*[newCap];
cap = newCap;
for (int c = 0; c < size; c++) {
newArr[c] = player[c];
}
delete[] player;
player = newArr;
}
player[size++] = other.clone();
}
const Player &operator[](int index) {
return *player[index];
}
const Player &operator[](int index) const {
return *player[index];
}
int getSize() const {
return size;
}
void removePlayer(const char *name) {
for (int c = 0; c < size; c++) {
if (strcmp(name, player[c]->getName()) == 0) {
player[c]->freeMem();
for (int r = c + 1; r < size; r++, c++) {
player[c] = player[r];
}
--size;
}
}
}
};
class Necromancer : public Mage {
PlayerCollection deadCopies;
public:
Necromancer(const char *name, unsigned health, const Position &pos, Weapon weapon, unsigned damage, const char *ability, float multiplier)
: Mage(name, health, pos, weapon, damage, ability, multiplier)
{}
void addPlayer(const Player &deadPlayer) {
deadCopies.addPlayer(deadPlayer);
}
Player *getRandomDeadPlayer() const {
return const_cast<Player*>(
deadCopies.getSize() == 0
? nullptr
: &deadCopies[rand() % deadCopies.getSize()]
);
}
};
Player *Player::clone() const {
switch (type) {
case PlayerType::Mage:
return new Mage(static_cast<const Mage&>(*this));
case PlayerType::Warrior:
return new Warrior(static_cast<const Warrior&>(*this));
case PlayerType::Necromancer:
return new Necromancer(static_cast<const Necromancer&>(*this));
}
}
void Warrior::handleAttack(Player &other) {
if (abs(position - other.getPosition()) != Position{1, 1}) {
return;
}
if (other.getType() == PlayerType::Mage) {
health *= static_cast<const Mage &>(other).getAttackMultiplier();
}
unsigned attackPower = other.getAttackPower();
if (attackPower >= armor) {
attackPower -= armor;
armor = 0;
} else {
armor -= attackPower;
return;
}
const bool isNecromancer = other.getType() == PlayerType::Necromancer;
Necromancer *necro = isNecromancer ? static_cast<Necromancer*>(&other) : nullptr;
if (attackPower >= health) {
health = 0;
if (necro) {
necro->addPlayer(*this);
}
return;
} else {
health -= attackPower;
}
Player *otherAttacker = necro->getRandomDeadPlayer();
if (otherAttacker) {
handleAttack(*otherAttacker);
}
}
void Player::handleAttack(Player &other) {
if (abs(position - other.position) != Position{1, 1}) {
return;
}
if (other.type == PlayerType::Mage) {
health *= static_cast<const Mage &>(other).getAttackMultiplier();
}
if (other.attack_damage >= health) {
health = 0;
} else {
health -= other.attack_damage;
}
const bool isNecromancer = other.getType() == PlayerType::Necromancer;
Necromancer *necro = isNecromancer ? static_cast<Necromancer*>(&other) : nullptr;
if (other.getAttackPower() >= health) {
health = 0;
if (necro) {
necro->addPlayer(*this);
}
return;
} else {
health -= other.getAttackPower();
}
Player *otherAttacker = necro->getRandomDeadPlayer();
if (otherAttacker) {
if (type == PlayerType::Warrior) {
static_cast<Warrior*>(this)->handleAttack(*otherAttacker);
} else {
handleAttack(*otherAttacker);
}
}
}
void Player::freeMem() {
switch (type) {
case PlayerType::Player:
delete static_cast<Player*>(this);
break;
case PlayerType::Necromancer:
delete static_cast<Necromancer*>(this);
break;
case PlayerType::Mage:
delete static_cast<Mage*>(this);
break;
case PlayerType::Warrior:
delete static_cast<Warrior*>(this);
break;
}
}
int main() {
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment