더 큰 숫자를 담아내보자

DeDi·2023년 2월 15일
post-thumbnail

기하급수적으로 커지는 화폐단위

게임의 초반 화폐 단위와 후반 화폐 단위를 보면 기하급수적으로 단위가 커진다는 점을 알 수 있다. 이는 화폐 단위의 성장으로 인해 플레이어에게 주는 성취감과 기획 초반에서 설정된 화폐 단위 성장의 관성으로 인해 화폐 단위가 기존 8byte 범위의 정수 체계(-900경~+900경/0~1800경)로는 계산할 수 없는 상황이 도래하게 되었다.

1차적 해결: 나만의 계산 체계

일단 그 당시 상황에서 떠오르는 해결책은 이전부터 써오던 ulong 타입을 2개를 붙여서 사용하는 것이었다. 하지만 이를 실현하는 과정이 그렇게 구조적이고 활용하기 좋은 방법이 아니었다.
그때 당시에 짰던 코드에 표기만 조금 수정하였다.

public static bool Add(ulong lowAdd, ulong highAdd, ref ulong lowUnit, ref ulong highUnit)
{
    if (더할 값이 처리 가능한 값인지 확인)
    {
        if (기존 값에 더했을 때 처리 가능한 값인지 확인)
        {
            lowUnit += lowAdd;
            highUnit += highAdd;
            if (더한 결과가 경 단위를 넘었을 때)
            {
                ulong tmp = lowUnit / 1;
                highUnit += tmp;
                lowUnit -= tmp * 1;
            }
            // 연산 성공
            return true;
        }
        else
        {
            ulong tmpHigh = lowAdd / 1;
            ulong tmpLow = lowAdd % 1;
            highUnit += highAdd + tmpHigh;
            lowUnit += tmpLow;
            // 연산 성공
            return true;
        }
    }

    // 연산을 처리하지 못함
    return false; 
}

본 함수는 큰 단위의 숫자끼리 더하기 연산을 하기 위해 만들어졌다.
이 기능을 활용하여 자산에 수익을 더하려고 한다면, 통일되고 제어된 환경에서 사용하기 위해서 또 연산을 도와주는 함수를 만들어야 했다.

public bool 자산_관련_연산(ulong lowUnit, ulong HighUnit, bool plus)
{
    ulong moneyLow = 자산 값(1경 미만 단위);
    ulong moneyHigh = 자산 값(1경 이상 단위);
    bool result;
    // 덧셈 연산일 때
    if (plus)
        result = Add(lowUnit, HighUnit, ref moneyLow, ref moneyHigh);
    // 뺄셈 연산일 때
    else
        result = Subtract(lowUnit, HighUnit, ref moneyLow, ref moneyHigh);

    자산 값(1경 미만 단위) = moneyLow;
    자산 값(1경 이상 단위) = moneyHigh;

    return result;
}

앞선 설명에 없던 Subtract 함수가 생겼는데, Add와 마찬가지로 큰 단위 숫자끼리의 뺄셈 연산을 도와주는 함수이다.
두 함수 모두 연산이 여러 이유로 실패할 수 있기 때문에, 바로 자산 값을 참조하도록 연결하지 않고, 동일한 값을 복사해둔 뒤에 연산이 성공하면 값을 적용하도록 만들었다.

이렇게 "자산 관련 연산" 이라는 함수를 사용하여 각종 비용 처리나, 자산 보상 지급 등의 처리를 하나의 함수로 통일하여 사용할 수 있게되었다.
하지만, 단순 덧셈 뺄셈임에도 불구하고 최종적으로 사용하는 코드에서 C#의 기본 덧셈 뺄셈에서 벗어나서 함수를 사용한다는 점이 보기에도 좋지 않다는 개인적인 아쉬움이 있었다.
그래서 큰 단위의 값을 일괄적으로 처리해줄 구조체를 만들어서 하나의 타입처럼 사용해봐야겠다는 생각이 들었다.

2차적 해결: 개선된 계산 체계

나의 새로운 연산 구조체를 소개한다.
우선 언제나 0과의 비교가 쉽도록 public static의 형태로 zero를 제공한다.
그리고 기본적으로 ulong 타입으로 1경 미만 단위와 1경 이상 단위가 있으며,
ToString()을 Override 하기 때문에 이 값을 글자로 변환할 때 세부적으로 표기할지, 간단하게 표기할지 결정하는 bool 타입의 변수도 가지고 있다.

그리고 덧셈, 뺄셈, 곱셈(+,-,*)과 더불어 비교 연산(>,>=,<,<=,==,!=)까지 모두 연산자 오버로딩을 사용하여 기존 함수 사용을 위주로 구성되어있던 부분들을 (성공 결과가 중요한 일부 코드를 제외하고) 단순 연산자로 연산할 수 있게 되었다!

(덧셈을 처리하는 연산자 오버로딩 코드)

public static 연산_구조체 operator +(연산_구조체 v1, 연산_구조체 v2)
{
    ulong lowUnit = v1.lowUnit, highUnit = v1.highUnit;
    Add(v2.lowUnit, v2.highUnit, ref lowUnit, ref highUnit);
    return new 연산_구조체(lowUnit, highUnit);
}

(추가 이윤 금액(10%) 계산 예시)

interest *= 1.1f;

이 글을 작성하다가 생각난 것인데, 특히 비교연산에서의 편의성 체감이 제일 큰 것 같다.
기본에는 Compare 함수로 -1 0 1이 나와서 0을 기준으로 비교 연산을 해야 해서 헷갈리는 경우가 있었지만, 이제는 a > b 이런 식으로 직관적으로 비교가 가능해졌다!

public static bool operator >= (연산_구조체 variable1, 연산_구조체 variable2)
{
    int result = Compare(variable1.lowUnit, variable1.highUnit, variable2.lowUnit, variable2.highUnit);
    if (result >= 0)
        return true;
    else
        return false;
}

(Example)
if(Money >= Price)
{
	...
}

하지만 이 연산 구조체를 모든 코드에 적용하지는 않았다.
기존에 작성된 코드는 이미 라이브 서비스에 적용되어 있어서 새로운 구조체를 무리하게 도입하다가 예상치 못한 오류가 발생하면, 예상되는 피해의 규모가 크기 때문이다.
따라서 비교적 단순한 기능에 신규로 코드를 작성할 때 위주로 활용하였다.

0개의 댓글