Валютная система RPG

Я только что провел последние несколько дней, построив валютную систему для своей игры, и задавался вопросом, есть ли у вас, ребята, какие-то предложения о том, как, если вообще ... я мог бы это улучшить. Прежде чем я покажу код, позвольте мне объяснить систему, как она сейчас стоит. Он очень похож на World of Warcraft, поскольку Copper автоматически преобразуется в Silver, когда вы получаете 100 из них. Затем серебро превращается в золото, а золото - в платину. Для тех из вас, кто играл в Everquest, эта система показалась бы еще более знакомой.

Я уже сделал несколько оптимизаций для корневой структуры всего этого, например, как я сохраняю все знаменатели валюты за один долго. Используя это долго, я могу делать вычисления для «подделки» других наименований. По сути, все внутренне хранится как медь.

Я также подразумевал максимальное значение знаменателя базы «999999999». Используя это несколько произвольное число, я могу ограничить общую валюту в 999 Platinum, 99 Gold, 99 Silver и 99 Copper.

Мне просто интересно, есть ли у вас у вас какие-либо предложения (помимо таких, как «эй, вы должны полностью использовать var прямо здесь!»), которые могут помочь улучшить мою текущую реализацию.

Вот код, который в настоящее время стоит:

using System;

namespace Some.Arbitrary.Framework
{

    public enum Coins
    {
        /// <summary>
        /// Copper is the lowest denominator of currency.
        /// It requires 100 Copper to make 1 Silver.
        /// </summary>
        Copper = 1,
        /// <summary>
        /// Silver is the second most common form of currency.
        /// It requires 100 Silver to Make 1 Gold.
        /// </summary>
        Silver = 2,
        /// <summary>
        /// Gold is the most common form of currency. It takes
        /// part in most expensive transactions.
        /// It requires 100 Gold to make 1 Platinum.
        /// </summary>
        Gold = 3,
        /// <summary>
        /// Platinum is a coin which most people never see. A single
        /// Platinum coin can purchase almost anything.
        /// 1 Platinum Coin = 100 Gold.
        /// 1 Platinum Coin = 10,000 Silver.
        /// 1 Platinum Coin = 1,000,000 Copper.
        /// </summary>
        Platinum = 4
    }

    public class MoneyBag : IEquatable<MoneyBag>, IComparable<MoneyBag>
    {

        private long _baseDenomination;
        public const string CopperName = "Copper";
        public const string SilverName = "Silver";
        public const string GoldName = "Gold";
        public const string PlatinumName = "Platinum";
        public const char CopperAbbreviation = 'c';
        public const char SilverAbbreviation = 's';
        public const char GoldAbbreviation = 'g';
        public const char PlatinumAbbreviation = 'p';
        public const long MaximumBaseDenomination = 999999999;
        public static readonly MoneyBag FilledBag = new MoneyBag(MaximumBaseDenomination);
        public static readonly MoneyBag EmptyBag = new MoneyBag();

        public long BaseDenomination
        {
            get
            {
                return _baseDenomination;
            }
            set
            {
                _baseDenomination = value;
                // Clamp if required.
                if (_baseDenomination > MaximumBaseDenomination)
                {
                    _baseDenomination = MaximumBaseDenomination;
                }
                if (_baseDenomination < 0)
                {
                    _baseDenomination = 0;
                }
            }
        }

        /// <summary>
        /// The total amount of Copper.
        /// </summary>
        public int Copper
        {
            get
            {
                return ComputeCopper(_baseDenomination);
            }
        }

        /// <summary>
        /// The total amount of Silver.
        /// </summary>
        public int Silver
        {
            get
            {
                return ComputeSilver(_baseDenomination);
            }
        }

        /// <summary>
        /// The total amount of Gold.
        /// </summary>
        public int Gold
        {
            get
            {
                return ComputeGold(_baseDenomination);
            }
        }

        /// <summary>
        /// The total amount of Platinum.
        /// </summary>
        public int Platinum
        {
            get
            {
                return ComputePlatinum(_baseDenomination);
            }
        }

        public bool IsFull
        {
            get
            {
                return _baseDenomination == MaximumBaseDenomination;
            }
        }

        public bool IsEmpty
        {
            get
            {
                return _baseDenomination == 0;
            }
        }

