TIL(26)

이채원·2026년 7월 20일

TIL

목록 보기
26/79

오늘의 할일
1. 코드카타 정리
3. 과제
4. 궁금증 해결


코드카타 정리

정수 n의 각 자릿수를 큰 것부터 작은 순으로 정렬한 새로운 정수를 리턴, 예를 들어 n이 118372면 873211을 리턴

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

bool compare(char a, char b) {
    return a > b;
}

long long solution(long long n) {
    long long answer = 0;
    string s = to_string(n);
    sort(s.begin(), s.end(), compare);
    answer = stoll(s);
    return answer;
}
  • to_string(n)으로 숫자를 문자열로 변환
  • stoll 함수 = String TO Long Long

과제

도전 STEP 6 - 인벤토리 자동 확장 + 정렬 (Resize, std::sort)

Inventory.h 수정

#pragma once
#include <iostream>
#include <string>
#include <algorithm>	//sort 때문에 추가

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_; }
//추가
	void Resize(int newCapacity) {
		if (newCapacity <= capacity_) return;

		T* newItems = new T[newCapacity];

		for (int i = 0; i < size_; i++) {
			newItems[i] = pItems_[i];
		}

		delete[] pItems_;

		pItems_ = newItems;
		int oldCapacity = capacity_;
		capacity_ = newCapacity;
		cout << "-> Inventory auto-expanded! (" << oldCapacity << " -> " << capacity_ << ")" << endl;
	}
//수정
	bool AddItem(const T& item) {
		if (size_ >= capacity_) {
			cout << "Adding item... (" << size_ << "/" << capacity_ << " full)" << endl;
			Resize(capacity_ * 2);
		}
		pItems_[size_] = item;
		size_++;
		cout << "-> Item added" << endl << endl;
		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();
		}
	}
//추가
	void SortItems(bool (*compareFunc)(const T&, const T&)) {
		if (size_ <= 1) return;

		sort(pItems_, pItems_ + size_, compareFunc);
	}
};

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;
	}

};
//추가
bool compareByPrice(const Item& a, const Item& b) {
	return a.price < b.price;
}

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;
		}
	}
}
// 2번 수정, 4번 추가
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 << "4. Sort Inventory (by Price)" << 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() << "/" << inventory.GetCapacity() << ") ]" << endl;
			inventory.PrintAllItems();
			cout << endl;
			break;

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

		case 4:
			system("cls");
			printTitle();
			if (inventory.GetSize() == 0) {
				cout << "The inventory is empty." << endl << endl;
			}
			else {
				inventory.SortItems(compareByPrice);

				cout << "[ Inventory sorted by price ]" << endl;
				inventory.PrintAllItems();
				cout << endl;
			}
			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;

}
  • 자동 확장 (Resize):
    기존 메모리보다 2배 큰 방을 new로 새로 파서 이사시킨 후, 옛날 방은 delete[]로 부수는 방식

  • 정렬 (SortItems):
    sort(시작주소, 끝주소, 비교함수)는 정렬(Introsort 알로기즘). pItems 포인터는 배열의 시작 주소/ size를 더한 pItems + size는 배열의 끝 주소


궁금증 해결하기

1

Q. malloc?

malloc : 메모리 임대차 계약{Memory Allocation(메모리 할당)}
-> 프로그램이 실행되는 도중에(Runtime), 필요한 만큼의 메모리 공간을 운영체제(OS)로부터 빌려오는 함수

  • 정적 할당 (int arr[100];): 프로그램 시작 전에 "나 방 100개 필요해!"라고 미리 못 박아두는 것. (도중에 크기 변경 불가, 낭비 발생)

  • 동적 할당 (malloc): 게임을 실행하다가 몬스터가 10마리 나타나면 10마리분, 1000마리 나타나면 1000마리분만큼 그때그때 실시간으로 방을 계약해서 빌리는 것

  • malloc의 기본 문법과 3대 필수 요소 -
    malloc을 쓰려면 <stdlib.h> (C++에서는 <cstdlib>) 헤더가 필요

int* ptr = (int*)malloc(sizeof(int) * 10);

① 원하는 용량 계산 : sizeof(타입) 개수
malloc은 컴파일러처럼 똑똑하지 않아서 우리가 무슨 타입을 담을지 모름. 오직 "몇 바이트(Byte)를 빌려줄까?"만 물어봄.
-> 직접 sizeof(int)
10 (4바이트 × 10개 = 40바이트)처럼 정확한 바이트 크기를 계산해서 전달해야 함
② 타입 캐스팅 (형변환) : (int)
malloc은 방을 빌려준 뒤, 그 방의 주소를 void
(아무 형태도 정해지지 않은 포인터)로 반환.
-> "이 방은 정수(int)들을 담을 방이야!"라고 명시적으로 (int*) 형변환(Casting) 해주기
③ 해제는 반드시 free()!
C++에 new가 있으면 delete가 있듯, C 언어에서 malloc으로 빌린 메모리는 free(ptr); 함수로 반드시 반환해야 함. 안 그러면 프로그램이 종료될 때까지 메모리를 계속 잡아먹는 메모리 누수(Memory Leak)가 발생함

  1. 사용 예제 (할당 ➡️ 사용 ➡️ 해제)
#include <stdio.h>
#include <stdlib.h> // 💡 malloc과 free를 쓰기 위한 필수 헤더

int main() {
    int size = 5;
    
    // 1. int형 데이터 5개를 담을 수 있는 메모리(20바이트) 할당
    int* arr = (int*)malloc(sizeof(int) * size);
    
    // 2. 안전장치: 메모리가 부족해서 할당에 실패하면 NULL을 반환
    if (arr == NULL) {
        printf("메모리 할당 실패!\n");
        return -1;
    }
    
    // 3. 일반 배열처럼 똑같이 사용 가능
    for (int i = 0; i < size; i++) {
        arr[i] = (i + 1) * 10;
        printf("%d ", arr[i]); // 출력: 10 20 30 40 50
    }
    
    // 4. 사용이 끝난 메모리는 반드시 운영체제에 반납
    free(arr);
    
    // 5. 실무 꿀팁: 반납 후 포인터를 비워주어 허공을 가리키지 않게 함 (댕글링 포인터 방지)
    arr = NULL;
    
    return 0;
}
  1. C언어 malloc vs C++ new 차이점

  2. malloc 사용 시 절대 지켜야 할 3가지 주의점

  • 초기화가 안 됨 (쓰레기값 주의) : malloc으로 빌린 방에는 이전 사용자가 남긴 쓰레기값(Garbage Value)이 그대로 들어있음
    만약 빌리자마자 모두 0으로 깨끗하게 초기화된 메모리를 받고 싶다면 calloc() 함수를 써야 함
  • 할당 후 NULL 체크 필수 : 컴퓨터 메모리가 부족하면 malloc은 방을 주지 못하고 NULL을 뱉음. 이걸 확인 안 하고 바로 쓰면 프로그램이 즉시 강제 종료
  • 짝 맞추기 (malloc ➡️ free) : 빌렸으면 반드시 1대1로 free를 호출해 주어야 함. 깜빡하고 안 적으면 서버나 게임이 며칠 뒤에 메모리 부족으로 뻗어버림

💡 요약
malloc -> 내가 원하는 바이트(Byte) 크기만큼 메모리를 날것 그대로 빌려오는 C 언어의 강력한 원시 도구

profile
초보 개발자

0개의 댓글