惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recorded Future
Recorded Future
P
Privacy & Cybersecurity Law Blog
Latest news
Latest news
Cyberwarzone
Cyberwarzone
Spread Privacy
Spread Privacy
F
Future of Privacy Forum
NISL@THU
NISL@THU
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Troy Hunt's Blog
Attack and Defense Labs
Attack and Defense Labs
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Schneier on Security
Schneier on Security
腾讯CDC
P
Privacy International News Feed
有赞技术团队
有赞技术团队
AWS News Blog
AWS News Blog
K
Kaspersky official blog
C
Cisco Blogs
The Hacker News
The Hacker News
B
Blog
Stack Overflow Blog
Stack Overflow Blog
T
ThreatConnect
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
宝玉的分享
宝玉的分享
F
Full Disclosure
C
Check Point Blog
Cisco Talos Blog
Cisco Talos Blog
美团技术团队
S
Security Archives - TechRepublic
E
Exploit-DB.com RSS Feed
D
Docker
Security Latest
Security Latest
Blog — PlanetScale
Blog — PlanetScale
S
Secure Thoughts
T
Threatpost
D
DataBreaches.Net
博客园 - 【当耐特】
N
Netflix TechBlog - Medium
The Register - Security
The Register - Security
T
True Tiger Recordings
P
Proofpoint News Feed
L
LINUX DO - 最新话题
F
Fox-IT International blog
W
WeLiveSecurity
T
The Exploit Database - CXSecurity.com
月光博客
月光博客
G
Google Developers Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
B
Blog RSS Feed

Все публикации подряд на Хабре