        public bool HasPlatinum
        {
            get
            {
                return Platinum > 0;
            }
        }

        public bool HasGold
        {
            get
            {
                return Gold > 0 || Platinum > 0;
            }
        }

        public bool HasSilver
        {
            get
            {
                return Silver > 0 || Gold > 0 || Platinum > 0;
            }
        }

        public bool HasCopper
        {
            get
            {
                return Copper > 0 || Silver > 0 || Gold > 0 || Platinum > 0;
            }
        }

        public MoneyBag()
        {

        }

        public MoneyBag(int platinum, int gold, int silver, int copper)
        {
            Add(platinum, gold, silver, copper);
        }

        public MoneyBag(long baseDenomination)
        {
            BaseDenomination = baseDenomination;
        }

        public void Add(int platinum, int gold, int silver, int copper)
        {
            BaseDenomination += platinum * 1000000;
            BaseDenomination += gold * 10000;
            BaseDenomination += silver * 100;
            BaseDenomination += copper;
        }

        public void Add(int amount, Coins type)
        {
            if (amount <= 0) return;
            switch (type)
            {
                case Coins.Copper:
                    Add(0, 0, 0, amount);
                    break;
                case Coins.Silver:
                    Add(0, 0, amount, 0);
                    break;
                case Coins.Gold:
                    Add(0, amount, 0, 0);
                    break;
                case Coins.Platinum:
                    Add(amount, 0, 0, 0);
                    break;
            }
        }

        public void Add(MoneyBag other)
        {
            BaseDenomination += other._baseDenomination;
        }

        public void Subtract(int platinum, int gold, int silver, int copper)
        {
            BaseDenomination -= platinum * 1000000;
            BaseDenomination -= gold * 10000;
            BaseDenomination -= silver * 100;
            BaseDenomination -= copper;
        }

        public void Subtract(int amount, Coins type)
        {
            if (amount <= 0) return;
            switch (type)
            {
                case Coins.Copper:
                    Subtract(0, 0, 0, amount);
                    break;
                case Coins.Silver:
                    Subtract(0, 0, amount, 0);
                    break;
                case Coins.Gold:
                    Subtract(0, amount, 0, 0);
                    break;
                case Coins.Platinum:
                    Subtract(amount, 0, 0, 0);
                    break;
            }
        }

        public void Subtract(MoneyBag other)
        {
            BaseDenomination -= other._baseDenomination;
        }

        public void Empty()
        {
            _baseDenomination = 0;
        }

        public void Fill()
        {
            _baseDenomination = MaximumBaseDenomination;
        }

        public static MoneyBag operator +(MoneyBag b1, MoneyBag b2)
        {
            return new MoneyBag(b1._baseDenomination + b2._baseDenomination);
        }

        public static MoneyBag operator -(MoneyBag b1, MoneyBag b2)
        {
            return new MoneyBag(b1._baseDenomination - b2._baseDenomination);
        }

        public bool Equals(MoneyBag other)
        {
            return _baseDenomination == other._baseDenomination;
        }

        public override bool Equals(object obj)
        {
            return (obj is MoneyBag) && Equals((MoneyBag)obj);
        }

        public override int GetHashCode()
        {
            return _baseDenomination.GetHashCode();
        }

        public static bool operator ==(MoneyBag a, MoneyBag b)
        {
            if (ReferenceEquals(a, null)) return false;
            if (ReferenceEquals(b, null)) return false;
            return a.Equals(b);
        }

        public static bool operator !=(MoneyBag a, MoneyBag b)
        {
            if (ReferenceEquals(a, null)) return false;
            if (ReferenceEquals(b, null)) return false;
            return !a.Equals(b);
        }

        public static bool operator <(MoneyBag a, MoneyBag b)
        {
            return a.CompareTo(b) < 0;
        }

        public static bool operator >(MoneyBag a, MoneyBag b)
        {
            return a.CompareTo(b) > 0;
        }

        public int CompareTo(MoneyBag other)
        {
            // The shit was null, dumbass!
            if (other == null) return 0;
            if (_baseDenomination > other._baseDenomination)
            {
                return 1;
            }
            if (_baseDenomination < other._baseDenomination)
            {
                return -1;
            }
            // They were equal.
            return 0;
        }

