TIL(25)

이채원·2026년 7월 16일

TIL

목록 보기
25/77

오늘의 할일
1. 코드카타 정리
2. 과제
3. 궁금증 해결
4. 다음 학습 예습

개념 요약

  • 복사 대입 연산자, Rule of Three?

메모리 이중 해제를 막기 위해 생긴 게 Rule of Three
-> 어떤 클래스가 3가지 법칙 중 단 하나라도 직접 구현해야 한다면 무조건 나머지 2개도 함께 직접 구현해야 한다는 법칙
1. 소멸자 (Destructor): ~Inventory()
2. 복사 생성자 (Copy Constructor): Inventory(const Inventory& other)
3. 복사 대입 연산자 (Copy Assignment Operator): operator=(const Inventory& other)

복사 생성자 vs 복사 대입 연산자

  • 복사 생성자 : 객체가 새로 태어날 때 기존 객체를 복사해서 태어남
    -> 새로 태어나는 중이라 기존 메모리가 아예 비어있음
Inventory inv2 = inv1;
Inventory inv2(inv1);
  • 복사 대입 연산자 : 이미 태어나서 살아있는 두 객체끼리 값을 덮어씌움
    -> 이미 살아있었기 때문에 기존에 쓰던 메모리가 들어있음
Inventory inv1;
Inventory inv2;
inv2 = inv1;	//나중에 대입💡

시간 복잡도(Big-O 표기법) : 데이터의 개수가 늘어날 때 연산 횟수가 얼마나 기하급수적으로 늘어나는지를 수학적으로 표현한 것

  • O(1) - 상수 시간 (상수 복잡도) : "즉시 실행"
  • O(N) - 선형 시간 (선형 복잡도) : "하나씩 확인"
  • O(N²) - 이차 시간 (이차 복잡도) : "이중 반복문"

큐 : 선입선출(FIFO) 자료구조
-> 스택은 정반대로 후입선출

2개의 위치 포인터

  • Front (프론트 / 머리): 줄의 맨 앞, 데이터가 빠져나가는 출구
  • Rear (리어 / 꼬리): 줄의 맨 뒤, 새로운 데이터가 들어오는 입구

4대 핵심 연산 (모두 속도는 O(1) - 즉시 실행)

  • Enqueue (인큐 / Push): 줄 맨 뒤(Rear)에 새로운 데이터를 추가하는 연산.
  • Dequeue (디큐 / Pop): 줄 맨 앞(Front)에 있는 데이터를 꺼내서 제거하는 연산.
  • Peek / Front (피크 / 프론트): 맨 앞에 누가 서 있는지 제거하지 않고 구경만 하는 연산.
  • IsEmpty (이즈엠프티): 대기열이 비어있는지 확인하는 연산.

코드카타 정리

임의의 양의 정수 n에 대해, n이 어떤 양의 정수 x의 제곱인지 아닌지 판단하고
n이 양의 정수 x의 제곱이라면 x+1의 제곱을 리턴하고, n이 양의 정수 x의 제곱이 아니라면 -1을 리턴하는 함수

#include <string>
#include <vector>

using namespace std;

long long solution(long long n) {
    long long answer = 0;
    for (long long i = 1; i * i <= n; i++)
    {
        if (i * i == n) {
            answer = (i + 1) * (i + 1);
        }
    }
    answer = -1;
    return answer;
}

과제

도전 STEP 4 - 전투 중 아이템 사용 (시스템 연결)

  • Player.h 수정
#pragma once
#include <iostream>
#include <string>

using namespace std;
class Monster;

class Player {
protected:
	string name;
	string job;
	int hp;
	int mp;
	int power;
	int defence;

	int level;
	int exp;
	int maxExp;
	int maxHp;	//추가
	int maxMp;	//추가

public:
	Player(string name, string job, int hp, int mp, int power, int defence);
	virtual ~Player() {}

	virtual void attack(Monster* monster) = 0;

	void gainExp(int rewardExp);

	void printPlayerStatus() const;

	string getName() { return name; }
	string getJob() { return job; }
	int getLevel() { return level; }
	int getHp() { return hp; }
	int getMp() { return mp; }
	int getPower() { return power; }
	int getDefence() { return defence; }
	int getMaxHp() { return maxHp; }	//추가
	int getMaxMp() { return maxMp; }	//추가

	void setName(string name) { this->name = name; }
	void setJob(string job) { this->job = job; }
	void setLevel(int level) { this->level = level; }
	void setHp(int hp) { this->hp = hp; }
	void setMp(int mp) { this->mp = mp; }
	void setPower(int power) { this->power = power; }
	void setDefence(int defence) { this->defence = defence; }
};
  • Player.cpp
#include "Player.h" 

