PCG32:Roguelikes的完美PRNG(2018)

2021-04-26 11:03:17

Roguelikes是游戏,除其他外,还有很多程序生成的内容。要生成内容,我们需要随机数。要获取随机数,我们需要一个伪随机数发生器。

我们不仅想要随机数,但我们想要可预测的随机数!我们希望玩家能够分享种子,看看谁在同样的情况下做得更好,同时每次玩耍时都会让人们完全清新的经历。

我们不能只是用种子创造一个prng并完成。例如,如果我们使用相同的PRNG,例如级别生成和战斗,那么我们的第二级可能会根据玩家进入战斗的次数!

因此,我们需要巧妙地了解我们如何创建和存储我们的PRNG,以确保程序生成的地图不受游戏过程中发生的任何事情的影响。

如果我们使用PCG家族的PRGS,我们的工作很容易。有许多PCG的变体,但在这篇文章中,我将仅引用PCG32,该PCG32将其生成32位无符号整数,并将其状态保持为两个64位无符号整数。

PCG有很多,而且网站做好解释优势,但对于Roguelike开发人员来说,这一切都归结为API。

PCG实例使用不是一个,但两个值:种子和流。种子与PRNG种子的传统想法相匹配,但是流是新的东西:它可以从同一种子产生的多个随机数播出!

对于种子:uint64在[12345,67890] {print("种子:" seed; seed)for seq:uint64在[0,1,2]中{//两个数字:种子和seq让RNG = PCG32Generator(种子:种子,SEQ:SEQ),Let值= [0,1,2,3,4,5,6,7,8,9]。地图({_" \(RNG。获得(上行:100))"})打印("流\(SEQ):"值。加入(分隔符:& #34;,")))} print()}

种子:12345流0:9,26,14,74,3,82,86,75,82,92流1:24,4,3,76,54,90,40,98,15,34流2: 60,17,46,15,6,30,0,68,29,91种子:67890流0:54,68,74,56,1,63,43,47,21,96流1:36,16 ,80,58,36,31,5,14,29,73流2:89,61,71,58,85,14,70,6,59,31

这意味着您可以使用一种种子开始您的游戏,然后在您需要时懒洋洋地为每个级别创建单个PCG实例,而不必担心您的PRNG状态只是通过玩游戏搞砸了。 (还有其他方法可以保证并不困难,但这一个特别方便。)

一旦我们创建了一个PCG实例,我们只需要存储两个无符号整数!序列化是一个捕捉。

想象一下你有一个分支级别的结构。您已经通过正常地牢-30遇到了正常的Dungeon-1,您可能会在5级和15级之间的某处分支为Dwarvish-Mines-1。

不是在游戏开始并提前生成所有级别种子时完全分支如何分支,而是可以创建一个对象,如果级别ID字符串,则基于哈希值基于哈希值创建PCG32实例!

公共类Prngstore:可编码{公共令种子:UINT64私有var rngcache = [uint64:pcg32generator]()private var collisionchecks = [uint64:string]()公共init(种子:uint64){self。 SEED = SEED} // /懒洋洋地根据给定的流公共FUNC获取(流:UINT64) - > PCG32Generator {如果让RNG = rngcache [流] {return rng} {return rng}让rng = pcg32generator(种子:seed,seq:stream)rngcache [stream] = rng return rng} ///通过get基于给定的字符串创建PRNG它的哈希值///和处理哈希碰撞公共Func get(id:string) - > PCG32Generator {var seq = uint64(BitPattern:int64(id. hashvalue)),而CollisionChecks [SEQ]!=零&& CollisionChecks [SEQ]!= ID {打印("&#34之间的碰撞;"和#34;和#34; collisionchecks [seq]?" nil")seq + = 1如果CollisionChecks [SEQ] == NIL {CollisionChecks [SEQ] = ID}如果返回自我。 GET(Stream:SEQ)}公共Func Get(Stream:Uint64) - > PCG32Generator {如果让RNG = RNGCache [流] {return rng}让RNG = PCG32Generator(Seed:Seed,SEQ:Stream)RNGCache [Stream] = RNG Return RNG}}

一旦你有这样的课程,你再也没有想到了何时以及如何生成PRNG实例,因为它只是魔法发生。 使用该示例在本节开始时,您现在可以使用Prngstore [LevelID]的PRNG实例生成任何Dungeon,例如, prngstore ["侏儒 - 矿山-1"]。 PCG32非常容易实现,因此它具有广泛的语言支持,即使算法仅在2014年推出。我自己写了两次实现:Python和Swift。 您应该能够通过搜索网络来找到您喜欢的语言的良好实现。 如果你找不到一个,那真的不是太难了! 这只是一个少数的位运作。