diff options
| author | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-10-25 12:30:48 -0300 |
|---|---|---|
| committer | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-10-25 12:30:48 -0300 |
| commit | 6b76d2ff5a3df3931c0983d915eed33e83e892e0 (patch) | |
| tree | e2d959c0d59a6cae412dafee307cbd25b99a1a9d /dice | |
| parent | 80b48c9fcba9c7c9d64501a6e2ac7c5b98fcd1d2 (diff) | |
Move dice to new module
Diffstat (limited to 'dice')
| -rw-r--r-- | dice/pom.xml | 2 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/CompoundDie.java | 60 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/CompoundingDie.java | 115 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/DiceBox.java | 310 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/Die.java | 39 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/DieExpression.java | 69 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/DieList.java | 31 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/ExplodingDice.java | 124 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/FudgeDie.java | 67 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/MathDie.java | 121 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/ScalarDie.java | 47 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/SimpleDie.java | 119 | ||||
| -rw-r--r-- | dice/src/main/java/bjc/dicelang/dice/SimpleDieList.java | 77 |
13 files changed, 1180 insertions, 1 deletions
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 @@ <parent> <groupId>bjc</groupId> - <artifactId>dicelang-Parent</artifactId> + <artifactId>dicelang-parent</artifactId> <version>1.0.0</version> </parent> 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<Long> 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<Long> 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<Long> 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!!<complex-pattern>", 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<Long> 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<Long> 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<Long> 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<Long> 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<Long> 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<Long> 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<Long> 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<Long> 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<Long> 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!<complex-pred>", 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(); + } +} |
