본문 바로가기

숙제

C++ 4번 과제 도전 기능

어제 구현한 4번 과제의 필수 기능은 레시피를 이름으로 검색하고, 재료로 검색하는 기능 추가하기 였다.

도전 기능에서는 아래 요구사항을 충족해야한다.

  • 레시피가 공방에 추가될 때, 해당 물약은 자동으로 3개의 초기 재고를 가집니다. (별도의 '조합' 과정 없음)
  • 지급 및 반환:
    • 특정 물약의 현재 재고 수량을 알 수 있어야 합니다.
    • 물약 이름으로 재고를 검색하고, 재고가 1개 이상인 경우 모험가에게 지급할 수 있습니다.
    • 모험가에게 지급했던 물약의 공병을 다시 반환 받을 수 있어야 합니다.
  • 모든 물약의 최대 재고는 3개로 고정되며, 반환 시 3개를 초과할 수 없습니다.

가이드(참고용) 똑같이 만들 필요는 없음.


과제에서 참고용으로 올려준 클래스 다이어그램을 따라해보려했지만 StockManager를 먼저 만들고 보니 요구사항 대로는 작동을 하길래 다른 방식으로 구현이 됐다.

필수 기능에서 구현한 AlchemyWorkshop과 PotionRecipe은 수정한 부분이 없고 StockManager 클래스만 새로 만들었다.

StockManager

#pragma once
#include <map>
#include <string>
class StockManager
{
private:
	std::map<std::string, int> potionStock;
	const int MAX_STOCK = 3;

public:
	void initializeStock(const std::string potionName);
	bool dispensePotion(const std::string potionName);
	void returnPotion(const std::string potionName);
	int getStock(const std::string potionName) const;
};
#include "StockManager.h"
#include <iostream>

using namespace std;

void StockManager::initializeStock(const std::string potionName) // 공방에 추가 될 때 해당 물약은 자동으로 3개 초기 재고를 가진다.
{
	potionStock[potionName] = MAX_STOCK;
}

bool StockManager::dispensePotion(const std::string potionName) // 모험가에게 포션 지급 함수
{
	auto it = potionStock.find(potionName); // 이름 검색

	if (it != potionStock.end() && it->second > 0) // 포션이 있고 재고가 있을 때
	{
		it->second -= 1;
		return it->second + 1 > 0; // 재고에 따라 true, false 반환
	}
	else
	{
		return false;
	}
}

void StockManager::returnPotion(const std::string potionName) // 포션 반환 함수
{
	auto it = potionStock.find(potionName);

	if (it != potionStock.end() && it->second < MAX_STOCK) // 포션이 있고 재고가 3개 미만일 때
	{
		it->second += 1;
		cout << it->first << "이(가) 반환 됐습니다. 남은 재고: " << it->second << endl;
	}
	else if(it == potionStock.end()) // 못찾았을 때
	{
		cout << "없는 포션입니다." << endl;
	}
	else if (it->second >= 3) // 재고가 3개 이상일 때
	{
		cout << "이미 최대 개수를 소지하고 있습니다." << endl;
	}
}

int StockManager::getStock(const std::string potionName) const // 포션 이름 검색해서 재고 출력하기
{
	auto it = potionStock.find(potionName); // 이름 검색

	if (it != potionStock.end()) // 있으면
	{
		return it->second; // 재고량 반환
	}
	else
	{
		return 0;
	}
}

위와 같이 StockManager를 작성하고 보니 과제에서 요구한 기능대로 겉으로는 작동하는 듯 하지만 사실 StockManager의 함수에 들어오는 매개변수(potionName)과 실제 AlchemyWorkshop의 addRecipe 함수에서 객체화 한 PotionRecipe의 네임과 연동(연결)이 안된다는 사실을 깨달았다.

그래서 연결을 하려고 오래 생각해봤는데 답을 찾는데 좀 오래 걸릴것 같아서 우선 위 방식대로 구현을 했다.

