For my game I need to roll some values with a die. A die is formally described as:
nDs[+a]
- n amount of die (optional, 1 of not set, zero must be set explicitly)
D
is a fixed part representing this expression as die- s amount sides of the die/dice
- +a additional amount
examples:
2D6
: 2 six-sided dice, result 2..122D6+1
: 2 six-sided dice + 1, result 3..133D4+2
: 3 four-sided dice + 2, result 5..14
Implementation of class Die
public class Die {
private static final String INPUT_VALIDATION_PATTERN = "^\\d*D\\d*(\\+\\d+)?$";
private static final String SINGLE_DIE_PATTERN = "\\d*D\\d*";
private static final String ADDITION_SEPARATOR_PATTERN = "\\+";
private static final String DIE_SEPARATOR_PATTERN = "D";
private static final String ADD_PATTERN = "\\d*";
private static final String WHITESPACE_PATTERN = "\\s+";
//package visible for testing
final int amount;
final int sides;
final int addition;
public Die(String input) {
String adjusted = input.toUpperCase(Locale.ROOT).replaceAll(WHITESPACE_PATTERN,"");
if (!adjusted.matches(INPUT_VALIDATION_PATTERN)) {
throw new IllegalArgumentException(
"invalid dice input pattern: "+adjusted+" - does not match "+INPUT_VALIDATION_PATTERN);
}
String[] parts = input.split(ADDITION_SEPARATOR_PATTERN);
if(parts[0].matches(SINGLE_DIE_PATTERN)){
String dicePart = parts[0];
int dIndex = dicePart.indexOf(DIE_SEPARATOR_PATTERN);
if(dIndex == 0){
amount = 1;
}else{
amount = Integer.parseInt(dicePart.substring(0,dIndex));
}
sides = Integer.parseInt(dicePart.substring(dIndex+1));
}else{
amount = 0;
sides = 0;
}
if(parts[0].matches(ADD_PATTERN)){
addition = Integer.parseInt(parts[0]);
}else if(parts.length == 2 && parts[1].matches(ADD_PATTERN)){
addition = Integer.parseInt(parts[1]);
}else{
addition = 0;
}
}
public int result(long seed){
Random random = new Random(seed);
int result = 0;
for(int roll = 0; roll < amount; roll++){
result = result + random.nextInt(sides)+1;
}
result = result + addition;
return result;
}
public int result(){
return result(0);
}
public int min(){
return amount + addition;
}
public int max(){
return amount * sides + addition;
}
}
Test Case DieTest
public class DieTest {
@Test
public void singleDieTest(){
Die d6 = new Die("D6");
Assert.assertEquals(6, d6.sides);
Assert.assertEquals(1, d6.amount);
Assert.assertEquals(0,d6.addition);
Assert.assertEquals(1,d6.min());
Assert.assertEquals(6,d6.max());
Die oneD6 = new Die("1D6");
Assert.assertEquals(6, oneD6.sides);
Assert.assertEquals(1, oneD6.amount);
Assert.assertEquals(0,oneD6.addition);
Assert.assertEquals(1,oneD6.min());
Assert.assertEquals(6,oneD6.max());
Die twoD6 = new Die("2D6");
Assert.assertEquals(6, twoD6.sides);
Assert.assertEquals(2, twoD6.amount);
Assert.assertEquals(0,twoD6.addition);
Assert.assertEquals(2,twoD6.min());
Assert.assertEquals(12,twoD6.max());
Die twentyD6 = new Die("20D6");
Assert.assertEquals(6, twentyD6.sides);
Assert.assertEquals(20, twentyD6.amount);
Assert.assertEquals(0,twentyD6.addition);
Assert.assertEquals(20,twentyD6.min());
Assert.assertEquals(120,twentyD6.max());
Die twentyD60 = new Die("20D60");
Assert.assertEquals(60, twentyD60.sides);
Assert.assertEquals(20, twentyD60.amount);
Assert.assertEquals(0,twentyD60.addition);
Assert.assertEquals(20,twentyD60.min());
Assert.assertEquals(1200,twentyD60.max());
}
@Test
public void dieWithSingleAdditionTest(){
Die d6 = new Die("D6+1");
Assert.assertEquals(6, d6.sides);
Assert.assertEquals(1, d6.amount);
Assert.assertEquals(1,d6.addition);
Assert.assertEquals(2,d6.min());
Assert.assertEquals(7,d6.max());
Die oneD6 = new Die("1D6+1");
Assert.assertEquals(6, oneD6.sides);
Assert.assertEquals(1, oneD6.amount);
Assert.assertEquals(1,oneD6.addition);
Assert.assertEquals(2,oneD6.min());
Assert.assertEquals(7,oneD6.max());
Die twoD6 = new Die("2D6+1");
Assert.assertEquals(6, twoD6.sides);
Assert.assertEquals(2, twoD6.amount);
Assert.assertEquals(1,twoD6.addition);
Assert.assertEquals(3,twoD6.min());
Assert.assertEquals(13,twoD6.max());
Die twentyD6 = new Die("20D6+1");
Assert.assertEquals(6, twentyD6.sides);
Assert.assertEquals(20, twentyD6.amount);
Assert.assertEquals(1,twentyD6.addition);
Assert.assertEquals(21,twentyD6.min());
Assert.assertEquals(121,twentyD6.max());
Die twentyD60 = new Die("20D60+1");
Assert.assertEquals(60, twentyD60.sides);
Assert.assertEquals(20, twentyD60.amount);
Assert.assertEquals(1,twentyD60.addition);
Assert.assertEquals(21,twentyD60.min());
Assert.assertEquals(1201,twentyD60.max());
}
@Test
public void dieWithTwoDigitAdditionTest(){
Die d6 = new Die("D6+13");
Assert.assertEquals(6, d6.sides);
Assert.assertEquals(1, d6.amount);
Assert.assertEquals(13,d6.addition);
Assert.assertEquals(14,d6.min());
Assert.assertEquals(19,d6.max());
Die oneD6 = new Die("1D6+13");
Assert.assertEquals(6, oneD6.sides);
Assert.assertEquals(1, oneD6.amount);
Assert.assertEquals(13,oneD6.addition);
Assert.assertEquals(14,oneD6.min());
Assert.assertEquals(19,oneD6.max());
Die twoD6 = new Die("2D6+13");
Assert.assertEquals(6, twoD6.sides);
Assert.assertEquals(2, twoD6.amount);
Assert.assertEquals(13,twoD6.addition);
Assert.assertEquals(15,twoD6.min());
Assert.assertEquals(25,twoD6.max());
Die twentyD6 = new Die("20D6+13");
Assert.assertEquals(6, twentyD6.sides);
Assert.assertEquals(20, twentyD6.amount);
Assert.assertEquals(13,twentyD6.addition);
Assert.assertEquals(33,twentyD6.min());
Assert.assertEquals(133,twentyD6.max());
Die twentyD60 = new Die("20D60+13");
Assert.assertEquals(60, twentyD60.sides);
Assert.assertEquals(20, twentyD60.amount);
Assert.assertEquals(13,twentyD60.addition);
Assert.assertEquals(33,twentyD60.min());
Assert.assertEquals(1213,twentyD60.max());
}
@Test
public void rangeTest(){
Die twoD6plus1 = new Die("2D6+1");
for (int i = 0; i < 100; i++){
Assert.assertTrue(twoD6plus1.result() >= twoD6plus1.min() );
Assert.assertTrue(twoD6plus1.result() <= twoD6plus1.max() );
}
}
}
INPUT_VALIDATION_PATTERN
into regex101 and found that it matches"D"
. This causes a crash when trying to parse thesides
. Are you sure that you need this amount of regex (or regex at all, for that matter)? It looks very overengineered. \$\endgroup\$my ($n,$s,$a)=/(\d+)D(\d+)(?:\+(\d+))/
. \$\endgroup\$