Player::Player(string name, string job, int hp, int mp, int power, int defence) : name(name), job(job), hp(hp), mp(mp), maxHp(hp), maxMp(mp), power(power), defence(defence), level(1), exp(0), maxExp(100) {}
//최대체력, 최대마나 추가
void Player::gainExp(int rewardExp) {
    exp += rewardExp;
    cout << " -> +" << rewardExp << " EXP! (EXP: " << exp << "/" << maxExp << ")" << endl;

    while (exp >= maxExp) {
        cout << endl;
        cout << " -> Level up! Lv." << level << " -> Lv." << (level + 1) << endl;
        level++;

        hp += 10;
        maxHp += 10;	//추가
        mp += 5;
        power += 5;
        cout << " -> HP +10, MP +5, Attack +5" << endl;

        exp -= maxExp;
        maxExp += 50;
        cout << " -> (Current EXP: " << exp << "/" << maxExp << ")" << endl;
    }
}

void Player::printPlayerStatus() const {
    cout << "===========================================" << endl;
    cout << "    [ " << name << " (" << job << ") - Lv." << level << " ]" << endl;
    cout << "===========================================" << endl;
    cout << " EXP : " << exp << " / " << maxExp << endl;
    cout << " HP  : " << hp << "        MP      : " << mp << endl;
    cout << " Att : " << power << "        Defense : " << defence << endl;
    cout << "===========================================" << endl << endl;
}
  • RPG.cpp 수정
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>	//추가 (min 함수)
#include "Player.h"
#include "Warrior.h"
#include "Mage.h"
#include "Rogue.h"
#include "Archer.h"
#include "Monster.h"

using namespace std;

string name;
const int SIZE = 4;
int playerstat[SIZE] = { 0 };
// {HP, MP, 공격력, 방어력}
int PotionHP = 0;
int PotionMP = 0;
int choice;
bool isGameStart = false;

struct Item {
	string name;
	int price = 0;

	void PrintItem() const {
		cout << name << " (" << price << "G)" << endl;
	}

};
vector<Item> inventory;

struct PotionRecipe {
	string potionName;
	string ingredient1;
	string ingredient2;

	void PrintRecipe() const {
		cout << "-> " << potionName << " : " << ingredient1 << " x1, " << ingredient2 << " x1" << endl;

	}
};

vector<PotionRecipe> recipes = {
	{"HPPotion", "Herb", "Clear Water"},
	{"MPPotion", "Slime Jelly", "Clear Water"},
	{"StaminaPotioin", "Herb", "Berry"}
};

void printTitle()
{
	cout << "===========================================" << endl;
	cout << "	[ Dungeon Escape Text RPG ]" << endl;
	cout << "===========================================" << endl;

}

void statsMenu()
{
	cout << "===========================================" << endl;
	cout << "	" << name << "'s Stats" << endl;
	cout << "===========================================" << endl;
	cout << "HP: " << playerstat[0] << "		MP: " << playerstat[1] << endl;
	cout << "Attack: " << playerstat[2] << "		Defense: " << playerstat[3] << endl;
	cout << endl;
}

