Add random.org for random number generation.

This commit is contained in:
runebaas 2020-09-23 16:05:43 +02:00
parent b743539c74
commit ae9b9caeb9
No known key found for this signature in database
GPG key ID: 2677AF508D0300D6
6 changed files with 62 additions and 12 deletions

View file

@ -46,7 +46,7 @@ namespace Geekbot.Bot.Commands.Utils.Quote
return; return;
} }
var random = _randomNumberGenerator.Next(0, s.Count); var random = _randomNumberGenerator.Next(0, s.Count - 1);
var quote = s[random]; var quote = s[random];
var embed = QuoteBuilder(quote); var embed = QuoteBuilder(quote);

View file

@ -164,7 +164,7 @@ namespace Geekbot.Bot
var emojiConverter = new EmojiConverter(); var emojiConverter = new EmojiConverter();
var mtgManaConverter = new MtgManaConverter(); var mtgManaConverter = new MtgManaConverter();
var wikipediaClient = new WikipediaClient(); var wikipediaClient = new WikipediaClient();
var randomNumberGenerator = new RandomNumberGenerator(); var randomNumberGenerator = new RandomNumberGenerator(_globalSettings);
var mediaProvider = new MediaProvider(_logger, randomNumberGenerator); var mediaProvider = new MediaProvider(_logger, randomNumberGenerator);
var kvMemoryStore = new KvInInMemoryStore(); var kvMemoryStore = new KvInInMemoryStore();
var errorHandler = new ErrorHandler(_logger, _runParameters, () => Localization.Internal.SomethingWentWrong); var errorHandler = new ErrorHandler(_logger, _runParameters, () => Localization.Internal.SomethingWentWrong);

View file

@ -11,6 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Anemonis.RandomOrg" Version="1.14.0" />
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Discord.Net" Version="2.2.0" /> <PackageReference Include="Discord.Net" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.0-rc.1.*" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.0-rc.1.*" />

View file

@ -1,24 +1,68 @@
using System; using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using Anemonis.RandomOrg;
using Geekbot.Core.GlobalSettings;
namespace Geekbot.Core.RandomNumberGenerator namespace Geekbot.Core.RandomNumberGenerator
{ {
public class RandomNumberGenerator : IRandomNumberGenerator public class RandomNumberGenerator : IRandomNumberGenerator
{ {
readonly RNGCryptoServiceProvider csp; private readonly RNGCryptoServiceProvider csp;
private readonly bool _canUseRandomOrg;
private readonly RandomOrgClient _randomOrgClient;
public RandomNumberGenerator() public RandomNumberGenerator(IGlobalSettings globalSettings)
{ {
csp = new RNGCryptoServiceProvider(); csp = new RNGCryptoServiceProvider();
var randomOrgApiKey = globalSettings.GetKey("RandomOrgApiKey");
if (!string.IsNullOrEmpty(randomOrgApiKey))
{
_canUseRandomOrg = true;
_randomOrgClient = new RandomOrgClient(randomOrgApiKey);
}
} }
public int Next(int minValue, int maxExclusiveValue) public int Next(int minValue, int maxInclusiveValue)
{ {
if (minValue >= maxExclusiveValue) if (minValue == maxInclusiveValue)
{
return maxInclusiveValue;
}
if (minValue >= maxInclusiveValue)
{ {
throw new ArgumentOutOfRangeException("minValue must be lower than maxExclusiveValue"); throw new ArgumentOutOfRangeException("minValue must be lower than maxExclusiveValue");
} }
if (_canUseRandomOrg)
{
try
{
return GetFromRandomOrg(minValue, maxInclusiveValue);
}
catch
{
// ignore
}
}
return GetFromCrypto(minValue, maxInclusiveValue);
}
private int GetFromRandomOrg(int minValue, int maxInclusiveValue)
{
return _randomOrgClient
.GenerateIntegersAsync(1, minValue, maxInclusiveValue, false)
.Result
.Random
.Data[0];
}
private int GetFromCrypto(int minValue, int maxInclusiveValue)
{
var maxExclusiveValue = maxInclusiveValue + 1;
var diff = (long) maxExclusiveValue - minValue; var diff = (long) maxExclusiveValue - minValue;
var upperBound = uint.MaxValue / diff * diff; var upperBound = uint.MaxValue / diff * diff;
@ -27,6 +71,7 @@ namespace Geekbot.Core.RandomNumberGenerator
{ {
ui = GetRandomUInt(); ui = GetRandomUInt();
} while (ui >= upperBound); } while (ui >= upperBound);
return (int) (minValue + (ui % diff)); return (int) (minValue + (ui % diff));
} }

View file

@ -1,14 +1,16 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json; using System.Text.Json;
using Geekbot.Core.DiceParser; using Geekbot.Core.DiceParser;
using Geekbot.Core.GlobalSettings;
using Geekbot.Core.RandomNumberGenerator; using Geekbot.Core.RandomNumberGenerator;
using Moq;
using Xunit; using Xunit;
namespace Tests.Core.DiceParser namespace Tests.Core.DiceParser
{ {
public class DiceParserTest public class DiceParserTest
{ {
private static readonly RandomNumberGenerator _randomNumberGenerator = new RandomNumberGenerator(); private static readonly RandomNumberGenerator _randomNumberGenerator = new RandomNumberGenerator(new Mock<IGlobalSettings>().Object);
public struct DiceParserTestDto public struct DiceParserTestDto
{ {

View file

@ -1,5 +1,7 @@
using Geekbot.Core.DiceParser; using Geekbot.Core.DiceParser;
using Geekbot.Core.GlobalSettings;
using Geekbot.Core.RandomNumberGenerator; using Geekbot.Core.RandomNumberGenerator;
using Moq;
using Xunit; using Xunit;
namespace Tests.Core.DiceParser namespace Tests.Core.DiceParser
@ -43,7 +45,7 @@ namespace Tests.Core.DiceParser
[Theory, MemberData(nameof(SingleDieNameTestData))] [Theory, MemberData(nameof(SingleDieNameTestData))]
public void SingleDieNameTestFunc(string testName, SingleDieNameTestDto testData) public void SingleDieNameTestFunc(string testName, SingleDieNameTestDto testData)
{ {
var die = new SingleDie(new RandomNumberGenerator()) {AdvantageType = testData.AdvantageType}; var die = new SingleDie(new RandomNumberGenerator(new Mock<IGlobalSettings>().Object)) {AdvantageType = testData.AdvantageType};
Assert.Equal(die.DiceName, testData.Expected); Assert.Equal(die.DiceName, testData.Expected);
} }
@ -106,7 +108,7 @@ namespace Tests.Core.DiceParser
[Theory, MemberData(nameof(SingleDieValidationTestData))] [Theory, MemberData(nameof(SingleDieValidationTestData))]
public void SingleDieValidationTestFunc(string testName, SingleDieValidationTestDto testData) public void SingleDieValidationTestFunc(string testName, SingleDieValidationTestDto testData)
{ {
var die = new SingleDie(new RandomNumberGenerator()) var die = new SingleDie(new RandomNumberGenerator(new Mock<IGlobalSettings>().Object))
{ {
Amount = testData.Amount, Amount = testData.Amount,
Sides = testData.Sides Sides = testData.Sides