Skip to content

Commit

Permalink
Merge pull request #61 from phi1010/sr5
Browse files Browse the repository at this point in the history
ShadowRun 5 Dice Roll
  • Loading branch information
Phergus authored Aug 29, 2020
2 parents 246482d + d584cb9 commit a63e623
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 17 deletions.
10 changes: 10 additions & 0 deletions src/main/java/net/rptools/common/expression/ExpressionParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ public class ExpressionParser {
new String[] {"\\b(\\d+)[sS][rR]4[gG](\\d+)\\b", "sr4($1, $2)"},
new String[] {"\\b(\\d+)[sS][rR]4\\b", "sr4($1)"},

// Shadowrun 5 Edge or Exploding Test
new String[] {"\\b(\\d+)[sS][rR]5[eE][gG](\\d+)\\b", "sr5e($1, $2)"},
new String[] {"\\b(\\d+)[sS][rR]5[eE]\\b", "sr5e($1)"},

// Shadowrun 5 Normal Test
new String[] {"\\b(\\d+)[sS][rR]5[gG](\\d+)\\b", "sr5($1, $2)"},
new String[] {"\\b(\\d+)[sS][rR]5\\b", "sr5($1)"},

// Subtract X with minimum of Y
new String[] {
"\\b(\\d+)[dD](\\d+)[sS](\\d+)[lL](\\d+)\\b", "rollSubWithLower($1, $2, $3, $4)"
Expand Down Expand Up @@ -188,6 +196,8 @@ public ExpressionParser(String[][] regexpTransforms) {
parser.addFunction(new UbiquityRoll());
parser.addFunction(new ShadowRun4Dice());
parser.addFunction(new ShadowRun4ExplodeDice());
parser.addFunction(new ShadowRun5Dice());
parser.addFunction(new ShadowRun5ExplodeDice());
parser.addFunction(new Roll());
parser.addFunction(new ExplodingSuccessDice());
parser.addFunction(new OpenTestDice());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,22 @@ public static int countSuccessDice(int times, int sides, int success) {
return result;
}

public static String countShadowRun4(int times, int gremlins, boolean explode) {
public enum ShadowrunEdition {
EDITION_4,
EDITION_5
}

public static String countShadowRun(
int poolSize, int gremlins, boolean explode, ShadowrunEdition edition) {
RunData runData = RunData.getCurrent();

int hitCount = 0;
int oneCount = 0;
int sides = 6;
int success = 5;
String actual = "";
String glitch = "";
StringBuilder actual = new StringBuilder();

int times = poolSize;
for (int i = 0; i < times; i++) {
int value = runData.randomInt(sides);

Expand All @@ -231,19 +237,37 @@ public static String countShadowRun4(int times, int gremlins, boolean explode) {

if (value == 6 && explode) times++;

actual = actual + value + " ";
actual.append(value).append(" ");
}

// Check for Glitchs
if (oneCount != 0) {
if ((hitCount == 0) && ((double) times / 2 - gremlins) <= (double) oneCount) {
glitch = " *Critical Glitch*";
} else if ((double) (times / 2 - gremlins) <= (double) oneCount) {
glitch = " *Glitch*";
}
}

String result = "Hits: " + hitCount + " Ones: " + oneCount + glitch + " Results: " + actual;
// TODO check, if there already was a bug here concerning glitches on exploding dice in SR4
// in SR5, Exploding dice are re-rolled and do not increase the pool size tested here
boolean normalGlitch =
edition == ShadowrunEdition.EDITION_4
// SR4: half of pool or more
? ((double) oneCount >= ((double) times / 2))
// SR5: strictly more than half of pool
: ((double) oneCount > ((double) poolSize / 2));

boolean gremlinGlitch =
edition == ShadowrunEdition.EDITION_4
? ((double) oneCount >= ((double) times / 2 - gremlins))
: ((double) oneCount > ((double) poolSize / 2 - gremlins));

boolean noSuccess = hitCount == 0;
// Both Editions: Critical, if no success
String criticalPart = noSuccess ? "Critical " : "";
// Signalize glitches only caused due to gremlins for storytelling
String gremlinPart = (gremlinGlitch ^ normalGlitch) ? "Gremlin " : "";
// but only if this was a glitch.
// if anyone feeds invalid negative gremlin values into this, non-glitches will become gremlin
// glitches.
String glitchFormatted =
(normalGlitch || gremlinGlitch) ? " *" + criticalPart + gremlinPart + "Glitch*" : "";

String result =
"Hits: " + hitCount + " Ones: " + oneCount + glitchFormatted + " Results: " + actual;

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ public Object childEvaluate(
int times = ((BigDecimal) parameters.get(n++)).intValue();
if (parameters.size() == 2) gremlins = ((BigDecimal) parameters.get(n++)).intValue();

return DiceHelper.countShadowRun4(times, gremlins, false);
return DiceHelper.countShadowRun(times, gremlins, false, DiceHelper.ShadowrunEdition.EDITION_4);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ public Object childEvaluate(
int times = ((BigDecimal) parameters.get(n++)).intValue();
if (parameters.size() == 2) gremlins = ((BigDecimal) parameters.get(n++)).intValue();

return DiceHelper.countShadowRun4(times, gremlins, true);
return DiceHelper.countShadowRun(times, gremlins, true, DiceHelper.ShadowrunEdition.EDITION_4);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This software Copyright by the RPTools.net development team, and
* licensed under the Affero GPL Version 3 or, at your option, any later
* version.
*
* MapTool Source Code is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public
* License * along with this source Code. If not, please visit
* <http://www.gnu.org/licenses/> and specifically the Affero license
* text at <http://www.gnu.org/licenses/agpl.html>.
*/
package net.rptools.common.expression.function;

import java.math.BigDecimal;
import java.util.List;
import net.rptools.parser.Parser;
import net.rptools.parser.VariableResolver;
import net.rptools.parser.function.AbstractNumberFunction;
import net.rptools.parser.function.EvaluationException;

public class ShadowRun5Dice extends AbstractNumberFunction {

public ShadowRun5Dice() {
super(1, 2, true, "sr5");
}

@Override
public Object childEvaluate(
Parser parser, VariableResolver resolver, String functionName, List<Object> parameters)
throws EvaluationException {

int n = 0;
int gremlins = 0;
int times = ((BigDecimal) parameters.get(n++)).intValue();
if (parameters.size() == 2) gremlins = ((BigDecimal) parameters.get(n++)).intValue();

return DiceHelper.countShadowRun(times, gremlins, false, DiceHelper.ShadowrunEdition.EDITION_5);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This software Copyright by the RPTools.net development team, and
* licensed under the Affero GPL Version 3 or, at your option, any later
* version.
*
* MapTool Source Code is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public
* License * along with this source Code. If not, please visit
* <http://www.gnu.org/licenses/> and specifically the Affero license
* text at <http://www.gnu.org/licenses/agpl.html>.
*/
package net.rptools.common.expression.function;

import java.math.BigDecimal;
import java.util.List;
import net.rptools.parser.Parser;
import net.rptools.parser.VariableResolver;
import net.rptools.parser.function.AbstractNumberFunction;
import net.rptools.parser.function.EvaluationException;

public class ShadowRun5ExplodeDice extends AbstractNumberFunction {

public ShadowRun5ExplodeDice() {
super(1, 2, true, "sr5e");
}

@Override
public Object childEvaluate(
Parser parser, VariableResolver resolver, String functionName, List<Object> parameters)
throws EvaluationException {

int n = 0;
int gremlins = 0;
int times = ((BigDecimal) parameters.get(n++)).intValue();
if (parameters.size() == 2) gremlins = ((BigDecimal) parameters.get(n++)).intValue();

return DiceHelper.countShadowRun(times, gremlins, true, DiceHelper.ShadowrunEdition.EDITION_5);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public void testEvaluate_SR4GremlinSuccess() throws ParserException {
Result result = new ExpressionParser().evaluate("5sr4g2");
assertEquals("5sr4g2", result.getExpression());
assertEquals("sr4(5, 2)", result.getDetailExpression());
assertEquals("Hits: 1 Ones: 1 *Glitch* Results: 3 1 4 6 3 ", result.getValue());
assertEquals("Hits: 1 Ones: 1 *Gremlin Glitch* Results: 3 1 4 6 3 ", result.getValue());
}

public void testEvaluate_SR4ExplodingSuccess() throws ParserException {
Expand All @@ -152,7 +152,39 @@ public void testEvaluate_SR4ExplodingGremlinSuccess() throws ParserException {
Result result = new ExpressionParser().evaluate("5sr4eg2");
assertEquals("5sr4eg2", result.getExpression());
assertEquals("sr4e(5, 2)", result.getDetailExpression());
assertEquals("Hits: 1 Ones: 2 *Glitch* Results: 3 1 4 6 3 1 ", result.getValue());
assertEquals("Hits: 1 Ones: 2 *Gremlin Glitch* Results: 3 1 4 6 3 1 ", result.getValue());
}

public void testEvaluate_SR5Success() throws ParserException {
RunData.setSeed(10523L);
Result result = new ExpressionParser().evaluate("5sr5");
assertEquals("5sr5", result.getExpression());
assertEquals("sr5(5)", result.getDetailExpression());
assertEquals("Hits: 1 Ones: 1 Results: 3 1 4 6 3 ", result.getValue());
}

public void testEvaluate_SR5GremlinSuccess() throws ParserException {
RunData.setSeed(10523L);
Result result = new ExpressionParser().evaluate("5sr5g2");
assertEquals("5sr5g2", result.getExpression());
assertEquals("sr5(5, 2)", result.getDetailExpression());
assertEquals("Hits: 1 Ones: 1 *Gremlin Glitch* Results: 3 1 4 6 3 ", result.getValue());
}

public void testEvaluate_SR5ExplodingSuccess() throws ParserException {
RunData.setSeed(10523L);
Result result = new ExpressionParser().evaluate("5sr5e");
assertEquals("5sr5e", result.getExpression());
assertEquals("sr5e(5)", result.getDetailExpression());
assertEquals("Hits: 1 Ones: 2 Results: 3 1 4 6 3 1 ", result.getValue());
}

public void testEvaluate_SR5ExplodingGremlinSuccess() throws ParserException {
RunData.setSeed(10523L);
Result result = new ExpressionParser().evaluate("5sr5eg2");
assertEquals("5sr5eg2", result.getExpression());
assertEquals("sr5e(5, 2)", result.getDetailExpression());
assertEquals("Hits: 1 Ones: 2 *Gremlin Glitch* Results: 3 1 4 6 3 1 ", result.getValue());
}

public void testEvaluate_HeroRoll() throws ParserException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,106 @@ private void setUpMockRunData(int[] rolls) {
RunData.setCurrent(mockRD);
}

public void testEvaluate_ShadowRun4NonGlich25() throws ParserException {
int[] rolls = {2, 5};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr4");
assertEquals("Hits: 1 Ones: 0 Results: 2 5 ", result.getValue());
}

public void testEvaluate_ShadowRun4GremlinGlich25() throws ParserException {
int[] rolls = {2, 5};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr4g1");
assertEquals("Hits: 1 Ones: 0 *Gremlin Glitch* Results: 2 5 ", result.getValue());
}

public void testEvaluate_ShadowRun4CriticalGremlinGlich22() throws ParserException {
int[] rolls = {2, 2};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr4g1");
assertEquals("Hits: 0 Ones: 0 *Critical Gremlin Glitch* Results: 2 2 ", result.getValue());
}

public void testEvaluate_ShadowRun4Glitch15() throws ParserException {
int[] rolls = {1, 5};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr4");
assertEquals("Hits: 1 Ones: 1 *Glitch* Results: 1 5 ", result.getValue());
}

public void testEvaluate_ShadowRun4CriticalGlitch12() throws ParserException {
int[] rolls = {1, 2};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr4");
assertEquals("Hits: 0 Ones: 1 *Critical Glitch* Results: 1 2 ", result.getValue());
}

public void testEvaluate_ShadowRun4CriticalGlitch11() throws ParserException {
int[] rolls = {1, 1};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr4");
assertEquals("Hits: 0 Ones: 2 *Critical Glitch* Results: 1 1 ", result.getValue());
}

public void testEvaluate_ShadowRun5NonGlich25() throws ParserException {
int[] rolls = {2, 5};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5");
assertEquals("Hits: 1 Ones: 0 Results: 2 5 ", result.getValue());
}

public void testEvaluate_ShadowRun5NonGlitch15() throws ParserException {
int[] rolls = {1, 5};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5");
assertEquals("Hits: 1 Ones: 1 Results: 1 5 ", result.getValue());
}

public void testEvaluate_ShadowRun5CriticalGlitch11() throws ParserException {
int[] rolls = {1, 1};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5");
assertEquals("Hits: 0 Ones: 2 *Critical Glitch* Results: 1 1 ", result.getValue());
}

public void testEvaluate_ShadowRun5CriticalGremlinGlitch12() throws ParserException {
int[] rolls = {1, 2};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5g1");
assertEquals("Hits: 0 Ones: 1 *Critical Gremlin Glitch* Results: 1 2 ", result.getValue());
}

public void testEvaluate_ShadowRun5GremlinGlitch15() throws ParserException {
int[] rolls = {1, 5};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5g1");
assertEquals("Hits: 1 Ones: 1 *Gremlin Glitch* Results: 1 5 ", result.getValue());
}

public void testEvaluate_ShadowRun5CriticalNonGremlinGlitch11() throws ParserException {
int[] rolls = {1, 1};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5g1");
// This one would have glitched even without gremlins, thus, don't show the gremlin mark
assertEquals("Hits: 0 Ones: 2 *Critical Glitch* Results: 1 1 ", result.getValue());
}

public void testEvaluate_ShadowRun5Glitch61Exploding1() throws ParserException {
int[] rolls = {6, 1, 1};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5e");
assertEquals("Hits: 1 Ones: 2 *Glitch* Results: 6 1 1 ", result.getValue());
}

public void testEvaluate_ShadowRun5Glitch66Exploding6611() throws ParserException {
int[] rolls = {6, 6, 6, 6, 1, 1};
setUpMockRunData(rolls);
Result result = new ExpressionParser().evaluate("2sr5e");
// this is still a glitch.
assertEquals("Hits: 4 Ones: 2 *Glitch* Results: 6 6 6 6 1 1 ", result.getValue());
}

public void testEvaluate_ExplodeWithMockRunData() throws ParserException {
int[] rolls = {3, 6, 6, 2}; // explode both sixes
setUpMockRunData(rolls);
Expand Down

0 comments on commit a63e623

Please sign in to comment.