-
Notifications
You must be signed in to change notification settings - Fork 0
/
Dice.cs
226 lines (191 loc) · 7.66 KB
/
Dice.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Discord.Commands;
namespace discordTRPGHelper
{
public class Dice : ModuleBase<SocketCommandContext>
{
private enum ElementType
{
Invaild = 0,
Number,
Dice
};
private Random _rnd; // The dice
private Regex _regexForDice;
public Dice()
{
_rnd = new Random();
_regexForDice = new Regex(@"^\d*[Dd]\d+$");
}
/*
* @brief The slot for the command \"!dice <dice_formula>\"
*
* For the format of the _dice\_formula_, please see Calculate().
*/
[Command("dice")]
[Alias("骰", "請賜予我一發神骰")]
[Summary("Roll the dice")]
public async Task RollTheDice([Remainder][Summary("The dicing formula")]string diceFormula)
{
string result = Calculate(diceFormula);
if (string.IsNullOrEmpty(result))
result = "Bad dice formula";
// Get the user who types the command
var user = Context.User;
await ReplyAsync($"{user.Mention} {result}");
}
/*
* @brief Calculate the dicing result from formula and return the result in string.
*
* The format of _formula_ is "[+/-]<element>(<+/-><element>)*".
* The _formula_ is seperated by '+' or '-', and all the substrings are "element".
* The dice (such as 3D4) and the number (such as 2) are the vaild elements, and
* invaild elements will generate 0.
* The method will return a null string if the input _formula_ contains continous operators.
*
* @param formula Specify the dicing formula, such as "4 + 6D6".
* @return The dicing result in string.
*/
private string Calculate(string formula)
{
StringBuilder outputStr = new StringBuilder();
int finalSum = 0;
/* Parse the dice formula */
// Remove all the spaces in the formula
formula = formula.Replace(" ", string.Empty);
// Get each element from the formula and do corresponding action
char[] searchAny = { '+', '-' };
bool isPlus = true;
// operatorId always is the index of '+' or '-'.
// elementFrom and elementTo mark the substring index of the element.
int operatorId = formula.IndexOfAny(searchAny), elementFrom = 0, elementTo = 0;
// Insert a leading '+'
if (operatorId != 0) {
formula = formula.Insert(0, "+");
}
do {
/* Find the operator */
operatorId = formula.IndexOfAny(searchAny, elementFrom);
isPlus = (formula[operatorId] == '+') ? true : false;
/* Find the element */
elementFrom = operatorId + 1;
operatorId = formula.IndexOfAny(searchAny, elementFrom);
if (operatorId == -1)
elementTo = formula.Length - 1;
else
elementTo = operatorId - 1;
// Invaild: Continous operators
if (operatorId == elementFrom)
return null;
/* Get the element sum */
int[] elementResult = GetElementResult(formula.Substring(elementFrom, elementTo - elementFrom + 1));
int elementSum = 0;
foreach (int i in elementResult)
elementSum += i;
/* Add the final value of the element to the final result */
if (isPlus)
finalSum += elementSum;
else
finalSum -= elementSum;
/* Append the message to the output string */
// Append the operator
if (elementFrom == 1) // Append the leading '-' if needed
outputStr.Append(isPlus ? "" : "-");
else
outputStr.Append(isPlus ? '+' : '-');
// Append the element sum
if (elementResult.Length == 1)
outputStr.Append(elementSum.ToString());
else { // The multiple dice result
outputStr.Append('(');
foreach(int i in elementResult)
outputStr.Append(i.ToString() + "+");
outputStr.Replace('+', ')', outputStr.Length - 1, 1); // Replace the last '+'
}
} while (operatorId != -1);
/* Insert the final result at the begin of the string */
outputStr.Insert(0, finalSum.ToString() + " => ");
return outputStr.ToString();
}
/*
* @brief Get the calculating result from the given element.
*
* If the input element is invaild, the method will return an array
* with only one element '0'.
*
* @param element Specify the content of the element.
* @return An array contains the result of each dice or a number.
*/
private int[] GetElementResult(string element)
{
int[] singleReult = { 0 };
switch (GetElementType(element)) {
// The dice command
case ElementType.Dice:
int[] diceResult = GetDiceResult(element);
// The formula is invaild
if (diceResult == null)
break;
return diceResult;
// The number
case ElementType.Number:
singleReult[0] = int.Parse(element);
break;
// Invaild input
default:
break;
}
return singleReult;
}
/*
* @brief Get the type of the element in the formula
* @param element Specify the content of the element.
* @return The type of the element
*/
private ElementType GetElementType(string element)
{
int i;
if (string.IsNullOrEmpty(element))
return ElementType.Invaild;
if (int.TryParse(element, out i))
return ElementType.Number;
if (_regexForDice.IsMatch(element))
return ElementType.Dice;
return ElementType.Invaild;
}
/*
* @brief Roll the dice.
*
* The format of the _diceCmd_ will be "[number\_of\_dices]<d/D><number\_of\_faces>", which
* the number of dice is 1 when _number\_of\_dices_ is not specified.
*
* @param diceCmd Specify the number and the faces of the dices, such as "3d6" or "4D12".
* @return An array of the dicing result of each dice
* @retval null If the diceCmd is invaild.
*/
private int[] GetDiceResult(string diceCmd)
{
int dices, faces;
/* Parse the diceCmd */
int d = diceCmd.IndexOf("D");
if (d < 0) // Not "D", instead of "d".
d = diceCmd.IndexOf("d");
if (d == 0) // number_of_dices is not specified
dices = 1;
else
dices = int.Parse(diceCmd.Substring(0, d));
faces = int.Parse(diceCmd.Substring(d + 1));
// Check if the value is vaild
if (dices < 1 || faces < 1)
return null;
/* Roll the dice */
int[] diceResult = new int[dices];
for (int i = 0; i < dices; ++i)
diceResult[i] = _rnd.Next(1, faces + 1);
return diceResult;
}
}
}