Очень много букв… Или кейс по специфической настройке рабочего окружения Segmentation Fault: как оно устроено? Python в enterprise: момент, когда пора открыть Java не только ради собеседований MonoGame — игровой движок для тех, кто любит изобретать велосипеды Спасти рядового Буридана Рефакторинг выпадающих списков: от enum к конфигу-константе Free Porn Storage: передаём мемы в TLS-трафике, не привлекая внимания санитаров Мониторинг цен на Авито: MikroTik RouterOS Script Венесуэльская нефть после января 2026 Разговоры с ИИ Хотел упростить мониторинг проектов и в отпуск — пришлось обучать свой LLM. Часть 4. Тестирование Как вытащить ИТ из кризиса перегрузки, если найм запрещён Как мы подключили LLM к поддержке, а получили идеального лжеца Zero — новый agent-first язык программирования от Vercel, который изменит все (нет) Запускаем рекламу в дачной нише: какие креативы и форматы работают, на что смотреть в аналитике Паттерны организационного дизайна: практическое руководство Почему алгоритмы сливают твой депозит? 3 причины, о которых молчат «успешные» бэктесты Как «спят» вкладки в браузере Приоритет задач определяется не только ощущением срочности [Перевод] Махинации с прибылью Anthropic Project Loom: Virtual Threads, Scoped Values и preview #7 Structured Concurrency Мнения математиков о том, как ИИ опроверг гипотезу Эрдёша Слабоумие и отвага: как я за выходные сделала прототип ИИ-помощника для UX-дизайнера ИИ учит нас писать лучше. Или хуже? Как проектировать ИИ-инструменты, которые делают пользователей лучше «Раньше хотел каждый, сейчас и бесплатно не надо»: гаджеты, про которые мы все забыли ИИ-агенты в бизнесе: почему 80% компаний увольняют людей, но не получают ROI Как я строил ИИ-стартап, или Новые архитектурные риски 2026 4 интересных парадокса, рождающих жаркие дискуссии Рабочее место не-вайбкодера: настраиваем harness Когнитивный инжиниринг Feature Based Clean Architecture. Часть 1: Эволюция NestJS-приложения в неподдерживаемое состояние Как мы перестали бояться «пустых охватов» и сделали инфлюенс-маркетинг управляемым каналом роста Подключили B2B email-платформу к голосовым ассистентам через MCP. Архитектура, код, где ломается [Перевод] Почему AI-агенты ломаются на длинных задачах — и как обвязка помогает им дописывать приложения Облачно, возможны нейросети: кризис датасетов и ахиллесова пята систем машинного зрения — DIY-чтение на выходные Спустя 5 лет и $5 миллионов: почему создание нового языка для веб-разработки оказалось ошибкой Безопасная песочница Облачная LLM на 16 ГБ VRAM — часть 2: LangGraph Server, LangSmith и SDK Современный SSH-клиент для MS-DOS Как продвигать агентство недвижимости: от вывески до прямых эфиров MCP для GitHub + GitLab: инженерный гайд 2026 Вы платите OpenAI $20 в месяц, а он зарабатывает на вас ещё $100 млн за полтора месяца. И это только начало ИИ забирает работу «белых воротничков»: чему учить детей, чтобы выжить в будущем Практический ИИ-агент Python: LangGraph + Qdrant Как я делал ping и traceroute на iOS без entitlements — и почему это оказалось проще, чем UMP-консент для AdMob 4 MVP за 4 месяца, 30 холодных DM, 1 регистрация: building in public по-русски VPS-бастион: доступ к домашнему серверу без белого IP Kampus AI — нейросеть для генерации учебных работ для студентов и школьников Игры, помогающие продавать — примеры интересных рекламных акций с видеоиграми €500 в Telegram Ads принесли сделку на 350 000 ₽. Разбор B2B-кампании Чтение на выходные: «Разработка игр и теория развлечений» Рафа Костера Личный архив: сбор, бэкап, таймлайн фотографий INFOSTART TECH EVENT или INFOSTART A&PM EVENT — как понять, куда вам нужнее? Peer testing на основе Закона Линуса Релиз GitLab 19.0: ИИ-оркестрация, которая наконец-то догнала темп написания кода Как бизнесу оценить готовность к аттестации по новому Приказу ФСТЭК № 117 Технический гайд по сторис – часть 4: как мы добавили видео формат Представительство в арбитражном процессе: правовые различия между внешним защитником и инхаусом «Где новые фичи?» — Как AI-миграция легаси вернет IT-бюджет бизнесу Что нужно знать работнику про увольнение Новые требования Москвы к ЦИМ для АГР: готовый инструмент для проектировщиков в nanoCAD BIM Строительство WireGuard: простота и надёжность современного VPN-туннеля или секретное рукопожатие в тёмной комнате Выйдет ли GTA 6 в 2026 году, и чего ждать от игры Как меня назвали «невовлечённым», а я нашёл офшоры на Кипре Как LLM научила рекомендательную модель видеть больше, чем историю взаимодействий От хаоса к экосистеме: Модель зрелости комьюнити в бизнесе Свет, тьма, VEML7700 и Python Сказ о том, как мы процессы разработки в GRI меняли. Часть 2 Майский «В тренде VM»: громкие уязвимости в Linux, ActiveMQ, SharePoint и Acrobat Reader Статический анализ, заряженный ИИ: как LLM ищут уязвимости в коде и где их границы Блок “Процессы” и почему мы называем его нашим мини-n8n Как поменялся рынок интернет-рекламы: сравнение первых кварталов 2025 и 2026 годов: исследование click.ru Мониторинг Kerio Connect через Zabbix 7: разбор шаблона без агентов и regex по DAT 671 Allow в Claude Code за день: как родился сетап Spec-build 3 известные интересные задачи на логику Как айтишнику позаботиться о менталке и не перерабатывать OpenAI vs Anthropic: битва экс-коллег за корпоративного клиента и $1 трлн на IPO SEO для интернет-магазина в 2026: что поменялось и как с этим работать Сможете ли вы спроектировать Maven‑монорепозиторий для 5 микросервисов? 6 неудобных вопросов про американское произношение, которые айтишники боятся задать Неожиданная встреча: теория графов вновь помогла решить проблему в анализе Фурье Иллюзия трансформации: почему компании платят за спектакль вместо изменений AMD представила Ryzen 9 PRO 9965X3D и еще 5 процессоров, которые пойдут далеко не всем История IDE в Google Первые отзывы на новинки о System Design Влияние параметра planner_upper_limit_estimation на планы выполнения и профиль нагрузки PostgreSQL при использовании 1C Границы 100% разработки с агентами Быстрый OCR на основе Paddle Дооснащение любительской электровакуумной мастерской. Вакуумметр, течеискатель, полярископ Mythos: модель, о которой Anthropic не говорит. Реверс по жертвам — от 27-летней дыры в OpenBSD до побега из песочницы Как использовать Qwen3.7-Max и Grok Build 0.1 для ИИ-агентов в России Suricata IPS NFQueue with nDPI. Часть VI Важные изменения в защите информации в России: что нового? В чем секрет достоверного замедления биологического старения? Вредное ускорение: Умный светофор на перегруженных перекрестках Как сисадмин написал свою библиотеку для Jira на Ruby: история Rujira Сломанный найм: почему рынок труда превратился в казино и что с этим делать Физики нашли свидетельства того, что Вселенная не идеально однородна, вопреки стандартной модели космологии Вопросы на собеседованиях, к которым лучше готовиться заранее
Написание консольного симулятора баттл-арены на языке С++ с реализацией «умных» ботов
rebble · 2026-05-23 · via Все публикации подряд на Хабре

