c# - C# 中的功能纯骰子掷

标签 c# random functional-programming

我正在用 C# 编写一个基于骰子的游戏。我希望我所有的游戏逻辑都是纯粹的,所以我设计了一个像这样的掷骰子生成器:

public static IEnumerable<int> CreateDiceStream(int seed)
{
    var random = new Random(seed);

    while (true)
    {
        yield return 1 + random.Next(5);
    }
}

现在我可以在我的游戏逻辑中使用它了:

var playerRolls = players.Zip(diceRolls, (player, roll) => Tuple.Create(player, roll));

问题是,下次我从 diceRolls 中获取时,我想跳过我已经获取的掷骰:

var secondPlayerRolls = players.Zip(
    diceRolls.Skip(playerRolls.Count()), 
    (player, roll) => Tuple.Create(player, roll));

这已经很丑陋并且容易出错。随着代码变得更加复杂,它不能很好地扩展。

这也意味着我在函数之间使用掷骰子序列时必须小心:

var x = DoSomeGameLogic(diceRolls);

var nextRoll = diceRolls.Skip(x.NumberOfDiceRollsUsed).First();

我应该在这里使用一个好的设计模式吗?

请注意,由于同步和回放要求,我的函数保持纯净很重要。


这道题不是关于正确初始化 System.Random 的。请阅读我写的内容,如果不清楚,请发表评论。

最佳答案

这是一个非常好的谜题。

由于操作 diceRolls 的状态是不可能的(否则,我们会遇到您提到的那些同步和重播问题),我们需要一个将 (a) 值返回到被消耗和 (b) 一个新的 diceRolls 可枚举,它在消耗的项目之后开始。

我的建议是使用 (a) 的返回值和 (b) 的输出参数:

static IEnumerable<int> Consume(this IEnumerable<int> rolls, int count, out IEnumerable<int> remainder)
{
    remainder = rolls.Skip(count);
    return rolls.Take(count);
}

用法:

var firstRolls = diceRolls.Consume(players.Count(), out diceRolls);
var secondRolls = diceRolls.Consume(players.Count(), out diceRolls);

DoSomeGameLogic 将在内部使用 Consume 并返回剩余的掷骰。因此,需要按如下方式调用它:

var x = DoSomeGameLogic(diceRolls, out diceRolls);
// or
var x = DoSomeGameLogic(ref diceRolls);
// or
x = DoSomeGameLogic(diceRolls);
diceRolls = x.RemainingDiceRolls;

关于c# - C# 中的功能纯骰子掷,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40794594/

相关文章:

c# - 阻塞的线程可以触发事件吗?

PHP 为网站创建标签系统

python - 生成仅填充值 0 或 1 的随机稀疏矩阵

javascript - 纯函数可以依赖于外部常量吗?

c# - 如何触发 WebHandler 请求的 session 开始 (Global.asax) 事件?

c# - 大文件的 MD5 哈希值会不一致吗?

c# - 使用 Entity Framework 更新记录而不先加载

c# - Random.Next 不起作用?

scala - 应用于 Scala 函数的类型参数

java - Java中的flatMap时是否可以使用组合器函数?