Remove floating points from map generation

Removes all use of floating point from the RaMapGenerator map generator
and its dependencies.

Floating point behavior is potentially non-portable across client
hardware. Removing them should make the map generation logic
consistent even across clients with different floating point hardware
or compiler behavior. This may be useful for sync-safe multiplayer map
generation where clients independently generate the map from settings.

Most previously fractional public-facing settings are now represented
as numbers out of 1000, with some exceptions using 1000000. Most
internal logic which relies on fixed-point concepts now uses 1024ths,
though some floating point mechanisms have been replaced with
alternative discrete approximations (e.g. gaussian to binomial).
This commit is contained in:
Ashley Newson
2025-01-26 16:45:46 +00:00
committed by Gustas Kažukauskas
parent 04e9cef38e
commit 037326024b
17 changed files with 847 additions and 827 deletions

View File

@@ -11,7 +11,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenRA.Support
{
@@ -35,7 +34,7 @@ namespace OpenRA.Support
}
/// <summary>
/// Produces an unsigned integer between -0x80000000 and 0x7fffffff inclusive.
/// Produces a random unsigned 32-bit integer.
/// </summary>
public uint NextUint()
{
@@ -54,7 +53,7 @@ namespace OpenRA.Support
}
/// <summary>
/// Produces an unsigned integer between -0x80000000 and 0x7fffffff inclusive.
/// Produces a random unsigned 64-bit integer.
/// </summary>
public ulong NextUlong()
{
@@ -98,48 +97,32 @@ namespace OpenRA.Support
}
/// <summary>
/// Produces uniformally distributed random floats between 0 inclusive and 1 exclusive.
/// Note that whilst floats are 32-bit (23-bit mantissa), each output contains exactly 23 bits of entropy.
/// Pick a random index from a list of weights.
/// </summary>
public float NextFloatExclusive()
public int PickWeighted(IReadOnlyList<int> weights)
{
return (NextUint() & 0x7fffff) / (float)0x800000;
}
ulong total = 0;
foreach (var weight in weights)
{
if (weight < 0)
throw new ArgumentException("Found a negative weight.");
total += (ulong)weight;
}
/// <summary>
/// Produces uniformally distributed random doubles between 0 inclusive and 1 exclusive.
/// Note that whilst doubles are 64-bit (52-bit mantissa), each output contains exactly 52 bits of entropy.
/// </summary>
public double NextDoubleExclusive()
{
return (NextUlong() & 0xfffffffffffffL) / (double)0x10000000000000L;
}
if (total == 0)
return Next(0, weights.Count);
/// <summary>
/// Pick a random an index from a list of weights.
/// </summary>
public int PickWeighted(IReadOnlyList<float> weights)
{
var total = weights.Sum();
var spin = NextFloatExclusive() * total;
var spin = NextUlong() % total;
int i;
float acc = 0;
ulong acc = 0;
for (i = 0; i < weights.Count; i++)
{
acc += weights[i];
acc += (ulong)weights[i];
if (spin < acc)
return i;
}
// This might be possible due to floating point precision loss
// (in rare cases). Or we might have been given rubbish
// weights. Return anything > 0.
for (i = 0; i < weights.Count; i++)
if (weights[i] > 0)
return i;
// All <= 0!
return Next(0, weights.Count);
throw new InvalidOperationException("unreachable");
}
/// <summary>