20231113_기록_개인과제

2023. 11. 13. 20:59IT/TIL

오늘 한 것들

 

Chapter 2 프로그래밍기초 개인과제

 

 

오늘은 Chapter 2 프로그래밍 기초 개인과제의 1차 제출일로

오늘까지 구현했던 내용들을 정리하여 제출하였다

 

과제를 간략히 요약하면

더보기

과제 개요
1. 던전을 떠나기 전 마을에서 장비를 구하는 게임을 텍스트로 구현합니다.
-> 던전이 메인이 아닌, 마을에서 상호작용하는 것이 메인인 게임


2. 상점의 아이템 중에서 나만의 장비를 구성하는 부분이 포인트입니다.

3. 장비는 여러개의 데이터가 함께 있는 만큼 객체나 구조체를 활용하는 편이 효율적 입니다.
-> 특히 상점의 아이템을 구현하는 것이 포인트
-> 각 아이템들을 분류를 나눠서 구현해보자
ex) class 아이템 -> 하위에 무기, 방어구, 소모템 등으로 나누기

4. 관련된 여러 데이터를 다루는 부분은 배열이 도움이 됩니다.


요구사항
- 필수요구사항
1. 게임 시작 화면
-  게임 시작시 간단한 소개 말과 마을에서 할 수 있는 행동을 알려줍니다.
-  원하는 행동의 숫자를 타이핑하면 실행합니다.
-  1 ~ 2 이외 입력시 - "잘못된 입력입니다" 출력

2. 상태 보기
- 캐릭터의 정보를 표시합니다.
- 7개의 속성을 가지고 있습니다.
- 레벨 / 이름 / 직업 / 공격력 / 방어력 / 체력 / Gold
- 처음 기본값은 이름을 제외하고는 아래와 동일하게 만들어주세요
- 이후 장착한 아이템에 따라 수치가 변경 될 수 있습니다.

3. 인벤토리
- 보유 중인 아이템을 전부 보여줍니다.
- 이때 장착중인 아이템 앞에는 [E] 표시를 붙여 줍니다.
- 처음 시작 시에는 2가지 아이템이 있습니다.

3 - 1. 장착 관리
- 장착 관리가 시작되면 아이템 목록 앞에 숫자가 표시됩니다.
- 일치하는 아이템을 선택했다면 (예제에서 1~2 선택 시)
- 장착중이지 않다면 -> 장착
- [E]표시 추가
- 이미 장착중이라면 -> 장착 해제
- [E]표시 없애기
- 일치하는 아이템을 선택하지 않았다면 (예제에서 1~3 이외 선택 시)
- "잘못된 입력입니다" 출력
- 아이템의 중복 장착을 허용합니다.
- 창과 검을 동시에 장착 가능
- 갑옷도 동시에 착용 가능
- 장착 갯수 제한 X
- 아이템이 장착되었다면 1.상태보기에 정보가 반영되어야 합니다.

선택 요구 사항
1. 아이템 정보를 클래스 / 구조체로 활용해보기
2. 아이템 정보를 배열로 관리하기
3. 아이템 추가하기 - 인벤토리에 나만의 새로운 아이템을 추가해보기
4. 콘솔 꾸미기 - 콘솔의 색 지정, 라인 정렬 등을 이용해 꾸며보기
5. 인벤토리 크기 맞춤
6. 인벤토리 정렬하기
7. 상점 - 아이템 구매 -> 7-1. 상점 - 아이템 판매
8. 장착 개선
9. 던전 입장 -> 9.1 휴식 기능, 9.2 레벨업 기능
10. 게임 저장하기

 

으로 정리할 수 있는데

 

기본적인 필수요구사항을 제외하고 내가 추가로 구현한 것은

 

1. 아이템 정보를 클래스로 구현해서 배열로 관리하기

2. 콘솔 꾸미기

3. 인벤토리 크기 맞춤

4. 인벤토리 정렬

 

이다.

 

위에서부터 정리하면

1. 아이템 정보를 클래스로 구현해서 배열로 관리하기

 

아이템의 클래스는 아래와 같이 정리했으며

public class Item
{
    public string Name { get; set; }
    public int Atk { get; }
    public int Def { get; }
    public int Pri { get; }
    public bool IsEquip { get; set; }

    public Item(string name, int atk, int def, int pri, bool isEquip = false)
    {
        Name = name;
        Atk = atk;
        Def = def;
        Pri = pri;
        IsEquip = isEquip;
    }
}

items = new Item[]
{
    new Item("무쇠 갑옷", 0, 5, 0),		// new Item("무쇠 갑옷", 0, 5, 0, true)
    new Item("낡은 검", 2, 0, 0),		// new Item("낡은 검", 2, 0, 0, true)
    new Item("단검", 1, 0, 0),
    new Item("숏소드", 5, 0, 100)
};

 

items = new Item[]으로 배열을 선언하고 그 안에 들어가는 값을 입력함으로써 배열을 만들 수 있었다.

class 선언 시에 bool isEquip = false라고 기본적으로 선언 했기 때문에 값을 입력하지 않아도 된다.

주석처럼 true값을 입력하면 isEquip이 true인 상태(장착한 상태)로 시작할 수 있다.

 

 

 

2. 콘솔 꾸미기

콘솔 꾸미기는 글자의 색상을 변경하는 것을 주로 했으며 기본적으로 아래의 방법을 사용했다.

Console.ForegroundColor = ConsoleColor.Red;  // 글씨 색상을 빨간색으로 변경
Console.ResetColor();			     // 글씨 색상을 원래대로 변경

 