Написание консольного симулятора баттл-арены на языке С++ с реализацией «умных» ботов

Уровень сложностиПростой

Время на прочтение9 мин

Охват и читатели0

Обзор

Привет, Хабр! Как‑то появилась у меня идея сделать свой симулятор бойцовского клуба, но чтобы бой был не кулачный, а с элементами интересных механик, так как я люблю фэнтези и фантастику и моими любимыми сагами являются:«Ведьмак» и «Властелин колец»(да Азог из другой книги, но это ведь одна вселенная), то я решил написать этот небольшой проектик для усвоения теории, полученной при создании таких мейнстримных консольных игр как змейка и морской бой.

Проект написан полностью на чистом С++ без применения специфических библиотек, единственная «экзотика» которая может встретиться это #include <windows.h>, но применение этой библиотеки обосновывается необходимостью в создании задержки для того, чтобы человек смог воспринять происходящее на экране(можно использовать другой способ, как вам угодно).


Инициализация карты и её отрисовка

Карта по канону создается с помощью двумерного массива char.

const int HEIGHT = 14;
const int WIDTH = 14;
char MAP[HEIGHT][WIDTH] =
{
    '#','#','#','#','#','#','#','#','#','#','#','#','#','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','#',
    '#','#','#','#','#','#','#','#','#','#','#','#','#','#'
};

Отрисовкой карты занимается функция showMap(...), объявленная в файле main.cpp. Функция принимает два указателя на базовый класс Character, о котором мы поговорим чуть ниже.

void showMap(Character* ch1, Character* ch2)
{
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (i == ch1->getPosY() && j == ch1->getPosX())
            {
                cout << ch1->getAppearance();//вывести отображение бойца 1 на экран
            }
            else if (i == ch2->getPosY() && j == ch2->getPosX())
            {
                cout << ch2->getAppearance();//вывести отображение бойца 2 на экран
            }
            else cout << MAP[i][j];//вывести границы арены и незанятые клетки на экран
        }
        cout << endl;
    }
}

Базовый виртуальный класс

Пробежимся по основному функционалу класса, не принимая во внимание геттеры и сеттеры, основная задача которых заложена в их названиях get-выдать, set-установить.

class Character {
protected:
    string name;//имя персонажа
    string feature;//его особенность(преимущество)
    char appearance;//оторажение на экране
    int HP;//уровень здоровья
    int damage;//сколько HP снимает ближняя атака
    string weapon;//название оружия
    int posX, posY;//позиция на арене
public:
    Character(string C = "Unknown", int h = 100, int d = 10, int x = 5, int y = 5, string f = "close combat", char ch = 'W',string w="sword");
    virtual ~Character() {}
    //геттеры 
    string getName() const { return name; }
    int getHP() const { return HP; }
    int getDamage() const { return damage; }
    char getAppearance() const { return appearance; }
    string getFeature() const { return feature; }
    string getWeapon()const { return weapon; }
    int getPosX()const { return posX; };
    int getPosY()const { return posY; };
    //сеттеры
    int setHP(int hp=0) { return HP=hp; };
    //функционал
    void move(int dx, int dy, Character& o);//передвижение по арене
    bool inBorders(int d)const;//проверка на достижение границы
    bool isOccupied(int y, int x, Character& o)const;//проверка на занятость клетки на арене,чтобы избежать столкновение двух бойцов
    void toRun(Character& o, bool isUnderAttack);//отбежать, если враг имеет преимущество в ближнем бою
    void toPursue(Character& o);//преследовать врага, если он слаб в ближнем бою
    bool isAllowedToAttack(Character& o)const;//проверка достаточно ли близко подошел для ближней атаки
    void showCloseAttack(Character& o)const;//отобразить ближнюю атаку на экране
    void animateRemoteAttack(Character& o, char symb);//отобразить атаку на расстоянии
    void showRemoteAttack(Character& o, int x, int y, char& symb);//отобразить позицию стрелы(пламенного шара) на данный момент
    virtual void Attack(Character& obj) = 0;
    virtual bool isOnSight(Character& obj) = 0;
    virtual void Character_info(Character& obj) = 0;
    friend ostream& operator<<(ostream& os, Character& o);//вывести основную информацию о персонаже
};

