TIL 0110 TextRPG (팀) - 2 / 스택, 큐

강성원·2024년 1월 10일
0

TIL 오늘 배운 것

목록 보기
13/70

09시 알고리즘 특강시간에 스택, 큐, 정렬(버블,선택,퀵)에 대해서 다루었다.

강의 시간에 구현한 스택과 큐는 string 타입으로만 구현해서 제네릭으로 다시 만들어봤다.

1. 스택(Stack)

public class Stack<T>
{
    private T[] values;
    private int top = -1;
    public Stack(int length)
    { 
        values = new T[length];
    }

    public void Push(T data)
    {
        if(IsFull())
        {
            Console.Error.WriteLine("꽉 참");
            return;
        }
        values[++top] = data;
    }

    public T Pop()
    {
        if (IsEmpty())
            throw new InvalidOperationException("스택이 비어있음");
        return values[top--];
    }

    public bool IsFull() { return top >= values.Length-1; }

    public bool IsEmpty() { return top == -1; }

    public void Print()
    {
        for(int i=0; i <= top; ++i)
        {
            Console.Write(values[i]+" ");
        }
    }
}
  • 스택의 기본 원리에대해서는 찾아보면 잘 나오겠지만, 그보다도 직접 구현할 때 top의 초기 값에 신경을 제대로 쓰지 못하면 실수할 수 있을 것 같다는 생각이 들었다.

  • 위에서는 top의 초기 값을 -1로 두어서 Push 메서드가 호출되면 전위 증가연산자로 top에 1이 바로 더해지도록 한다.

  • Pop메서드의 경우에는 요소를 먼저 반환해준 뒤에 후위 감소연산자를 호출하도록 했다.

이런 사소한 순서의 디테일을 계속 지킬 수 있도록 신경써야겠다.

2. 큐(Queue)

큐에 대해서는 따로 정리했다.

큐에 대해서 정리한 글

큐의 문제점.

  • Dequeue를 하다보면 front가 점점 증가하며 배열의 앞 부분은 쓰지 못하는 공간이 돼버린다.
    배열의 모든 요소를 앞으로 당겨오는 작업은 문제 해결은 되지만 성능 이슈가 있어서 잘 사용되지는 않는다.
    그래서 "원형 큐"를 사용한다.
    원형 큐 포스트

의외로 너무 간단한 부분에서 고민을 아주 조금 했다.
위 코드에서(스택, 큐 공통) 요소를 빼올 때 IsEmpty()의 값이 참이면 아무 것도 리턴하고 싶지 않으려면 어떡하나.. 고민을 하다가 찾은게 throw와 try, catch이다.
throw로 예외를 던지는 문법이 있어서 연습해볼 수 있었다.

Stack<int> stack = new Stack<int>(10);
try
{
    stack.Pop();
}
catch (InvalidOperationException ex) 
{
    Console.WriteLine(ex.Message);
}

try문에서 시도해보고 catch문에서 오류를 띄울 수 있는 기능인데

사실 전에는 무슨 이런 문법이 다 있나 싶었지만, 직접 써보니 꽤 편리하고 깔끔한 느낌도 받았다.


3. 스킬 구현

오늘 내가 자발적으로 스킬 구현을 하겠다고 했지만... 고민만 하다가 절반 정도만 만들었다. 슬프다

internal class Skill
{
    public int OriginNumber         { get; set; } //스킬 넘버
    public string Name              { get; set; } //스킬 이름
    public int Cost                 { get; set; } //스킬 사용 시 드는 Mp
    public int AtkMultiplier        { get; set; } //기본 데미지에 곱할 배수
    public string Description       { get; set; } //스킬 설명

    static private int tempNumber = 1; //정적 변수 증가시켜 넘버링

    public Skill(string name, int cost, int atkMultiplier, string description)
    {
        Name = name;
        Cost = cost;
        AtkMultiplier = atkMultiplier;
        Description = description;
        OriginNumber = tempNumber++;
    } 

    public void Use(Player player, Monster monster)
    {
        //플레이어 MP 소모
        player.Mp -= Cost;
        //몬스터 피격
        monster.GetDamage(player.Atk * AtkMultiplier);
    }

    public void ShowText()
    {
        Console.WriteLine($"{OriginNumber}. {Name} - MP {Cost}");
        Console.WriteLine($"    {Description}\n");
    }
}
  • 정적 변수tempNumber를 생성자에서 증가시키며 스킬에 넘버링을 해주었다.
    큰 이유가 있는 것은 아니고 그냥 스킬 목록 출력할 때 써먹기 좋겠다 싶었다.
  • AtkMultiplier는 플레이어의 기본 데미지에 곱할 배수이다. 이 부분은 더 좋은 아이디어가 없으면 계속 가져갈 듯 싶다.

여기서 만든 스킬을 던전의 결투 부분에서 어떻게 써먹을지 한참을 고민 하고 써보고 다시 되돌리고를 반복하는 하루였다. (그냥 메서드 막 만들어버릴걸...)
기존에 있는 전투 메서드의 줄기도 꽤 큰편이어서 따로 메서드를 만들자니 코드가 너무 비대해지고, 기존의 공격 메서드에 어떻게든 넣어보자니 그것도 녹록치 않았다.

그리고 요구사항에는 한 번에 다수의 적을 랜덤으로 공격하는 스킬도 있었다.
그 부분도 나중에 개발할 것을 고려하니 그냥 뇌와 손가락 이 멈춰버렸다.
(저장이 별 5개인데 스킬이 별 4개라고요..??)

전투 시 선택지가 기본 공격과 스킬이므로 스킬 클래스의 Use 메서드를 어떻게든 활용해보는 방법으로 해봐야겠다.
Use에서 다수 공격인지 단일 공격인지도 판단해보면 얼추 그림이 나올 듯 하다.

우선 오늘은 내 컨디션을 탓해보며.. 내일은 좀 더 쭉쭉 뻗어나가는 코딩의날이 되길 바란다.

profile
개발은삼순이발

0개의 댓글