이를 응용하여

 

Write($"공격력 : {Data.TotalAtk} + ");
ForegroundColor = ConsoleColor.Red;
WriteLine($"({Data.ChangedAtk})");
ResetColor();
Write($"방어력 : {Data.TotalDef} + ");
ForegroundColor = ConsoleColor.Blue;
WriteLine($"({Data.ChangedDef})");
ResetColor();

 

 

위와 같이 공격력과 방어력의 추가분을 표시할 수 있다.

 

또, table을 만드는 과정에 응용해서 아래와 같이

장착한 아이템을 녹색으로, 공격력 추가분은 빨간색으로, 방어력 추가분은 파란색으로

플레이어가 잘 알아볼 수 있도록 강조할 수 있다.

 

 

 

3. 인벤토리 크기 맞춤

 

table을 그리는건 수작업으로 직접 그렸으며,

전체 table의 가로 길이를 지정한 후에 그에 맞춰서 테두리와 구분선을 그리고

그 아래로 items[i]의 정보를 for 문을 통해 입력해서 그렸다.

 

더보기
public static void ItemTable()
{
    int tableWidth = 47;
    WriteLine(new string('-', tableWidth));
    Write("| 번호 | ");
    Write("     아이템명      | ");
    ForegroundColor = ConsoleColor.Red;
    Write("공격력");
    ResetColor();
    Write(" | ");
    ForegroundColor = ConsoleColor.Blue;
    Write("방어력");
    ResetColor();
    WriteLine(" |");

    WriteLine(new string('-', tableWidth));

    for (int i = 0; i < items.Length; i++)
    {
        Write($"|  {i + 1}   | ");
        if (items[i].IsEquip)
        {
            ForegroundColor = ConsoleColor.Green;
            Write(" ");
            Write(PadRightForMixedText(items[i].Name, 18));
            ResetColor();
        }
        else
        {
            Write(" ");
            Write(PadRightForMixedText(items[i].Name, 18));
        }
        Write("|");
        ForegroundColor = ConsoleColor.Red;
        if (items[i].Atk == 0)
        {
            Write(" ".PadRight(8));
        }
        else
        {
            Write("  + ");
            Write(items[i].Atk.ToString().PadRight(4));
        }
        ResetColor();
        Write("|");
        ForegroundColor = ConsoleColor.Blue;
        if (items[i].Def == 0)
        {
            Write(" ".PadRight(8));
        }
        else
        {
            Write("  + ");
            Write(items[i].Def.ToString().PadRight(4));
        }
        ResetColor();
        Write("|");
        WriteLine("");
    }
    WriteLine(new string('-', tableWidth));
}

public static int GetPrintableLength(string str)
{
    int length = 0;
    foreach (char c in str)
    {
        if (char.GetUnicodeCategory(c) == System.Globalization.UnicodeCategory.OtherLetter)
        {
            length += 2;  // 한글과 같은 넓은 문자에 대해 길이를 2로 취급
        }
        else
        {
            length += 1;  // 나머지 문자에 대해 길이를 1로 취급
        }
    }

    return length;
}

public static string PadRightForMixedText(string str, int totalLength)
{
    int currentLength = GetPrintableLength(str);
    int padding = totalLength - currentLength;
    return str.PadRight(str.Length + padding);
}

 

생각보다 작업의 양이 많아지고 시간이 길어졌는데,

여기서 중요하게 배울 수 있는 점이 PadRight()를 사용해서 특정 텍스트에 그 길이를 제한했는데

(예를들어 단검은 2글자이지만 이 단검이 차지하는 공간을 18로 지정했다)

이 과정에서 한글은 자리를 2칸 차지하는 문제가 있어, 이를 해결하는데 시간이 걸렸다.

아래 있는 GetPrintableLength() 함수와 PadRightForMixedText() 함수가 이를 해결해주는 함수로

실제로는 2칸을 차지하고 있는 한글의 실제 길이를 구한 후에

이를 전체 길이에서 빼주는 방식으로 차지하는 길이를 구한 것이다.

이를 통해 아래와 같이 깔끔하게 table을 그릴 수 있었다.

 

 

 

 

 

4. 인벤토리 정렬

마지막으로 인벤토리 정렬은 OrderBy와 OrderByDescending을 사용했는데

Name을 기준으로 정렬하는 함수는 아래와 같다.

 

public static void SortName()
{
    if (SortedName)
    {
        SortedName = false;
        items = items.OrderBy(item => item.Name).ToArray();
    }
    else
    {
        SortedName = true;
        items = items.OrderByDescending(item => item.Name).ToArray();
    }

}

 

bool 값 SortedName을 true값으로 선언한 후에 SortName()함수를 실행할 때마다

Name을 기준으로 오름차순, 내림차순을 번갈아가게 정렬되게 코딩했다.

Name뿐만 아니라 Atk, Def로도 정렬될 수 있게 코딩했다.

 

 

 

정리하면서 보면 기능들이 어려운 것은 없고 쉬운 기능들만 있는거 같은데

이를 코딩하는데는 많은 난관이 있었다.

특히 표를 그리는 과정에서 한글이 2자리를 차지하는 문제로 표가 마음대로 그려지지 않았던 것이 가장 큰 난관이였다.

오늘은 집중적으로 문제를 만들고(구현할 목표를 세우고) 그 문제를 푸는 과정을

하나의 주제로(텍스트 게임 만들기) 계속해서 풀어나갔던 하루로

아직은 부족해서 어려움이 많았지만, 많은 것을 배운 느낌이 드는 하루였다.