        public static void ComputeWealth(long baseDenomination, out int platinum, out int gold, out int silver, out int copper)
        {
            platinum = ComputePlatinum(baseDenomination);
            gold = ComputeGold(baseDenomination);
            silver = ComputeSilver(baseDenomination);
            copper = ComputeCopper(baseDenomination);
        }

        public static int ComputeCopper(long baseDenomination)
        {
            return (int)Math.Floor((double)Math.Abs(baseDenomination % 100));
        }

        public static int ComputeSilver(long baseDenomination)
        {
            return (int)Math.Floor((double)Math.Abs((baseDenomination / 100) % 100));
        }

        public static int ComputeGold(long baseDenomination)
        {
            return (int)Math.Floor((double)Math.Abs((baseDenomination / 10000) % 100));
        }

        public static int ComputePlatinum(long baseDenomination)
        {
            return (int)Math.Floor((double)Math.Abs(baseDenomination / 1000000));
        }

        public override string ToString()
        {
            return
                "" + Platinum + PlatinumAbbreviation + "," +
                Gold + GoldAbbreviation + "," +
                Silver + SilverAbbreviation + "," +
                Copper + CopperAbbreviation;
        }
    }
}

Краткое описание использования (не стесняйтесь использовать его больше самостоятельно):

using Some.Arbitrary.Framework;

namespace SomeGameOrSomething
{
    static class Program
    {
        static void Main()
        {
            MoneyBag bag = new MoneyBag(1,22,44,55);
            bag.Subtract(0,50,22,0);
            Console.WriteLine(bag);
            Console.Read();
        }
    }
}

EDIT /Update

В случае, если кто-то заинтересован в том, что у меня получилось после всех замечательных предложений, код можно найти здесь: http://pastebin.com /sqVjZYry

44 голоса | спросил Krythic 16 MaramWed, 16 Mar 2016 03:55:14 +03002016-03-16T03:55:14+03:0003 2016, 03:55:14

7 ответов


39

Есть несколько вещей, которые выделяются мне:

public const string CopperName = "Copper";
public const string SilverName = "Silver";
public const string GoldName = "Gold";
public const string PlatinumName = "Platinum";

... имена и описательный текст должны (почти) всегда входить в файлы ресурсов. Зачем? Интернационализация. Это означает, что вам не нужно перекомпилировать код, если имя изменится.
Да, вам нужно каким-то образом получить ключи поиска, но они действительно не принадлежат здесь - это функция, но вы выводите на плеер.

public long BaseDenomination
{
     get
     {
         return _baseDenomination;
     }
     set
     {
         _baseDenomination = value;
         // Clamp if required.
         if (_baseDenomination > MaximumBaseDenomination)
         {
             _baseDenomination = MaximumBaseDenomination;
         }
         if (_baseDenomination < 0)
         {
             _baseDenomination = 0;
         }
     }
 }

Возможно, более обычным является использование стандартных функций max /min здесь:

public long BaseDenomination
{
     get
     {
         return _baseDenomination;
     }
     set
     {
         // Clamp if required.
         _baseDenomination = Math.Max(0, Math.Min(MaximumBaseDenomination, value));
     }
 }  

Кроме того, используемое и возвращаемое значение является long, но ваша максимальная база хорошо вписывается в int, вы можете подумать об этом.

public void Add(int platinum, int gold, int silver, int copper)
{
    BaseDenomination += platinum * 1000000;
    BaseDenomination += gold * 10000;
    BaseDenomination += silver * 100;
    BaseDenomination += copper;
}

Все эти цифры? Их называют «магическими числами», и вы не хотите их. Вместо этого вы должны определять и использовать константы, что-то вроде этого:

private const int SilverInCopper = 100;
private const int GoldInCopper = SilverInCopper * 100;
private const int PlatinumInCopper = GoldInCopper * 100;

В ваших методах AddSubtract) есть гораздо более серьезный дефект: вы не следите за переполнением целых чисел. Они примут 1200 платиновых монет и с радостью установят содержимое кошелька до нуля (из-за зажима - хотя это предполагает, что он был близок к максимуму для начала). Вы также не следите за вещами, как сочетание положительных и отрицательных монет, что может показаться странным.

Если вы измените это на структуру, это не имеет значения, но здесь у вас нет нулевой проверки (бросит NullReferenceException):

