From 6b76d2ff5a3df3931c0983d915eed33e83e892e0 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Wed, 25 Oct 2017 12:30:48 -0300 Subject: Move dice to new module --- base/pom.xml | 12 +- base/src/bjc/dicelang/dice/CompoundDie.java | 60 ---- base/src/bjc/dicelang/dice/CompoundingDie.java | 115 -------- base/src/bjc/dicelang/dice/DiceBox.java | 310 --------------------- base/src/bjc/dicelang/dice/Die.java | 39 --- base/src/bjc/dicelang/dice/DieExpression.java | 69 ----- base/src/bjc/dicelang/dice/DieList.java | 31 --- base/src/bjc/dicelang/dice/ExplodingDice.java | 124 --------- base/src/bjc/dicelang/dice/FudgeDie.java | 67 ----- base/src/bjc/dicelang/dice/MathDie.java | 121 -------- base/src/bjc/dicelang/dice/ScalarDie.java | 47 ---- base/src/bjc/dicelang/dice/SimpleDie.java | 119 -------- base/src/bjc/dicelang/dice/SimpleDieList.java | 77 ----- dice/pom.xml | 2 +- .../main/java/bjc/dicelang/dice/CompoundDie.java | 60 ++++ .../java/bjc/dicelang/dice/CompoundingDie.java | 115 ++++++++ dice/src/main/java/bjc/dicelang/dice/DiceBox.java | 310 +++++++++++++++++++++ dice/src/main/java/bjc/dicelang/dice/Die.java | 39 +++ .../main/java/bjc/dicelang/dice/DieExpression.java | 69 +++++ dice/src/main/java/bjc/dicelang/dice/DieList.java | 31 +++ .../main/java/bjc/dicelang/dice/ExplodingDice.java | 124 +++++++++ dice/src/main/java/bjc/dicelang/dice/FudgeDie.java | 67 +++++ dice/src/main/java/bjc/dicelang/dice/MathDie.java | 121 ++++++++ .../src/main/java/bjc/dicelang/dice/ScalarDie.java | 47 ++++ .../src/main/java/bjc/dicelang/dice/SimpleDie.java | 119 ++++++++ .../main/java/bjc/dicelang/dice/SimpleDieList.java | 77 +++++ pom.xml | 13 + 27 files changed, 1194 insertions(+), 1191 deletions(-) delete mode 100644 base/src/bjc/dicelang/dice/CompoundDie.java delete mode 100644 base/src/bjc/dicelang/dice/CompoundingDie.java delete mode 100644 base/src/bjc/dicelang/dice/DiceBox.java delete mode 100644 base/src/bjc/dicelang/dice/Die.java delete mode 100644 base/src/bjc/dicelang/dice/DieExpression.java delete mode 100644 base/src/bjc/dicelang/dice/DieList.java delete mode 100644 base/src/bjc/dicelang/dice/ExplodingDice.java delete mode 100644 base/src/bjc/dicelang/dice/FudgeDie.java delete mode 100644 base/src/bjc/dicelang/dice/MathDie.java delete mode 100644 base/src/bjc/dicelang/dice/ScalarDie.java delete mode 100644 base/src/bjc/dicelang/dice/SimpleDie.java delete mode 100644 base/src/bjc/dicelang/dice/SimpleDieList.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/CompoundDie.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/CompoundingDie.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/DiceBox.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/Die.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/DieExpression.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/DieList.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/ExplodingDice.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/FudgeDie.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/MathDie.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/ScalarDie.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/SimpleDie.java create mode 100644 dice/src/main/java/bjc/dicelang/dice/SimpleDieList.java diff --git a/base/pom.xml b/base/pom.xml index a8ef779..5f5b6be 100644 --- a/base/pom.xml +++ b/base/pom.xml @@ -3,7 +3,7 @@ bjc - dicelang-Parent + dicelang-parent 1.0.0 @@ -19,16 +19,6 @@ src - - maven-compiler-plugin - 3.7.0 - - - 1.8 - 1.8 - - - org.codehaus.mojo exec-maven-plugin diff --git a/base/src/bjc/dicelang/dice/CompoundDie.java b/base/src/bjc/dicelang/dice/CompoundDie.java deleted file mode 100644 index 0793872..0000000 --- a/base/src/bjc/dicelang/dice/CompoundDie.java +++ /dev/null @@ -1,60 +0,0 @@ -package bjc.dicelang.dice; - -/** - * A die whose rolls result from concatenating two other rolls together. - * - * @author Ben Culkin - */ -public class CompoundDie implements Die { - /* The dice that form this die */ - private final Die left; - private final Die right; - - /** - * Create a new compound die. - * - * @param lft - * The left die - * @param rght - * The right die - */ - public CompoundDie(final Die lft, final Die rght) { - left = lft; - right = rght; - } - - @Override - public boolean canOptimize() { - return left.canOptimize() && right.canOptimize(); - } - - @Override - public long optimize() { - long leftOpt = left.optimize(); - long rightOpt = right.optimize(); - - return Long.parseLong(String.format("%d%d", leftOpt, rightOpt)); - } - - @Override - public long roll() { - long leftRoll = left.optimize(); - long rightRoll = right.optimize(); - - return Long.parseLong(String.format("%d%d", leftRoll, rightRoll)); - } - - @Override - public long rollSingle() { - /* Actually one dice built using two, can't be split. */ - return roll(); - } - - @Override - public String toString() { - String leftString = left.toString(); - String rightString = right.toString(); - - return String.format("%sc%s", leftString, rightString); - } -} diff --git a/base/src/bjc/dicelang/dice/CompoundingDie.java b/base/src/bjc/dicelang/dice/CompoundingDie.java deleted file mode 100644 index 023282e..0000000 --- a/base/src/bjc/dicelang/dice/CompoundingDie.java +++ /dev/null @@ -1,115 +0,0 @@ -package bjc.dicelang.dice; - -import java.util.function.Predicate; - -/** - * Implements a compounding die. - * - * This means that the source will be rolled, and then more single rolls will be - * added while it meets a qualification. - * - * @author Ben Culkin - */ -public class CompoundingDie implements Die { - /* The source die to compound. */ - private final Die source; - - /* The predicate that marks when to compound. */ - private final Predicate compoundOn; - /* The string version of the predicate, if one exists. */ - private final String compoundPattern; - - /** - * Create a new compounding die with no pattern. - * - * @param src - * The die to compound from - * @param compound - * The conditions to compound on - */ - public CompoundingDie(final Die src, final Predicate compound) { - this(src, compound, null); - } - - /** - * Create a new compounding die with a specified pattern. - * - * @param src - * The die to compound from - * @param compound - * The conditions to compound on - * @param patt - * The string pattern the condition came from, for - * printing - */ - public CompoundingDie(final Die src, final Predicate compound, final String patt) { - source = src; - - compoundOn = compound; - compoundPattern = patt; - } - - @Override - public boolean canOptimize() { - if (source.canOptimize()) { - /* We can only be optimized for a result of zero. */ - return source.optimize() == 0; - } - - return false; - } - - @Override - public long optimize() { - /* If we can be optimized, its to zero. */ - return 0; - } - - @Override - public long roll() { - /* The current result. */ - long res = source.roll(); - /* The last result. */ - long oldRes = res; - - while (compoundOn.test(oldRes)) { - /* Compound while the result should be compounded. */ - oldRes = source.rollSingle(); - - /* Accumulate. */ - res += oldRes; - } - - return res; - } - - @Override - public long rollSingle() { - /* Just compound on an initial single role. */ - long res = source.rollSingle(); - /* The last result. */ - long oldRes = res; - - while (compoundOn.test(oldRes)) { - /* Compound while the result should be compounded. */ - oldRes = source.rollSingle(); - - /* Accumulate. */ - res += oldRes; - } - - return res; - } - - @Override - public String toString() { - String sourceString = source.toString(); - - /* Can't print a parseable version. */ - if (compoundPattern == null) { - return String.format("%s!!", sourceString); - } - - return String.format("%s!!%s", sourceString, compoundPattern); - } -} diff --git a/base/src/bjc/dicelang/dice/DiceBox.java b/base/src/bjc/dicelang/dice/DiceBox.java deleted file mode 100644 index 8d00d96..0000000 --- a/base/src/bjc/dicelang/dice/DiceBox.java +++ /dev/null @@ -1,310 +0,0 @@ -package bjc.dicelang.dice; - -import java.util.Random; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -/** - * Contains static methods for producing dice from strings. - * - * @author Ben Culkin - */ -public class DiceBox { - static final Random rng = new Random(); - - /** - * Parse a die expression from a string. - * - * @param expString - * The string to parse. - * - * @return The die expression from the string, or null if it wasn't one - */ - public static DieExpression parseExpression(final String expString) { - try { - return doParseExpression(expString); - } catch (Exception ex) { - /* - * @TODO 10/08/17 Ben Culkin :DieErrors :ErrorRefactor - * Use different types of exceptions to provide - * better error messages. - */ - String exMessage = ex.getMessage(); - - System.out.printf("ERROR: Could not parse die expression (Cause: %s)\n", exMessage); - ex.printStackTrace(); - - return null; - } - } - - private static DieExpression doParseExpression(final String expString) { - /* Only bother with valid expressions. */ - if (!isValidExpression(expString)) { - return null; - } - - if (scalarDiePattern.matcher(expString).matches()) { - /* Parse scalar die. */ - /* @TODO 10/08/17 Ben Culkin :SubstringIndexOf - * This substring/index of call should be - * abstracted into its own method so as to make the - * code more explanatory and ensure that things - * like the return code of indexOf are correctly - * checked. - */ - final String dieString = expString.substring(0, expString.indexOf('s')); - - final long lar = Long.parseLong(dieString); - - final Die scal = new ScalarDie(lar); - - return new DieExpression(scal); - } else if (simpleDiePattern.matcher(expString).matches()) { - /* Parse simple die groups. */ - final String[] dieParts = expString.split("d"); - - final long right = Long.parseLong(dieParts[1]); - final long left; - - if (dieParts[0].equals("")) { - /* Handle short-form expressions. */ - left = 1; - } else { - left = Long.parseLong(dieParts[0]); - } - - final Die scal = new SimpleDie(left, right); - - return new DieExpression(scal); - } else if (fudgeDiePattern.matcher(expString).matches()) { - /* Parse fudge dice. */ - /* :SubstringIndexOf */ - final String nDice = expString.substring(0, expString.indexOf('d')); - final Die fudge = new FudgeDie(Long.parseLong(nDice)); - - return new DieExpression(fudge); - } else if (compoundDiePattern.matcher(expString).matches()) { - /* Parse compound die expressions. */ - final String[] dieParts = expString.split("c"); - - /* @TODO 10/08/17 :SplitParse - * Should this split string/parse split parts be - * abstracted into something else that handles - * doing the splitting correctly, as well as - * making sure that the resulting DieExpressions - * are of the right type? - */ - final DieExpression left = parseExpression(dieParts[0]); - final DieExpression right = parseExpression(dieParts[1]); - - /* :ErrorRefactor */ - if (left.isList) { - System.out.printf("ERROR: Expected a scalar die expression for lhs of compound die, got a list expression instead (%s)\n", - left); - } else if (right.isList) { - System.out.printf("ERROR: Expected a scalar die expression for rhs of compound die, got a list expression instead (%s)\n", - right); - } - - final Die compound = new CompoundDie(left.scalar, right.scalar); - - return new DieExpression(new CompoundDie(left.scalar, right.scalar)); - } else if (compoundingDiePattern.matcher(expString).matches()) { - /* Parse compounding die expressions. */ - final String[] dieParts = expString.split("!!"); - - final DieExpression left = parseExpression(dieParts[0]); - final Predicate right = deriveCond(dieParts[1]); - - final Die scal = new CompoundingDie(left.scalar, right, dieParts[1]); - - return new DieExpression(scal); - } else if (explodingDiePattern.matcher(expString).matches()) { - /* Parse exploding die expressions. */ - final String[] dieParts = expString.split("!"); - - final DieExpression left = parseExpression(dieParts[0]); - final Predicate right = deriveCond(dieParts[1]); - - final DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], false); - - return new DieExpression(lst); - } else if (penetratingDiePattern.matcher(expString).matches()) { - /* Parse penetrating die expressions. */ - final String[] dieParts = expString.split("p!"); - - final DieExpression left = parseExpression(dieParts[0]); - final Predicate right = deriveCond(dieParts[1]); - - final DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], true); - - return new DieExpression(lst); - } else if (diceListPattern.matcher(expString).matches()) { - /* Parse simple die lists. */ - final String[] dieParts = expString.split("dl"); - - final DieExpression left = parseExpression(dieParts[0]); - final DieExpression right = parseExpression(dieParts[1]); - - final DieList lst = new SimpleDieList(left.scalar, right.scalar); - return new DieExpression(lst); - } - - /* Unhandled type of die expression. */ - System.out.printf("INTERNAL ERROR: Valid die expression '%s' not parsed\n", expString); - return null; - } - - /* The strings and patterns used for matching. */ - /* @TODO 10/08/17 Ben Culkin :RegexResource - * These regexes and patterns should be moved to something - * external, probably using the SimpleProperties-based system that - * BJC-Utils2 uses. - */ - /* Defines a comparison predicate. */ - private static final String comparePoint = "[<>=]\\d+"; - - /* - * Defines a scalar die. - * - * This is just a number. - */ - private static final String scalarDie = "[\\+\\-]?\\d+sd"; - private static final Pattern scalarDiePattern = Pattern.compile( - String.format("\\A%s\\Z", scalarDie)); - - /* - * Defines a simple die. - * - * This is a group of one or more dice of the same size. - */ - private static final String simpleDie = "(?:\\d+)?d\\d+"; - private static final Pattern simpleDiePattern = Pattern.compile("\\A" + - simpleDie + "\\Z"); - - /* - * Defines a fudge die. - * - * This is like a simple die, but all the die give -1, 0, or 1 as - * results. - */ - private static final String fudgeDie = "(?:\\d+)?dF"; - private static final Pattern fudgeDiePattern = Pattern.compile("\\A" + fudgeDie + - "\\Z"); - - /* - * Defines a compound die. - * - * This is like using two d10's to simulate a d100 - */ - private static final String compoundDie = simpleDie + "c(?:(?:" + - simpleDie + ")|(?:\\d+))"; - private static final Pattern compoundDiePattern = Pattern.compile("\\A" + - compoundDie + "\\Z"); - - /* - * Defines a compound group. - * - * This is used for forming die list type expressions. - */ - private static final String compoundGroup = "(?:(?:" + scalarDie + ")|(?:" + simpleDie + - ")|(?:" + compoundDie - + ")|(?:" + fudgeDie + "))"; - - /* - * Defines a compounding die. - * - * This is like an exploding die, but is a single die, not a group of - * them. - */ - private static final String compoundingDie = compoundGroup + "!!" + - comparePoint; - private static final Pattern compoundingDiePattern = Pattern.compile("\\A" + - compoundingDie + "\\Z"); - - /* - * Defines an exploding die. - * - * This is a die that you reroll the component of if it meets a certain - * condition. - */ - private static final String explodingDie = compoundGroup + "!" + - comparePoint; - private static final Pattern explodingDiePattern = Pattern.compile("\\A" + - explodingDie + "\\Z"); - - /* - * Defines a penetrating die. - * - * This is like an exploding die, but the exploded result gets a -1 - * penalty. - */ - private static final String penetratingDie = compoundGroup + "!" + - comparePoint; - private static final Pattern penetratingDiePattern = Pattern.compile("\\A" + - penetratingDie + "\\Z"); - - /* - * Defines a die list. - * - * This is an array of dice of the specified size. - */ - private static final String diceList = compoundGroup + "dl" + compoundGroup; - private static final Pattern diceListPattern = Pattern.compile("\\A" + diceList + - "\\Z"); - - /** - * Check if a given string is a valid die expression. - * - * @param exp - * The string to check validity of. - * - * @return Whether or not the string is a valid command. - */ - public static boolean isValidExpression(final String exp) { - /* @NOTE - * Should this matcher/matches expression be abstracted in - * some way? - */ - if (scalarDiePattern.matcher(exp).matches()) { - return true; - } else if (simpleDiePattern.matcher(exp).matches()) { - return true; - } else if (fudgeDiePattern.matcher(exp).matches()) { - return true; - } else if (compoundDiePattern.matcher(exp).matches()) { - return true; - } else if (compoundingDiePattern.matcher(exp).matches()) { - return true; - } else if (explodingDiePattern.matcher(exp).matches()) { - return true; - } else if (penetratingDiePattern.matcher(exp).matches()) { - return true; - } else if (diceListPattern.matcher(exp).matches()) { - return true; - } else { - return false; - } - } - - /* Derive a predicate from a compare point */ - private static Predicate deriveCond(final String patt) { - final long num = Long.parseLong(patt.substring(1)); - - /* @NOTE - * Should this be extended in some way, to provide other - * operators? - */ - switch (patt.charAt(0)) { - case '<': - return (roll) -> (roll < num); - case '=': - return (roll) -> (roll == num); - case '>': - return (roll) -> (roll > num); - default: - return (roll) -> (false); - } - } -} diff --git a/base/src/bjc/dicelang/dice/Die.java b/base/src/bjc/dicelang/dice/Die.java deleted file mode 100644 index 630e8b9..0000000 --- a/base/src/bjc/dicelang/dice/Die.java +++ /dev/null @@ -1,39 +0,0 @@ -package bjc.dicelang.dice; - -/** - * Represents one or more dice that produce a scalar result. - * - * @author Ben Culkin - */ -public interface Die { - /** - * Can this die be optimized to a single number? - * - * @return Whether this die can be optimized or not. - */ - boolean canOptimize(); - - /** - * Optimize this die to a single number. - * - * Calling optimize on dice that return false for canOptimize produces - * undefined behavior - * - * @return The optimized form of this die - */ - long optimize(); - - /** - * Roll this die. - * - * @return A possible roll of this die - */ - long roll(); - - /** - * Roll only a single portion of this die. - * - * @return A possible roll of a single portion of this die. - */ - long rollSingle(); -} diff --git a/base/src/bjc/dicelang/dice/DieExpression.java b/base/src/bjc/dicelang/dice/DieExpression.java deleted file mode 100644 index b8faeda..0000000 --- a/base/src/bjc/dicelang/dice/DieExpression.java +++ /dev/null @@ -1,69 +0,0 @@ -package bjc.dicelang.dice; - -import java.util.Arrays; - -/* - * @NOTE - * I'm not a particularly large fan of sticking everything on this class - * and just documenting which fields are tied together in a non-obvious - * way. I think a class hierarchy might be better, but I am unsure of the - * details. - */ -/** - * Represents either a die or a die list. - * - * @author Ben Culkin - */ -public class DieExpression { - /** Is this expression a list? */ - public final boolean isList; - - /** The scalar value in this expression, if there is one. */ - public Die scalar; - /** The list value in this expression, if there is one. */ - public DieList list; - - /** - * Create a scalar die expression. - * - * @param scal - * The scalar value of this expression. - */ - public DieExpression(final Die scal) { - isList = false; - scalar = scal; - } - - /** - * Create a list die expression. - * - * @param lst - * The list value of this expression. - */ - public DieExpression(final DieList lst) { - isList = true; - list = lst; - } - - @Override - public String toString() { - if (isList) { - return list.toString(); - } - - return scalar.toString(); - } - - /** - * Get the value of this expression as a string. - * - * @return The value of the expression as a string. - */ - public String value() { - if (isList) { - return Arrays.toString(list.roll()); - } - - return Long.toString(scalar.roll()); - } -} diff --git a/base/src/bjc/dicelang/dice/DieList.java b/base/src/bjc/dicelang/dice/DieList.java deleted file mode 100644 index 48006d4..0000000 --- a/base/src/bjc/dicelang/dice/DieList.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.dicelang.dice; - -/** - * Represents a group of dice. - * - * @author Ben Culkin. - */ -public interface DieList { - /** - * Can this list be optimized? - * - * @return Whether or not this list can be optimized. - */ - boolean canOptimize(); - - /** - * Optimize this list, if it can be done. - * - * Invoking this on unoptimizable expression is undefined. - * - * @return The optimized form of this list. - */ - long[] optimize(); - - /** - * Roll this group of dice. - * - * @return A possible roll of this group. - */ - long[] roll(); -} diff --git a/base/src/bjc/dicelang/dice/ExplodingDice.java b/base/src/bjc/dicelang/dice/ExplodingDice.java deleted file mode 100644 index e891a1c..0000000 --- a/base/src/bjc/dicelang/dice/ExplodingDice.java +++ /dev/null @@ -1,124 +0,0 @@ -package bjc.dicelang.dice; - -import java.util.LinkedList; -import java.util.List; -import java.util.function.Predicate; - -/** - * An exploding die. - * - * Represents a die list that keeps getting another added die as long as a - * condition is met. - * - * @author Ben Culkin - */ -public class ExplodingDice implements DieList { - /* The source die to use. */ - private final Die source; - - /* The conditions for exploding. */ - private final Predicate explodeOn; - private final String explodePattern; - /* Whether or not to apply a -1 penalty to explosions. */ - private final boolean explodePenetrates; - - /** - * Create a new exploding die. - * - * @param src - * The source die for exploding. - * @param explode - * The condition to explode on. - */ - public ExplodingDice(final Die src, final Predicate explode) { - this(src, explode, null, false); - } - - /** - * Create a new exploding die that may penetrate. - * - * @param src - * The source die for exploding. - * @param explode - * The condition to explode on. - * @param penetrate - * Whether or not for explosions to penetrate (-1 to - * exploded die). - */ - public ExplodingDice(final Die src, final Predicate explode, - final boolean penetrate) { - this(src, explode, null, penetrate); - } - - /** - * Create a new exploding die that may penetrate. - * - * @param src - * The source die for exploding. - * @param explode - * The condition to explode on. - * @param penetrate - * Whether or not for explosions to penetrate (-1 to - * exploded die). - * @param patt - * The string the condition came from, for printing. - */ - public ExplodingDice(final Die src, final Predicate explode, final String patt, - final boolean penetrate) { - source = src; - explodeOn = explode; - explodePattern = patt; - explodePenetrates = penetrate; - } - - @Override - public boolean canOptimize() { - return false; - } - - @Override - public long[] optimize() { - return new long[0]; - } - - @Override - public long[] roll() { - final long res = source.roll(); - long oldRes = res; - - final List resList = new LinkedList<>(); - resList.add(res); - - while (explodeOn.test(oldRes)) { - oldRes = source.rollSingle(); - - if (explodePenetrates) { - oldRes -= 1; - } - - resList.add(oldRes); - } - - final long resArr[] = new long[resList.size()]; - - int i = 0; - for(long rll : resList) { - resArr[i] = rll; - i += 1; - } - - return resArr; - } - - @Override - public String toString() { - String penString = explodePenetrates ? "p" : ""; - String sourceString = source.toString(); - - if (explodePattern == null) { - return String.format("%s%s!", sourceString, penString); - } - - return String.format("%s%s!%s", sourceString, penString, explodePattern); - } -} diff --git a/base/src/bjc/dicelang/dice/FudgeDie.java b/base/src/bjc/dicelang/dice/FudgeDie.java deleted file mode 100644 index 23951b2..0000000 --- a/base/src/bjc/dicelang/dice/FudgeDie.java +++ /dev/null @@ -1,67 +0,0 @@ -package bjc.dicelang.dice; - -/** - * A fudge die, one that has -1, 0 and 1 as its sides. - * - * @author EVE - * - */ -public class FudgeDie implements Die { - /* The number of dice to roll. */ - private final Die numDice; - - /** - * Create a new fudge die. - * - * @param nDice - * The number of dice to roll. - */ - public FudgeDie(final long nDice) { - numDice = new ScalarDie(nDice); - } - - /** - * Create a new fudge die. - * - * @param nDice - * The number of dice to roll. - */ - public FudgeDie(final Die nDice) { - numDice = nDice; - } - - @Override - public boolean canOptimize() { - return numDice.canOptimize() && numDice.optimize() == 0; - } - - @Override - public long optimize() { - return 0; - } - - @Override - public long roll() { - long res = 0; - - final long nDice = numDice.roll(); - - for (int i = 0; i < nDice; i++) { - res += rollSingle(); - } - - return res; - } - - @Override - public long rollSingle() { - return DiceBox.rng.nextInt(3) - 1; - } - - @Override - public String toString() { - String dieString = numDice.toString(); - - return String.format("%sdF", dieString); - } -} diff --git a/base/src/bjc/dicelang/dice/MathDie.java b/base/src/bjc/dicelang/dice/MathDie.java deleted file mode 100644 index e4f2953..0000000 --- a/base/src/bjc/dicelang/dice/MathDie.java +++ /dev/null @@ -1,121 +0,0 @@ -package bjc.dicelang.dice; - -/** - * A die that represents two dice with an applied math operator. - * - * @author EVE - * - */ -public class MathDie implements Die { - /* - * @TODO 10/08/17 Ben Culkin :MathGeneralize - * Why do we have the operator types hardcoded, instead of just - * having a general thing for applying a binary operator to dice? - * Fix this by changing it to the more general form. - */ - /** - * The types of a math operator. - * - * @author EVE - * - */ - public static enum MathOp { - /** Add two dice. */ - ADD, - /** Subtract two dice. */ - SUBTRACT, - /** Multiply two dice. */ - MULTIPLY; - - @Override - public String toString() { - switch (this) { - case ADD: - return "+"; - - case SUBTRACT: - return "-"; - - case MULTIPLY: - return "*"; - - default: - return this.name(); - } - } - } - - private final MathDie.MathOp type; - - private final Die left; - private final Die right; - - /** - * Create a new math die. - * - * @param op - * The operator to apply. - * - * @param lft - * The left operand. - * - * @param rght - * The right operand. - */ - public MathDie(final MathDie.MathOp op, final Die lft, final Die rght) { - type = op; - - left = lft; - right = rght; - } - - @Override - public boolean canOptimize() { - return left.canOptimize() && right.canOptimize(); - } - - private long performOp(final long lft, final long rght) { - switch (type) { - case ADD: - return lft + rght; - - case SUBTRACT: - return lft - rght; - - case MULTIPLY: - return lft * rght; - - default: - return 0; - } - } - - @Override - public long optimize() { - final long lft = left.optimize(); - final long rght = right.optimize(); - - return performOp(lft, rght); - } - - @Override - public long roll() { - final long lft = left.roll(); - final long rght = right.roll(); - - return performOp(lft, rght); - } - - @Override - public long rollSingle() { - final long lft = left.rollSingle(); - final long rght = right.rollSingle(); - - return performOp(lft, rght); - } - - @Override - public String toString() { - return left.toString() + " " + type.toString() + " " + right.toString(); - } -} diff --git a/base/src/bjc/dicelang/dice/ScalarDie.java b/base/src/bjc/dicelang/dice/ScalarDie.java deleted file mode 100644 index cbe9d3f..0000000 --- a/base/src/bjc/dicelang/dice/ScalarDie.java +++ /dev/null @@ -1,47 +0,0 @@ -package bjc.dicelang.dice; - -/** - * A scalar die, that always returns a given number. - * - * @author EVE - * - */ -public class ScalarDie implements Die { - /* The die value. */ - private final long val; - - /** - * Create a new scalar die with a set value. - * - * @param vl - * The value to use. - */ - public ScalarDie(final long vl) { - val = vl; - } - - @Override - public boolean canOptimize() { - return true; - } - - @Override - public long optimize() { - return val; - } - - @Override - public long roll() { - return val; - } - - @Override - public long rollSingle() { - return val; - } - - @Override - public String toString() { - return String.format("%d", val); - } -} diff --git a/base/src/bjc/dicelang/dice/SimpleDie.java b/base/src/bjc/dicelang/dice/SimpleDie.java deleted file mode 100644 index 6cae423..0000000 --- a/base/src/bjc/dicelang/dice/SimpleDie.java +++ /dev/null @@ -1,119 +0,0 @@ -package bjc.dicelang.dice; - -/** - * A simple group of dice. - * - * @author EVE - * - */ -public class SimpleDie implements Die { - /* The number of dice to roll. */ - private final Die numDice; - /* - * The size of each dice to roll. - * - * Rolled once per role, not once for each dice rolled. - * - * @NOTE - * Would having some way to roll it once for each dice rolled be - * useful in any sort of case? - */ - private final Die diceSize; - - /** - * Create a new dice group. - * - * @param nDice - * The number of dice. - * - * @param size - * The size of the dice. - */ - public SimpleDie(final long nDice, final long size) { - this(new ScalarDie(nDice), new ScalarDie(size)); - } - - /** - * Create a new dice group. - * - * @param nDice - * The number of dice. - * - * @param size - * The size of the dice. - */ - public SimpleDie(final Die nDice, final long size) { - this(nDice, new ScalarDie(size)); - } - - /** - * Create a new dice group. - * - * @param nDice - * The number of dice. - * - * @param size - * The size of the dice. - */ - public SimpleDie(final long nDice, final Die size) { - this(new ScalarDie(nDice), size); - } - - /** - * Create a new dice group. - * - * @param nDice - * The number of dice. - * - * @param size - * The size of the dice. - */ - public SimpleDie(final Die nDice, final Die size) { - numDice = nDice; - diceSize = size; - } - - @Override - public boolean canOptimize() { - if (diceSize.canOptimize() && diceSize.optimize() <= 1) { - return numDice.canOptimize(); - } - - return false; - } - - @Override - public long optimize() { - final long optSize = diceSize.optimize(); - - if (optSize == 0) { - return 0; - } - - return numDice.optimize(); - } - - @Override - public long roll() { - long total = 0; - - final long nDice = numDice.roll(); - final long dSize = diceSize.roll(); - - for (int i = 0; i < nDice; i++) { - total += Math.abs(DiceBox.rng.nextLong()) % dSize + 1; - } - - return total; - } - - @Override - public long rollSingle() { - return Math.abs(DiceBox.rng.nextLong()) % diceSize.roll() + 1; - } - - @Override - public String toString() { - return numDice + "d" + diceSize; - } -} diff --git a/base/src/bjc/dicelang/dice/SimpleDieList.java b/base/src/bjc/dicelang/dice/SimpleDieList.java deleted file mode 100644 index 8391054..0000000 --- a/base/src/bjc/dicelang/dice/SimpleDieList.java +++ /dev/null @@ -1,77 +0,0 @@ -package bjc.dicelang.dice; - -/** - * A simple list of dice. - * - * @author EVE - * - * @TODO 10/08/17 Ben Culkin :DieListGeneralize - * DieList in general should be changed to be able to be - * constructed from an arbitrary die using rollSingle and things - * like that. - */ -public class SimpleDieList implements DieList { - /* The number of dice to roll. */ - private final Die numDice; - /* - * The size of each die to roll. - * - * Checked once per roll, not once per dice rolled. - */ - private final Die size; - - /** - * Create a new list of dice. - * - * @param nDice - * The number of dice in the list. - * - * @param sze - * The size of dice in the list. - */ - public SimpleDieList(final Die nDice, final Die sze) { - numDice = nDice; - size = sze; - } - - @Override - public boolean canOptimize() { - if (size.canOptimize() && size.optimize() <= 1) { - return numDice.canOptimize(); - } - - return false; - } - - @Override - public long[] optimize() { - final int sze = (int) numDice.optimize(); - final long res = size.optimize(); - - final long[] ret = new long[sze]; - - for (int i = 0; i < sze; i++) { - ret[i] = res; - } - - return ret; - } - - @Override - public long[] roll() { - final int num = (int) numDice.roll(); - - final long[] ret = new long[num]; - - for (int i = 0; i < num; i++) { - ret[i] = size.roll(); - } - - return ret; - } - - @Override - public String toString() { - return numDice.toString() + "dl" + size.toString(); - } -} diff --git a/dice/pom.xml b/dice/pom.xml index 9371b96..cf53f8a 100644 --- a/dice/pom.xml +++ b/dice/pom.xml @@ -5,7 +5,7 @@ bjc - dicelang-Parent + dicelang-parent 1.0.0 diff --git a/dice/src/main/java/bjc/dicelang/dice/CompoundDie.java b/dice/src/main/java/bjc/dicelang/dice/CompoundDie.java new file mode 100644 index 0000000..0793872 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/CompoundDie.java @@ -0,0 +1,60 @@ +package bjc.dicelang.dice; + +/** + * A die whose rolls result from concatenating two other rolls together. + * + * @author Ben Culkin + */ +public class CompoundDie implements Die { + /* The dice that form this die */ + private final Die left; + private final Die right; + + /** + * Create a new compound die. + * + * @param lft + * The left die + * @param rght + * The right die + */ + public CompoundDie(final Die lft, final Die rght) { + left = lft; + right = rght; + } + + @Override + public boolean canOptimize() { + return left.canOptimize() && right.canOptimize(); + } + + @Override + public long optimize() { + long leftOpt = left.optimize(); + long rightOpt = right.optimize(); + + return Long.parseLong(String.format("%d%d", leftOpt, rightOpt)); + } + + @Override + public long roll() { + long leftRoll = left.optimize(); + long rightRoll = right.optimize(); + + return Long.parseLong(String.format("%d%d", leftRoll, rightRoll)); + } + + @Override + public long rollSingle() { + /* Actually one dice built using two, can't be split. */ + return roll(); + } + + @Override + public String toString() { + String leftString = left.toString(); + String rightString = right.toString(); + + return String.format("%sc%s", leftString, rightString); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/CompoundingDie.java b/dice/src/main/java/bjc/dicelang/dice/CompoundingDie.java new file mode 100644 index 0000000..023282e --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/CompoundingDie.java @@ -0,0 +1,115 @@ +package bjc.dicelang.dice; + +import java.util.function.Predicate; + +/** + * Implements a compounding die. + * + * This means that the source will be rolled, and then more single rolls will be + * added while it meets a qualification. + * + * @author Ben Culkin + */ +public class CompoundingDie implements Die { + /* The source die to compound. */ + private final Die source; + + /* The predicate that marks when to compound. */ + private final Predicate compoundOn; + /* The string version of the predicate, if one exists. */ + private final String compoundPattern; + + /** + * Create a new compounding die with no pattern. + * + * @param src + * The die to compound from + * @param compound + * The conditions to compound on + */ + public CompoundingDie(final Die src, final Predicate compound) { + this(src, compound, null); + } + + /** + * Create a new compounding die with a specified pattern. + * + * @param src + * The die to compound from + * @param compound + * The conditions to compound on + * @param patt + * The string pattern the condition came from, for + * printing + */ + public CompoundingDie(final Die src, final Predicate compound, final String patt) { + source = src; + + compoundOn = compound; + compoundPattern = patt; + } + + @Override + public boolean canOptimize() { + if (source.canOptimize()) { + /* We can only be optimized for a result of zero. */ + return source.optimize() == 0; + } + + return false; + } + + @Override + public long optimize() { + /* If we can be optimized, its to zero. */ + return 0; + } + + @Override + public long roll() { + /* The current result. */ + long res = source.roll(); + /* The last result. */ + long oldRes = res; + + while (compoundOn.test(oldRes)) { + /* Compound while the result should be compounded. */ + oldRes = source.rollSingle(); + + /* Accumulate. */ + res += oldRes; + } + + return res; + } + + @Override + public long rollSingle() { + /* Just compound on an initial single role. */ + long res = source.rollSingle(); + /* The last result. */ + long oldRes = res; + + while (compoundOn.test(oldRes)) { + /* Compound while the result should be compounded. */ + oldRes = source.rollSingle(); + + /* Accumulate. */ + res += oldRes; + } + + return res; + } + + @Override + public String toString() { + String sourceString = source.toString(); + + /* Can't print a parseable version. */ + if (compoundPattern == null) { + return String.format("%s!!", sourceString); + } + + return String.format("%s!!%s", sourceString, compoundPattern); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/DiceBox.java b/dice/src/main/java/bjc/dicelang/dice/DiceBox.java new file mode 100644 index 0000000..8d00d96 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/DiceBox.java @@ -0,0 +1,310 @@ +package bjc.dicelang.dice; + +import java.util.Random; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +/** + * Contains static methods for producing dice from strings. + * + * @author Ben Culkin + */ +public class DiceBox { + static final Random rng = new Random(); + + /** + * Parse a die expression from a string. + * + * @param expString + * The string to parse. + * + * @return The die expression from the string, or null if it wasn't one + */ + public static DieExpression parseExpression(final String expString) { + try { + return doParseExpression(expString); + } catch (Exception ex) { + /* + * @TODO 10/08/17 Ben Culkin :DieErrors :ErrorRefactor + * Use different types of exceptions to provide + * better error messages. + */ + String exMessage = ex.getMessage(); + + System.out.printf("ERROR: Could not parse die expression (Cause: %s)\n", exMessage); + ex.printStackTrace(); + + return null; + } + } + + private static DieExpression doParseExpression(final String expString) { + /* Only bother with valid expressions. */ + if (!isValidExpression(expString)) { + return null; + } + + if (scalarDiePattern.matcher(expString).matches()) { + /* Parse scalar die. */ + /* @TODO 10/08/17 Ben Culkin :SubstringIndexOf + * This substring/index of call should be + * abstracted into its own method so as to make the + * code more explanatory and ensure that things + * like the return code of indexOf are correctly + * checked. + */ + final String dieString = expString.substring(0, expString.indexOf('s')); + + final long lar = Long.parseLong(dieString); + + final Die scal = new ScalarDie(lar); + + return new DieExpression(scal); + } else if (simpleDiePattern.matcher(expString).matches()) { + /* Parse simple die groups. */ + final String[] dieParts = expString.split("d"); + + final long right = Long.parseLong(dieParts[1]); + final long left; + + if (dieParts[0].equals("")) { + /* Handle short-form expressions. */ + left = 1; + } else { + left = Long.parseLong(dieParts[0]); + } + + final Die scal = new SimpleDie(left, right); + + return new DieExpression(scal); + } else if (fudgeDiePattern.matcher(expString).matches()) { + /* Parse fudge dice. */ + /* :SubstringIndexOf */ + final String nDice = expString.substring(0, expString.indexOf('d')); + final Die fudge = new FudgeDie(Long.parseLong(nDice)); + + return new DieExpression(fudge); + } else if (compoundDiePattern.matcher(expString).matches()) { + /* Parse compound die expressions. */ + final String[] dieParts = expString.split("c"); + + /* @TODO 10/08/17 :SplitParse + * Should this split string/parse split parts be + * abstracted into something else that handles + * doing the splitting correctly, as well as + * making sure that the resulting DieExpressions + * are of the right type? + */ + final DieExpression left = parseExpression(dieParts[0]); + final DieExpression right = parseExpression(dieParts[1]); + + /* :ErrorRefactor */ + if (left.isList) { + System.out.printf("ERROR: Expected a scalar die expression for lhs of compound die, got a list expression instead (%s)\n", + left); + } else if (right.isList) { + System.out.printf("ERROR: Expected a scalar die expression for rhs of compound die, got a list expression instead (%s)\n", + right); + } + + final Die compound = new CompoundDie(left.scalar, right.scalar); + + return new DieExpression(new CompoundDie(left.scalar, right.scalar)); + } else if (compoundingDiePattern.matcher(expString).matches()) { + /* Parse compounding die expressions. */ + final String[] dieParts = expString.split("!!"); + + final DieExpression left = parseExpression(dieParts[0]); + final Predicate right = deriveCond(dieParts[1]); + + final Die scal = new CompoundingDie(left.scalar, right, dieParts[1]); + + return new DieExpression(scal); + } else if (explodingDiePattern.matcher(expString).matches()) { + /* Parse exploding die expressions. */ + final String[] dieParts = expString.split("!"); + + final DieExpression left = parseExpression(dieParts[0]); + final Predicate right = deriveCond(dieParts[1]); + + final DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], false); + + return new DieExpression(lst); + } else if (penetratingDiePattern.matcher(expString).matches()) { + /* Parse penetrating die expressions. */ + final String[] dieParts = expString.split("p!"); + + final DieExpression left = parseExpression(dieParts[0]); + final Predicate right = deriveCond(dieParts[1]); + + final DieList lst = new ExplodingDice(left.scalar, right, dieParts[1], true); + + return new DieExpression(lst); + } else if (diceListPattern.matcher(expString).matches()) { + /* Parse simple die lists. */ + final String[] dieParts = expString.split("dl"); + + final DieExpression left = parseExpression(dieParts[0]); + final DieExpression right = parseExpression(dieParts[1]); + + final DieList lst = new SimpleDieList(left.scalar, right.scalar); + return new DieExpression(lst); + } + + /* Unhandled type of die expression. */ + System.out.printf("INTERNAL ERROR: Valid die expression '%s' not parsed\n", expString); + return null; + } + + /* The strings and patterns used for matching. */ + /* @TODO 10/08/17 Ben Culkin :RegexResource + * These regexes and patterns should be moved to something + * external, probably using the SimpleProperties-based system that + * BJC-Utils2 uses. + */ + /* Defines a comparison predicate. */ + private static final String comparePoint = "[<>=]\\d+"; + + /* + * Defines a scalar die. + * + * This is just a number. + */ + private static final String scalarDie = "[\\+\\-]?\\d+sd"; + private static final Pattern scalarDiePattern = Pattern.compile( + String.format("\\A%s\\Z", scalarDie)); + + /* + * Defines a simple die. + * + * This is a group of one or more dice of the same size. + */ + private static final String simpleDie = "(?:\\d+)?d\\d+"; + private static final Pattern simpleDiePattern = Pattern.compile("\\A" + + simpleDie + "\\Z"); + + /* + * Defines a fudge die. + * + * This is like a simple die, but all the die give -1, 0, or 1 as + * results. + */ + private static final String fudgeDie = "(?:\\d+)?dF"; + private static final Pattern fudgeDiePattern = Pattern.compile("\\A" + fudgeDie + + "\\Z"); + + /* + * Defines a compound die. + * + * This is like using two d10's to simulate a d100 + */ + private static final String compoundDie = simpleDie + "c(?:(?:" + + simpleDie + ")|(?:\\d+))"; + private static final Pattern compoundDiePattern = Pattern.compile("\\A" + + compoundDie + "\\Z"); + + /* + * Defines a compound group. + * + * This is used for forming die list type expressions. + */ + private static final String compoundGroup = "(?:(?:" + scalarDie + ")|(?:" + simpleDie + + ")|(?:" + compoundDie + + ")|(?:" + fudgeDie + "))"; + + /* + * Defines a compounding die. + * + * This is like an exploding die, but is a single die, not a group of + * them. + */ + private static final String compoundingDie = compoundGroup + "!!" + + comparePoint; + private static final Pattern compoundingDiePattern = Pattern.compile("\\A" + + compoundingDie + "\\Z"); + + /* + * Defines an exploding die. + * + * This is a die that you reroll the component of if it meets a certain + * condition. + */ + private static final String explodingDie = compoundGroup + "!" + + comparePoint; + private static final Pattern explodingDiePattern = Pattern.compile("\\A" + + explodingDie + "\\Z"); + + /* + * Defines a penetrating die. + * + * This is like an exploding die, but the exploded result gets a -1 + * penalty. + */ + private static final String penetratingDie = compoundGroup + "!" + + comparePoint; + private static final Pattern penetratingDiePattern = Pattern.compile("\\A" + + penetratingDie + "\\Z"); + + /* + * Defines a die list. + * + * This is an array of dice of the specified size. + */ + private static final String diceList = compoundGroup + "dl" + compoundGroup; + private static final Pattern diceListPattern = Pattern.compile("\\A" + diceList + + "\\Z"); + + /** + * Check if a given string is a valid die expression. + * + * @param exp + * The string to check validity of. + * + * @return Whether or not the string is a valid command. + */ + public static boolean isValidExpression(final String exp) { + /* @NOTE + * Should this matcher/matches expression be abstracted in + * some way? + */ + if (scalarDiePattern.matcher(exp).matches()) { + return true; + } else if (simpleDiePattern.matcher(exp).matches()) { + return true; + } else if (fudgeDiePattern.matcher(exp).matches()) { + return true; + } else if (compoundDiePattern.matcher(exp).matches()) { + return true; + } else if (compoundingDiePattern.matcher(exp).matches()) { + return true; + } else if (explodingDiePattern.matcher(exp).matches()) { + return true; + } else if (penetratingDiePattern.matcher(exp).matches()) { + return true; + } else if (diceListPattern.matcher(exp).matches()) { + return true; + } else { + return false; + } + } + + /* Derive a predicate from a compare point */ + private static Predicate deriveCond(final String patt) { + final long num = Long.parseLong(patt.substring(1)); + + /* @NOTE + * Should this be extended in some way, to provide other + * operators? + */ + switch (patt.charAt(0)) { + case '<': + return (roll) -> (roll < num); + case '=': + return (roll) -> (roll == num); + case '>': + return (roll) -> (roll > num); + default: + return (roll) -> (false); + } + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/Die.java b/dice/src/main/java/bjc/dicelang/dice/Die.java new file mode 100644 index 0000000..630e8b9 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/Die.java @@ -0,0 +1,39 @@ +package bjc.dicelang.dice; + +/** + * Represents one or more dice that produce a scalar result. + * + * @author Ben Culkin + */ +public interface Die { + /** + * Can this die be optimized to a single number? + * + * @return Whether this die can be optimized or not. + */ + boolean canOptimize(); + + /** + * Optimize this die to a single number. + * + * Calling optimize on dice that return false for canOptimize produces + * undefined behavior + * + * @return The optimized form of this die + */ + long optimize(); + + /** + * Roll this die. + * + * @return A possible roll of this die + */ + long roll(); + + /** + * Roll only a single portion of this die. + * + * @return A possible roll of a single portion of this die. + */ + long rollSingle(); +} diff --git a/dice/src/main/java/bjc/dicelang/dice/DieExpression.java b/dice/src/main/java/bjc/dicelang/dice/DieExpression.java new file mode 100644 index 0000000..b8faeda --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/DieExpression.java @@ -0,0 +1,69 @@ +package bjc.dicelang.dice; + +import java.util.Arrays; + +/* + * @NOTE + * I'm not a particularly large fan of sticking everything on this class + * and just documenting which fields are tied together in a non-obvious + * way. I think a class hierarchy might be better, but I am unsure of the + * details. + */ +/** + * Represents either a die or a die list. + * + * @author Ben Culkin + */ +public class DieExpression { + /** Is this expression a list? */ + public final boolean isList; + + /** The scalar value in this expression, if there is one. */ + public Die scalar; + /** The list value in this expression, if there is one. */ + public DieList list; + + /** + * Create a scalar die expression. + * + * @param scal + * The scalar value of this expression. + */ + public DieExpression(final Die scal) { + isList = false; + scalar = scal; + } + + /** + * Create a list die expression. + * + * @param lst + * The list value of this expression. + */ + public DieExpression(final DieList lst) { + isList = true; + list = lst; + } + + @Override + public String toString() { + if (isList) { + return list.toString(); + } + + return scalar.toString(); + } + + /** + * Get the value of this expression as a string. + * + * @return The value of the expression as a string. + */ + public String value() { + if (isList) { + return Arrays.toString(list.roll()); + } + + return Long.toString(scalar.roll()); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/DieList.java b/dice/src/main/java/bjc/dicelang/dice/DieList.java new file mode 100644 index 0000000..48006d4 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/DieList.java @@ -0,0 +1,31 @@ +package bjc.dicelang.dice; + +/** + * Represents a group of dice. + * + * @author Ben Culkin. + */ +public interface DieList { + /** + * Can this list be optimized? + * + * @return Whether or not this list can be optimized. + */ + boolean canOptimize(); + + /** + * Optimize this list, if it can be done. + * + * Invoking this on unoptimizable expression is undefined. + * + * @return The optimized form of this list. + */ + long[] optimize(); + + /** + * Roll this group of dice. + * + * @return A possible roll of this group. + */ + long[] roll(); +} diff --git a/dice/src/main/java/bjc/dicelang/dice/ExplodingDice.java b/dice/src/main/java/bjc/dicelang/dice/ExplodingDice.java new file mode 100644 index 0000000..e891a1c --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/ExplodingDice.java @@ -0,0 +1,124 @@ +package bjc.dicelang.dice; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.Predicate; + +/** + * An exploding die. + * + * Represents a die list that keeps getting another added die as long as a + * condition is met. + * + * @author Ben Culkin + */ +public class ExplodingDice implements DieList { + /* The source die to use. */ + private final Die source; + + /* The conditions for exploding. */ + private final Predicate explodeOn; + private final String explodePattern; + /* Whether or not to apply a -1 penalty to explosions. */ + private final boolean explodePenetrates; + + /** + * Create a new exploding die. + * + * @param src + * The source die for exploding. + * @param explode + * The condition to explode on. + */ + public ExplodingDice(final Die src, final Predicate explode) { + this(src, explode, null, false); + } + + /** + * Create a new exploding die that may penetrate. + * + * @param src + * The source die for exploding. + * @param explode + * The condition to explode on. + * @param penetrate + * Whether or not for explosions to penetrate (-1 to + * exploded die). + */ + public ExplodingDice(final Die src, final Predicate explode, + final boolean penetrate) { + this(src, explode, null, penetrate); + } + + /** + * Create a new exploding die that may penetrate. + * + * @param src + * The source die for exploding. + * @param explode + * The condition to explode on. + * @param penetrate + * Whether or not for explosions to penetrate (-1 to + * exploded die). + * @param patt + * The string the condition came from, for printing. + */ + public ExplodingDice(final Die src, final Predicate explode, final String patt, + final boolean penetrate) { + source = src; + explodeOn = explode; + explodePattern = patt; + explodePenetrates = penetrate; + } + + @Override + public boolean canOptimize() { + return false; + } + + @Override + public long[] optimize() { + return new long[0]; + } + + @Override + public long[] roll() { + final long res = source.roll(); + long oldRes = res; + + final List resList = new LinkedList<>(); + resList.add(res); + + while (explodeOn.test(oldRes)) { + oldRes = source.rollSingle(); + + if (explodePenetrates) { + oldRes -= 1; + } + + resList.add(oldRes); + } + + final long resArr[] = new long[resList.size()]; + + int i = 0; + for(long rll : resList) { + resArr[i] = rll; + i += 1; + } + + return resArr; + } + + @Override + public String toString() { + String penString = explodePenetrates ? "p" : ""; + String sourceString = source.toString(); + + if (explodePattern == null) { + return String.format("%s%s!", sourceString, penString); + } + + return String.format("%s%s!%s", sourceString, penString, explodePattern); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/FudgeDie.java b/dice/src/main/java/bjc/dicelang/dice/FudgeDie.java new file mode 100644 index 0000000..23951b2 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/FudgeDie.java @@ -0,0 +1,67 @@ +package bjc.dicelang.dice; + +/** + * A fudge die, one that has -1, 0 and 1 as its sides. + * + * @author EVE + * + */ +public class FudgeDie implements Die { + /* The number of dice to roll. */ + private final Die numDice; + + /** + * Create a new fudge die. + * + * @param nDice + * The number of dice to roll. + */ + public FudgeDie(final long nDice) { + numDice = new ScalarDie(nDice); + } + + /** + * Create a new fudge die. + * + * @param nDice + * The number of dice to roll. + */ + public FudgeDie(final Die nDice) { + numDice = nDice; + } + + @Override + public boolean canOptimize() { + return numDice.canOptimize() && numDice.optimize() == 0; + } + + @Override + public long optimize() { + return 0; + } + + @Override + public long roll() { + long res = 0; + + final long nDice = numDice.roll(); + + for (int i = 0; i < nDice; i++) { + res += rollSingle(); + } + + return res; + } + + @Override + public long rollSingle() { + return DiceBox.rng.nextInt(3) - 1; + } + + @Override + public String toString() { + String dieString = numDice.toString(); + + return String.format("%sdF", dieString); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/MathDie.java b/dice/src/main/java/bjc/dicelang/dice/MathDie.java new file mode 100644 index 0000000..e4f2953 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/MathDie.java @@ -0,0 +1,121 @@ +package bjc.dicelang.dice; + +/** + * A die that represents two dice with an applied math operator. + * + * @author EVE + * + */ +public class MathDie implements Die { + /* + * @TODO 10/08/17 Ben Culkin :MathGeneralize + * Why do we have the operator types hardcoded, instead of just + * having a general thing for applying a binary operator to dice? + * Fix this by changing it to the more general form. + */ + /** + * The types of a math operator. + * + * @author EVE + * + */ + public static enum MathOp { + /** Add two dice. */ + ADD, + /** Subtract two dice. */ + SUBTRACT, + /** Multiply two dice. */ + MULTIPLY; + + @Override + public String toString() { + switch (this) { + case ADD: + return "+"; + + case SUBTRACT: + return "-"; + + case MULTIPLY: + return "*"; + + default: + return this.name(); + } + } + } + + private final MathDie.MathOp type; + + private final Die left; + private final Die right; + + /** + * Create a new math die. + * + * @param op + * The operator to apply. + * + * @param lft + * The left operand. + * + * @param rght + * The right operand. + */ + public MathDie(final MathDie.MathOp op, final Die lft, final Die rght) { + type = op; + + left = lft; + right = rght; + } + + @Override + public boolean canOptimize() { + return left.canOptimize() && right.canOptimize(); + } + + private long performOp(final long lft, final long rght) { + switch (type) { + case ADD: + return lft + rght; + + case SUBTRACT: + return lft - rght; + + case MULTIPLY: + return lft * rght; + + default: + return 0; + } + } + + @Override + public long optimize() { + final long lft = left.optimize(); + final long rght = right.optimize(); + + return performOp(lft, rght); + } + + @Override + public long roll() { + final long lft = left.roll(); + final long rght = right.roll(); + + return performOp(lft, rght); + } + + @Override + public long rollSingle() { + final long lft = left.rollSingle(); + final long rght = right.rollSingle(); + + return performOp(lft, rght); + } + + @Override + public String toString() { + return left.toString() + " " + type.toString() + " " + right.toString(); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/ScalarDie.java b/dice/src/main/java/bjc/dicelang/dice/ScalarDie.java new file mode 100644 index 0000000..cbe9d3f --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/ScalarDie.java @@ -0,0 +1,47 @@ +package bjc.dicelang.dice; + +/** + * A scalar die, that always returns a given number. + * + * @author EVE + * + */ +public class ScalarDie implements Die { + /* The die value. */ + private final long val; + + /** + * Create a new scalar die with a set value. + * + * @param vl + * The value to use. + */ + public ScalarDie(final long vl) { + val = vl; + } + + @Override + public boolean canOptimize() { + return true; + } + + @Override + public long optimize() { + return val; + } + + @Override + public long roll() { + return val; + } + + @Override + public long rollSingle() { + return val; + } + + @Override + public String toString() { + return String.format("%d", val); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/SimpleDie.java b/dice/src/main/java/bjc/dicelang/dice/SimpleDie.java new file mode 100644 index 0000000..6cae423 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/SimpleDie.java @@ -0,0 +1,119 @@ +package bjc.dicelang.dice; + +/** + * A simple group of dice. + * + * @author EVE + * + */ +public class SimpleDie implements Die { + /* The number of dice to roll. */ + private final Die numDice; + /* + * The size of each dice to roll. + * + * Rolled once per role, not once for each dice rolled. + * + * @NOTE + * Would having some way to roll it once for each dice rolled be + * useful in any sort of case? + */ + private final Die diceSize; + + /** + * Create a new dice group. + * + * @param nDice + * The number of dice. + * + * @param size + * The size of the dice. + */ + public SimpleDie(final long nDice, final long size) { + this(new ScalarDie(nDice), new ScalarDie(size)); + } + + /** + * Create a new dice group. + * + * @param nDice + * The number of dice. + * + * @param size + * The size of the dice. + */ + public SimpleDie(final Die nDice, final long size) { + this(nDice, new ScalarDie(size)); + } + + /** + * Create a new dice group. + * + * @param nDice + * The number of dice. + * + * @param size + * The size of the dice. + */ + public SimpleDie(final long nDice, final Die size) { + this(new ScalarDie(nDice), size); + } + + /** + * Create a new dice group. + * + * @param nDice + * The number of dice. + * + * @param size + * The size of the dice. + */ + public SimpleDie(final Die nDice, final Die size) { + numDice = nDice; + diceSize = size; + } + + @Override + public boolean canOptimize() { + if (diceSize.canOptimize() && diceSize.optimize() <= 1) { + return numDice.canOptimize(); + } + + return false; + } + + @Override + public long optimize() { + final long optSize = diceSize.optimize(); + + if (optSize == 0) { + return 0; + } + + return numDice.optimize(); + } + + @Override + public long roll() { + long total = 0; + + final long nDice = numDice.roll(); + final long dSize = diceSize.roll(); + + for (int i = 0; i < nDice; i++) { + total += Math.abs(DiceBox.rng.nextLong()) % dSize + 1; + } + + return total; + } + + @Override + public long rollSingle() { + return Math.abs(DiceBox.rng.nextLong()) % diceSize.roll() + 1; + } + + @Override + public String toString() { + return numDice + "d" + diceSize; + } +} diff --git a/dice/src/main/java/bjc/dicelang/dice/SimpleDieList.java b/dice/src/main/java/bjc/dicelang/dice/SimpleDieList.java new file mode 100644 index 0000000..8391054 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dice/SimpleDieList.java @@ -0,0 +1,77 @@ +package bjc.dicelang.dice; + +/** + * A simple list of dice. + * + * @author EVE + * + * @TODO 10/08/17 Ben Culkin :DieListGeneralize + * DieList in general should be changed to be able to be + * constructed from an arbitrary die using rollSingle and things + * like that. + */ +public class SimpleDieList implements DieList { + /* The number of dice to roll. */ + private final Die numDice; + /* + * The size of each die to roll. + * + * Checked once per roll, not once per dice rolled. + */ + private final Die size; + + /** + * Create a new list of dice. + * + * @param nDice + * The number of dice in the list. + * + * @param sze + * The size of dice in the list. + */ + public SimpleDieList(final Die nDice, final Die sze) { + numDice = nDice; + size = sze; + } + + @Override + public boolean canOptimize() { + if (size.canOptimize() && size.optimize() <= 1) { + return numDice.canOptimize(); + } + + return false; + } + + @Override + public long[] optimize() { + final int sze = (int) numDice.optimize(); + final long res = size.optimize(); + + final long[] ret = new long[sze]; + + for (int i = 0; i < sze; i++) { + ret[i] = res; + } + + return ret; + } + + @Override + public long[] roll() { + final int num = (int) numDice.roll(); + + final long[] ret = new long[num]; + + for (int i = 0; i < num; i++) { + ret[i] = size.roll(); + } + + return ret; + } + + @Override + public String toString() { + return numDice.toString() + "dl" + size.toString(); + } +} diff --git a/pom.xml b/pom.xml index 3252e76..decd74c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,19 @@ 1.0.0 pom + + + + maven-compiler-plugin + 3.7.0 + + + 1.8 + 1.8 + + + + base dice -- cgit v1.2.3