Функция isOccupied(...) проверяет не занята ли клетка, куда хочет шагнуть боец 1 бойцом 2.

bool Character::isOccupied(int y, int x,Character& o) const
{

    return o.getPosY() == y&&o.getPosX()==x;
}

Функция move(...) позволяет перемещать бойца по полю, проверяя каждую клетку на занятость другим бойцом. Данная функция используется в функциях, объявленных в файле main.cpp: void move_ch(int& d, Character& o, Character& check) и void move_ch_opposite(int& d, Character& o, Character& check) в которых и реализована логика выбора направления и передачи координат.

void Character::move(int dx, int dy,Character& o)  {
    if (isOccupied(posY + dy, posX + dx, o))
    {
        int ddx = -dx;
        int ddy = -dy;
        int newX = posX + ddx;
        int newY = posY + ddy;
        if (MAP[newY][newX] != '#')
        {
            posX = newX; posY = newY;
        }
        else
        {
            posX += 0;
            posY += 0;
        }
    }
    else
    {
        posX += dx; posY += dy;
    }
}

Функция inBorders(...) проверяет не вышел ли боец за границы арены. Если вышел функция возвращает false, иначе true.

bool Character::inBorders(int d) const { 
    int newX = posX;
    int newY = posY;

    switch (d) {
    case UP:    newY--; break;
    case DOWN:  newY++; break;
    case LEFT:  newX--; break;
    case RIGHT: newX++; break;
    default: return false;
    }

    // Проверка выхода за границы массива
    if (newX < 1 || newX >= WIDTH-1 || newY < 1 || newY >= HEIGHT-1)
        return false;

    // Проверка стены 
    if (MAP[newY][newX] == '#') return false;

    return true;
}

Функция toRun(...) позволяет добавить логику побега от более сильного физически бойца. Если боец 1 сильнее бойца 2 в ближнем бою и боец 1 наносит урон бойцу 2, то боец 2 старается отдалиться от противника.

void Character::toRun(Character& o, bool isUnderAttack)
{
    if (!isUnderAttack) return;          // убегаем только после получения удара

    int x_enemy = o.getPosX();
    int y_enemy = o.getPosY();
    int x_me = posX;
    int y_me = posY;

    // Направление ОТ врага
    int dx = 0, dy = 0;
    if (x_me < x_enemy) dx = -1;         // враг справа - бежим влево
    else if (x_me > x_enemy) dx = 1;     // враг слева – бежим вправо

    if (dx == 0)
    {
        if (y_me < y_enemy) dy = -1;         // враг снизу – бежим вверх
        else if (y_me > y_enemy) dy = 1;     // враг сверху – бежим вниз
    }
    int newX = x_me + dx;
    int newY = y_me + dy;

    // Функция проверки проходимости (стены + границы)
    auto isWalkable = [](int x, int y) -> bool {
        return (x >= 1 && x < WIDTH - 1 && y >= 1 && y < HEIGHT - 1 && MAP[y][x] != '#');
        };

    if (isWalkable(newX, newY)) {
        move(dx, dy, o);
        return;
    }
}

Функция toPursue(...) противоположна по логике функции toRun(...): более сильный боец преследует более слабого пока не сблизится с ним.