public bool Equals(MoneyBag other)
{
    return _baseDenomination == other._baseDenomination;
}

Следующие два взяты вместе:

public static bool operator ==(MoneyBag a, MoneyBag b)
{
    if (ReferenceEquals(a, null)) return false;
    if (ReferenceEquals(b, null)) return false;
    return a.Equals(b);
}

public static bool operator !=(MoneyBag a, MoneyBag b)
{
    if (ReferenceEquals(a, null)) return false;
    if (ReferenceEquals(b, null)) return false;
    return !a.Equals(b);
}

Это приводит к:

  • ((MoneyBag)null) == ((MoneyBag)null) возвращает false.
  • ((MoneyBag)null) != ((MoneyBag)null) также возвращает false.

Опять же, это не имеет значения, станет ли оно структурой, но вам нужно проверить, что экземпляры класса не являются и null. Во-первых, это нарушило бы коммутативное свойство равенства. Также более обычным образом реализовать эти операторы в терминах друг друга:

public static bool operator !=(MoneyBag a, MoneyBag b)
{
    return !(a == b);
}

Операторы сравнения не имеют нулевой проверки:

public static bool operator <(MoneyBag a, MoneyBag b)
{
    return a.CompareTo(b) < 0;
}

public static bool operator >(MoneyBag a, MoneyBag b)
{
    return a.CompareTo(b) > 0;
}

Опять же, не проблема со структурой, но в противном случае имеет плохие последствия: вы получите случайные сбои (NullReferenceException) из другого кода, в зависимости от того, какие элементы сортировки выбирают для опорных точек. Вам также не хватает больше или равно и меньше или равно, и вы, вероятно, захотите основать три из четырех оставшихся, плюс равно (перейдя по соглашениям на C /C ++, используйте <, меньше).

public int CompareTo(MoneyBag other)
{
    // The shit was null, dumbass!
    if (other == null) return 0;
    if (_baseDenomination > other._baseDenomination)
    {
        return 1;
    }
    if (_baseDenomination < other._baseDenomination)
    {
        return -1;
    }
    // They were equal.
    return 0;
}

Как точка стиля, обычно считается плохой формой, чтобы ругаться или использовать иначе вульгарныйязык в коде, который вы пишете для бизнеса, или планируете выпустить публикацию.
Что еще более важно, однако, null теперь считается равным всем остальным элементам. Это полностью нарушает коммутативное свойство равенства. Если у вас есть коллекция с нулевым элементом в ней, она нарушит сортировку и поиск (независимо от того, получите ли вы значение null, когда вы ожидали чего-то другого или ошибка утверждения). Вместо этого вы должны сортировать нули до «bottom»:

 // If other is not a valid object reference, this instance is greater.
 if (other == null) return 1;

В нестандартной IComparable ссылке также указано :

  

По определению любой объект сравнивается больше (или следует) null, а две нулевые ссылки сравниваются друг с другом.

(по какой-то причине это замечание не содержится в общей документации, что представляет собой серьезный надзор с чьей-то стороны)

Кроме того, поскольку вы настраиваете упорядочение внутреннего представления, вы можете использовать long.CompareTo(...):

public int CompareTo(MoneyBag other)
{
    if (other == null) return 1;
    return _baseDenomination.CompareTo(other._baseDenomination);
}

... зависит ли вы от фактического базового поля или публичного участника.

Наконец, обо всех ваших методах ComputeX:

public static int ComputeCopper(long baseDenomination)
{
    return (int)Math.Floor((double)Math.Abs(baseDenomination % 100));
}

public static int ComputePlatinum(long baseDenomination)
{
    return (int)Math.Floor((double)Math.Abs(baseDenomination / 1000000));
}

... Вся математика, которую вы делаете в этом случае, является целочисленной математикой. Если вы не знаете, что это такое, я рекомендую прочитать его, но суть такова:

int i1 = 10;
int i2 = 3;
Console.Out.WriteLine(10 / 3); // Prints '3'

По сути, язык (и почти все компьютерные языки работают таким образом) усекает результат (для положительных чисел это эквивалентно напольному покрытию).
Они также превращают негативные суммы в позитивные! Это может быть использовано в другом месте вашего кода - либо привязать к 0, либо вернуть отрицательные значения.
О, а ComputePlatinum - также работает в переполнении целых чисел: вход является long, но вывод - это int. Достаточно большие положительные значения превратятся в ... что-то еще, вполне возможно отрицательное. Вы должны либо возвращать long здесь, либо только принимать int в первую очередь. (Или используя checked и бросая исключение, но это может быть проблематично). В любом случае, я бы, вероятно, написал методы в этих строках:

