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

推荐订阅源

Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
量子位
腾讯CDC
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
The Hacker News
The Hacker News
T
The Exploit Database - CXSecurity.com
B
Blog
S
SegmentFault 最新的问题
P
Privacy & Cybersecurity Law Blog
T
Threatpost
博客园 - 聂微东
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
C
Check Point Blog
N
Netflix TechBlog - Medium
D
DataBreaches.Net
爱范儿
爱范儿
IT之家
IT之家
S
Secure Thoughts
M
MIT News - Artificial intelligence
NISL@THU
NISL@THU
C
Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
有赞技术团队
有赞技术团队
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
Schneier on Security
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
雷峰网
雷峰网
Project Zero
Project Zero
博客园 - Franky
H
Heimdal Security Blog
A
About on SuperTechFans
Security Latest
Security Latest
Webroot Blog
Webroot Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Написание консольного симулятора баттл-арены на языке С++ с реализацией «умных» ботов
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

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