C# 向上兑换(虚构的)货币
在一个使用古老的铜、银、金和白金货币体系的游戏中,每个面额的100个单位等于下一个最高面额的1个单位,这是一种可以接受的输入值“排序”或“向上改变”的方法吗C# 向上兑换(虚构的)货币,c#,C#,在一个使用古老的铜、银、金和白金货币体系的游戏中,每个面额的100个单位等于下一个最高面额的1个单位,这是一种可以接受的输入值“排序”或“向上改变”的方法吗 public struct Coinage { private int _copper; private int _silver; private int _gold; private int _platinum; public int Copper { get { ret
public struct Coinage
{
private int _copper;
private int _silver;
private int _gold;
private int _platinum;
public int Copper
{
get { return _copper; }
set
{
int val = value;
while (val > 99) { val -= 100; Silver ++; }
_copper += val;
}
}
public int Silver
{
get { return _silver; }
set
{
int val = value;
while (val > 99) { val -= 100; Gold ++; }
_silver += val;
}
}
public int Gold
{
get { return _gold; }
set
{
int val = value;
while (val > 99) { val -= 100; Platinum ++; }
_gold += val;
}
}
public int Platinum { get { return _platinum; } set { _platinum = value; } }
}
因此,无论我输入多少面值(低于铂金),它都能正确地为我兑换货币?像这样将属性的set方法链接起来是个坏主意吗?有没有一种更有效的方法可以用单一的方法来实现这一点
谢谢。不,那太糟糕了。你不需要循环,你只需要除法和模 我也同意克里斯托弗的观点;只需将所有内容存储为最小的货币,并以您喜欢的方式显示即可。容易多了
编辑:哦,看看这个;我上面的代码[删除]中有一个严重的错误,看看你是否能发现它。更多的证据表明,您应该将所有内容存储为最小的货币,这就容易多了。您的代码违反了最小惊喜原则。属性不应更改另一个属性的值 您应该有一个Add方法,该方法为每个面额接受一个参数,然后执行每个检查 如前所述,您不需要循环
silver += copper / 100;
copper = copper % 100;
gold += silver / 100;
silver = silver % 100;
//etc..
最简单也是最常用的方法就是将其存储为一个大整数(铜币数),然后使用模和除法提取每个“字段”。这是魔兽世界(World of Warcraft)等游戏所使用的(这就是为什么您在该游戏中的黄金上限为214748g 36s 47c-2147483647是您可以存储在32位整数中的最高数字) 例如,假设您有12345铜。这相当于1g,23s,45c(我们现在将忽略铂,因为这是相同的原理)。您可以按如下方式获取每个字段:
gold = money / 10000; //integer division
silver = (money % 10000) / 100; //first remove the part that was used for the gold, then do the division
copper = money % 100
鉴于您达到白金级别(每白金100万铜),在这种情况下选择64位整数(
long
)可能是个好主意。首先,您的代码不起作用。如果我将白银设定为1000000,它将不起作用
造币的方式非常容易用数学计算。直到最后一分钟,才忘记它们之间的差异
public struct Coinage
{
private int _val;
public int Copper
{
get { return _val % 100; }
set { _val += value }
}
public int Silver
{
get { return (_val % 10000) / 100; }
set { _val += value * 100; }
}
public int Gold
{
get { return (_val % 1000000) / 10000; }
set { _val += value * 10000; }
}
public int Platinum
{
get { return (_val % 100000000) / 1000000; }
set { _val += value * 1000000; }
}
}
好的-所以我评论说我会将其存储为一个值,并按照您的需要显示。下面是一个快速而肮脏的实现,让大家了解这个想法。我并没有费心检查负面或优化-只是想让大家了解这个想法
public class Coinage
{
public long Copper { get; set; }
public override string ToString()
{
long change = Copper;
var denominations = new[] {"Gold", "Silver"};
int numberOfDenominations = denominations.Count();
var result = new StringBuilder();
foreach (var denomination in denominations)
{
int coppersToCurrentDenomination = ((int) Math.Pow(100, numberOfDenominations));
long currentAmount = change / coppersToCurrentDenomination;
result.AppendFormat("{0}:{1}", denomination, currentAmount);
change -= (currentAmount * coppersToCurrentDenomination);
numberOfDenominations--;
}
result.AppendFormat("Copper:{0}", change);
return result.ToString();
}
}
我还建议您将
struct
设置为不可变DateTime
是我如何设计它的一个很好的例子
public Coinage AddCopper(int amount)
{
return new Coinage(_value + amount);
}
public Coinage AddSilver(int amount)
{
return new Coinage(_value + (amount * 100));
}
放弃存储最小货币的想法,并在显示时向上计算: 我自己也试过了,下面是我的想法。如果我更加努力,我会创建一个名为“Currency”的单独类,该类包含一个名称和一个铜币值,并使用货币集合,而不是使用字符串数组和铜币值数组。此外,您可以将AddCurrency(int value)方法放在该类中一次,而不是针对每种不同类型的货币将其写出 如果你打算使用更多的货币,我建议你这样做。唯一的问题是确保所有货币都从最有价值到最不有价值
public class Coinage
{
// Total number of liquidated copper coins
private int _value = 0;
// Conversion ratios for each currency type
private const int PLATINUM_VALUE = 1000;
private const int GOLD_VALUE = 100;
private const int SILVER_VALUE = 10;
private const int COPPER_VALUE = 1;
// Array of other denominations
private string[] _currencyNames = { "Platinum", "Gold", "Silver", "Copper" };
private int[] _currencyValues = { PLATINUM_VALUE, GOLD_VALUE, SILVER_VALUE, COPPER_VALUE };
// Constructor
public Coinage(int value)
{
_value = value;
}
public override string ToString()
{
string output = "";
int value = _value;
for (int i = 0; i < _currencyValues.Length; i++)
{
output += string.Format("{0}: " + (value / _currencyValues[i]) + "\n", _currencyNames[i]);
value = value % _currencyValues[i];
}
return output;
}
public void AddCopper(int copper)
{
_value += copper;
}
public void AddSilver(int silver)
{
_value += silver * 10;
}
public void AddGold(int gold)
{
_value += gold * 100;
}
public void AddPlatinum(int platinum)
{
_value += platinum * 1000;
}
}
公共等级硬币
{
//已清算的铜币总数
私有int_值=0;
//每种货币类型的兑换率
私有常数int白金值=1000;
私人建筑的黄金价值=100;
私人建筑单位银价=10;
私有const int铜_值=1;
//其他面额的数组
私有字符串[]_currencyNames={“白金”、“金”、“银”、“铜”};
私有int[]_currencyValues={白金值、黄金值、白银值、铜值};
//建造师
公共货币(整数值)
{
_价值=价值;
}
公共重写字符串ToString()
{
字符串输出=”;
int值=_值;
对于(int i=0;i<\u currencyValues.Length;i++)
{
output+=string.Format(“{0}:”+(value/_currencyValues[i])+“\n”、_currencyNames[i]);
value=value%\u currencyValues[i];
}
返回输出;
}
公共铜(内部铜)
{
_价值+=铜;
}
公共银牌(国际银牌)
{
_价值+=银*10;
}
公共无效添加黄金(整数黄金)
{
_价值+=黄金*100;
}
公共白金(国际白金)
{
_价值+=白金*1000;
}
}
当然,我应该测试输入是否为正,但为了简洁起见,我将保持原样。为什么还要这样存储它?现在,你也必须面对改变。为什么不将其全部存储为最低公分母(铜)并根据需要显示。我会将setter限制在[0,100)独占。您的代码不能很好地处理溢出,处理溢出只会使事情变得复杂。相反,我会创建一个helper函数来获取任意数量的溢出,然后将其分发。Superstringcheese:是的,有时是百分之一百的便士。但肯定是一个特定大小的整数。@Superstringcheese:您是说eLVE是不存在的?!?B-B-B-但是…*世界正在分崩离析*嗯,一个缺陷是它允许0除法。另一个缺陷是它让我看起来像个该死的业余爱好者,但我会让它溜走。它允许0除法在哪里?嗯,这有点苛刻。每个人都从某个地方开始。我个人建议对那些bil使用long
lionaires在那里。我会在mod操作之前进行除法操作,这会让你更清楚地知道你在做什么。我会支持Chaos的建议,在这里使用long。使用int,它将溢出2000多个白金币。我仍然怀疑我可能想在代码的其他地方处理货币(def