public static int ComputeCopper(int baseDenomination)
{
    return baseDenomination % SilverInCopper;
}

public static int ComputeSilver(int baseDenomination)
{
    return baseDenomination % GoldInCopper / SilverInCopper ;
}

// I assume you can figure out ComputeGold

public static int ComputePlatinum(int baseDenomination)
{
    return baseDenomination / PlatinumInCopper;
}
ответил Clockwork-Muse 16 MarpmWed, 16 Mar 2016 13:36:33 +03002016-03-16T13:36:33+03:0001 2016, 13:36:33
28
public enum Coins

Имена типов Enum должны быть уникальными, если они не украшены атрибутом [Flags]:

  
  • Используйте аргумент Pascal для имен и имен имен.
  •   
  • Сократите использование сокращений.
  •   
  • Не используйте суффикс Enum для имен типа Enum.
  •   
  • Используйте уникальное имя для большинства типов Enum, но используйте множественное имя для типов Enum, которые являются битовыми полями.
  •   
  • Всегда добавляйте атрибут FlagsAttribute к типу Enum в поле бит.
  •   

https: //msdn .microsoft.com /EN-US /библиотека /4x252001 (v = vs.71) .aspx суб>

Следовательно, CoinType будет лучшим именем. Теперь, поскольку это не перечисление [Flags], базовые значения int совершенно бессмысленны и вообще не нужно явно указывать.


public const char CopperAbbreviation = 'c';
public const char SilverAbbreviation = 's';
public const char GoldAbbreviation = 'g';
public const char PlatinumAbbreviation = 'p';

Семантически это не константы . Это значения, которые вы хотите использовать now в этой версии кода. Проблема состоит в том, что, подвергая их полям public const, вы в значительной степени нарисовали себя в углу, если вы выпустили свою библиотеку: если будущая версия изменяет эти значения, вы заставляете все клиентский код, подлежащий перекомпиляции, поскольку значения const «сжигаются на месте» при компиляции - поэтому, если бы я использовал вашу библиотеку, и теперь вы выпускаете новую версию, мне приходится перекомпилировать my , чтобы получить новые значения, которые предоставляет ваша инфраструктура.

С другой стороны, если у вас их было так:

public static readonly char CopperAbbreviation = 'c';
public static readonly char SilverAbbreviation = 's';
public static readonly char GoldAbbreviation = 'g';
public static readonly char PlatinumAbbreviation = 'p';

.. то я мог бы просто поменять вашу старую версию на новую, и я бы получил обновленный контент, не перекомпилируя ничего.

Тем не менее, единственное использование этих констант похоже в реализации ToString, которая объединяет все значения:

public override string ToString()
{
    return
        "" + Platinum + PlatinumAbbreviation + "," +
        Gold + GoldAbbreviation + "," +
        Silver + SilverAbbreviation + "," +
        Copper + CopperAbbreviation;
}

Эти конкатенации не очень хороши, а "" выглядит как хак, чтобы заставить оператор + скомпилировать и получить числовые значения для неявного преобразования в строки.

Если это переопределение ToString предназначено для целей отладки, тогда я бы даже не потрудился с XxxxAbbreviation значениями - пусть код клиента будет выглядеть так, и если вы действительно хотите переопределить ToString, тогда вы можете просто сделать это:

return string.Format("{0}p, {1}g, {2}s, {3}c", Platinum, Gold, Silver, Copper);

Еще лучше, украсьте свой класс с помощью DebuggerDisplayAttribute:

[DebuggerDisplay("{Platinum}p, {Gold}g, {Silver}s, {Copper}c")]

public static void ComputeWealth(long baseDenomination, out int platinum, out int gold, out int silver, out int copper)

Параметры out воняют. Очень жаль, что метод возвращает void, когда вы можете просто вернуть экземпляр MoneyBag с помощью одной маленькой инструкции:

return new MoneyBag(baseDenomination);

Но почему, зачем вам нужен этот член? Удалите его, он лишний.