void Character::toPursue(Character& o)
{
    int x_enemy = o.getPosX();
    int y_enemy = o.getPosY();
    int x_me = posX;
    int y_me = posY;

    int dist = sqrt(pow(x_enemy - x_me, 2) + pow(y_enemy - y_me, 2));
    if (dist <= 1) return;              // уже рядом – атакуем в основном цикле

    // Направление к врагу
    int dx = 0, dy = 0;
    if (x_me < x_enemy) dx = 1;
    else if (x_me > x_enemy) dx = -1;

    if (dx == 0)
    {
        if (y_me < y_enemy) dy = 1;
        else if (y_me > y_enemy) dy = -1;
    }

    int newX = x_me + dx;
    int newY = y_me + dy;

    // Проверка на стены
    bool walkable = (newX >= 1 && newX < WIDTH - 1 && newY >= 1 && newY < HEIGHT - 1 && MAP[newY][newX] != '#');
    // Запрещаем занимать клетку врага
    if (walkable && (newX != x_enemy || newY != y_enemy))
        move(dx, dy, o);
}

Функция isAllowedToAttack(...) позволяет проверить достаточно ли близко подошел боец 1 для того чтобы атаковать бойца 2 в ближнем бою. Число 1.5 выбрано по причине того что sqrt(2) это приблизительно 1,41 , соответственно бить по диагонали можно.

bool Character::isAllowedToAttack(Character& o)const
{
    int x_enemy = o.getPosX();
    int y_enemy = o.getPosY();
    int x_me = posX;
    int y_me = posY;
    double distance = sqrt(pow(x_me - x_enemy, 2) + pow(y_me - y_enemy, 2));
    return distance <= 1.5;
}

Функция showCloseAttack(...) обновляет карту и отображает бойца по которому наносится урон в виде 'X'.

void Character::showCloseAttack(Character& o) const
{
    system("cls");
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (i == posY && j == posX)
            {
                cout << getAppearance();
            }
            else if (i == o.getPosY() && j == o.getPosX())
            {
                cout << 'X';
            }
            else cout << MAP[i][j];
        }
        cout << endl;
    }
}

Функция showRemoteAttack(...) отображает позицию снаряда в данном кадре. Сама анимация полета снаряда происходит в функции animateRemoteAttack(...).

void Character::showRemoteAttack(Character& o, int x, int y, char& symb)
{
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (i == posY && j == posX)
            {
                cout << getAppearance();
            }
            else if (i == o.getPosY() && j == o.getPosX())
            {
                cout << o.getAppearance();
            }
            else if (i == y && j == x)cout << symb;
            else cout << MAP[i][j];
        }
        cout << endl;
    }
}

Функция animateRemoteAttack(...), как было сказано выше реализует отрисовку полета снаряда. При расчете количества шагов до цели я просто вычислил длину вектора и данный способ оказался неверным, поскольку по диагонали не всегда получается целое число и при такой отрисовке снаряд отображается некорректно для выстрела по диагонали. Я надеюсь более опытные программисты подправят данную функцию и скажут о решении, которое они нашли в комментариях.

void Character::animateRemoteAttack(Character& o, char symb)
{
    system("cls");
    int x_target = o.getPosX();
    int y_target = o.getPosY();
    int dx = (x_target > posX) ? 1 : (x_target < posX) ? -1 : 0;
    int dy = (y_target > posY) ? 1 : (y_target < posY) ? -1 : 0;
    double realDistance = sqrt(dx * dx + dy * dy);
    int steps = int(realDistance);
    for (int i = 0; i <= steps; i++)
    {
        int cx = posX + dx * i, cy = posY + dy * i;
        showRemoteAttack(o, cx, cy, symb);
        Sleep(100);
        system("cls");
    }
    showCloseAttack(o);
    Sleep(100);
}

Остальные функции являются чисто виртуальными и реализуются в каждом производном классе в зависимости от параметров, переданных ему.

Класс Warrior

Я не буду подробно расписывать каждую функцию для дальнейших классов, просто приведу объявление для каждого класса.

