icon안동민 개발노트

최신 C++ 기능을 활용한 프로그램 작성


프로그램 개요 : 텍스트 기반 RPG 게임

 이 절에서는 C++ 20의 새로운 기능들을 활용하여 실제 프로그램을 작성해보겠습니다.

 간단한 텍스트 기반 게임을 구현하면서 최신 C++ 기능들을 적용해 볼 것입니다.

 우리가 만들 게임은 다음 기능을 포함합니다.

  1. 캐릭터 생성 및 관리
  2. 아이템 시스템
  3. 간단한 전투 시스템
  4. 퀘스트 시스템
  5. 게임 진행 상황 저장 및 불러오기

기본 구조 설계

 먼저 게임의 기본 구조를 설계합니다.

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <random>
#include <concepts>
#include <coroutine>
#include <fstream>
#include <json.hpp> // nlohmann/json 라이브러리 사용
 
// 캐릭터 관련 클래스
class Character {
public:
    Character(std::string name, int health, int attack, int defense)
        : name_(std::move(name)), health_(health), attack_(attack), defense_(defense) {}
    
    virtual ~Character() = default;
 
    virtual void attack(Character& target) = 0;
    virtual void take_damage(int damage) = 0;
 
    const std::string& name() const { return name_; }
    int health() const { return health_; }
    int attack() const { return attack_; }
    int defense() const { return defense_; }
 
protected:
    std::string name_;
    int health_;
    int attack_;
    int defense_;
};
 
// 아이템 클래스
class Item {
public:
    Item(std::string name, int value) : name_(std::move(name)), value_(value) {}
 
    const std::string& name() const { return name_; }
    int value() const { return value_; }
 
private:
    std::string name_;
    int value_;
};
 
// 게임 관리 클래스
class Game {
public:
    void run();
 
private:
    void create_character();
    void battle();
    void shop();
    void save_game();
    void load_game();
 
    std::unique_ptr<Character> player_;
    std::vector<Item> inventory_;
};

개념 (Concepts) 활용

 캐릭터와 아이템에 대한 개념을 정의합니다.

template<typename T>
concept Attackable = requires(T a, T b) {
    { a.attack(b) } -> std::same_as<void>;
    { a.take_damage(int{}) } -> std::same_as<void>;
};
 
template<typename T>
concept Inventoriable = requires(T a) {
    { a.name() } -> std::convertible_to<std::string>;
    { a.value() } -> std::convertible_to<int>;
};
 
static_assert(Attackable<Character>);
static_assert(Inventoriable<Item>);

코루틴을 활용한 비동기 전투 시스템

struct BattleResult {
    bool player_won;
    int exp_gained;
};
 
struct BattleAwaiter {
    bool await_ready() const noexcept { return false; }
    void await_suspend(std::coroutine_handle<> h) const noexcept {}
    BattleResult await_resume() const noexcept { return result; }
    BattleResult result;
};
 
class BattleCoroutine {
public:
    struct promise_type {
        BattleResult result;
        BattleCoroutine get_return_object() { return BattleCoroutine(this); }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
        BattleAwaiter yield_value(BattleResult r) {
            result = r;
            return BattleAwaiter{r};
        }
    };
 
    BattleCoroutine(promise_type* p) : coro_(std::coroutine_handle<promise_type>::from_promise(*p)) {}
    ~BattleCoroutine() { if (coro_) coro_.destroy(); }
 
    BattleResult get_result() { return coro_.promise().result; }
 
private:
    std::coroutine_handle<promise_type> coro_;
};
 
BattleCoroutine battle_system(Character& player, Character& enemy) {
    while (player.health() > 0 && enemy.health() > 0) {
        player.attack(enemy);
        if (enemy.health() <= 0) {
            co_yield BattleResult{true, 100};  // Player won
            co_return;
        }
        enemy.attack(player);
        if (player.health() <= 0) {
            co_yield BattleResult{false, 0};  // Player lost
            co_return;
        }
        co_yield BattleResult{false, 0};  // Battle ongoing
    }
}

게임 클래스 구현

void Game::run() {
    std::cout << "Welcome to the Text RPG Game!" << std::endl;
 
    while (true) {
        std::cout << "\n1. Create Character\n2. Battle\n3. Shop\n4. Save Game\n5. Load Game\n6. Exit\n";
        std::cout << "Enter your choice: ";
        int choice;
        std::cin >> choice;
 
        switch (choice) {
            case 1: create_character(); break;
            case 2: battle(); break;
            case 3: shop(); break;
            case 4: save_game(); break;
            case 5: load_game(); break;
            case 6: return;
            default: std::cout << "Invalid choice. Please try again.\n";
        }
    }
}
 
void Game::create_character() {
    std::string name;
    std::cout << "Enter character name: ";
    std::cin >> name;
    player_ = std::make_unique<PlayerCharacter>(name, 100, 10, 5);
    std::cout << "Character created successfully!" << std::endl;
}
 
void Game::battle() {
    if (!player_) {
        std::cout << "Please create a character first." << std::endl;
        return;
    }
 
    EnemyCharacter enemy("Goblin", 50, 8, 3);
    std::cout << "Battle started! " << player_->name() << " vs " << enemy.name() << std::endl;
 
    auto battle = battle_system(*player_, enemy);
    auto result = battle.get_result();
 
    if (result.player_won) {
        std::cout << "You won the battle! EXP gained: " << result.exp_gained << std::endl;
    } else {
        std::cout << "You lost the battle." << std::endl;
    }
}
 
// shop(), save_game(), load_game() 메서드의 구현은 생략

컴파일 및 실행

 이 프로그램을 컴파일하려면 C++ 20을 지원하는 최신 컴파일러가 필요합니다.

g++ -std=c++20 rpg_game.cpp -o rpg_game
./rpg_game

프로그램 설명

 이 프로그램은 다음과 같은 C++ 20 기능을 활용합니다.

  1. 개념(Concepts) : AttackableInventoriable 개념을 사용하여 캐릭터와 아이템의 인터페이스를 정의했습니다.
  2. 코루틴(Coroutines) : 전투 시스템을 비동기적으로 구현하기 위해 코루틴을 사용했습니다.
  3. std::unique_ptr : 스마트 포인터를 사용하여 메모리 관리를 자동화했습니다.

연습 문제

  1. 캐릭터에 레벨 시스템을 추가하고, 전투에서 승리할 때마다 경험치를 얻어 레벨업할 수 있도록 구현해보세요.
  2. 아이템 시스템을 확장하여 무기, 방어구, 포션 등 다양한 종류의 아이템을 추가하고, 이를 인벤토리에서 사용할 수 있도록 구현해보세요.

 참고자료

  • C++ 20 표준 문서
  • "C++ 20 : The Complete Guide" by Nicolai M. Josuttis
  • CppCon 발표 영상들 - C++ 20 관련 세션들
  • C++ Reference 웹사이트의 C++ 20 섹션
  • "Mastering the C++17 STL" by Arthur O'Dwyer (C++ 20 업데이트 참고)