Подумайте об этом, весь тип действительно инкапсулирует значения 4 int: подумайте над тем, чтобы сделать его неизменяемый struct вместо класса; все мутирующие методы просто вернут новое значение.

Я бы только изменил эти два члена:

public static readonly MoneyBag FilledBag = new MoneyBag(MaximumBaseDenomination);
public static readonly MoneyBag EmptyBag = new MoneyBag();

:

public static readonly MoneyBag Full = new MoneyBag(MaximumBaseDenomination);
public static readonly MoneyBag Empty = new MoneyBag();

«Сумка» избыточна, когда вы ее вызываете. Рассмотрим:

var emptyBag = MoneyBag.EmptyBag;
var fullBag = MoneyBag.FilledBag;

против.

var emptyBag = MoneyBag.Empty;
var fullBag = MoneyBag.Full;
ответил Mathieu Guindon 16 MaramWed, 16 Mar 2016 05:13:10 +03002016-03-16T05:13:10+03:0005 2016, 05:13:10
18

Вы уверены, что ваша бизнес-логика действительно должна знать обо всех этих разных типах монет? Мне это кажется ненужным осложнением. Это должно быть намного проще для создания кода, который всегда обрабатывает деньги просто как long number (с некоторыми дополнительными методами). Только когда дело доходит до уровня пользовательского интерфейса, вы должны подумать о том, что является лучшим способом отображения вашей денежной сумки. Там вы можете иметь конвертер, который будет конвертировать деньги в монеты, которые ваш пользователь видит на экране. Но это должен быть класс, отдельный из MoneyBag, и он должен существовать только как часть слоя пользовательского интерфейса.

Некоторые другие вещи, касающиеся вашего существующего кода:

  1. При внедрении методов и операторов равенства вы всегда должны стараться как можно больше использовать свою реализацию. a != b совпадает с !(a == b), a - b совпадает с a + (-b) и т. д. Для eaxmple вы можете реализовать равенство как:

    public override bool Equals(object obj)
    {
        return Equals(obj as MoneyBag);
    }
    
    public static bool operator ==(MoneyBag a, MoneyBag b)
    {
        return a.Equals(b);
    }
    
    public static bool operator !=(MoneyBag a, MoneyBag b)
    {
        return !a.Equals(b); // or return !(a == b)
    }
    
    public bool Equals(MoneyBag other)
    {
        //here goes the actual implementation, 
        //which you reuse in all other methods
    }
    

    Таким образом, вы можете гарантировать, что все методы равенства всегда возвращают один и тот же результат. И исправление ошибки в вашей логике равенства становится так же просто, как и установка одного метода. В настоящий момент метод null to IEquatable.Equals будет throw, при использовании == будет работать нормально, например.

  2. У вас есть изменяемый хеш-код, который является плохой идеей вообще.

  3. У вас есть изменяемые статические feilds (FilledBag и EmptyBag)), что также является плохой идеей. Рассмотрим этот код:

    var myBag = MoneyBag.EmptyBag;
    //blah-blah 100 lines of code
    myBag.Add(...);
    

    Возможно, вы думаете: «Эй, я не тот парень! Я не буду использовать EmptyBag, как это!» Но каждый из этих парней, вероятно, думал о том же.

  4. Такие вещи, как platinum * 1000000, должны быть выделены в выделенные методы, поэтому вам не нужно копировать их (или подсчитывать вручную нули) каждый раз, когда вам нужно конвертировать один тип валюты к другому.

ответил Nikita B 16 MarpmWed, 16 Mar 2016 13:10:44 +03002016-03-16T13:10:44+03:0001 2016, 13:10:44
7

Уже есть отличные ответы, которые касаются стиля кода и срабатываний, поэтому я хотел бы подходить под другим углом: как потенциальный пользователь /отладчик вашего кода.

Добавить /вычесть не являются обратными операциями

Хотя фиксация ценностей привлекательна для безопасности, которую она нам дает, она также ставит под угрозу Принцип наименьшего сюрприза , Если у меня есть 500p, и я добавляю 800p, я получаю 999p (и 99g99s99c). Если я затем вычитаю 800p снова, у меня нет моего оригинального 500p; скорее, у меня осталось бы меньше 200p.

