C# format arbitrarily large BigInteger for endless game

前端 未结 2 1575
我寻月下人不归
我寻月下人不归 2021-01-13 22:00

I am trying to create an endless game such as Tap Titans, Clicker Heroes, etc. I have a BigInteger class that is capable of representing arbitrarily large integers as long a

2条回答
  •  遥遥无期
    2021-01-13 22:41

    After a long time of thinking (a month of avoiding the work actually) and a few hours of coding, I used a part of your code to create my own solution.

    It uses prefixes in order: empty string, k, M, B, Q, a, b ... z (excluding k because of thousands), aa, bb, ..., zz, aaa, bbb, ..., zzz, and so on. It trims the zeros from the end of the number, e.g. 1000 = 1k.

    (There's also a possibility to use the scientific notation but it doesn't trim zeros.)

    using System.Collections.Generic;
    using System.Numerics;
    
    /// 
    /// Static class used to format the BigIntegers.
    /// 
    public static class BigIntegerFormatter
    {
        private static List suffixes = new List();
    
        /// 
        /// If it's equal to 0, there are only suffixes from an empty string to Q on the suffixes list.
        /// If it's equal to 1, there are a - z suffixes added.
        /// If it's equal to 2, there are aa - zz suffixes added and so on.
        /// 
        private static int suffixesCounterForGeneration = 0;
    
        /// 
        /// Formats BigInteger using scientific notation. Returns a number without the exponent if the length
        /// of the number is smaller than 4.
        /// 
        /// Number to format.
        /// Returns string that contains BigInteger formatted using scientific notation.
        public static string FormatScientific(BigInteger number)
        {
            return FormatNumberScientificString(number.ToString());
        }
    
        /// 
        /// Formats BigInteger using engineering notation - with a suffix. Returns a number without the
        /// suffix if the length of the number is smaller than 4.
        /// 
        /// Number to format.
        /// Returns string that contains BigInteger formatted using engineering notation.
        public static string FormatWithSuffix(BigInteger number)
        {
            return FormatNumberWithSuffixString(number.ToString());
        }
    
        private static string FormatNumberScientificString(string numberString)
        {
            // if number length is smaller than 4, just returns the number
            if (numberString.Length < 4) return numberString;
    
            // Exponent counter. E.g. for 1000 it will be 3 and the number will
            // be presented as 1.000e3 because 1000.Length = 4
            var exponent = numberString.Length - 1;
    
            // Digit before a comma. Always only one.
            var leadingDigit = numberString.Substring(0, 1);
    
            // Digits after a comma. Always three of them.
            var decimals = numberString.Substring(1, 3);
    
            // Returns the number in scientific format. 
            // Example: 12345 -> 1.234e4
            return $"{leadingDigit}.{decimals}e{exponent}";
        }
    
        private static string FormatNumberWithSuffixString(string numberAsString)
        {
            // if number length is smaller than 4, just returns the number
            if (numberAsString.Length < 4) return numberAsString;
    
            // Counts scientific exponent. This will be used to determine which suffix from the 
            // suffixes List should be used. 
            var exponentIndex = numberAsString.Length - 1;
    
            // Digits before a comma. Can be one, two or three of them - that depends on the exponentsIndex.
            var leadingDigit = "";
    
            // Digits after a comma. Always three of them or less, if the formatted number will have zero 
            // on its end.
            var decimals = "";
    
            // Example: if the number the methods is formatting is 12345, exponentsIndex is 4, 4 % 3 = 1. 
            // There will be two leading digits. There will be three decimals. Formatted number will look like:
            // 12.345k
            switch (exponentIndex % 3)
            {
                case 0:
                    leadingDigit = numberAsString.Substring(0, 1);
                    decimals = numberAsString.Substring(1, 3);
                    break;
    
                case 1:
                    leadingDigit = numberAsString.Substring(0, 2);
                    decimals = numberAsString.Substring(2, 3);
                    break;
    
                case 2:
                    leadingDigit = numberAsString.Substring(0, 3);
                    decimals = numberAsString.Substring(3, 3);
                    break;
            }
    
            // Trims zeros from the number's end.
            var numberWithoutSuffix = $"{leadingDigit}.{decimals}";
            numberWithoutSuffix = numberWithoutSuffix.TrimEnd('0').TrimEnd('.');
    
            var suffix = GetSuffixForNumber(exponentIndex / 3);
    
            // Returns number in engineering format.
            // return $"{numberWithoutSuffix}{suffixes[exponentIndex / 3]}";
    
            return $"{numberWithoutSuffix}{suffix}";
        }
    
        /// 
        /// Gets suffix under a given index which is actually a number of thousands.
        /// 
        /// Suffix index. Number of thousands.
        /// Suffix under a given index - suffix for a given number of thousands.
        private static string GetSuffixForNumber(int suffixIndex)
        {
            // Creates initial suffixes List with an empty string, k, M, B and Q
            if (suffixes.Count == 0) suffixes = CreateSuffixesList();
    
            // Fills the suffixes list if there's a need to
            if (suffixes.Count - 1 < suffixIndex) FillSuffixesList(suffixes, suffixIndex);
    
            return suffixes[suffixIndex];
        }
    
        private static List CreateSuffixesList()
        {
            var suffixesList = new List
            {
                "", "k", "M", "B", "Q"
            };
    
            return suffixesList;
        }
    
        private static void FillSuffixesList(List suffixesList, int suffixIndex)
        {
            // while the suffixes list length - 1 is smaller than the suffix index of the suffix that we need
            // (e.g.: when there's a need for an 'a' suffix:
            // when suffixesList = "", "k", "M", "B", "Q"
            // suffixesList.Count = 5, suffixIndex for a 'Q' is 4,
            // suffixIndex for an 'a' is 5)
            while (suffixesList.Count - 1 < suffixIndex)
            {
                // happens only once, when suffixList is filled only with 
                // initial values
                if (suffixesCounterForGeneration == 0)
                {
                    for (int i = 97; i <= 122; i++)
                    {
                        // k excluded because of thousands suffix
                        if (i == 107) continue;
    
                        // cache the character a - z
                        char character = (char)i;
                        suffixesList.Add(char.ToString(character));
                    }
    
                    suffixesCounterForGeneration++;
                }
                else
                {
                    // for every character (a - z) counts how many times the character should be generated as the suffix
                    for (var i = 97; i <= 122; i++)
                    {
                        // cache the character a - z
                        char character = (char)i;
    
                        // placeholder for a generated suffix
                        string generatedSuffix = "";
    
                        // counts how many times one character should be used as one suffix and adds them
                        // basing on the suffixesCounterForGeneration which is the number telling us how many times 
                        // the suffixes were generated
                        for (var counter = 1; counter <= suffixesCounterForGeneration + 1; counter++)
                        {
                            generatedSuffix += character.ToString();
                        }
    
                        // adds the generated suffix to the suffixes list
                        suffixesList.Add(generatedSuffix);
                    }
    
                    suffixesCounterForGeneration++;
                }
            }
        }
    }
    

提交回复
热议问题