生成最短的字母数字保存代码

时间:2018-12-11 15:41:01

标签: c# powerset

出于游戏的目的,我需要生成一个保存代码,用户可以在某个地方记录该保存代码,并在以后用于重新加载其游戏状态(不可能存在持久数据)。 保存代码必须简短如6DZF1D3(基数为36或基数为62的字符串)。

游戏级别的分数可以简化为string,例如1232312321321321321,该序列中的每个字符都是“星”(1、2或3星)中的级别得分。 大约有30个游戏关卡。

我想为用户生成最短的代码,所以我的第一个想法是在Array内生成所有可能性。然后在用户所在的位置生成密钥的基数为62的代码。但是有3 ^ 30的可能性,这将生成一个具有2e + 14键/值的数组,这对内存和CPU不利。

第二个想法是使用4到62的基数转换器,但是我发现的大多数代码都使用intlong,它们的大小受限制并且少于30个字符。

您是否知道如何生成由字母数字字符组成的最短保存代码?

4 个答案:

答案 0 :(得分:2)

将二进制数据转换为文本表示形式的最常见方法是Base64。每个字符代表6位信息。您只有不到48位的信息,这使您可以轻松达到8位Base64位。

因此该策略将是:
     1.使用this algorithm将基数3(星号)数组转换为基数2。
     2.将这些位转换为字节数组using Convert.ToByte();
     3.使用Convert.ToBase64String()创建一个Base64字符串。

编辑:我知道您想将它放在Base36中,there are some code examples that can do it.该代码需要一个字符串作为输入,但是将其转换为char[],因此您可以只需提供ByteArray即可。

Edit2: 证明是在吃东西,只是为不超过base36的任何基础创建了一个来回转换器(但可以扩展)。对于星星,您只需要提供一个字符串,其星星值就是数字(1到3)。

    private static string ConvertToOtherBase(string toConvert, int fromBase, int toBase)
    {
        const string characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        long value = 0;
        string result = "";

        foreach (char digit in toConvert.ToCharArray())
            value = (value * fromBase) + characters.IndexOf(digit);

        while (value > 0)
        {
            result = characters[(int)(value % toBase)] + result;
            value /= toBase;
        }

        return result;
    }

您可以这样称呼它(来回):

        var stars = "112131121311213112131121311213";

        string base36Result = ConvertToOtherBase(stars, 4, 36);
        // 32NSB7MBR9T3

        string base4Result = ConvertToOtherBase(base36Result, 36, 4);
        // 112131121311213112131121311213

答案 1 :(得分:1)

当然,这个问题是基于意见的,但这是保存的一种简单方法

创建对象

public class Memento
{
     public int Id {get; set;}
     public int Level {get; set;}
     public int Score {get; set;}
}

然后仅使用Newtonsoft.Json库对其进行序列化。最重要的是,您可以加密序列化的JSON,以便用户看不到已保存数据的内部并将其写入磁盘。但是,当然,有很多方法可以保持得分。顺便说一句,我班级的名称应该指向您一个专门解决此问题的编程模式

更新

阅读您的评论-这是您想要的吗?

    int x = 5, y = 10;
    byte[]xb  = BitConverter.GetBytes(x);
    var enumer  = xb.Concat(BitConverter.GetBytes(y));
    string outStr = Convert.ToBase64String(enumer.ToArray());

    Console.WriteLine(outStr);
    // your code: BQAAAAoAAAA=

顺便说一句,如果您使用int16,则您的代码会更短:BQAKAA==

    byte[] back = Convert.FromBase64String(outStr);
    short a = BitConverter.ToInt16(back, 0);
    short b = BitConverter.ToInt16(back, 2);
    Console.WriteLine(a + "_" + b); 

答案 2 :(得分:1)

这是我以@Yosh的想法编写的代码,它具有以下功能:https://www.pvladov.com/2012/07/arbitrary-to-decimal-numeral-system.html

string code = "";
string[] scoreArray = new string[100];
foreach (KeyValuePair<string, LevelScore> l in scores)
{
    scoreArray[l.Value.levelNum - 1] = Convert.ToString(l.Value.stars, 2).PadLeft(2, '0');
}
for (int s = 0; s < scoreArray.Length; s++)
{
    code = scoreArray[s] + code;
}
string b2 = code ;// like "111111111111111111111111111111111111111111111111111111111111";
print("b2 " + b2);

long b10 = ScoreUtils.ArbitraryToDecimalSystem(b2, 2);
print("b10 " + b10);

string b36 = ScoreUtils.DecimalToArbitrarySystem(b10, 36);
print("b36 " + b36);

答案 3 :(得分:0)

如果用户应该能够将其写下来,则我希望使用Base58编码。因此,对于每个级别1-3个可能的星星,我们需要2位来对每个级别进行编码。