Если вы обеспокоены тем, как отображать большие суммы денег в ограниченном пространстве, этот компонент может понять это. Возможно, он мог бы постепенно отказаться от медных /серебряных /золотых монет, если ему нужно больше пространства. Если у меня будет столько денег, я бы не стал беспокоиться о том, сколько медных монет у меня есть. ;)

Отсутствие обратной связи при сбое

(Я не программист на C #, поэтому я могу ошибаться.)

Похоже, что я могу вычесть 50 монет из 20 монет и в итоге получить 0 монет. Это меня удивляет. Если функция не может делать то, что она утверждает (или, кажется, претендует на то, чтобы сделать это сложно) он должен каким-то образом сигнализировать о неисправности, например, бросая исключение.

HasXYZ vs GetXYZ

В случае, если у вас есть 200cp или 2sp, что здесь и здесь, HasCopper () вернет true, потому что он также проверяет HasSilver (), но Copper () вернет 0. Он чувствует для меня, что HasCopper () должен подразумевать Copper ()> 0, поэтому либо HasCopper () должен возвращать false, либо Copper () должен дать общую сумму в медных монетах (200).

Упростить или увеличить большой

Мой базовый совет будет либо:

  • сделать вещи проще, удаляя типы монет и оставляя это для представления;
  • или пройти весь путь и рассчитать все разные типы монет отдельно, то есть иметь счетчик для каждого типа, а затем добавить функцию Wealth () или Value (), которая дает вам общую сумму от добавления всех значений монет. Добавление будет проще, но вычесть будет сложнее.

В конце концов, в чем проблема, связанная с наличием 4 разных типов монет, если эта разница не имеет смысла? (Подумайте о том, как вы будете использовать этот класс: установили ли вы цены на предметы в магазине в платиновых, золотых, серебряных, медных монетах? Или вы бы установили ценность базы?)

ответил JvR 17 MaramThu, 17 Mar 2016 00:18:47 +03002016-03-17T00:18:47+03:0012 2016, 00:18:47
5

Похоже, вы полностью смешиваете бизнес и отображаете уровни /обязанности. Вы можете упростить ситуацию, избавившись от разделения c /s /g /p в большинстве функций. Add(int amount, Coins type) и Add(int platinum, int gold, int silver, int copper), например, не нужно. Если вы придерживаетесь только базового значения везде, кроме дисплея, у вас будет меньше работы с:

  • storage (это всего лишь одно значение)
  • магазины (опять же, одно значение, а не четыре)
  • исправления в экономике (вы хотите пересчитать все значения или просто сделать prices*=1.1 в какой-то момент в будущем)
  • любые функции, которые влияют на цены как долю (навыки торгуются?)

HasCopper и подобные методы не являются супер-интуитивными. Делает ли мешок с 1 gold HasCopper, или нет?

Изменчивые пустые /заполненные мешки - это несчастный случай, ожидаемый.

Я не понимаю, почему вы используете Math.Abs в функциях Compute..... Они не должны быть всегда отрицательными, не так ли?

ответил viraptor 18 MaramFri, 18 Mar 2016 05:41:09 +03002016-03-18T05:41:09+03:0005 2016, 05:41:09
5

Используйте один числовой тип. Золото /Серебро /и т. Д. - всего лишь одно представление вашей модели, и это значительно упростит все. Все цены на самом деле находятся в вашем базовом числовом типе, но просматриваются дружелюбно, и вся математика выполняется только для вашего числового типа.

ответил ghelyar 18 MarpmFri, 18 Mar 2016 12:54:34 +03002016-03-18T12:54:34+03:0012 2016, 12:54:34
4

Я довольно удивляюсь, почему вы раскалываете свои ценности вообще, и еще более любопытно, почему это еще не было указано. Когда вы смотрите на свой банковский счет, вы не говорите, что у меня есть 10 $ 100 плюс 50 $ 10, вы просто говорите, что у меня есть 1500 долларов.

Разделение вашей валюты только что появилось много потенциальных ошибок и только интересно для front-end. Я бы просто дал количество валюты в один номер, и пусть фронт-контракт с делением на медь, серебро, золото. То, как вы показываете валюту, должно, по моему мнению, быть неактуальным для вашей игровой логики.

ответил Niels 17 MaramThu, 17 Mar 2016 11:04:36 +03002016-03-17T11:04:36+03:0011 2016, 11:04:36

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132