{"id":929,"date":"2026-06-01T13:57:51","date_gmt":"2026-06-01T13:57:51","guid":{"rendered":"https:\/\/angbandos.skarstech.com\/wiki\/?p=929"},"modified":"2026-06-02T16:10:27","modified_gmt":"2026-06-02T16:10:27","slug":"random-number-generation","status":"publish","type":"post","link":"https:\/\/angbandos.skarstech.com\/wiki\/random-number-generation\/","title":{"rendered":"Random Number Generation"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Title:<\/strong> Why We Switched to a Custom Xoshiro256++ RNG for Our Game<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Content:<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In our ongoing effort to improve save\/load determinism and replay reliability in our game, we recently evaluated the random number generator (RNG) system at the core of our gameplay mechanics. Here\u2019s the detailed reasoning behind the change, what we discovered, and what we decided.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">The Problem with <code>System.Random<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">C# provides a built-in random number generator via <code>System.Random<\/code>. While this generator is statistically high-quality in modern .NET (using <code>xoshiro256**<\/code> on 64-bit systems and <code>xoshiro128**<\/code> on 32-bit), it has a critical limitation for our use case: <strong>its internal state is not accessible<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This creates two major issues for a game like ours:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Save\/Load Determinism<\/strong>: To perfectly reproduce a game state after saving, we need to pause and restore the RNG exactly where it left off. With <code>System.Random<\/code>, there\u2019s no way to capture or restore the internal state, so recreating the same sequence of random events becomes impossible.<\/li>\n\n\n\n<li><strong>Replay\/Debugging<\/strong>: Our game requires deterministic replays for debugging and potential multiplayer lockstep simulation. Without access to the RNG state, reproducing exact sequences of events is not feasible.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Workarounds\u2014like generating a new seed from the RNG and reinitializing it\u2014are possible but cumbersome, error-prone, and inefficient.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Evaluating Alternatives<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We considered several options:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>RNG<\/th><th>Quality<\/th><th>Ease of Serialization<\/th><th>Notes<\/th><\/tr><\/thead><tbody><tr><td>LCG<\/td><td>Fair<\/td><td>Very easy (1 <code>ulong<\/code>)<\/td><td>Simple, but statistically weak<\/td><\/tr><tr><td>PCG32<\/td><td>Excellent<\/td><td>Easy (2 <code>ulong<\/code>s)<\/td><td>Widely used in games, very fast<\/td><\/tr><tr><td>Xoshiro256++<\/td><td>Excellent<\/td><td>Moderate (4 <code>ulong<\/code>s)<\/td><td>Extremely fast, excellent statistical properties, recommended for reproducible simulations<\/td><\/tr><tr><td>System.Random<\/td><td>Excellent<\/td><td>Not accessible<\/td><td>Cannot serialize or restore state, even though underlying algorithm is strong<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">While <code>System.Random<\/code> is statistically solid, it fails the <strong>determinism requirement<\/strong>. LCGs are too weak for long-term simulations. PCG32 and Xoshiro256++ are both excellent candidates.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Decision: Xoshiro256++<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We decided to implement <strong>Xoshiro256++<\/strong> for several reasons:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>High Quality Randomness<\/strong>: Xoshiro256++ provides top-tier statistical randomness, far exceeding the needs of our game, ensuring no patterns or correlations will affect gameplay.<\/li>\n\n\n\n<li><strong>Deterministic State Control<\/strong>: The RNG state consists of four 64-bit integers (<code>ulong s0, s1, s2, s3<\/code>), which can be fully saved and restored. This allows us to perfectly reproduce game states during save\/load or replay.<\/li>\n\n\n\n<li><strong>Performance<\/strong>: Xoshiro256++ is extremely fast\u2014faster than PCG32 in most scenarios\u2014making it suitable for procedural generation, combat rolls, loot drops, and AI decisions without any noticeable impact on performance.<\/li>\n\n\n\n<li><strong>Future-Proof<\/strong>: It supports \u201cjump\u201d and \u201clong jump\u201d features for independent RNG streams, useful if we want separate RNGs for world generation vs. combat vs. AI.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Implementation Highlights<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Seeding<\/strong>: We use <code>SplitMix64<\/code> to expand a single seed into four 64-bit state variables.<\/li>\n\n\n\n<li><strong>Serialization<\/strong>: The state can be saved to a save file and restored exactly, ensuring reproducibility.<\/li>\n\n\n\n<li><strong>Next Functions<\/strong>: The implementation supports <code>Next()<\/code>, <code>Next(max)<\/code>, <code>Next(min, max)<\/code>, and <code>NextDouble()<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">This change ensures that every random number generated in our game is <strong>fully deterministic and reproducible<\/strong>, solving a critical limitation we had with <code>System.Random<\/code> while maintaining excellent performance and statistical quality.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">While <code>System.Random<\/code> uses a modern xoshiro-based algorithm and is statistically excellent, the inability to access or serialize its internal state makes it unsuitable for deterministic game mechanics. By switching to a custom Xoshiro256++ RNG, we now have:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Full control over RNG state<\/li>\n\n\n\n<li>Perfect reproducibility for saves and replays<\/li>\n\n\n\n<li>High-quality randomness without compromise<\/li>\n\n\n\n<li>A fast, future-proof foundation for procedural generation<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">This change allows us to build more reliable and predictable gameplay experiences while maintaining high performance and statistical integrity.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Title: Why We Switched to a Custom Xoshiro256++ RNG for Our Game Content: In our ongoing effort to improve save\/load determinism and replay reliability in&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"footnotes":""},"categories":[70],"tags":[],"class_list":["post-929","post","type-post","status-publish","format-standard","hentry","category-developer-design-notes"],"_links":{"self":[{"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/posts\/929","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/comments?post=929"}],"version-history":[{"count":1,"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/posts\/929\/revisions"}],"predecessor-version":[{"id":930,"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/posts\/929\/revisions\/930"}],"wp:attachment":[{"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/media?parent=929"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/categories?post=929"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/angbandos.skarstech.com\/wiki\/wp-json\/wp\/v2\/tags?post=929"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}