내일배움캠프-언리얼

[내일배움캠프 - 언리얼] DAY 18 - 연금술 공방 관리 시스템 구현

이시보 2025. 12. 22. 21:18
#include <iostream>
#include <vector>
#include <map>
#include <string>

using namespace std;

// PotionRecipe 클래스: 재료 목록을 vector<string>으로 변경
class PotionRecipe {
public:
    string potionName;
    vector<string> ingredients; // 단일 재료에서 재료 '목록'으로 변경

    // 생성자: 재료 목록을 받아 초기화하도록 수정
    PotionRecipe(const string& name, const vector<string>& ingredients)
        : potionName(name), ingredients(ingredients) {
    }
};

// StockManager 클래스: 레시피 재고 관리
class StockManager {
private:
    map<string, int> potionStock;
    const int MAX_STOCK = 3;

public:
    // 재고 설정
    void initializeStock(string potionName) {
        if (potionStock.find(potionName) == potionStock.end()) potionStock.emplace(potionName, MAX_STOCK);
        else return;
    }

    // 물약 사용
    bool dispensePotion(string potionName) {
        if (potionStock[potionName] > 0) {
            potionStock[potionName]--;
            return true;
        }
        else return false;
    }

    // 빈병 반환
    void returnPotion(string potionName) {
        if (potionStock[potionName] < MAX_STOCK) {
            potionStock[potionName]++;
        }
        else {
            potionStock[potionName] = MAX_STOCK;
        }
    }

    // 재고 정보 반환
    int getStock(string potionName) const{
        if (potionStock.find(potionName) != potionStock.end()) {
            return potionStock.find(potionName)->second;
        }
        else return -1;
    }
};

// RecipeManager 클래스: 레시피 관리
class RecipeManager {
private:
    vector<PotionRecipe> recipes; // 레시피를 벡터로 저장

public:

    // addRecipe 메서드: 재료 목록(vector)을 매개변수로 받도록 수정
    PotionRecipe* addRecipe(const string& name, const vector<string>& ingredients) {
        if (findRecipeByName(name) != nullptr) return nullptr; // 같은 이름의 포션이 있으면 nullptr 반환
        PotionRecipe pRecipe = PotionRecipe(name, ingredients);
        recipes.push_back(pRecipe);
        return &recipes.back();
    }

    // 이름으로 레시피 찾기
    PotionRecipe* findRecipeByName(string name) {
        for (size_t i = 0; i < recipes.size(); ++i) {
            if (name == recipes[i].potionName) {
                return &recipes[i];
            }
        }
        return nullptr;
    }

    // 재료로 레시피 찾기
    vector<PotionRecipe> findRecipesByIngredient(string ingredient) {
        vector<PotionRecipe> searcedRecipes;
        for (size_t i = 0; i < recipes.size(); ++i) {
            for (size_t j = 0; j < recipes[i].ingredients.size(); ++j) {
                if (ingredient == recipes[i].ingredients[j]) {
                    searcedRecipes.push_back(recipes[i]);
                    break;
                }
            }
        }
        return searcedRecipes;
    }

    // 모든 레시피 반환
    const vector<PotionRecipe>& getAllRecipes() const{
        return recipes;
    }
};

// AlchemyWorkshop 클래스
class AlchemyWorkshop {
private:
    RecipeManager rManager;
    StockManager sManager;

public:
    // addRecipe 메서드
    void addRecipe(const string& name, const vector<string>& ingredients) {
        if (rManager.addRecipe(name, ingredients) != nullptr) {  // 해당 이름의 레시피가 없는지 확인
            cout << ">> 새로운 레시피 '" << name << "'이(가) 추가되었습니다." << endl;
            sManager.initializeStock(name);
        }
        else cout << ">> 해당 이름의 레시피가 이미 존재합니다." << endl;
    }

    // 모든 레시피 출력 메서드
    void displayAllRecipes() const {
        if (rManager.getAllRecipes().empty()) {
            cout << "아직 등록된 레시피가 없습니다." << endl;
            return;
        }

        cout << "\n--- [ 전체 레시피 목록 ] ---" << endl;
        for (size_t i = 0; i < rManager.getAllRecipes().size(); ++i) {
            cout << "- 물약 이름: " << rManager.getAllRecipes()[i].potionName;
            cout << " 재고: " << sManager.getStock(rManager.getAllRecipes()[i].potionName) << "병" << endl;
            cout << "  > 필요 재료: ";

            // 재료 목록을 순회하며 출력
            for (size_t j = 0; j < rManager.getAllRecipes()[i].ingredients.size(); ++j) {
                cout << rManager.getAllRecipes()[i].ingredients[j];
                // 마지막 재료가 아니면 쉼표로 구분
                if (j < rManager.getAllRecipes()[i].ingredients.size() - 1) {
                    cout << ", ";
                }
            }
            cout << endl;
        }
        cout << "---------------------------\n";
    }

