Rewrite the !dice command from scratch
This commit is contained in:
parent
d7e313c9fa
commit
6d44960867
13 changed files with 376 additions and 126 deletions
13
Geekbot.net/Lib/DiceParser/DiceException.cs
Normal file
13
Geekbot.net/Lib/DiceParser/DiceException.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public class DiceException : Exception
|
||||
{
|
||||
public DiceException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public string DiceName { get; set; }
|
||||
}
|
||||
}
|
11
Geekbot.net/Lib/DiceParser/DiceInput.cs
Normal file
11
Geekbot.net/Lib/DiceParser/DiceInput.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public class DiceInput
|
||||
{
|
||||
public List<SingleDie> Dice { get; set; } = new List<SingleDie>();
|
||||
public DiceInputOptions Options { get; set; } = new DiceInputOptions();
|
||||
public int SkillModifier { get; set; }
|
||||
}
|
||||
}
|
7
Geekbot.net/Lib/DiceParser/DiceInputOptions.cs
Normal file
7
Geekbot.net/Lib/DiceParser/DiceInputOptions.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public struct DiceInputOptions
|
||||
{
|
||||
public bool ShowTotal { get; set; }
|
||||
}
|
||||
}
|
102
Geekbot.net/Lib/DiceParser/DiceParser.cs
Normal file
102
Geekbot.net/Lib/DiceParser/DiceParser.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Geekbot.net.Lib.RandomNumberGenerator;
|
||||
|
||||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public class DiceParser : IDiceParser
|
||||
{
|
||||
private readonly IRandomNumberGenerator _randomNumberGenerator;
|
||||
private readonly Regex _inputRegex;
|
||||
private readonly Regex _singleDieRegex;
|
||||
|
||||
public DiceParser(IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
_randomNumberGenerator = randomNumberGenerator;
|
||||
_inputRegex = new Regex(
|
||||
@"((?<DieAdvantage>\+\d+d\d+)|(?<DieDisadvantage>\-\d+d\d+)|(?<DieNormal>\d+d\d+)|(?<Keywords>(total))|(?<SkillModifer>(\+|\-)\d+))\s",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase,
|
||||
new TimeSpan(0, 0, 2));
|
||||
_singleDieRegex = new Regex(
|
||||
@"\d+d\d+",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase,
|
||||
new TimeSpan(0, 0, 0, 0, 500));
|
||||
}
|
||||
|
||||
public DiceInput Parse(string input)
|
||||
{
|
||||
// adding a whitespace at the end, otherwise the parser might pickup on false items
|
||||
var inputWithExtraWhitespace = $"{input} ";
|
||||
|
||||
var matches = _inputRegex.Matches(inputWithExtraWhitespace);
|
||||
var result = new DiceInput();
|
||||
var resultOptions = new DiceInputOptions();
|
||||
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
foreach (Group matchGroup in match.Groups)
|
||||
{
|
||||
if (matchGroup.Success)
|
||||
{
|
||||
switch (matchGroup.Name)
|
||||
{
|
||||
case "DieNormal":
|
||||
result.Dice.Add(Die(matchGroup.Value, DieAdvantageType.None));
|
||||
break;
|
||||
case "DieAdvantage":
|
||||
result.Dice.Add(Die(matchGroup.Value, DieAdvantageType.Advantage));
|
||||
break;
|
||||
case "DieDisadvantage":
|
||||
result.Dice.Add(Die(matchGroup.Value, DieAdvantageType.Disadvantage));
|
||||
break;
|
||||
case "Keywords":
|
||||
Keywords(matchGroup.Value, ref resultOptions);
|
||||
break;
|
||||
case "SkillModifer":
|
||||
result.SkillModifier = SkillModifer(matchGroup.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.Dice.Any())
|
||||
{
|
||||
result.Dice.Add(new SingleDie(_randomNumberGenerator));
|
||||
}
|
||||
|
||||
result.Options = resultOptions;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private SingleDie Die(string match, DieAdvantageType advantageType)
|
||||
{
|
||||
var x = _singleDieRegex.Match(match).Value.Split('d');
|
||||
var die = new SingleDie(_randomNumberGenerator)
|
||||
{
|
||||
Amount = int.Parse(x[0]),
|
||||
Sides = int.Parse(x[1]),
|
||||
AdvantageType = advantageType
|
||||
};
|
||||
die.ValidateDie();
|
||||
return die;
|
||||
}
|
||||
|
||||
private int SkillModifer(string match)
|
||||
{
|
||||
return int.Parse(match);
|
||||
}
|
||||
|
||||
private void Keywords(string match, ref DiceInputOptions options)
|
||||
{
|
||||
switch (match)
|
||||
{
|
||||
case "total":
|
||||
options.ShowTotal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
Geekbot.net/Lib/DiceParser/DieAdvantageType.cs
Normal file
9
Geekbot.net/Lib/DiceParser/DieAdvantageType.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public enum DieAdvantageType
|
||||
{
|
||||
Advantage,
|
||||
Disadvantage,
|
||||
None
|
||||
}
|
||||
}
|
30
Geekbot.net/Lib/DiceParser/DieResult.cs
Normal file
30
Geekbot.net/Lib/DiceParser/DieResult.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public class DieResult
|
||||
{
|
||||
// public int Result { get; set; }
|
||||
public int Roll1 { get; set; }
|
||||
public int Roll2 { get; set; }
|
||||
public DieAdvantageType AdvantageType { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AdvantageType switch
|
||||
{
|
||||
DieAdvantageType.Advantage => Roll1 > Roll2 ? $"(**{Roll1}**, {Roll2})" : $"({Roll1}, **{Roll2}**)",
|
||||
DieAdvantageType.Disadvantage => Roll1 < Roll2 ? $"(**{Roll1}**, {Roll2})" : $"({Roll1}, **{Roll2}**)",
|
||||
_ => Result.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
public int Result => AdvantageType switch
|
||||
{
|
||||
DieAdvantageType.None => Roll1,
|
||||
DieAdvantageType.Advantage => Math.Max(Roll1, Roll2),
|
||||
DieAdvantageType.Disadvantage => Math.Min(Roll1, Roll2),
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
}
|
7
Geekbot.net/Lib/DiceParser/IDiceParser.cs
Normal file
7
Geekbot.net/Lib/DiceParser/IDiceParser.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public interface IDiceParser
|
||||
{
|
||||
DiceInput Parse(string input);
|
||||
}
|
||||
}
|
72
Geekbot.net/Lib/DiceParser/SingleDie.cs
Normal file
72
Geekbot.net/Lib/DiceParser/SingleDie.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using System.Collections.Generic;
|
||||
using Geekbot.net.Lib.Extensions;
|
||||
using Geekbot.net.Lib.RandomNumberGenerator;
|
||||
|
||||
namespace Geekbot.net.Lib.DiceParser
|
||||
{
|
||||
public class SingleDie
|
||||
{
|
||||
private readonly IRandomNumberGenerator _random;
|
||||
|
||||
public SingleDie(IRandomNumberGenerator random)
|
||||
{
|
||||
_random = random;
|
||||
}
|
||||
|
||||
public int Sides { get; set; } = 20;
|
||||
public int Amount { get; set; } = 1;
|
||||
public DieAdvantageType AdvantageType { get; set; } = DieAdvantageType.None;
|
||||
|
||||
public string DiceName => AdvantageType switch
|
||||
{
|
||||
DieAdvantageType.Advantage => $"{Amount}d{Sides} (with advantage)",
|
||||
DieAdvantageType.Disadvantage => $"{Amount}d{Sides} (with disadvantage)",
|
||||
_ => $"{Amount}d{Sides}"
|
||||
};
|
||||
|
||||
public List<DieResult> Roll()
|
||||
{
|
||||
var results = new List<DieResult>();
|
||||
|
||||
Amount.Times(() =>
|
||||
{
|
||||
var result = new DieResult
|
||||
{
|
||||
Roll1 = _random.Next(1, Sides),
|
||||
AdvantageType = AdvantageType
|
||||
};
|
||||
|
||||
if (AdvantageType == DieAdvantageType.Advantage || AdvantageType == DieAdvantageType.Disadvantage)
|
||||
{
|
||||
result.Roll2 = _random.Next(1, Sides);
|
||||
}
|
||||
|
||||
results.Add(result);
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public void ValidateDie()
|
||||
{
|
||||
if (Amount < 1)
|
||||
{
|
||||
throw new DiceException("To few dice, must be a minimum of 1");
|
||||
}
|
||||
if (Amount > 24)
|
||||
{
|
||||
throw new DiceException("To many dice, maximum allowed is 24") { DiceName = DiceName };
|
||||
}
|
||||
|
||||
if (Sides < 2)
|
||||
{
|
||||
throw new DiceException("Die must have at least 2 sides") { DiceName = DiceName };
|
||||
}
|
||||
|
||||
if (Sides > 144)
|
||||
{
|
||||
throw new DiceException("Die can not have more than 144 sides") { DiceName = DiceName };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue