diff --git a/solutions/lab6/main.cpp b/solutions/lab6/main.cpp index e69de29..39cb50a 100644 --- a/solutions/lab6/main.cpp +++ b/solutions/lab6/main.cpp @@ -0,0 +1,165 @@ +#include "src/dragon.h" +#include "src/elf.h" +#include "src/knight_errant.h" +#include "src/npc.h" + +class TextObserver : public IFightObserver { +private: + TextObserver(){}; + +public: + static std::shared_ptr get() { + static TextObserver instance; + return std::shared_ptr(&instance, [](IFightObserver *) {}); + } + + void on_fight(const std::shared_ptr attacker, + const std::shared_ptr defender, bool win) override { + if (win) { + std::cout << std::endl << "Murder --------" << std::endl; + attacker->print(); + defender->print(); + } + } +}; + +std::shared_ptr factory(std::istream &is) { + std::shared_ptr result; + int type{0}; + if (is >> type) { + switch (type) { + case KnightErrantType: + result = std::make_shared(is); + break; + case ElfType: + result = std::make_shared(is); + break; + case DragonType: + result = std::make_shared(is); + break; + } + } else + std::cerr << "unexpected NPC type:" << type << std::endl; + + if (result) + result->subscribe(TextObserver::get()); + + return result; +} + +std::shared_ptr factory(NpcType type, int x, int y) { + std::shared_ptr result; + switch (type) { + case KnightErrantType: + result = std::make_shared(x, y); + break; + case ElfType: + result = std::make_shared(x, y); + break; + case DragonType: + result = std::make_shared(x, y); + break; + default: + break; + } + if (result) + result->subscribe(TextObserver::get()); + + return result; +} + +void save(const set_t &array, const std::string &filename) { + std::ofstream fs(filename); + fs << array.size() << std::endl; + for (auto &n : array) + n->save(fs); + fs.flush(); + fs.close(); +} + +set_t load(const std::string &filename) { + set_t result; + std::ifstream is(filename); + if (is.good() && is.is_open()) { + int count; + is >> count; + for (int i = 0; i < count; ++i) + result.insert(factory(is)); + is.close(); + } else + std::cerr << "Error: " << std::strerror(errno) << std::endl; + return result; +} + +std::ostream &operator<<(std::ostream &os, const set_t &array) { + for (auto &n : array) + n->print(); + return os; +} + +class AttackerVisitor : public Visitor { +public: + AttackerVisitor(const std::shared_ptr &attacker) : attacker_(attacker) {} + + bool visit(KnightErrant &knight) override { + return attacker_->fight(std::make_shared(knight)); + } + bool visit(Elf &elf) override { + return attacker_->fight(std::make_shared(elf)); + } + bool visit(Dragon &dragon) override { + return attacker_->fight(std::make_shared(dragon)); + } + +private: + std::shared_ptr attacker_; +}; + +set_t fight(const set_t &array, size_t distance) { + set_t dead_list; + + for (const auto &attacker : array) { + for (const auto &defender : array) { + if (attacker != defender && attacker->is_close(defender, distance)) { + AttackerVisitor visitor(attacker); + if (defender->accept(visitor)) { + dead_list.insert(defender); + } + } + } + } + return dead_list; +} + +int main() { + set_t array; + + std::cout << "Generating ..." << std::endl; + for (size_t i = 0; i < 10; ++i) + array.insert(factory(NpcType(std::rand() % 3 + 1), std::rand() % 100, + std::rand() % 100)); + std::cout << "Saving ..." << std::endl; + + save(array, "npc.txt"); + + std::cout << "Loading ..." << std::endl; + array = load("npc.txt"); + + std::cout << "Fighting ..." << std::endl << array; + + for (size_t distance = 20; (distance <= 100) && !array.empty(); + distance += 10) { + auto dead_list = fight(array, distance); + for (auto &d : dead_list) + array.erase(d); + std::cout << "Fight stats ----------" << std::endl + << "distance: " << distance << std::endl + << "killed: " << dead_list.size() << std::endl + << std::endl + << std::endl; + } + + std::cout << "Survivors:" << array; + + return 0; +} diff --git a/solutions/lab6/src/dragon.cpp b/solutions/lab6/src/dragon.cpp index e69de29..94c1d32 100644 --- a/solutions/lab6/src/dragon.cpp +++ b/solutions/lab6/src/dragon.cpp @@ -0,0 +1,35 @@ +#include "dragon.h" +#include "elf.h" +#include "knight_errant.h" + +Dragon::Dragon(int x, int y) : NPC(DragonType, x, y) {} +Dragon::Dragon(std::istream &is) : NPC(DragonType, is) {} + +void Dragon::print() { std::cout << *this; } + +bool Dragon::accept(Visitor &visitor) { return visitor.visit(*this); } + +bool Dragon::fight(std::shared_ptr other) { + fight_notify(other, true); + return true; +} + +bool Dragon::fight(std::shared_ptr other) { + fight_notify(other, true); + return true; +} + +bool Dragon::fight(std::shared_ptr other) { + fight_notify(other, true); + return true; +} + +void Dragon::save(std::ostream &os) { + os << DragonType << std::endl; + NPC::save(os); +} + +std::ostream &operator<<(std::ostream &os, Dragon &dragon) { + os << "Dragon: " << *static_cast(&dragon) << std::endl; + return os; +} diff --git a/solutions/lab6/src/dragon.h b/solutions/lab6/src/dragon.h index e69de29..002cb2a 100644 --- a/solutions/lab6/src/dragon.h +++ b/solutions/lab6/src/dragon.h @@ -0,0 +1,19 @@ +#pragma once +#include "npc.h" + +struct Dragon : public NPC { + Dragon(int x, int y); + Dragon(std::istream &is); + + void print() override; + + bool accept(Visitor &visitor) override; + + bool fight(std::shared_ptr other) override; + bool fight(std::shared_ptr other) override; + bool fight(std::shared_ptr other) override; + + void save(std::ostream &os) override; + + friend std::ostream &operator<<(std::ostream &os, Dragon &dragon); +}; diff --git a/solutions/lab6/src/elf.cpp b/solutions/lab6/src/elf.cpp index e69de29..f71a0a6 100644 --- a/solutions/lab6/src/elf.cpp +++ b/solutions/lab6/src/elf.cpp @@ -0,0 +1,35 @@ +#include "elf.h" +#include "dragon.h" +#include "knight_errant.h" + +Elf::Elf(int x, int y) : NPC(ElfType, x, y) {} +Elf::Elf(std::istream &is) : NPC(ElfType, is) {} + +void Elf::print() { std::cout << *this; } + +bool Elf::accept(Visitor &visitor) { return visitor.visit(*this); } + +bool Elf::fight(std::shared_ptr other) { + fight_notify(other, true); + return true; +} + +bool Elf::fight(std::shared_ptr other) { + fight_notify(other, false); + return false; +} + +bool Elf::fight(std::shared_ptr other) { + fight_notify(other, false); + return false; +} + +void Elf::save(std::ostream &os) { + os << ElfType << std::endl; + NPC::save(os); +} + +std::ostream &operator<<(std::ostream &os, Elf &elf) { + os << "Elf: " << *static_cast(&elf) << std::endl; + return os; +} diff --git a/solutions/lab6/src/elf.h b/solutions/lab6/src/elf.h index e69de29..dfff16a 100644 --- a/solutions/lab6/src/elf.h +++ b/solutions/lab6/src/elf.h @@ -0,0 +1,19 @@ +#pragma once +#include "npc.h" + +struct Elf : public NPC { + Elf(int x, int y); + Elf(std::istream &is); + + void print() override; + + bool accept(Visitor &visitor) override; + + bool fight(std::shared_ptr other) override; + bool fight(std::shared_ptr other) override; + bool fight(std::shared_ptr other) override; + + void save(std::ostream &os) override; + + friend std::ostream &operator<<(std::ostream &os, Elf &elf); +}; diff --git a/solutions/lab6/src/knight_errant.cpp b/solutions/lab6/src/knight_errant.cpp index e69de29..164f953 100644 --- a/solutions/lab6/src/knight_errant.cpp +++ b/solutions/lab6/src/knight_errant.cpp @@ -0,0 +1,35 @@ +#include "knight_errant.h" +#include "dragon.h" +#include "elf.h" + +KnightErrant::KnightErrant(int x, int y) : NPC(KnightErrantType, x, y) {} +KnightErrant::KnightErrant(std::istream &is) : NPC(KnightErrantType, is) {} + +void KnightErrant::print() { std::cout << *this; } + +bool KnightErrant::accept(Visitor &visitor) { return visitor.visit(*this); } + +bool KnightErrant::fight(std::shared_ptr other) { + fight_notify(other, false); + return false; +} + +bool KnightErrant::fight(std::shared_ptr other) { + fight_notify(other, false); + return false; +} + +bool KnightErrant::fight(std::shared_ptr other) { + fight_notify(other, true); + return true; +} + +void KnightErrant::save(std::ostream &os) { + os << KnightErrantType << std::endl; + NPC::save(os); +} + +std::ostream &operator<<(std::ostream &os, KnightErrant &knight_errant) { + os << "Knight Errant: " << *static_cast(&knight_errant) << std::endl; + return os; +} diff --git a/solutions/lab6/src/knight_errant.h b/solutions/lab6/src/knight_errant.h index e69de29..d9e203f 100644 --- a/solutions/lab6/src/knight_errant.h +++ b/solutions/lab6/src/knight_errant.h @@ -0,0 +1,20 @@ +#pragma once +#include "npc.h" + +struct KnightErrant : public NPC { + KnightErrant(int x, int y); + KnightErrant(std::istream &is); + + void print() override; + + bool accept(Visitor& visitor) override; + + bool fight(std::shared_ptr other) override; + bool fight(std::shared_ptr other) override; + bool fight(std::shared_ptr other) override; + + void save(std::ostream &os) override; + + friend std::ostream &operator<<(std::ostream &os, + KnightErrant &knight_errant); +}; diff --git a/solutions/lab6/src/npc.cpp b/solutions/lab6/src/npc.cpp index e69de29..10f15c5 100644 --- a/solutions/lab6/src/npc.cpp +++ b/solutions/lab6/src/npc.cpp @@ -0,0 +1,37 @@ +#include "npc.h" + +NPC::NPC(NpcType t, int _x, int _y) : type(t), x(_x), y(_y) {} + +NPC::NPC(NpcType t, std::istream &is) : type(t) { + is >> x; + is >> y; +} + +void NPC::subscribe(std::shared_ptr observer) { + observers.push_back(observer); +} + +void NPC::fight_notify(const std::shared_ptr defender, bool win) { + for (auto &o : observers) { + o->on_fight(shared_from_this(), defender, win); + } +} + +bool NPC::is_close(const std::shared_ptr &other, size_t distance) const { + if (std::pow(x - other->x, 2) + std::pow(y - other->y, 2) <= + std::pow(distance, 2)) { + return true; + } else { + return false; + } +} + +void NPC::save(std::ostream &os) { + os << x << std::endl; + os << y << std::endl; +} + +std::ostream &operator<<(std::ostream &os, NPC &npc) { + os << "{ x:" << npc.x << ", y:" << npc.y << " }"; + return os; +} diff --git a/solutions/lab6/src/npc.h b/solutions/lab6/src/npc.h index e69de29..0f7ee97 100644 --- a/solutions/lab6/src/npc.h +++ b/solutions/lab6/src/npc.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +struct NPC; +struct KnightErrant; +struct Elf; +struct Dragon; +using set_t = std::set>; + +enum NpcType { Unknown = 0, KnightErrantType = 1, ElfType = 2, DragonType = 3 }; + +struct IFightObserver { + virtual void on_fight(const std::shared_ptr attacker, + const std::shared_ptr defender, bool win) = 0; +}; + +class Visitor { +public: + virtual bool visit(KnightErrant &knight) = 0; + virtual bool visit(Elf &elf) = 0; + virtual bool visit(Dragon &dragon) = 0; + virtual ~Visitor() = default; +}; + +struct NPC : public std::enable_shared_from_this { + NpcType type; + int x{0}; + int y{0}; + std::vector> observers; + + NPC(NpcType t, int _x, int _y); + NPC(NpcType t, std::istream &is); + + void subscribe(std::shared_ptr observer); + void fight_notify(const std::shared_ptr defender, bool win); + virtual bool is_close(const std::shared_ptr &other, + size_t distance) const; + + virtual bool accept(Visitor &visitor) = 0; + + virtual bool fight(std::shared_ptr other) = 0; + virtual bool fight(std::shared_ptr other) = 0; + virtual bool fight(std::shared_ptr other) = 0; + virtual void print() = 0; + + virtual void save(std::ostream &os); + + friend std::ostream &operator<<(std::ostream &os, NPC &npc); +}; diff --git a/solutions/lab6/tests/tests.cpp b/solutions/lab6/tests/tests.cpp index e69de29..b5bbf4d 100644 --- a/solutions/lab6/tests/tests.cpp +++ b/solutions/lab6/tests/tests.cpp @@ -0,0 +1,251 @@ +#include "../src/dragon.h" +#include "../src/elf.h" +#include "../src/knight_errant.h" +#include "../src/npc.h" + +#include +#include +#include + +// Mock Observer for testing +class MockFightObserver : public IFightObserver { +public: + int fight_count = 0; + std::shared_ptr last_attacker; + std::shared_ptr last_defender; + bool last_win; + + void on_fight(const std::shared_ptr attacker, + const std::shared_ptr defender, bool win) override { + fight_count++; + last_attacker = attacker; + last_defender = defender; + last_win = win; + } +}; + +class TextObserver : public IFightObserver { +private: + TextObserver(){}; + +public: + static std::shared_ptr get() { + static TextObserver instance; + return std::shared_ptr(&instance, [](IFightObserver *) {}); + } + + void on_fight(const std::shared_ptr attacker, + const std::shared_ptr defender, bool win) override { + if (win) { + std::cout << std::endl << "Murder --------" << std::endl; + attacker->print(); + defender->print(); + } + } +}; + +std::shared_ptr factory(std::istream &is) { + std::shared_ptr result; + int type{0}; + if (is >> type) { + switch (type) { + case KnightErrantType: + result = std::make_shared(is); + break; + case ElfType: + result = std::make_shared(is); + break; + case DragonType: + result = std::make_shared(is); + break; + } + } else + std::cerr << "unexpected NPC type:" << type << std::endl; + + if (result) + result->subscribe(TextObserver::get()); + + return result; +} + +std::shared_ptr factory(NpcType type, int x, int y) { + std::shared_ptr result; + switch (type) { + case KnightErrantType: + result = std::make_shared(x, y); + break; + case ElfType: + result = std::make_shared(x, y); + break; + case DragonType: + result = std::make_shared(x, y); + break; + default: + break; + } + if (result) + result->subscribe(TextObserver::get()); + + return result; +} + +void save(const set_t &array, const std::string &filename) { + std::ofstream fs(filename); + fs << array.size() << std::endl; + for (auto &n : array) + n->save(fs); + fs.flush(); + fs.close(); +} + +set_t load(const std::string &filename) { + set_t result; + std::ifstream is(filename); + if (is.good() && is.is_open()) { + int count; + is >> count; + for (int i = 0; i < count; ++i) + result.insert(factory(is)); + is.close(); + } else + std::cerr << "Error: " << std::strerror(errno) << std::endl; + return result; +} + +std::ostream &operator<<(std::ostream &os, const set_t &array) { + for (auto &n : array) + n->print(); + return os; +} + +class AttackerVisitor : public Visitor { +public: + AttackerVisitor(const std::shared_ptr &attacker) : attacker_(attacker) {} + + bool visit(KnightErrant &knight) override { + return attacker_->fight(std::make_shared(knight)); + } + bool visit(Elf &elf) override { + return attacker_->fight(std::make_shared(elf)); + } + bool visit(Dragon &dragon) override { + return attacker_->fight(std::make_shared(dragon)); + } + +private: + std::shared_ptr attacker_; +}; + +set_t fight(const set_t &array, size_t distance) { + set_t dead_list; + + for (const auto &attacker : array) { + for (const auto &defender : array) { + if (attacker != defender && attacker->is_close(defender, distance)) { + AttackerVisitor visitor(attacker); + if (defender->accept(visitor)) { + dead_list.insert(defender); + } + } + } + } + return dead_list; +} + +TEST(FactoryTest, CreateKnight) { + auto knight = factory(KnightErrantType, 10, 20); + ASSERT_NE(knight, nullptr); + ASSERT_EQ(knight->type, KnightErrantType); + ASSERT_EQ(knight->x, 10); + ASSERT_EQ(knight->y, 20); +} + +TEST(FactoryTest, CreateElf) { + auto elf = factory(ElfType, 30, 40); + ASSERT_NE(elf, nullptr); + ASSERT_EQ(elf->type, ElfType); + ASSERT_EQ(elf->x, 30); + ASSERT_EQ(elf->y, 40); +} + +TEST(FactoryTest, CreateDragon) { + auto dragon = factory(DragonType, 50, 60); + ASSERT_NE(dragon, nullptr); + ASSERT_EQ(dragon->type, DragonType); + ASSERT_EQ(dragon->x, 50); + ASSERT_EQ(dragon->y, 60); +} + +TEST(FactoryTest, LoadKnight) { + std::stringstream ss("1\n10\n20\n"); + auto knight = factory(ss); + ASSERT_NE(knight, nullptr); + ASSERT_EQ(knight->type, KnightErrantType); + ASSERT_EQ(knight->x, 10); + ASSERT_EQ(knight->y, 20); +} + +TEST(FightTest, DragonKillsAll) { + auto dragon = std::make_shared(0, 0); + auto knight = std::make_shared(0, 0); + auto elf = std::make_shared(0, 0); + + ASSERT_TRUE(dragon->fight(knight)); + ASSERT_TRUE(dragon->fight(elf)); + ASSERT_TRUE(dragon->fight(dragon)); +} + +TEST(FightTest, KnightKillsDragon) { + auto knight = std::make_shared(0, 0); + auto dragon = std::make_shared(0, 0); + + ASSERT_TRUE(knight->fight(dragon)); +} + +TEST(FightTest, ElfKillsKnight) { + auto elf = std::make_shared(0, 0); + auto knight = std::make_shared(0, 0); + + ASSERT_TRUE(elf->fight(knight)); +} + +TEST(FightTest, DistanceCheck) { + auto knight1 = std::make_shared(0, 0); + auto knight2 = std::make_shared(4, 3); + + ASSERT_TRUE(knight1->is_close(knight2, 5)); + ASSERT_FALSE(knight1->is_close(knight2, 4)); +} + +TEST(ObserverTest, FightNotification) { + auto mockObserver = std::make_shared(); + auto knight = std::make_shared(0, 0); + auto dragon = std::make_shared(0, 0); + + knight->subscribe(mockObserver); + knight->fight(dragon); + + ASSERT_EQ(mockObserver->fight_count, 1); + ASSERT_EQ(mockObserver->last_attacker, knight); + ASSERT_EQ(mockObserver->last_defender, dragon); + ASSERT_TRUE(mockObserver->last_win); +} + +TEST(SaveLoadTest, SaveAndLoad) { + set_t array; + array.insert(std::make_shared(10, 20)); + array.insert(std::make_shared(30, 40)); + + save(array, "test_save.txt"); + + set_t loaded_array = load("test_save.txt"); + + ASSERT_EQ(array.size(), loaded_array.size()); + + std::remove("test_save.txt"); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}