    // 이름으로 물약 재고 찾기
    int getStockByName(const string& name) const {
        return sManager.getStock(name);
    }

    // 이름으로 물약 지급
    bool dispensePotionByName(const string& name) {
        if (sManager.dispensePotion(name)) return true;
        else return false;
    }

    // 재료로 물약 지급
    vector<string> dispensePotionsByIngredient(const string& ingredient) {
        vector<string> recipesName;
        vector<PotionRecipe> recipes = rManager.findRecipesByIngredient(ingredient);
        for (PotionRecipe rec : recipes) {
            if (sManager.dispensePotion(rec.potionName)) {
                recipesName.push_back(rec.potionName);
            }
        }
        if (recipes.empty()) cout << "해당 재료의 레시피가 없습니다." << endl;
        return recipesName;
    }
    
    // 물약 반환
    void returnPotionByName(const string& name) {
        sManager.returnPotion(name);
    }
};

int main() {
    AlchemyWorkshop myWorkshop;

    while (true) {
        cout << "⚗️ 연금술 공방 관리 시스템" << endl;
        cout << "1. 레시피 추가" << endl;
        cout << "2. 모든 레시피 출력" << endl;
        cout << "3. 물약 재고 확인" << endl;
        cout << "4. 이름으로 물약 지급 받기" << endl;
        cout << "5. 재료로 물약 지급 받기" << endl;
        cout << "6. 빈병 반환하기" << endl;
        cout << "7. 종료" << endl;
        cout << "선택: ";

        int choice;
        cin >> choice;

        if (cin.fail()) {
            cout << "잘못된 입력입니다. 숫자를 입력해주세요." << endl;
            cin.clear();
            cin.ignore(10000, '\n');
            continue;
        }

        if (choice == 1) {
            string name;
            cout << "물약 이름: ";
            cin.ignore(10000, '\n');
            getline(cin, name);

            // 여러 재료를 입력받기 위한 로직
            vector<string> ingredients_input;
            string ingredient;
            cout << "필요한 재료들을 입력하세요. (입력 완료 시 '끝' 입력)" << endl;

            while (true) {
                cout << "재료 입력: ";
                getline(cin, ingredient);

                // 사용자가 '끝'을 입력하면 재료 입력 종료
                if (ingredient == "끝") {
                    break;
                }
                ingredients_input.push_back(ingredient);
            }

            // 입력받은 재료가 하나 이상 있을 때만 레시피 추가
            if (!ingredients_input.empty()) {
                myWorkshop.addRecipe(name, ingredients_input);
            }
            else {
                cout << ">> 재료가 입력되지 않아 레시피 추가를 취소합니다." << endl;
            }

        }
        else if (choice == 2) {
            myWorkshop.displayAllRecipes();
        }
        else if (choice == 3) {
            string potionName;
            cout << "확인하려는 물약의 이름: ";
            cin.ignore(10000, '\n');
            getline(cin, potionName);
            if (myWorkshop.getStockByName(potionName) != -1) {
                cout << potionName << "의 남은 재고: " << myWorkshop.getStockByName(potionName) << "개" << endl;
            }
            else {
                cout << "해당 이름의 포션이 존재하지 않습니다." << endl;
            }
        }
        else if (choice == 4) {
            string potionName;
            cout << "지급 받으려는 포션의 이름: ";
            cin.ignore(10000, '\n');
            getline(cin, potionName);
            if (myWorkshop.dispensePotionByName(potionName)) {
                cout << potionName << "포션이 하나 지급되었습니다." << endl;
            }
            else {
                cout << potionName << "포션의 재고가 부족합니다." << endl;
            }
        }
        else if (choice == 5) {
            string ingredientName;
            vector<string> potionNames;
            cout << "지급 받으려는 포션의 재료: ";
            cin.ignore(10000, '\n');
            getline(cin, ingredientName);
            potionNames = myWorkshop.dispensePotionsByIngredient(ingredientName);
            if (!potionNames.empty()) {
                for (size_t i = 0; i < potionNames.size(); ++i) {
                    cout << potionNames[i];
                    if (i != potionNames.size()-1) {
                        cout << ", ";
                    }
                }
                cout << "포션이 하나씩 지급되었습니다." << endl;
            }
        }
        else if (choice == 6) {
            string potionName;
            cout << "반환하려는 포션의 이름: ";
            cin.ignore(10000, '\n');
            getline(cin, potionName);
            myWorkshop.returnPotionByName(potionName);
        }
        else if (choice == 7) {
            cout << "공방 문을 닫습니다..." << endl;
            break;
        }
        else {
            cout << "잘못된 선택입니다. 다시 시도하세요." << endl;
        }
    }

    return 0;
}