00 => 0 star (would mean last unplayed level reached)
01 => 1 star
10 => 2 stars
11 => 3 stars

我们需要60位用于30个级别,所有带有3星的级别都应为十进制1152921504606846975。以base58编码的该级别应为 3gDmDv6tjHG ,不是太长,是吗?!

更新

@DrNootNoot很高兴您找到解决问题的方法!但是我很乐意为我提到的base58版本破解一小段代码。我使用了您使用的Pavel Vladov的两个功能。

也许某天其他人也有类似的问题:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] scoreArray = new string[30] { "1", "2", "3", "3", "1", "2", "2", "2", "3", "1", "1", "1", "2", "3", "2", "1", "2", "3", "1", "1", "1", "2", "2", "2", "1", "1", "2", "1", "2","3" };

            ulong numScore = ScoreToDecimal(scoreArray);

            string saveScore = UDecimalToBase58String(numScore);

            Console.WriteLine("Score array: " + String.Join("-",scoreArray));
            Console.WriteLine("Numeric score: " + Convert.ToString(numScore));
            Console.WriteLine("Base58 score: " + saveScore);

            ulong numScoreRestored = Base58StringToUDecimal(saveScore);
            string[] scoreArrayRestored = DecimalToScore(numScoreRestored);

            Console.WriteLine("From Base58 converted numeric score: " + Convert.ToString(numScoreRestored));
            Console.WriteLine("From Base58 converted score array: " + String.Join("-", scoreArray));
            Console.Read();
        }

        /// <summary>
        /// Converts the stars-per-level array to a decimal value for the saved game.
        /// </summary>
        /// <param name="score">score array to convert. Max. 32 entries/levels.</param>
        /// <returns></returns>
        public static ulong ScoreToDecimal(string[] score)
        {
            int arrLength = score.Length;

            if (arrLength > 32)
                throw new ArgumentException("The score array must not be larger than 32 entries");

            ulong result = 0;

            for (int i = arrLength - 1; i >= 0; i--)
            {
                ulong singleScore = Convert.ToUInt64(score[i]);

                if (singleScore > 3)
                    throw new ArgumentException(String.Format("Invalid score value. Max. allowed value is 3, but {0} was given at index {1}", singleScore, i), "score");

                result += (singleScore << ((arrLength - 1 - i) * 2));
            }

            return result;
        }

        /// <summary>
        /// Converts the decimal value of the saved game back to a stars-per-level array.
        /// </summary>
        /// <param name="decimalScore">Maximal 64-bit unsigned saved game number to convert.</param>
        /// <returns></returns>
        public static string[] DecimalToScore(ulong decimalScore)
        {
            List<string> result = new List<string>();
            while(decimalScore > 0)
            {
                result.Add(Convert.ToString(decimalScore % 4));
                decimalScore /= 4;
            }

            result.Reverse();
            return result.ToArray();
        }

        /// <summary>
        /// Adapted Unsigned-Base58-Version of Pavel Vladovs DecimalToArbitrarySystem function.
        /// See: https://www.pvladov.com/2012/05/decimal-to-arbitrary-numeral-system.html
        /// </summary>
        /// <param name="decimalNumber"></param>
        /// <returns></returns>
        public static string UDecimalToBase58String(ulong decimalNumber)
        {
            const int BitsInLong = 64;
            const int FixedRadix = 58;
            const string Digits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

            if (decimalNumber == 0)
                return "0";

            int index = BitsInLong - 1;
            ulong currentNumber = decimalNumber;
            char[] charArray = new char[BitsInLong];

            while (currentNumber != 0)
            {
                int remainder = (int)(currentNumber % FixedRadix);
                charArray[index--] = Digits[remainder];
                currentNumber = currentNumber / FixedRadix;
            }

            string result = new String(charArray, index + 1, BitsInLong - index - 1);

            return result;
        }

        /// <summary>
        /// Adapted Unsigned-Base58-Version of Pavel Vladovs ArbitraryToDecimalSystem function.
        /// See: https://www.pvladov.com/2012/07/arbitrary-to-decimal-numeral-system.html
        /// </summary>
        /// <param name="base58String"></param>
        /// <returns></returns>
        public static ulong Base58StringToUDecimal(string base58String)
        {
            const int FixedRadix = 58;
            const string Digits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

            if (String.IsNullOrEmpty(base58String))
                return 0;

            ulong result = 0;
            ulong multiplier = 1;
            for (int i = base58String.Length - 1; i >= 0; i--)
            {
                char c = base58String[i];
                int digit = Digits.IndexOf(c);
                if (digit == -1)
                    throw new ArgumentException(
                        "Invalid character in the arbitrary numeral system number",
                        "number");

                result += (uint)digit * multiplier;
                multiplier *= FixedRadix;
            }

            return result;
        }
    }
}

致谢