#include "AlchemyWorkshop.h"
#include "StockManager.h"
#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    AlchemyWorkshop myWorkshop;
    StockManager stock;

    while (true)
    {
        cout << "--------------------" << endl;
        cout << "연금술 공방 관리 시스템" << endl;
        cout << "1. 레시피 추가" << endl;
        cout << "2. 모든 레시피 출력" << endl;
        cout << "3. 레시피 이름으로 검색" << endl; // 필수기능
        cout << "4. 레시피 재료로 검색" << endl; // 필수기능
        cout << "5. 모험가에게 물약 지급" << endl; // 도전기능
        cout << "6. 물약 반환" << endl; // 도전기능
        cout << "7. 물약 재고 확인" << endl; // 도전기능
        cout << "8. 종료" << endl;
        
        cout << "선택: ";

        int choice;
        cin >> choice; // 사용자에게 입력 값 받음.

        if (cin.fail()) // 입력받아야 할 자료형과 다른 형식이 입력됐을 때 fail이 true가 됨.
        {
            cout << "잘못된 입력입니다. 숫자를 입력해주세요." << endl;
            cin.clear(); // clear는 입력 스트림의 오류 상태를 초기화 하는 함수.
            cin.ignore(10000, '\n'); // ignore은 입력 버퍼를 초기화 시키는 함수. 10000글자까지 \n전 까지 초기화시킴.
            continue; // 반복문의 처음으로
        }

        if (choice == 1)
        {
            string name;
            cout << "물약 이름: ";
            cin.ignore(10000, '\n');
            getline(cin, name); // 사용자가 엔터를 칠때까지 한 줄 단위로 입력을 받음

            stock.initializeStock(name); // 도전기능 - 물약 추가될 때 초기 재고 3개

            // 여러 재료를 입력받기 위한 로직
            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()) // 벡터가 비어있지 않을 때 true
            {
                myWorkshop.addRecipe(name, ingredients_input);
            }
            else
            {
                cout << ">> 재료가 입력되지 않아 레시피 추가를 취소합니다." << endl;
            }

        }
        else if (choice == 2)
        {
            myWorkshop.displayAllRecipes();

        }
        else if (choice == 3) // 필수 기능 - 이름으로 검색하기
        {
            string findpotion; // 검색할 이름을 입력받기 위한 문자열 변수 선언

            cout << "검색할 물약의 이름을 입력하세요: ";
            cin.ignore(10000, '\n');
            getline(cin, findpotion);

            myWorkshop.searchRecipeByName(findpotion);
        }
        else if (choice == 4) // 필수 기능 - 재료로 검색하기
        {
            string findingredient; // 검색할 재료를 입력받기 위한 문자열 변수 선언

            cout << "검색할 재료를 입력하세요: ";
            cin.ignore(10000, '\n');
            getline(cin, findingredient);

            myWorkshop.searchRecipeByIngredient(findingredient);
        }
        else if (choice == 5) // 도전 기능 - 물약 지급하기
        {
            string name;

            cout << "지급할 물약을 입력하세요: ";
            cin.ignore(10000, '\n');
            getline(cin, name);

            if (stock.dispensePotion(name))
            {
                cout << name << "이(가) 지급 되었습니다." << endl;
            }
            else
            {
                cout << "없는 물약이거나 재고가 0입니다." << endl;
            }
        }
        else if (choice == 6) // 도전 기능 - 물약 반환하기
        {
            string name;

            cout << "반환할 물약을 입력하세요: ";
            cin.ignore(10000, '\n');
            getline(cin, name);

            stock.returnPotion(name);
        }
        else if (choice == 7) // 도전기능 - 현재 재고 확인
        {
            string name;

            cout << "재고를 확인 할 물약을 입력하세요: ";
            cin.ignore(10000, '\n');
            getline(cin, name);

            cout << "현재 " << name << "의 재고는 " << stock.getStock(name) << "개 입니다." << endl;
        }
        else if (choice == 8)
        {
            cout << "공방 문을 닫습니다..." << endl;
            break;

        }
        else
        {
            cout << "잘못된 선택입니다. 다시 시도하세요." << endl;
        }
    }

    return 0;
}

choice 5, 6, 7번에 도전기능 추가했고 3, 4번에 cin >> 으로 입력 받던걸 모두 getline 함수로 일관성 있도록 바꿨다.

cin >> 으로 입력하면 입력값 사이에 공백이 있으면 공백 전까지만 입력이 되고 getline 함수는 공백까지 모두 입력이 된다.

실행 결과

물약 추가시 기초 재고 3개 지급 확인 후 3번 지급

재고가 없는 상태에서 더 지급하려하니 재고가 0입니다 출력

반환으로 4번 채우니 최대 개수를 소지하고 있다고 출력

지급을 한번하고 재고를 출력하니 재고 2개 출력

 

임시방편으로 포션의 이름을 연결하지 않고 겉으로만 작동하게끔 구현을 해놨지만 공부를 더 해서 이름끼리 연동되도록 해 볼 생각이다.