TIL(Today I Learned)

텍스트 RPG 게임 : ItemManger 나머지 기능 구현하기 - TIL#14

Najdorf 2024. 1. 11. 21:28
728x90

오늘의 알고리즘 오류 상황

 

문제: 정수 num1과 num2가 매개변수로 주어질 때, num1을 num2로 나눈 값에 1,000을 곱한 후 정수 부분을 return 하도록 soltuion 함수를 완성해주세요.

using System;

public class Solution
{
    public int solution(int num1, int num2)
    {
        double a = (double)num1;
        double b = (double)num2;

        double quotient = a / b;
        int answer = (double) quotient * 1000; // 나중에 보니 여기가 문제였음;;
        return answer;
    }
}

C#은 정수형 변수 끼리 '/' 연산자로 나눗셈을 하면 몫과 나머지로 구분해

정수형 몫만 반환한다.

 

나도 그걸 알고 있기에 double 자료형을 쓴 건데....

계속 원하는 결과가 나오질 않는다며 콘솔창이 뭐라하고 있었다.

 

계속 코드를 보면서 뭐가 잘못됐지...? 싶어서 생각해봤더니

아무래도 계산 결과에서 정수부분만 남겨서 반환하려는 게

나의 의도랑 다르게 뭔가 안 맞았던 모양이다.

 

그래서 어쩌면, double형에서 int형으로 캐스팅하는 과정에서

전체 수식에 괄호를 안 쳐서 그런가...? 싶어서

다음 코드로 수정해 돌려봤다.

{
    public int solution(int num1, int num2)
    {
        double a = (double)num1;
        double b = (double)num2;

        double quotient = a / b;
        double answer = quotient * 1000;
        return (int) answer;
    }
}

 

해결 됐다!

 

 

오류 발생 이유:

(double) quotient * 1000

1. 다시 보니 이 부분에서 잘못된 자료형으로 형변환을 하고 있었다;;

 

(int) quotient * 1000

2. int 자료형으로 변환하게 고치면 정상 작동될 줄 알았더니...

코드를 자세히 보니 quotient 값을 int형으로 변환하고,

1000을 곱하는 방식으로 되어있었다...

 

(int) (quotient * 1000)

결국은 이렇게 수정해야 의도했던 정수 부분만 남길 수 있는 것이다.

 


ItemManager 나머지 기능 구현하기

 

확실히 코드를 한번 이해하고 나니

나머지 기능을 구현하는 것은 그렇게 어렵지 않았다.

 

오늘 구현한 기능은 다음과 같다.

  • ShowInventory : 인벤토리 아이템 출력
  • ShowShop : 상점 판매 중인 아이템 출력
  • BuyItem / SellItem : 아이템 사고 팔기, (팔면 85%만 돌려받음)
  • OnEquipItem / OffEquipItem : 장비 탈착 기능
  • GetFieldItem / DropItem : 던전 필드에서 아이템 줍기 / 버리기
  • OnEvent - GetFieldItem 관련 이벤트 동작 시 처리할 것 추가

그리고 팀원들과 협의해서 변수명이 다른 것들은 통일했다.

(Comment -> Description, Atk -> ATK 등)

 

코드 전체:

더보기
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace TextRPG
{
    enum ItemType
    {
        Weapon,
        Armor
    }

    struct ItemData
    {
        public string[] inventory;
        public string[] equippedItem;
        public string[] fieldItem;
        public string[] saleItem;
    }

    internal class ItemManager : IListener
    {
        readonly Item[] items;
        readonly Item[] fieldItems;
        readonly Item[] shopItems;

        List<Item> inventory;
        List<Item> shopDisplay;
        List<Item> fieldDisplay;

        public int GetInventorySize { get { return inventory.Count; } }
        public int GetShopDisplaySize { get { return shopDisplay.Count; } }
        public int GetFieldDisplaySize { get { return fieldDisplay.Count; } }


        public ItemManager() 
        // ItemManager 생성자 : 
        {
            List<Item>? list = (List<Item>?)Utilities.LoadFile(LoadType.Item);
            items = list.ToArray();

            ItemData? data = (ItemData?)Utilities.LoadFile(LoadType.ItemData);
            inventory = [];
            shopDisplay = [];
            fieldDisplay = [];

            if (data != null)
            // 아이템 정보를 data에서 읽어와서 inventory및 기타 배열에 할당하기
            {
                foreach (string item in data.Value.inventory)
                {
                    Item _item = list.Find(x => x.Name == item);

                    inventory.Add(_item);
                }
                foreach (string item in data.Value.equippedItem)
                {
                    Item _item = list.Find(x => x.Name == item);
                    _item.IsEquipped = true;
                }
                foreach (string item in data.Value.saleItem)
                {
                    Item _item = list.Find(x => x.Name == item);
                    shopDisplay.Add(_item);
                }
                foreach (string item in data.Value.fieldItem)
                {
                    Item _item = list.Find(x => x.Name == item);
                    fieldDisplay.Add(_item);
                }
            }

            shopItems = list.FindAll(x => x.IsSale == true).ToArray();
            fieldItems = list.FindAll(x => x.IsOnField == true).ToArray();
            
            List<Item>? equippedItems = inventory.FindAll(x => x.IsEquipped);
            EventManager.Instance.PostEvent(EventType.eUpdateItem, null);

            // ItemManager에 Event Listener 등록
            EventManager.Instance.AddListener(EventType.eGetFieldItem, this);
            EventManager.Instance.AddListener(EventType.eGameEnd, this);

        }

        public void ShowInventory()
        // 인벤토리 보기 관련 메서드
        {
            Console.WriteLine("[아이템 목록]");

            int count = 0;
            foreach (Item item in inventory)
            // 양식에 맞춰 콘솔에 아이템 정보 출력
            // 1. [E] {아이템 이름}    | {공격력 or 방어력} {추가 스탯}  | {설명}          |
            {
                count++;
                if (item.IsEquipped)
                    Console.Write($"-{count} [E] ");
                else
                    Console.Write($"-{count} [-] ");

                Console.WriteLine($"{item.Name} | " +
                    $"{((item.ATK > 0) ? ("공격력" + $" +{item.ATK}") : "")} " +
                    $"{((item.DEF > 0) ? ("방어력" + $" +{item.DEF}") : "")} " +
                    $"{item.Description} | " );
            }
            
        }

        public void ShowShop()
        {
            Console.WriteLine("[아이템 목록]");

            int count = 0;
            foreach (Item item in shopDisplay)
            // 양식에 맞춰 콘솔에 아이템 정보 출력
            // 1. {아이템 이름}    | {공격력 or 방어력} {추가 스탯}  | {설명} | {가격} G
            {
                count++;
                Console.Write($"{item.Name} | " +
                    $"{((item.ATK > 0) ? ("공격력" + $" +{item.ATK}") : "")} " +
                    $"{((item.DEF > 0) ? ("방어력" + $" +{item.DEF}") : "")} " +
                    $"{item.Description}");
                if (item.IsSale)
                    Console.WriteLine($"{item.Cost} G");
                else
                    Console.WriteLine("구매 완료");
            }
            
        }

        public void BuyItem(int itemNum, int myWallet)
        {
            // 아이템 구매 관련 메서드
            Item item = shopDisplay[itemNum - 1];
            if (item.Cost > myWallet)
            {
                Console.WriteLine("소지금이 부족합니다.");
                return;
            }
            else
            {
                item.IsSale = false;
                inventory.Add(item);
                Console.WriteLine($"{item.Name}을 구매했습니다.");

                // EventManager로 골드 변경 이벤트 전달
                EventManager.Instance.PostEvent(EventType.eUpdateGold, -item.Cost);
            }
        }

        public void SellItem(int itemNum, int myWallet)
        {
            // 아이템 판매 관련 메서드
            Item item = inventory[itemNum - 1];
            item.IsSale = true;
            inventory.Remove(item);
            Console.WriteLine($"{item.Name}을 판매했습니다.");

            // EventManager로 골드 변경 이벤트 전달
            int resultGold = (int) (item.Cost * 0.85);
            EventManager.Instance.PostEvent(EventType.eUpdateGold, resultGold);
        }

        public void OnEquipItem(int itemNum)
        {
            // 장비 착용 관련 메서드
            Item item = inventory[itemNum - 1];
            item.IsEquipped = true;
            Console.WriteLine($"{item.Name}을 착용했습니다.");

            // EventManager로 스탯 변경 이벤트 전달
            EventManager.Instance.PostEvent(EventType.eUpdateStat, item);
        }

        public void OffEquipItem(int itemNum)
        {
            // 장비 해제 관련 메서드
            Item item = inventory[itemNum - 1];
            item.IsEquipped = false;
            Console.WriteLine($"{item.Name}을 해제했습니다.");

            // EventManager로 스탯 변경 이벤트 전달
            EventManager.Instance.PostEvent(EventType.eUpdateStat, item);
        }

        public void GetFieldItem(object? data = null)
        {
            // 필드에 드랍된 아이템 줍줍
            if (data != null)
            {
                fieldDisplay.Add((Item)data);
                foreach (Item item in fieldDisplay)
                {
                    inventory.Add(item);
                    Console.WriteLine($"{item.Name}을 획득했습니다.");
                }
            }
            else
            {
                Console.WriteLine($"아무 아이템도 얻지 못했습니다.");
            }
        }

        public void DropItem(int itemNum)
        {
            // 아이템 버리기(인벤토리에서 삭제)
            Item item = inventory[itemNum - 1];
            inventory.Remove(item);
            Console.WriteLine($"{item.Name}을 버렸습니다.");

            if (item.IsEquipped) // 버린 아이템이 장비중인 경우
            {
                // EventManager로 스탯 변경 이벤트 전달
                EventManager.Instance.PostEvent(EventType.eUpdateStat, item);
            }
        }

        public void OnEvent(EventType type, object data)
        {
            // 게임 이벤트에 따른 인벤토리 기능 구현
            switch (type)
            {
                // case가 eGameEnd인 경우 ItemData를 저장
                case EventType.eGameEnd:
                    ItemData itemData = new ItemData();
                    itemData.inventory = inventory.Select(x => x.Name).ToArray();
                    itemData.equippedItem = inventory.FindAll(x => x.IsEquipped).Select(x => x.Name).ToArray();
                    itemData.fieldItem = fieldDisplay.Select(x => x.Name).ToArray();
                    itemData.saleItem = shopDisplay.Select(x => x.Name).ToArray();

                    Utilities.SaveFile(SaveType.ItemData, itemData);
                    break;

                // case가 eGetItem인 경우 필드 아이템 획득
                case EventType.eGetFieldItem:
                    GetFieldItem(data);
                    break;
            }
        }
    }

    class Item
    // 아이템 자료 클래스
    {
        // { get; private set; } : 읽기 전용 프로퍼티, 외부에서 수정 불가
        public string Name { get; private set; }
        public ItemType Type { get; private set; }
        public int ATK { get; private set; }
        public int DEF { get; private set; }
        public string Description { get; private set; }
        public int Cost { get; private set; }

        public bool IsEquipped { get; set; } // 장비 착용 여부
        public bool IsSale { get; set; } // 상점에서 판매 가능한 지의 여부
        public bool IsOnField { get; private set; } // 던전에서 얻을 수 있는 지의 여부

        public Item(string name, int type, int atk, int def, string description, int reqGold, bool isSale, bool isOnField)
        // Item 클래스 생성자
        {
            Name = name;
            Type = (ItemType)type;
            ATK = atk;
            DEF = def;
            Description = description;
            Cost = reqGold;
            IsSale = isSale;
            IsOnField = isOnField;
        }
    }
}

느낀 점

 

오늘 아침 9시부터 알고리즘 문제풀이를 시작하려니

아직은 익숙하지 않아서 머리가 안 돌아가는 느낌도 났는데,

이건 적응의 문제라 꾸준히 하다보면

알고리즘 문제 풀이에도 뇌가 말랑말랑 해질 것 같은 생각이 든다.

 

그리고 이제 내가 맡은 부분을 구현은 했지만

팀원들이 작업한 코드를 하나로 뭉쳐서 테스트는 안해봤기에,

내일 하나로 뭉쳤을 때 어떤 버그가 생길지도 모른다.

 

하지만, 충분히 팀원과의 협업으로 극복 가능하다고 확신한다.


240111-15:55
+ShowInventory 기능 구현,
+ShowShop 기능 구현,
+BuyItem 기능 구현
240111-16:04
+SellItem 메서드 구현
+OnEquipItem 기능 구현
+OffEquipItem 기능 구현
+DropItem 기능 구현
240111-16:27
+ GetFieldItem 기능 구현
+ eGetFieldItem 이벤트 발생 시 관련 기능 추가
728x90