出于游戏的目的,我需要生成一个保存代码,用户可以在某个地方记录该保存代码,并在以后用于重新加载其游戏状态(不可能存在持久数据)。
保存代码必须简短如6DZF1D3
(基数为36或基数为62的字符串)。
游戏级别的分数可以简化为string
,例如1232312321321321321,该序列中的每个字符都是“星”(1、2或3星)中的级别得分。
大约有30个游戏关卡。
我想为用户生成最短的代码,所以我的第一个想法是在Array内生成所有可能性。然后在用户所在的位置生成密钥的基数为62的代码。但是有3 ^ 30的可能性,这将生成一个具有2e + 14键/值的数组,这对内存和CPU不利。
第二个想法是使用4到62的基数转换器,但是我发现的大多数代码都使用int
或long
,它们的大小受限制并且少于30个字符。
您是否知道如何生成由字母数字字符组成的最短保存代码?
答案 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;
}
}
}
致谢