Morgemil can be loosely defined as a roguelike. Indeed, I’ve drawn much inspiration from some roguelikes of which the first I ever played was Angband eight years ago. The largest influence is ToME 2 “Troubles of Middle Earth” which is now mostly defunct and replaced by ToME 4 “Tales of Maj’Eyal”.
Now something most roguelikes have is procedural generation placing the player at the mercy of the Random Number Generator (RNG). This, simply put, determines whether the player lives or dies by creating unique dungeon levels, the level’s population of monsters, and the loot dropped by monsters.
My search’s first action was to check RogueBasin’s article on RNGs which quoted the Mersenne Twister as a good candidate “which produces fast and high-quality random numbers“. This is a sufficient recommendation for me so I then started searching for an implementation. I am fortunate in that there is an existing Mersenne Twister implementation in F# from a reputable source, the Math.NET Numerics library. So armed with the NuGet package I set to work.
module Morgemil.Math.RNG open MathNet.Numerics.Random /// <summary> /// Type alias for a System.Random implementation /// </summary> type DefaultRNG = MersenneTwister let SeedRNG(seed : int) = DefaultRNG seed /// <summary> /// Given an RNG and likelihood, returns the success /// </summary> /// <param name="chance">[0.0, 1.0]</param> let Probability (rng : DefaultRNG) chance = match chance with | 0m -> false | 1m -> true | _ -> (rng.NextDecimal() >= chance)
This is not perfect, but it is at least a placeholder while I figure out what is needed. Testing this is a problem. All I know to test is the edge cases of 0.0 and 1.0.
module Morgemil.Test.RNGTest open Morgemil.Math open NUnit.Framework [<Test>] let ``RNG Probability test``() = let seed = 150 let rng = RNG.SeedRNG seed Assert.IsFalse(RNG.Probability rng 0m) Assert.True(RNG.Probability rng 1m)
With pseudo-random numbers I shall create a world generation equation that will dwarf any amount of creative content I could create myself.