Генерация кратчайшего буквенно-цифрового кода сохранения - PullRequest
0 голосов
/ 11 декабря 2018

Для игры мне нужно сгенерировать код сохранения, который пользователь может где-то записать и использовать для перезагрузки своего игрового состояния позже (постоянные данные невозможны).Код сохранения должен быть коротким, как 6DZF1D3, (строка из базы 36 или базы 62).

Множество игровых уровней можно упростить до string, например, 1232312321321321321, последовательность, в которой каждый символ представляет собойоценка уровня в «звездах» (1, 2 или 3 звезды).Всего будет около 30 игровых уровней.

Я хотел бы создать максимально короткий код для пользователя, поэтому моей первой идеей было создать все возможности внутри массива.Затем сгенерируйте базовый код 62 ключа, где находится пользователь.Но с 3 ^ 30 возможностями это генерирует массив с 2e + 14 ключом / значениями, что не хорошо для памяти и ЦП.

Второе предположение заключалось в использовании базового преобразователя 4–62, но большинствокодов, которые я обнаружил, используют int или long, размер которых ограничен и не превышает 30 символов.

Есть ли у вас какие-либо идеи о том, как создать кратчайший код сохранения, состоящий из буквенно-цифровых символов?

Ответы [ 4 ]

0 голосов
/ 12 декабря 2018

Итак, это код, который я написал с идеей @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);
0 голосов
/ 11 декабря 2018

Конечно, этот вопрос основан на мнениях, но вот один простой способ сохранить

Создать объект

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); 
0 голосов
/ 11 декабря 2018

Если пользователь сможет записать его, я бы предпочел кодировку 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.Я адаптировал две функции Павла Владова, которые вы использовали.

Может быть, когда-нибудь у кого-то возникнет похожая проблема:

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;
        }
    }
}

С уважением

0 голосов
/ 11 декабря 2018

Самым распространенным способом получения двоичных данных в текстовом представлении является Base64 .Каждый символ представляет 6 бит информации.У вас есть чуть менее 48 бит информации, которая позволяет получить 8 цифр Base64.

Таким образом, стратегия будет такой:
1. Преобразовать ваш базовый 3 (звездный) массив в базовый 2, используя этот алгоритм .
2. Преобразовать биты в байтовый массив с использованием Convert.ToByte ();
3. Используйте Convert.ToBase64String () для создания строки Base64.

Редактировать: Я понимаю, что вы хотите иметь его в Base36, есть несколько примеров кода, которые могут это сделать. Этот код нуждается в строке в качестве ввода, но преобразует ее в char[], так что вы можете просто предоставитьВместо этого ByteArray.

Edit2: Доказательство в еде, только что создан конвертер туда и обратно для любой базы вплоть до базы36 (но может быть расширен).Для ваших звезд вам нужно только указать строку со звездными значениями в виде чисел (от 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...