Player* createCharacter(string name, int playerstat[]) {
	cout << "< Job Selection >" << endl;
	cout << name << ", choose your job!" << endl;
	cout << "1. Warrior   2. Mage   3. Rogue   4. Archer" << endl;
	cout << "Choose: ";

	int jobChoice;
	cin >> jobChoice;

	Player* player = nullptr;

	switch (jobChoice) {
	case 1:
		cout << endl;
		player = new Warrior(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	case 2:
		cout << endl;
		player = new Mage(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	case 3:
		cout << endl;
		player = new Rogue(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	case 4:
		cout << endl;
		player = new Archer(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	default:
		cout << endl;
		player = new Warrior(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	}
	return player;
}

Player* statsCustom()
{
	Player* player = nullptr;

	while (!isGameStart) {
		cout << "===========================================" << endl;
		cout << "	< Character Upgrade >" << endl;
		cout << "1. HP UP    2. MP UP    3. Attack x2" << endl;
		cout << "4. Defense x2  5. Show Stats  0. Jop Select & Start Game" << endl;
		cout << "===========================================" << endl;

		cout << "Choose : ";
		cin >> choice;
		cout << endl;

		switch (choice) {
		case 0:
			system("cls");
			printTitle();
			cout << endl;

			player = createCharacter(name, playerstat);
			cout << endl;

			cout << "Starting the game!" << endl;
			cout << endl;
			cout << "      /| ________________" << endl;
			cout << " O|===|* >________________>" << endl;
			cout << "      ||" << endl;
			cout << endl << endl;

			isGameStart = true;
			break;

		case 1:
			if (PotionHP == 0)
			{
				cout << "No potions left!" << endl;
				cout << endl;
				break;
			}
			cout << "* HP increased by 20. (HP Potion used: " << PotionHP - 1 << " left)" << endl;
			PotionHP -= 1;
			playerstat[0] += 20;
			cout << endl;
			break;

		case 2:
			if (PotionMP == 0)
			{
				cout << "No potions left!" << endl;
				cout << endl;
				break;
			}
			cout << "* MP increased by 20. (MP Potion used: " << PotionMP - 1 << " left)" << endl;
			PotionMP -= 1;
			playerstat[1] += 20;
			cout << endl;
			break;

		case 3:
			cout << "* Attack multiplied by 2." << endl;
			playerstat[2] *= 2;
			cout << endl;

			break;

		case 4:
			cout << "* Defense multiplied by 2." << endl;
			playerstat[3] *= 2;
			cout << endl;

			break;

		case 5:
			system("cls");
			printTitle();
			cout << endl;
			statsMenu();
			break;

		default:
			cout << "Please choose another number." << endl;
			cout << endl;

			break;
		}

	}
	return player;
}

void enterStats()
{
	while (true)
	{
		cout << "Enter HP and MP: ";
		cin >> playerstat[0];
		cin >> playerstat[1];

		if (playerstat[0] < 50 || playerstat[1] < 50)
		{
			cout << "HP or MP is too low. Try again." << endl;
		}

		else {
			break;
		}
	}

	while (true)
	{
		cout << "Enter Attack and Defense: ";
		cin >> playerstat[2];
		cin >> playerstat[3];

		if (playerstat[2] < 50 || playerstat[3] < 50)
		{
			cout << "Attack or Defense is too low. Try again." << endl;
		}

		else {
			break;
		}
	}
}
// 수정
void startBattle(Player* player, Monster& monster) {
	cout << endl;
	cout << " [ Battle Start! ] " << player->getName() << "(" << player->getJob() << ") vs " << monster.getName() << endl;
	cout << endl;

	while (player->getHp() > 0 && monster.getHp() > 0) {
		cout << "--- Player Turn ---" << endl;

		cout << "1. Attack" << endl;
		cout << "2. Use Item" << endl;
		cout << "Choose: ";
		int turnChoice;
		cin >> turnChoice;
		cout << endl;

		if (turnChoice == 2) {
			cout << "[ Inventory ]" << endl;
			if (inventory.empty()) {
				cout << "The inventory is empty!" << endl << endl;
			}
			else {
				for (int i = 0; i < inventory.size(); i++) {
					cout << (i+1) << ". " << inventory[i].name << " (" << inventory[i].price << "G)" << endl;
				}
				cout << "0. Cancel" << endl;
				cout << "Choose item: ";
				int itemChoice;
				cin >> itemChoice;
				cout << endl;

				if (itemChoice > 0 && itemChoice <= inventory.size()) {
					int idx = itemChoice - 1; 
					string itemName = inventory[idx].name;

					if (itemName == "HPPotion" || itemName == "HP Potion") {
						int oldHp = player->getHp();
						player->setHp(min(oldHp + 50, player->getMaxHp()));
						cout << "* " << itemName << " used! HP restored by 50 (" << oldHp << " -> " << player->getHp() << ")" << endl << endl;

						inventory.erase(inventory.begin() + idx);
					}
					else if (itemName == "MPPotion" || itemName == "MP Potion") {
						int oldMp = player->getMp();
						player->setMp(min(oldMp + 50, player->getMaxMp()));
						cout << "* " << itemName << " used! MP restored by 50 (" << oldMp << " -> " << player->getMp() << ")" << endl << endl;

						inventory.erase(inventory.begin() + idx);
					}
					else {
						cout << "You cannot use this item in battle." << endl << endl;
					}
				}
				else if (itemChoice == 0) {
					cout << "Canceled item use." << endl << endl;
					continue;
				}
				else {
					cout << "Invalid item number!" << endl << endl;
				}
			}
		}
		else {
			player->attack(&monster);
		}

		if (monster.getHp() <= 0) {
			cout << " (Dead) ";
			cout << endl << endl;
			cout << "★ Victory!" << endl;

			player->gainExp(monster.getExpReward());
			cout << endl;

			cout << "  -> Got: " << monster.getDropItemName() << "!" << endl;
			cout << "  -> Saved to inventory." << endl << endl;

			Item droppedItem;
			droppedItem.name = monster.getDropItemName();
			droppedItem.price = monster.getDropItemPrice();
			inventory.push_back(droppedItem);

			break;
		}

		cout << endl << endl;
		cout << "--- Monster Turn ---" << endl;
		monster.attack(player);

		int monsterDamage = monster.getPower() - player->getDefence();
		if (monsterDamage <= 0) {
			monsterDamage = 1;
		}

		int playerOldHp = player->getHp();
		player->setHp(playerOldHp - monsterDamage);

		cout << monsterDamage << " damage to " << player->getName() << "!" << endl;
		cout << player->getName() << " HP : " << playerOldHp << " -> " << player->getHp();
		cout << endl << endl;

		if (player->getHp() <= 0) {
			cout << " (Dead) " << endl;
			cout << endl;
			cout << " Game Over..." << endl;
			break;
		}
	}
}

void AlchemyWorkshop() {
	int ChoiceShop;

	while (true) {
		cout << "=== Potion Shop ===" << endl;
		cout << "1. Show all recipes" << endl;
		cout << "2. Search by potion name" << endl;
		cout << "3. Search by ingredient" << endl;
		cout << "0. Go back" << endl;
		cout << endl;

		cout << "Choose : ";
		cin >> ChoiceShop;
		cout << endl;

		switch (ChoiceShop) {
		case 0:
			system("cls");
			printTitle();
			return;

		case 1:
			for (const PotionRecipe& PR : recipes) {
				PR.PrintRecipe();
			}
			cout << endl;
			break;

		case 2:
		{
			string searchName;
			cout << "Search potion name: ";
			cin.ignore();
			getline(cin, searchName);

			bool isFound = false;
			for (const PotionRecipe& PR : recipes) {
				if (PR.potionName == searchName) {
					PR.PrintRecipe();
					isFound = true;
					break;
				}
			}

			if (!isFound) {
				cout << "It cannot be found." << endl;
			}
			cout << endl;
			break;
		}

		case 3: {
			string searchIngredient;
			cout << "Search ingredient: ";
			cin.ignore();
			getline(cin, searchIngredient);

			int count = 0;
			for (const PotionRecipe& PR : recipes) {
				if (PR.ingredient1 == searchIngredient || PR.ingredient2 == searchIngredient)
				{
					PR.PrintRecipe();
					count++;
				}
			}

			if (count == 0) {
				cout << "It cannot be found." << endl;
			}
			else {
				cout << "Found " << count << " recipes." << endl;
			}
			cout << endl;
			break;
		}

		default:
			cout << "Please choose another number." << endl << endl;
			break;
		}
	}
}

void mainMenu(Player* player)
{
	int choiceMenu;

	while (true) {
		cout << "=== Main Menu ===" << endl;
		cout << "1. Enter Dungeon" << endl;
		cout << "2. Check Inventory" << endl;
		cout << "3. Potion Shop" << endl;
		cout << "0. Quit" << endl;
		cout << endl;

		cout << "Choose : ";
		cin >> choiceMenu;
		cout << endl;

		switch (choiceMenu) {
		case 0:
			system("cls");
			printTitle();
			cout << "Exit Game.." << endl;
			return;

		case 1:
		{
			Monster slime("Slime", 30, 20, 10, "Slime Jelly", 30, 30);
			Monster professor("Professor", 40, 30, 10, "paper", 30, 70);

			startBattle(player, slime);
			startBattle(player, professor);

			if (player->getHp() <= 0) {
				return;
			}
			break;
		}

		case 2:
			system("cls");
			printTitle();
			cout << "[ Inventory (" << inventory.size() << "/10) ]" << endl;
			if (inventory.empty()) {
				cout << "The inventory is empty." << endl;
			}
			else {
				int number = 1;
				for (const Item& item : inventory) {
					cout << number << ". ";
					item.PrintItem();
					number++;
				}
			}
			cout << endl;
			break;

		case 3:
			system("cls");
			printTitle();
			AlchemyWorkshop();
			break;

		default:
			cout << "Please choose another number." << endl << endl;

			break;
		}

	}
}

void setPotion(int count, int* p_HPPotion, int* p_MPPotion) {
	*p_HPPotion = count;
	*p_MPPotion = count;
}


int main() {
	printTitle();

	cout << "Enter your hero's name: ";
	cin >> name;
	cout << endl;

	enterStats();

	system("cls");
	printTitle();
	statsMenu();

	setPotion(5, &PotionHP, &PotionMP);

	cout << "* You received " << PotionHP << " HP Potions and " << PotionMP << " MP Potions." << endl;

	Player* player = statsCustom();
	player->printPlayerStatus();

	mainMenu(player);

	delete player;
	return 0;

}

도전 STEP 5

Inventory<T> 직접 구현 (template, 동적 메모리)
  • Inventory.h 추가
#pragma once
#include <iostream>
#include <string>

using namespace std;

template<typename T>
class Inventory {
private:
	T* pItems_;
	int capacity_;
	int size_;

public:
	Inventory(int capacity = 10) : capacity_(capacity), size_(0) {
		pItems_ = new T[capacity_];

	}

	~Inventory() {
		delete[] pItems_;
	}

	Inventory(const Inventory<T>& other) : capacity_(other.capacity_), size_(other.size_) {
		pItems_ = new T[capacity_];
		for (int i = 0; i < size_; i++) {
			pItems_[i] = other.pItems_[i];
		}
	}

	Inventory<T>& operator=(const Inventory<T>& other) {
		if (this == &other) return *this;

		delete[] pItems_;

		capacity_ = other.capacity_;
		size_ = other.size_;
		pItems_ = new T[capacity_];

		for (int i = 0; i < size_; i++) {
			pItems_[i] = other.pItems_[i];
		}
		return *this;
	}

	int GetSize() const { return size_; }
	int GetCapacity() const { return capacity_; }

	bool AddItem(const T& item) {
		if (size_ >= capacity_) {
			cout << "Inventory is full! (" << size_ << "/" << capacity_ << ")" << endl;
			return false;
		}
		pItems_[size_] = item;
		size_++;
		return true;
	}

	void RemoveLastItem() {
		if (size_ > 0) {
			size_--;
		}
		else {
			cout << "Inventory is already empty!" << endl;
		}
	}

	void RemoveItemAt(int index) {
		if (index < 0 || index >= size_) return;

		for (int i = index; i < size_ - 1; i++) {
			pItems[i] = pItems_[i + 1];
		}
		size_--;
	}

	T& operator[](int index) {
		return pItems_[index];
	}

	const T& operator[](int index) const {
		return pItems_[index];
	}

	void PrintAllItems() const {
		if (size_ == 0) {
			cout << "The inventory is empty." << endl;
			return;
		}
		for (int i = 0; i < size_; i++) {
			cout << (i + 1) << ". ";
			pItems_[i].PrintItem();
		}
	}
};
  • RPG.cpp 수정
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "Player.h"
#include "Warrior.h"
#include "Mage.h"
#include "Rogue.h"
#include "Archer.h"
#include "Monster.h"
#include "Inventory.h"	//추가

using namespace std;

string name;
const int SIZE = 4;
int playerstat[SIZE] = { 0 };
// {HP, MP, 공격력, 방어력}
int PotionHP = 0;
int PotionMP = 0;
int choice;
bool isGameStart = false;

struct Item {
	string name;
	int price = 0;

	void PrintItem() const {
		cout << name << " (" << price << "G)" << endl;
	}

};
//수정
Inventory<Item> inventory(10);

struct PotionRecipe {
	string potionName;
	string ingredient1;
	string ingredient2;

	void PrintRecipe() const {
		cout << "-> " << potionName << " : " << ingredient1 << " x1, " << ingredient2 << " x1" << endl;

	}
};

vector<PotionRecipe> recipes = {
	{"HPPotion", "Herb", "Clear Water"},
	{"MPPotion", "Slime Jelly", "Clear Water"},
	{"StaminaPotioin", "Herb", "Berry"}
};

void printTitle()
{
	cout << "===========================================" << endl;
	cout << "	[ Dungeon Escape Text RPG ]" << endl;
	cout << "===========================================" << endl;

}

void statsMenu()
{
	cout << "===========================================" << endl;
	cout << "	" << name << "'s Stats" << endl;
	cout << "===========================================" << endl;
	cout << "HP: " << playerstat[0] << "		MP: " << playerstat[1] << endl;
	cout << "Attack: " << playerstat[2] << "		Defense: " << playerstat[3] << endl;
	cout << endl;
}

Player* createCharacter(string name, int playerstat[]) {
	cout << "< Job Selection >" << endl;
	cout << name << ", choose your job!" << endl;
	cout << "1. Warrior   2. Mage   3. Rogue   4. Archer" << endl;
	cout << "Choose: ";

	int jobChoice;
	cin >> jobChoice;

	Player* player = nullptr;

	switch (jobChoice) {
	case 1:
		cout << endl;
		player = new Warrior(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	case 2:
		cout << endl;
		player = new Mage(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	case 3:
		cout << endl;
		player = new Rogue(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	case 4:
		cout << endl;
		player = new Archer(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	default:
		cout << endl;
		player = new Warrior(name, playerstat[0], playerstat[1], playerstat[2], playerstat[3]);
		break;
	}
	return player;
}

Player* statsCustom()
{
	Player* player = nullptr;

	while (!isGameStart) {
		cout << "===========================================" << endl;
		cout << "	< Character Upgrade >" << endl;
		cout << "1. HP UP    2. MP UP    3. Attack x2" << endl;
		cout << "4. Defense x2  5. Show Stats  0. Jop Select & Start Game" << endl;
		cout << "===========================================" << endl;

		cout << "Choose : ";
		cin >> choice;
		cout << endl;

		switch (choice) {
		case 0:
			system("cls");
			printTitle();
			cout << endl;

			player = createCharacter(name, playerstat);
			cout << endl;

			cout << "Starting the game!" << endl;
			cout << endl;
			cout << "      /| ________________" << endl;
			cout << " O|===|* >________________>" << endl;
			cout << "      ||" << endl;
			cout << endl << endl;

			isGameStart = true;
			break;

		case 1:
			if (PotionHP == 0)
			{
				cout << "No potions left!" << endl;
				cout << endl;
				break;
			}
			cout << "* HP increased by 20. (HP Potion used: " << PotionHP - 1 << " left)" << endl;
			PotionHP -= 1;
			playerstat[0] += 20;
			cout << endl;
			break;

		case 2:
			if (PotionMP == 0)
			{
				cout << "No potions left!" << endl;
				cout << endl;
				break;
			}
			cout << "* MP increased by 20. (MP Potion used: " << PotionMP - 1 << " left)" << endl;
			PotionMP -= 1;
			playerstat[1] += 20;
			cout << endl;
			break;

		case 3:
			cout << "* Attack multiplied by 2." << endl;
			playerstat[2] *= 2;
			cout << endl;

			break;

		case 4:
			cout << "* Defense multiplied by 2." << endl;
			playerstat[3] *= 2;
			cout << endl;

			break;

		case 5:
			system("cls");
			printTitle();
			cout << endl;
			statsMenu();
			break;

		default:
			cout << "Please choose another number." << endl;
			cout << endl;

			break;
		}

	}
	return player;
}

void enterStats()
{
	while (true)
	{
		cout << "Enter HP and MP: ";
		cin >> playerstat[0];
		cin >> playerstat[1];

		if (playerstat[0] < 50 || playerstat[1] < 50)
		{
			cout << "HP or MP is too low. Try again." << endl;
		}

		else {
			break;
		}
	}

	while (true)
	{
		cout << "Enter Attack and Defense: ";
		cin >> playerstat[2];
		cin >> playerstat[3];

		if (playerstat[2] < 50 || playerstat[3] < 50)
		{
			cout << "Attack or Defense is too low. Try again." << endl;
		}

		else {
			break;
		}
	}
}

void startBattle(Player* player, Monster& monster) {
	cout << endl;
	cout << " [ Battle Start! ] " << player->getName() << "(" << player->getJob() << ") vs " << monster.getName() << endl;
	cout << endl;

	while (player->getHp() > 0 && monster.getHp() > 0) {
		cout << "--- Player Turn ---" << endl;

		cout << "1. Attack" << endl;
		cout << "2. Use Item" << endl;
		cout << "Choose: ";
		int turnChoice;
		cin >> turnChoice;
		cout << endl;
//수정
		if (turnChoice == 2) {
			cout << "[ Inventory ]" << endl;
			if (inventory.GetSize() == 0) {
				cout << "The inventory is empty!" << endl << endl;
			}
			else {
				for (int i = 0; i < inventory.GetSize(); i++) {
					cout << (i+1) << ". " << inventory[i].name << " (" << inventory[i].price << "G)" << endl;
				}
				cout << "0. Cancel" << endl;
				cout << "Choose item: ";
				int itemChoice;
				cin >> itemChoice;
				cout << endl;

				if (itemChoice > 0 && itemChoice <= inventory.GetSize()) {
					int idx = itemChoice - 1; 
					string itemName = inventory[idx].name;

					if (itemName == "HPPotion" || itemName == "HP Potion") {
						int oldHp = player->getHp();
						player->setHp(min(oldHp + 50, player->getMaxHp()));
						cout << "* " << itemName << " used! HP restored by 50 (" << oldHp << " -> " << player->getHp() << ")" << endl << endl;

						inventory.RemoveItemAt(idx);
					}
					else if (itemName == "MPPotion" || itemName == "MP Potion") {
						int oldMp = player->getMp();
						player->setMp(min(oldMp + 50, player->getMaxMp()));
						cout << "* " << itemName << " used! MP restored by 50 (" << oldMp << " -> " << player->getMp() << ")" << endl << endl;

						inventory.RemoveItemAt(idx);
					}
					else {
						cout << "You cannot use this item in battle." << endl << endl;
					}
				}
				else if (itemChoice == 0) {
					cout << "Canceled item use." << endl << endl;
					continue;
				}
				else {
					cout << "Invalid item number!" << endl << endl;
				}
			}
		}
		else {
			player->attack(&monster);
		}

		if (monster.getHp() <= 0) {
			cout << " (Dead) ";
			cout << endl << endl;
			cout << "★ Victory!" << endl;

			player->gainExp(monster.getExpReward());
			cout << endl;

			cout << "  -> Got: " << monster.getDropItemName() << "!" << endl;
			cout << "  -> Saved to inventory." << endl << endl;

			Item droppedItem;
			droppedItem.name = monster.getDropItemName();
			droppedItem.price = monster.getDropItemPrice();
//수정            
			inventory.AddItem(droppedItem);

			break;
		}

		cout << endl << endl;
		cout << "--- Monster Turn ---" << endl;
		monster.attack(player);

		int monsterDamage = monster.getPower() - player->getDefence();
		if (monsterDamage <= 0) {
			monsterDamage = 1;
		}

		int playerOldHp = player->getHp();
		player->setHp(playerOldHp - monsterDamage);

		cout << monsterDamage << " damage to " << player->getName() << "!" << endl;
		cout << player->getName() << " HP : " << playerOldHp << " -> " << player->getHp();
		cout << endl << endl;

		if (player->getHp() <= 0) {
			cout << " (Dead) " << endl;
			cout << endl;
			cout << " Game Over..." << endl;
			break;
		}
	}
}

void AlchemyWorkshop() {
	int ChoiceShop;

	while (true) {
		cout << "=== Potion Shop ===" << endl;
		cout << "1. Show all recipes" << endl;
		cout << "2. Search by potion name" << endl;
		cout << "3. Search by ingredient" << endl;
		cout << "0. Go back" << endl;
		cout << endl;

		cout << "Choose : ";
		cin >> ChoiceShop;
		cout << endl;

		switch (ChoiceShop) {
		case 0:
			system("cls");
			printTitle();
			return;

		case 1:
			for (const PotionRecipe& PR : recipes) {
				PR.PrintRecipe();
			}
			cout << endl;
			break;

		case 2:
		{
			string searchName;
			cout << "Search potion name: ";
			cin.ignore();
			getline(cin, searchName);

			bool isFound = false;
			for (const PotionRecipe& PR : recipes) {
				if (PR.potionName == searchName) {
					PR.PrintRecipe();
					isFound = true;
					break;
				}
			}

			if (!isFound) {
				cout << "It cannot be found." << endl;
			}
			cout << endl;
			break;
		}

		case 3: {
			string searchIngredient;
			cout << "Search ingredient: ";
			cin.ignore();
			getline(cin, searchIngredient);

			int count = 0;
			for (const PotionRecipe& PR : recipes) {
				if (PR.ingredient1 == searchIngredient || PR.ingredient2 == searchIngredient)
				{
					PR.PrintRecipe();
					count++;
				}
			}

			if (count == 0) {
				cout << "It cannot be found." << endl;
			}
			else {
				cout << "Found " << count << " recipes." << endl;
			}
			cout << endl;
			break;
		}

		default:
			cout << "Please choose another number." << endl << endl;
			break;
		}
	}
}

void mainMenu(Player* player)
{
	int choiceMenu;

	while (true) {
		cout << "=== Main Menu ===" << endl;
		cout << "1. Enter Dungeon" << endl;
		cout << "2. Check Inventory" << endl;
		cout << "3. Potion Shop" << endl;
		cout << "0. Quit" << endl;
		cout << endl;

		cout << "Choose : ";
		cin >> choiceMenu;
		cout << endl;

		switch (choiceMenu) {
		case 0:
			system("cls");
			printTitle();
			cout << "Exit Game.." << endl;
			return;

		case 1:
		{
			Monster slime("Slime", 30, 20, 10, "Slime Jelly", 30, 30);
			Monster professor("Professor", 40, 30, 10, "paper", 30, 70);

			startBattle(player, slime);
			startBattle(player, professor);

			if (player->getHp() <= 0) {
				return;
			}
			break;
		}
//수정
		case 2:
			system("cls");
			printTitle();
			cout << "[ Inventory (" << inventory.GetSize() << "/10) ]" << endl;
			
			inventory.PrintAllItems();
			cout << endl;
			break;

		case 3:
			system("cls");
			printTitle();
			AlchemyWorkshop();
			break;

		default:
			cout << "Please choose another number." << endl << endl;

			break;
		}

	}
}

void setPotion(int count, int* p_HPPotion, int* p_MPPotion) {
	*p_HPPotion = count;
	*p_MPPotion = count;
}


int main() {
	printTitle();

	cout << "Enter your hero's name: ";
	cin >> name;
	cout << endl;

	enterStats();

	system("cls");
	printTitle();
	statsMenu();

	setPotion(5, &PotionHP, &PotionMP);

	cout << "* You received " << PotionHP << " HP Potions and " << PotionMP << " MP Potions." << endl;

	Player* player = statsCustom();
	player->printPlayerStatus();

	mainMenu(player);

	delete player;
	return 0;

}

궁금증 해결하기

1

Q. 복사 대입 연산자, Rule of Three?

얕은 복사를 하면 메모리 이중 해제 발생 가능
-> 1. inv2 = inv1; 을 했더니, inv1과 inv2가 완전히 똑같은 메모리 방 하나를 동시에 공유하게 됨.
2. 게임이 끝나고 inv2가 소멸하면서 delete[] pItems_;를 해버림 (방이 파괴됨)
3. 직후 inv1도 소멸하면서 "어? 내 주머니에 메모리 주소가 있네? 치워야지!" 하고 이미 파괴된 방을 또 파괴(delete[])하려고 시도함
4. 컴퓨터: "이미 지운 메모리를 또 지워?! 이거 보안 사고야!" ➡️ 프로그램 강제 종료 (Crash)

이 메모리 이중 해제를 막기 위해 생긴 게 Rule of Three
-> 어떤 클래스가 3가지 법칙 중 단 하나라도 직접 구현해야 한다면 무조건 나머지 2개도 함께 직접 구현해야 한다는 법칙
1. 소멸자 (Destructor): ~Inventory()
2. 복사 생성자 (Copy Constructor): Inventory(const Inventory& other)
3. 복사 대입 연산자 (Copy Assignment Operator): operator=(const Inventory& other)

💡소멸자에서 delete로 뭔가를 치워야한다는 건 생성할 때 new로 메모리를 빌렸다는 뜻! -> 복사가 일어날 때도 깊은 복사 2가지 문법을 무조건 직접 짜야함.

복사 생성자 vs 복사 대입 연산자

  • 복사 생성자 : 객체가 새로 태어날 때 기존 객체를 복사해서 태어남
    -> 새로 태어나는 중이라 기존 메모리가 아예 비어있음
Inventory inv2 = inv1;
Inventory inv2(inv1);
  • 복사 대입 연산자 : 이미 태어나서 살아있는 두 객체끼리 값을 덮어씌움
    -> 이미 살아있었기 때문에 기존에 쓰던 메모리가 들어있음
Inventory inv1;
Inventory inv2;
inv2 = inv1;	//나중에 대입💡
  • 과제할 때 쓴 복사 대입 연산자 해부(Inventory.h)
Inventory<T>& operator=(const Inventory<T>& other) {
    // 1단계: 자기 자신을 대입하는지 검사
    if (this == &other) return *this; 

    // 2단계: 기존에 들고 있던 메모리 청소
    delete[] pItems_; 

    // 3단계: 새로운 메모리 할당 및 데이터 복사 (Deep Copy)
    capacity_ = other.capacity_;
    size_ = other.size_;
    pItems_ = new T[capacity_]; 
    for (int i = 0; i < size_; i++) {
        pItems_[i] = other.pItems_[i]; 
    }

    // 4단계: 내 자신(*this)을 반환
    return *this; 
}

2

Q. 시간 복잡도 & Big-O 표기법

시간 복잡도(Big-O 표기법) : 데이터의 개수가 늘어날 때 연산 횟수가 얼마나 기하급수적으로 늘어나는지를 수학적으로 표현한 것

  • O(1) - 상수 시간 (상수 복잡도) : "즉시 실행"

데이터가 10개든 1억 개든 항상 딱 1번의 연산만으로 끝나는 속도.

비유: 배열에서 인덱스로 바로 찾아가기 (inventory[3]), 인벤토리 맨 뒤에 아이템 추가하기.

  • O(N) - 선형 시간 (선형 복잡도) : "하나씩 확인"

데이터 개수(N)만큼 연산 횟수가 정직하게 늘어나는 속도. 100개면 100번, 1만 개면 1만 번.

비유: 인벤토리에서 "전설의 검"을 찾으려고 1번 칸부터 끝까지 하나씩 다 열어보는 것.

  • O(N²) - 이차 시간 (이차 복잡도) : "이중 반복문"

데이터가 늘어날 때마다 연산 횟수가 제곱수로 폭증! (100개 -> 1만 번, 1만 개 -> 1억 번 연산).

비유: 몬스터 100마리가 서로 각자 다른 몬스터 100마리와 모두 거리를 계산해야 할 때 (for문 안에 for문).

3

Q. 큐?

큐 : 선입선출(FIFO) 자료구조
-> 스택은 정반대로 후입선출

  1. 선형 큐

  2. 원형 큐 : 배열의 처음과 끝이 이어져 있다고 생각하는 동그란 원형 큐
    -> (현재 위치 + 1) % 배열크기

2개의 위치 포인터

  • Front (프론트 / 머리): 줄의 맨 앞, 데이터가 빠져나가는 출구
  • Rear (리어 / 꼬리): 줄의 맨 뒤, 새로운 데이터가 들어오는 입구

4대 핵심 연산 (모두 속도는 O(1) - 즉시 실행)

  • Enqueue (인큐 / Push): 줄 맨 뒤(Rear)에 새로운 데이터를 추가하는 연산.
  • Dequeue (디큐 / Pop): 줄 맨 앞(Front)에 있는 데이터를 꺼내서 제거하는 연산.
  • Peek / Front (피크 / 프론트): 맨 앞에 누가 서 있는지 제거하지 않고 구경만 하는 연산.
  • IsEmpty (이즈엠프티): 대기열이 비어있는지 확인하는 연산.
#include <iostream>
#include <queue> // 💡 큐를 쓰기 위한 필수 헤더
#include <string>

using namespace std;

int main() {
    // 💡 문자열을 담는 큐(대기열) 생성
    queue<string> skillQueue;

    // 1. Enqueue (C++에서는 push라고 부름) - 줄 서기
    skillQueue.push("화염구");
    skillQueue.push("얼음창");
    skillQueue.push("번개폭풍");

    cout << "현재 대기 중인 스킬 개수: " << skillQueue.size() << endl; // 출력: 3

    // 2. 큐가 비어있지 않을 때까지 순서대로 실행 (FIFO)
    while (!skillQueue.empty()) {
        // 맨 앞(Front)에 있는 스킬 확인 (제거 X)
        string currentSkill = skillQueue.front(); 
        cout << "[스킬 발동] " << currentSkill << "!" << endl;

        // 맨 앞 스킬을 큐에서 제거 (Dequeue / C++에서는 pop)
        skillQueue.pop(); 
    }

    return 0;

예습

배열 선형 탐색 - O(N)

profile
초보 개발자

0개의 댓글