class Warrior : virtual public Character { 
    string ArmorName;//название брони
    int remote_damage;//урон от дальней атаки
    int defense;//добавляет хп в зависимости от брони
    int SwordSharpness;//добавляет урон в зависимости от остроты клинка
public:
    Warrior(string cl, int hp, int dam, int x, int y, string f, char a,string w,
        string A = "Wolf school armor", int def = 25, int SS = 10, int rd = 10)
        : Character(cl, hp, dam, x, y, f, a,w), ArmorName(A), defense(def), SwordSharpness(SS),remote_damage(rd) {
        HP += defense;
    }
    virtual ~Warrior() {}
    //геттеры
    string getArmorName()const { return ArmorName; }
    int getRemote_Damage()const { return remote_damage; }
    int get_defense()const { return defense; }
    int getSwordSharpness()const { return SwordSharpness; }
    //функционал
    void Attack(Character& o)override;//виртуальная функция атаки(здесь происходит логика отнимания HP при ударе),т.к. добавляется доп.урон в зависимости от остроты клинка
    void Remote_Attack(Character& o);//атака на расстоянии с помощью арбалета
    bool isAllowedRemoteAttack(Character& o);//проверка достаточно ли расстояния для того чтобы атаковать из арбалета, если слишком близко или далеко - не атаковать
    bool isOnSight(Character& obj)override;//вирутальная функция позволяющая обнаружить врага в поле зрения
    void Character_info(Character& obj)override;//виртуальная функция, выводит информацию об атаке:кто кого атаковал,тип атаки(вблизи), сколько HP снесла атака и сколько HP осталось у противника
    void Character_info_remote(Character& obj);//вывести информацию об атаке:кто кого атаковал,тип атаки(на расстоянии), сколько HP снесла атака и сколько HP осталось у противника
    friend ostream& operator<<(ostream& os, const Warrior& o); //вывести информацию о параметрах класса
};

Класс Orc

class Orc :virtual public Character
{
private:
	string race;//раса орка
	int buff;//доп.урон в зависимости от расы
public:
	Orc(string cl, int hp, int dam, int x, int y, string f, char a,string w,
		string r="Grey orc", int b=20) :Character(cl, hp, dam, x, y, f, a,w), race(r), buff(b) { }
	virtual ~Orc() {}
	void Attack(Character& o)override;//виртуальная функция атаки(здесь происходит логика отнимания HP при ударе),т.к. добавляется доп.урон в зависимости от расы орка(физ.сила)
	bool isOnSight(Character& o)override;//вирутальная функция позволяющая обнаружить врага в поле зрения(каждый класс имеет свою дальность)
	void Character_info(Character& obj)override;//виртуальная функция, выводит информацию об атаке:кто кого атаковал,тип атаки(вблизи), сколько HP снесла атака и сколько HP осталось у противника
	friend ostream& operator<<(ostream& os, const Orc& o);//вывести информацию о параметрах класса
};

Класс Magician

class Magician :public Character
{
private:
	string power;
	int power_damage;
public:
	Magician(string cl, int hp, int dam, int x, int y, string f, char a,string w,
		string p = "Fire", int pd = 25) :Character(cl, hp, dam, x, y, f, a,w),power(p),power_damage(pd){}
	virtual ~Magician() {}
	void Attack(Character& o)override;//ближняя атака стальным шестом
	void Magic_Attack(Character& o);//атака магией на расстоянии
	bool isAllowedMagicAttack(Character& o);//проверка достаточно ли расстояния для того чтобы атаковать магией, если слишком близко или далеко - не атаковать
	bool isOnSight(Character& obj)override;//видно ли врага, если расстояние до врага меньше или равно 15.0, то true, иначе false
	void Character_info(Character& obj)override;//вывести информацию об атаке:кто кого атаковал,тип атаки(вблизи), сколько HP снесла атака и сколько HP осталось у противника
	void Character_info_remote(Character& obj)const;//вывести информацию об атаке:кто кого атаковал,тип атаки(на расстоянии), сколько HP снесла атака и сколько HP осталось у противника
	friend ostream& operator<<(ostream& os, Magician& o);//вывести информацию о параметрах класса
};

Кто хочет более подробно разобрать код данного проекта, вот ссылка на данный проект, в описании рассказано более подробно о логике работы каждого класса:

GitHub

Надеюсь вам было интересно читать данную статью и у вас появились идеи как улучшить данный проект или создать свой! Делитесь своими идеями в комментариях, пожалуйста, будет интересно почитать как можно улучшить код, поскольку я не силен в алгоритмах, да и в программировании я ещё зелёный.