diff options
Diffstat (limited to 'dice')
23 files changed, 376 insertions, 660 deletions
diff --git a/dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java b/dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java index 557fd51..1d6e808 100644 --- a/dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java +++ b/dice/src/example/java/bjc/dicelang/neodice/DieBoxCLI.java @@ -20,61 +20,55 @@ import bjc.funcdata.*; */ public class DieBoxCLI { private static final Pattern INT_PATTERN = Pattern.compile("(?:\\+|-)?\\d+"); - Scanner input; + Scanner input; public PrintStream output; - + public IMap<String, StatementValue> bindings = new FunctionalMap<>(); public Random rng = new Random(); - + static final IMap<String, Command> builtInCommands; static final IMap<String, Command> builtInliterals; - + final IMap<String, Command> commands; final IMap<String, Command> literals; - + private int numStatements = 0; /** * Whether or not to print out a prompt before asking for input */ public boolean doPrompt = true; - + /** * Whether or not to output the results of each command. */ public boolean doOutput = true; - + /** * Should warning messages be printed? */ public boolean doWarn = true; - + static { // Initialize all of our literal-formers builtInliterals = new FunctionalMap<>(); - - builtInliterals.put("void", - new LiteralCommand( - VOID_INST, - "the unique instance of type VOID", - "Returns a reference to the unique instance of type VOID.")); - builtInliterals.put("true", - new LiteralCommand( - TRUE_INST, - "the unique true value of type BOOLEAN", - "Returns a reference to the unique true instance of type BOOLEAN")); - builtInliterals.put("false", - new LiteralCommand( - FALSE_INST, - "the unique false value of type BOOLEAN", - "Returns a reference to the unique false instance of type BOOLEAN")); - + + builtInliterals.put("void", new LiteralCommand(VOID_INST, + "the unique instance of type VOID", + "Returns a reference to the unique instance of type VOID.")); + builtInliterals.put("true", new LiteralCommand(TRUE_INST, + "the unique true value of type BOOLEAN", + "Returns a reference to the unique true instance of type BOOLEAN")); + builtInliterals.put("false", new LiteralCommand(FALSE_INST, + "the unique false value of type BOOLEAN", + "Returns a reference to the unique false instance of type BOOLEAN")); + builtInliterals.deepFreeze(); - - // Initialize all of our built-in commands + + // Initialize all of our built-in commands builtInCommands = new FunctionalMap<>(); - + builtInCommands.put("show-bindings", new ShowBindingsCommand()); builtInCommands.put("bind", new BindCommand()); builtInCommands.put("polyhedral-die", new PolyhedralDieCommand()); @@ -82,26 +76,30 @@ public class DieBoxCLI { builtInCommands.put("help", new HelpCommand()); builtInCommands.deepFreeze(); } - + /** * Create a new CLI for interacting with dice. * - * @param input The place to read input from. - * @param output The place to read output from. + * @param input + * The place to read input from. + * @param output + * The place to read output from. */ public DieBoxCLI(Scanner input, PrintStream output) { - this.input = input; + this.input = input; this.output = output; - + this.commands = builtInCommands.extend(); this.literals = builtInliterals.extend(); } - + /** * Create a new CLI for interacting with dice. * - * @param input The place to read input from. - * @param output The place to read output from. + * @param input + * The place to read input from. + * @param output + * The place to read output from. */ public DieBoxCLI(InputStream input, OutputStream output) { this(new Scanner(input), new PrintStream(output)); @@ -110,45 +108,51 @@ public class DieBoxCLI { /** * Main method. * - * @param args Currently unused CLI arguments. + * @param args + * Currently unused CLI arguments. */ public static void main(String[] args) { - Scanner input = new Scanner(System.in); + Scanner input = new Scanner(System.in); PrintStream output = System.out; - + DieBoxCLI box = new DieBoxCLI(input, output); box.run(); } - private void run() { + private void run() { if (doPrompt) { output.println("diebox CLI - enter 'help' for help, 'exit' to exit"); } - - if (doPrompt) output.printf("diebox(%d)> ", numStatements); - while(input.hasNextLine()) { + + if (doPrompt) + output.printf("diebox(%d)> ", numStatements); + while (input.hasNextLine()) { String nextLine = input.nextLine().trim(); - + numStatements += 1; - - if (nextLine.equals("")) continue; + + if (nextLine.equals("")) + continue; // @FIXME Nov 15th, 2020 Ben Culkin :HardcodeExit // Exit should not be hard-coded like this - if (nextLine.equals("exit")) break; - + if (nextLine.equals("exit")) + break; + String[] lineWords = nextLine.split("\\s+"); Iterator<String> wordIter = new ArrayIterator<>(lineWords); try { StatementValue val = runStatement(wordIter); - - if (doOutput) output.printf("%s%s\n", doPrompt ? "==> " : "", val); + + if (doOutput) + output.printf("%s%s\n", doPrompt ? "==> " : "", + val); } catch (DieBoxException dbex) { output.printf("ERROR (in statement %d): %s\n", numStatements, dbex.getMessage()); Throwable curEx = dbex.getCause(); while (curEx != null) { output.printf("...caused by: %s\n", curEx); - + curEx = dbex.getCause(); } } catch (Exception ex) { @@ -156,10 +160,11 @@ public class DieBoxCLI { numStatements, ex.getMessage()); ex.printStackTrace(output); } - - if (doPrompt) output.printf("diebox(%d)> ", numStatements); + + if (doPrompt) + output.printf("diebox(%d)> ", numStatements); } - + input.close(); output.close(); } @@ -168,52 +173,57 @@ public class DieBoxCLI { if (!words.hasNext()) { return VOID_INST; } - + String command = words.next().trim(); - + DieBoxCLI state = this; + if (command.startsWith("$")) { // All variable refs start with $ String varName = command.substring(1); - - if (bindings.containsKey(varName)) { - return bindings.get(varName); - } else { - // @TODO Nov 15th, 2020 Ben Culkin :Autovars - // Perhaps something along the lines of 'auto-variables' (here - // called 'spring-loaded variables') should be created? These - // would be essentially values which invoke a given expression - // whenever they are referenced. - throw new DieBoxException("Attempted to reference non-existing variable %s", varName); - } + + return bindings.get(varName) + .orElseThrow(() -> new DieBoxException( + "Attempted to reference non-existing variable %s", + varName)); + // @TODO Nov 15th, 2020 Ben Culkin :Autovars + // Perhaps something along the lines of 'auto-variables' (here + // called 'spring-loaded variables') should be created? These + // would be essentially values which invoke a given expression + // whenever they are referenced. } else if (command.startsWith("#")) { // All literals/literal-formers start with # String actualCommand = command.substring(1); - + // Attempt to use a mapped literal/literal-former - Command literalCommand = literals.get(actualCommand); - if (literalCommand != null) { - return literalCommand.execute(words, this); - } else { - if (INT_PATTERN.matcher(actualCommand).matches()) { - try { - int val = Integer.parseInt(actualCommand); - - return new IntegerStatementValue(val); - } catch (NumberFormatException nfex) { - throw new DieBoxException(nfex, "Improper integer literal (%s)", actualCommand); - } - } else { - throw new DieBoxException("Unknown literal format (%s)", actualCommand); - } + StatementValue val = literals.get(actualCommand) + .map((com) -> com.execute(words, state)) + .orElseGet(() -> parseActualLiteral(actualCommand)); + + return val; + } else { + // Attempt to use a mapped command first + StatementValue val = commands.get(command) + .map((com) -> com.execute(words, state)) + .orElseThrow(() -> new DieBoxException( + "Unknown command %s", command)); + return val; + } + } + + private StatementValue parseActualLiteral(String litText) { + if (INT_PATTERN.matcher(litText).matches()) { + try { + int val = Integer.parseInt(litText); + + return new IntegerStatementValue(val); + } catch (NumberFormatException nfex) { + throw new DieBoxException(nfex, + "Improper integer literal (%s)", + litText); } } else { - // Attempt to use a mapped command first - Command mapCommand = commands.get(command); - if (mapCommand != null) { - return mapCommand.execute(words, this); - } else { - throw new DieBoxException("Unknown command %s", command); - } + throw new DieBoxException("Unknown literal format (%s)", + litText); } } }
\ No newline at end of file diff --git a/dice/src/example/java/bjc/dicelang/neodice/commands/PolyhedralDieCommand.java b/dice/src/example/java/bjc/dicelang/neodice/commands/PolyhedralDieCommand.java index 95e9dff..6af495e 100644 --- a/dice/src/example/java/bjc/dicelang/neodice/commands/PolyhedralDieCommand.java +++ b/dice/src/example/java/bjc/dicelang/neodice/commands/PolyhedralDieCommand.java @@ -20,7 +20,11 @@ public class PolyhedralDieCommand implements Command { if (numSides < 0) throw new DieBoxException("Number of sides to polyhedral-die was not valid (must be less than 0, was %d)", numSides); - return new DieStatementValue(Die.polyhedral(numSides)); + IDie<StatementValue> die = IDie + .polyhedral(numSides) + .transform(IntegerStatementValue::new); + + return new DieStatementValue(INTEGER, die); } else { throw new DieBoxException("Number of sides to polyhedral-die wasn't an integer (was %s, of type %s)", sideValue, sideValue.type); diff --git a/dice/src/example/java/bjc/dicelang/neodice/commands/RollCommand.java b/dice/src/example/java/bjc/dicelang/neodice/commands/RollCommand.java index eb7b597..eb8beda 100644 --- a/dice/src/example/java/bjc/dicelang/neodice/commands/RollCommand.java +++ b/dice/src/example/java/bjc/dicelang/neodice/commands/RollCommand.java @@ -18,11 +18,16 @@ public class RollCommand implements Command { if (toRoll.type == DIE) { DieStatementValue die = (DieStatementValue) toRoll; - return new IntegerStatementValue(die.value.roll(state.rng)); + return die.value.roll(state.rng); } else if (toRoll.type == DIEPOOL) { DiePoolStatementValue pool = (DiePoolStatementValue) toRoll; - return new IntArrayStatementValue(pool.value.roll(state.rng)); + StatementValue[] values = pool.value + .roll(state.rng) + .toArray((sz) -> new StatementValue[sz]); + + return new ArrayStatementValue<>(pool.elementType, + values); } else { throw new DieBoxException("Roll was provided something that wasn't rollable (only DIE and DIEPOOL objects are rollable) (was %s, of type %s)", toRoll, toRoll.type); diff --git a/dice/src/example/java/bjc/dicelang/neodice/statements/DiePoolStatementValue.java b/dice/src/example/java/bjc/dicelang/neodice/statements/DiePoolStatementValue.java index a4a9104..551c0e9 100644 --- a/dice/src/example/java/bjc/dicelang/neodice/statements/DiePoolStatementValue.java +++ b/dice/src/example/java/bjc/dicelang/neodice/statements/DiePoolStatementValue.java @@ -7,11 +7,13 @@ import java.util.*; import bjc.dicelang.neodice.*; public class DiePoolStatementValue extends StatementValue { - public final DiePool value; + public final Type elementType; + public final IDiePool<StatementValue> value; - public DiePoolStatementValue(DiePool value) { + public DiePoolStatementValue(Type elementType, IDiePool<StatementValue> value) { super(DIEPOOL); + this.elementType = elementType; this.value = value; } diff --git a/dice/src/example/java/bjc/dicelang/neodice/statements/DieStatementValue.java b/dice/src/example/java/bjc/dicelang/neodice/statements/DieStatementValue.java index 55a6856..e1dbde8 100644 --- a/dice/src/example/java/bjc/dicelang/neodice/statements/DieStatementValue.java +++ b/dice/src/example/java/bjc/dicelang/neodice/statements/DieStatementValue.java @@ -7,11 +7,13 @@ import java.util.*; import bjc.dicelang.neodice.*; public class DieStatementValue extends StatementValue { - public final Die value; + public final Type sideType; + public final IDie<StatementValue> value; - public DieStatementValue(Die value) { + public DieStatementValue(Type sideType, IDie<StatementValue> value) { super(DIE); + this.sideType = sideType; this.value = value; } diff --git a/dice/src/example/java/bjc/dicelang/neodice/statements/IntArrayStatementValue.java b/dice/src/example/java/bjc/dicelang/neodice/statements/IntArrayStatementValue.java deleted file mode 100644 index b313d42..0000000 --- a/dice/src/example/java/bjc/dicelang/neodice/statements/IntArrayStatementValue.java +++ /dev/null @@ -1,50 +0,0 @@ -package bjc.dicelang.neodice.statements; - -import static bjc.dicelang.neodice.statements.StatementValue.Type.*; - -import java.util.*; - -public class IntArrayStatementValue extends StatementValue { - public final int[] values; - - public IntArrayStatementValue(int... values) { - super(INT_ARRAY); - - this.values = values; - } - - @Override - public String toString() { - StringBuilder buffer = new StringBuilder(); - - buffer.append('('); - for (int index = 0; index < values.length; index++) { - int value = values[index]; - - buffer.append(value); - if (index < values.length - 1) buffer.append(", "); - } - buffer.append(')'); - - return buffer.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + Arrays.hashCode(values); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!super.equals(obj)) return false; - if (getClass() != obj.getClass()) return false; - - IntArrayStatementValue other = (IntArrayStatementValue) obj; - - return Arrays.equals(values, other.values); - } -}
\ No newline at end of file diff --git a/dice/src/example/java/bjc/dicelang/neodice/statements/StatementValue.java b/dice/src/example/java/bjc/dicelang/neodice/statements/StatementValue.java index 5090bc7..de796e5 100644 --- a/dice/src/example/java/bjc/dicelang/neodice/statements/StatementValue.java +++ b/dice/src/example/java/bjc/dicelang/neodice/statements/StatementValue.java @@ -8,8 +8,6 @@ public abstract class StatementValue { BOOLEAN, INTEGER, - INT_ARRAY, - DIE, DIEPOOL, diff --git a/dice/src/main/java/bjc/dicelang/neodice/Die.java b/dice/src/main/java/bjc/dicelang/neodice/IDie.java index 9bcbb0f..274af66 100644 --- a/dice/src/main/java/bjc/dicelang/neodice/Die.java +++ b/dice/src/main/java/bjc/dicelang/neodice/IDie.java @@ -13,7 +13,7 @@ import bjc.dicelang.neodice.diepool.*; * */ @FunctionalInterface -public interface Die { +public interface IDie<SideType> { /** * Rolls this die. * @@ -21,7 +21,7 @@ public interface Die { * * @return The result of rolling the die. */ - public int roll(Random rng); + public SideType roll(Random rng); /** * Returns a die pool which rolls this die the specified number of times. @@ -30,8 +30,11 @@ public interface Die { * * @return A die pool that rolls this die the specified number of times. */ - default DiePool times(int numTimes) { - return new TimesDiePool(this, numTimes); + default IDiePool<SideType> times(int numTimes) { + return new ExpandDiePool<>(this, (die, rng) -> { + return Stream.generate(() -> die.roll(rng)) + .limit(numTimes); + }); }; /** @@ -41,8 +44,11 @@ public interface Die { * * @return A die that rerolls when the given condition is met. */ - default Die reroll(IntPredicate condition) { - return new RerollDie(this, condition, (lst) -> lst.get(lst.size())); + default IDie<SideType> reroll( + Comparator<SideType> comparer, + Predicate<SideType> condition) { + return RerollDie.create(comparer, this, condition, + (list) -> list.get(list.size())); } /** @@ -54,21 +60,29 @@ public interface Die { * * @return A die that rerolls when the given condition is met. */ - default Die reroll(IntPredicate condition, int limit) { - return new RerollDie(this, condition, (lst) -> lst.get(lst.size()), limit); + default IDie<SideType> reroll( + Comparator<SideType> comparer, + Predicate<SideType> condition, + int limit) { + return RerollDie.create(comparer, this, condition, + (list) -> list.get(list.size()), limit); } /** - * Create an iterator which gives rolls of this dice. + * Create an stream which gives rolls of this dice. * * @param rng The source for random numbers. * * @return An iterator which gives rolls of this dice. */ - default Iterator<Integer> iterator(Random rng) { - return IntStream.generate(() -> this.roll(rng)).iterator(); + default Stream<SideType> stream(Random rng) { + return Stream.generate(() -> this.roll(rng)); } - + + default <NewType> IDie<NewType> transform(Function<SideType, NewType> mapper) { + return (rng) -> mapper.apply(this.roll(rng)); + } + /** * Create a simple polyhedral die with a fixed number of sides. * @@ -76,7 +90,7 @@ public interface Die { * * @return A die which returns a result from 1 to sides. */ - static Die polyhedral(int sides) { + static IDie<Integer> polyhedral(int sides) { return new PolyhedralDie(sides); } }
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/DiePool.java b/dice/src/main/java/bjc/dicelang/neodice/IDiePool.java index ac35988..b887784 100644 --- a/dice/src/main/java/bjc/dicelang/neodice/DiePool.java +++ b/dice/src/main/java/bjc/dicelang/neodice/IDiePool.java @@ -2,6 +2,7 @@ package bjc.dicelang.neodice; import java.util.*; import java.util.function.*; +import java.util.stream.*; import bjc.dicelang.neodice.diepool.*; @@ -12,30 +13,30 @@ import bjc.dicelang.neodice.diepool.*; * */ @FunctionalInterface -public interface DiePool { +public interface IDiePool<SideType> { /** * Roll each die in the pool, and return the results. * - * Note that this array is not guaranteed to be the same size every time it + * Note that this list is not guaranteed to be the same size every time it * is rolled, because there are some pool types that could add/remove dice. * * @param rng The source for random numbers * * @return The result of rolling each die in the pool. */ - public int[] roll(Random rng); + public Stream<SideType> roll(Random rng); /** * Gets the dice contained in this pool. * - * Note that the length of this array may not be the same as the length of - * the array returned by roll, because certain pool types may add additional + * Note that the length of this list may not be the same as the length of + * the list returned by roll, because certain pool types may add additional * dice. * - * Also note that this array (and the Die instances contained in it) should + * Also note that this list (and the Die instances contained in it) should * not be modified. That may work for certain pool types, but it isn't * guaranteed to work, and can lead to unintuitive behavior. For instances, - * certain pool types may return an array where multiple elements of it refer + * certain pool types may return an list where multiple elements of it refer * to the same Die instance. * * The default implementation throws an UnsupportedOperationException. @@ -44,7 +45,7 @@ public interface DiePool { * * @throws UnsupportedOperationException If the composite dice can't be retrieved. */ - default Die[] contained() { + default List<IDie<SideType>> contained() { throw new UnsupportedOperationException("Can't get composite dice"); } @@ -56,16 +57,18 @@ public interface DiePool { * Returns a version of this die pool which returns its results in sorted * order. * - * At the moment, sorting in descending order is somewhat less efficent than - * sorting in ascending order, because Java doesn't provide a built-in - * descending sort for primitive arrays. - * * @param isDescending True to sort in descending order, false to sort in ascending order. * * @return The die pool, which returns its results in sorted order. */ - default DiePool sorted(boolean isDescending) { - return new SortedDiePool(this, isDescending); + default IDiePool<SideType> sorted( + Comparator<SideType> comparer, + boolean isDescending) { + return new TransformDiePool<>(this, + (pool) -> pool.sorted( + isDescending + ? comparer.reversed() + : comparer)); } /** @@ -76,8 +79,9 @@ public interface DiePool { * * @return A die pool which contains only entries that pass the predicate. */ - default DiePool filtered(IntPredicate matcher) { - return new FilteredDiePool(this, matcher); + default IDiePool<SideType> filtered(Predicate<SideType> matcher) { + return new TransformDiePool<>(this, + (pool) -> pool.filter(matcher)); } /** @@ -87,8 +91,9 @@ public interface DiePool { * * @return A die pool which has the first entries dropped. */ - default DiePool dropFirst(int number) { - return new DropFirstPool(this, number); + default IDiePool<SideType> dropFirst(int number) { + return new TransformDiePool<>(this, + (pool) -> pool.skip(number)); } /** @@ -98,8 +103,16 @@ public interface DiePool { * * @return A die pool which has the last entries dropped. */ - default DiePool dropLast(int number) { - return new DropLastPool(this, number); + default IDiePool<SideType> dropLast(int number) { + return new TransformDiePool<>(this, (pool) -> { + Deque<SideType> temp = new ArrayDeque<>(); + + pool.forEachOrdered((die) -> temp.add(die)); + + for (int i = 0; i < number; i++) temp.pollLast(); + + return temp.stream(); + }); } /** @@ -109,8 +122,9 @@ public interface DiePool { * * @return A die pool which has the first entries kept. */ - default DiePool keepFirst(int number) { - return new KeepFirstDiePool(this, number); + default IDiePool<SideType> keepFirst(int number) { + return new TransformDiePool<>(this, + (pool) -> pool.limit(number)); } /** @@ -120,8 +134,16 @@ public interface DiePool { * * @return A die pool which has the last entries kept. */ - default DiePool keepLast(int number) { - return new KeepLastDiePool(this, number); + default IDiePool<SideType> keepLast(int number) { + return new TransformDiePool<>(this, (pool) -> { + Deque<SideType> temp = new ArrayDeque<>(); + + pool.forEachOrdered((die) -> temp.add(die)); + + while (temp.size() > number) temp.pollFirst(); + + return temp.stream(); + }); } /* @@ -136,8 +158,8 @@ public interface DiePool { * * @return A die pool which has the lowest entries dropped. */ - default DiePool dropLowest(int number) { - return this.sorted(false).dropFirst(number); + default IDiePool<SideType> dropLowest(Comparator<SideType> comparer, int number) { + return this.sorted(comparer, false).dropFirst(number); } /** @@ -147,8 +169,8 @@ public interface DiePool { * * @return A die pool which has the lowest entries dropped. */ - default DiePool dropHighest(int number) { - return this.sorted(false).dropLast(number); + default IDiePool<SideType> dropHighest(Comparator<SideType> comparer,int number) { + return this.sorted(comparer, false).dropLast(number); } /** @@ -158,8 +180,8 @@ public interface DiePool { * * @return A die pool which has the lowest entries kept. */ - default DiePool keepLowest(int number) { - return this.sorted(false).keepFirst(number); + default IDiePool<SideType> keepLowest(Comparator<SideType> comparer,int number) { + return this.sorted(comparer, false).keepFirst(number); } /** @@ -169,8 +191,8 @@ public interface DiePool { * * @return A die pool which has the highest entries kept. */ - default DiePool keepHighest(int number) { - return this.sorted(false).keepLast(number); + default IDiePool<SideType> keepHighest(Comparator<SideType> comparer,int number) { + return this.sorted(comparer, false).keepLast(number); } /* These are misc. operations that don't form new dice pools. */ @@ -182,8 +204,8 @@ public interface DiePool { * * @return An iterator over a single roll of this die pool. */ - default Iterator<Integer> iterator(Random rng) { - return Arrays.stream(this.roll(rng)).iterator(); + default Iterator<SideType> iterator(Random rng) { + return this.roll(rng).iterator(); } /** @@ -193,7 +215,8 @@ public interface DiePool { * * @return A pool which contains the provided dice. */ - static DiePool containing(Die... dice) { - return new FixedDiePool(dice); + @SafeVarargs + static <Side> IDiePool<Side> containing(IDie<Side>... dice) { + return new FixedDiePool<>(dice); } }
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/die/PolyhedralDie.java b/dice/src/main/java/bjc/dicelang/neodice/die/PolyhedralDie.java index c1bb2ac..2909724 100644 --- a/dice/src/main/java/bjc/dicelang/neodice/die/PolyhedralDie.java +++ b/dice/src/main/java/bjc/dicelang/neodice/die/PolyhedralDie.java @@ -4,7 +4,7 @@ import java.util.*; import bjc.dicelang.neodice.*; -public class PolyhedralDie implements Die { +public class PolyhedralDie implements IDie<Integer> { private final int sides; public PolyhedralDie(int sides) { @@ -12,7 +12,7 @@ public class PolyhedralDie implements Die { } @Override - public int roll(Random rng) { + public Integer roll(Random rng) { // Dice are one-based, not zero-based. return rng.nextInt(sides) + 1; } diff --git a/dice/src/main/java/bjc/dicelang/neodice/die/RerollDie.java b/dice/src/main/java/bjc/dicelang/neodice/die/RerollDie.java index 3cd369f..f8cb0fa 100644 --- a/dice/src/main/java/bjc/dicelang/neodice/die/RerollDie.java +++ b/dice/src/main/java/bjc/dicelang/neodice/die/RerollDie.java @@ -6,39 +6,45 @@ import java.util.function.*; import bjc.dicelang.neodice.*; import bjc.esodata.*; -public class RerollDie implements Die { - private final Die contained; +public class RerollDie<SideType> implements IDie<SideType> { + private final IDie<SideType> contained; - private final IntPredicate condition; - private final Function<MinMaxList<Integer>, Integer> chooser; + private final Predicate<SideType> condition; + private final Function<MinMaxList<SideType>, SideType> chooser; - private int limit = Integer.MAX_VALUE; + private final Comparator<SideType> comparer; + private int limit = Integer.MAX_VALUE; - public RerollDie(Die contained, IntPredicate condition, - Function<MinMaxList<Integer>, Integer> chooser) { + private RerollDie( + Comparator<SideType> comparer, + IDie<SideType> contained, + Predicate<SideType> condition, + Function<MinMaxList<SideType>, SideType> chooser) { + this.comparer = comparer; + this.contained = contained; this.condition = condition; this.chooser = chooser; } - public RerollDie(Die contained, IntPredicate condition, - Function<MinMaxList<Integer>, Integer> chooser, int limit) { - this.contained = contained; - - this.condition = condition; - this.chooser = chooser; + private RerollDie( + Comparator<SideType> comparer, + IDie<SideType> contained, + Predicate<SideType> condition, + Function<MinMaxList<SideType>, SideType> chooser, + int limit) { + this(comparer, contained, condition, chooser); this.limit = limit; } @Override - public int roll(Random rng) { - int roll = contained.roll(rng); - - MinMaxList<Integer> newRolls = new MinMaxList<Integer>( - Comparator.naturalOrder(), roll); + public SideType roll(Random rng) { + SideType roll = contained.roll(rng); + + MinMaxList<SideType> newRolls = new MinMaxList<>(comparer, roll); int rerollCount = 0; while (condition.test(roll) && rerollCount < limit) { @@ -51,5 +57,36 @@ public class RerollDie implements Die { return chooser.apply(newRolls); } - // No toString, because IntPredicate can't be converted to a string + public static <Side extends Comparable<Side>> IDie<Side> create( + IDie<Side> contained, + Predicate<Side> condition, + Function<MinMaxList<Side>, Side> chooser) { + return new RerollDie<>(Comparator.naturalOrder(), contained, condition, chooser); + } + + public static <Side extends Comparable<Side>> IDie<Side> create( + IDie<Side> contained, + Predicate<Side> condition, + Function<MinMaxList<Side>, Side> chooser, + int limit) { + return new RerollDie<>(Comparator.naturalOrder(), contained, condition, chooser, limit); + } + + + public static <Side> IDie<Side> create( + Comparator<Side> comparer, + IDie<Side> contained, + Predicate<Side> condition, + Function<MinMaxList<Side>, Side> chooser) { + return new RerollDie<Side>(comparer, contained, condition, chooser); + } + + public static <Side> IDie<Side> create( + Comparator<Side> comparer, + IDie<Side> contained, + Predicate<Side> condition, + Function<MinMaxList<Side>, Side> chooser, + int limit) { + return new RerollDie<Side>(comparer, contained, condition, chooser, limit); + } }
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/DropFirstPool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/DropFirstPool.java deleted file mode 100644 index 959678b..0000000 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/DropFirstPool.java +++ /dev/null @@ -1,58 +0,0 @@ -package bjc.dicelang.neodice.diepool; - -import java.util.*; - -import bjc.dicelang.neodice.*; - -public class DropFirstPool implements DiePool { - private final int number; - private final DiePool pool; - - public DropFirstPool(DiePool pool, int number) { - this.pool = pool; - this.number = number; - } - - @Override - public int[] roll(Random rng) { - int[] rolls = pool.roll(rng); - - if (number >= rolls.length) { - return new int[0]; - } else { - int[] newRolls = new int[rolls.length - number]; - - for (int index = number - 1; index < rolls.length; index++) { - newRolls[index - number] = rolls[index]; - } - - return newRolls; - } - } - - @Override - public Die[] contained() { - return pool.contained(); - } - - @Override - public String toString() { - return String.format("%sdF%d", pool, number); - } - - @Override - public int hashCode() { - return Objects.hash(number, pool); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - DropFirstPool other = (DropFirstPool) obj; - - return number == other.number && Objects.equals(pool, other.pool); - } -}
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/DropLastPool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/DropLastPool.java deleted file mode 100644 index 9503e8f..0000000 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/DropLastPool.java +++ /dev/null @@ -1,58 +0,0 @@ -package bjc.dicelang.neodice.diepool; - -import java.util.*; - -import bjc.dicelang.neodice.*; - -public class DropLastPool implements DiePool { - private final int number; - private final DiePool pool; - - public DropLastPool(DiePool pool, int number) { - this.pool = pool; - this.number = number; - } - - @Override - public int[] roll(Random rng) { - int[] rolls = pool.roll(rng); - - if (number >= rolls.length) { - return new int[0]; - } else { - int[] newRolls = new int[rolls.length - number]; - - for (int index = 0; index < rolls.length - number; index++) { - newRolls[index] = rolls[index]; - } - - return newRolls; - } - } - - @Override - public Die[] contained() { - return pool.contained(); - } - - @Override - public String toString() { - return String.format("%sdL%d", pool, number); - } - - @Override - public int hashCode() { - return Objects.hash(number, pool); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - DropLastPool other = (DropLastPool) obj; - - return number == other.number && Objects.equals(pool, other.pool); - } -}
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/ExpandDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/ExpandDiePool.java new file mode 100644 index 0000000..7c58e72 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/neodice/diepool/ExpandDiePool.java @@ -0,0 +1,25 @@ +package bjc.dicelang.neodice.diepool; + +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import bjc.dicelang.neodice.*; + +public class ExpandDiePool<SideType> implements IDiePool<SideType> { + private final IDie<SideType> contained; + + private final BiFunction<IDie<SideType>, Random, Stream<SideType>> expander; + + public ExpandDiePool(IDie<SideType> contained, + BiFunction<IDie<SideType>, Random, Stream<SideType>> expander) { + this.contained = contained; + this.expander = expander; + } + + + @Override + public Stream<SideType> roll(Random rng) { + return expander.apply(contained, rng); + } +} diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/FilteredDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/FilteredDiePool.java deleted file mode 100644 index 34d274f..0000000 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/FilteredDiePool.java +++ /dev/null @@ -1,47 +0,0 @@ -package bjc.dicelang.neodice.diepool; - -import java.util.*; -import java.util.function.*; - -import bjc.dicelang.neodice.*; - -public class FilteredDiePool implements DiePool { - private final DiePool pool; - private final IntPredicate filter; - - public FilteredDiePool(DiePool pool, IntPredicate filter) { - this.pool = pool; - this.filter = filter; - } - - @Override - public int[] roll(Random rng) { - int[] rolls = pool.roll(rng); - - return Arrays.stream(rolls).filter(filter).toArray(); - } - - @Override - public Die[] contained() { - return pool.contained(); - } - - // No toString, since there isn't any sensible to output the filter - - @Override - public int hashCode() { - return Objects.hash(filter, pool); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - FilteredDiePool other = (FilteredDiePool) obj; - - return Objects.equals(filter, other.filter) - && Objects.equals(pool, other.pool); - } -}
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/FixedDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/FixedDiePool.java index c60b88a..a536f62 100644 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/FixedDiePool.java +++ b/dice/src/main/java/bjc/dicelang/neodice/diepool/FixedDiePool.java @@ -1,55 +1,46 @@ package bjc.dicelang.neodice.diepool; import java.util.*; +import java.util.stream.*; import bjc.dicelang.neodice.*; -public class FixedDiePool implements DiePool { - private final Die[] dice; +public class FixedDiePool<SideType> implements IDiePool<SideType> { + private final List<IDie<SideType>> dice; - public FixedDiePool(Die[] dice) { + public FixedDiePool(List<IDie<SideType>> dice) { this.dice = dice; } + + @SafeVarargs + public FixedDiePool(IDie<SideType>...dice) { + this.dice = new ArrayList<>(dice.length); + for (IDie<SideType> die : dice) { + this.dice.add(die); + } + } @Override - public int[] roll(Random rng) { - int[] results = new int[dice.length]; - - for (int index = 0; index < dice.length; index++) { - results[index] = dice[index].roll(rng); - } - - return results; + public Stream<SideType> roll(Random rng) { + return dice.stream().map((die) -> die.roll(rng)); } @Override - public Die[] contained() { + public List<IDie<SideType>> contained() { return dice; } @Override public String toString() { - StringBuilder builder = new StringBuilder(); - - for (int i = 0; i < dice.length; i++) { - Die die = dice[i]; - - builder.append(die); - - // Don't add an extra trailing comma - if (i < dice.length - 1) builder.append(", "); - } - - return builder.toString(); + return dice.stream() + .map(IDie<SideType>::toString) + .collect(Collectors.joining(", ")); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(dice); - return result; + return Objects.hash(dice); } @Override @@ -57,9 +48,9 @@ public class FixedDiePool implements DiePool { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; + + FixedDiePool<?> other = (FixedDiePool<?>) obj; - FixedDiePool other = (FixedDiePool) obj; - - return Arrays.equals(dice, other.dice); + return Objects.equals(dice, other.dice); } }
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/KeepFirstDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/KeepFirstDiePool.java deleted file mode 100644 index d31f104..0000000 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/KeepFirstDiePool.java +++ /dev/null @@ -1,58 +0,0 @@ -package bjc.dicelang.neodice.diepool; - -import java.util.*; - -import bjc.dicelang.neodice.*; - -public class KeepFirstDiePool implements DiePool { - private final int number; - private final DiePool pool; - - public KeepFirstDiePool(DiePool pool, int number) { - this.pool = pool; - this.number = number; - } - - @Override - public int[] roll(Random rng) { - int[] rolls = pool.roll(rng); - - if (rolls.length >= number) { - return rolls; - } else { - int[] newRolls = new int[number]; - - for (int index = 0; index < number; index++) { - newRolls[index] = rolls[index]; - } - - return newRolls; - } - } - - @Override - public Die[] contained() { - return pool.contained(); - } - - @Override - public String toString() { - return String.format("%skF%d", pool, number); - } - - @Override - public int hashCode() { - return Objects.hash(number, pool); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - KeepFirstDiePool other = (KeepFirstDiePool) obj; - - return number == other.number && Objects.equals(pool, other.pool); - } -}
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/KeepLastDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/KeepLastDiePool.java deleted file mode 100644 index 1e2d1ae..0000000 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/KeepLastDiePool.java +++ /dev/null @@ -1,58 +0,0 @@ -package bjc.dicelang.neodice.diepool; - -import java.util.*; - -import bjc.dicelang.neodice.*; - -public class KeepLastDiePool implements DiePool { - private final int number; - private final DiePool pool; - - public KeepLastDiePool(DiePool pool, int number) { - this.pool = pool; - this.number = number; - } - - @Override - public int[] roll(Random rng) { - int[] rolls = pool.roll(rng); - - if (rolls.length >= number) { - return rolls; - } else { - int[] newRolls = new int[number]; - - for (int index = number; index > index; index--) { - newRolls[index] = rolls[rolls.length - index]; - } - - return newRolls; - } - } - - @Override - public Die[] contained() { - return pool.contained(); - } - - @Override - public String toString() { - return String.format("%skL%d", pool, number); - } - - @Override - public int hashCode() { - return Objects.hash(number, pool); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - KeepLastDiePool other = (KeepLastDiePool) obj; - - return number == other.number && Objects.equals(pool, other.pool); - } -}
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/SortedDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/SortedDiePool.java deleted file mode 100644 index 79599c1..0000000 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/SortedDiePool.java +++ /dev/null @@ -1,63 +0,0 @@ -package bjc.dicelang.neodice.diepool; - -import java.util.*; - -import bjc.dicelang.neodice.*; - -public class SortedDiePool implements DiePool { - private final boolean isDescending; - private final DiePool pool; - - public SortedDiePool(DiePool pool, boolean isDescending) { - this.pool = pool; - this.isDescending = isDescending; - } - - @Override - public int[] roll(Random rng) { - int[] rolls = pool.roll(rng); - - Arrays.sort(rolls); - - if (isDescending) { - int[] newRolls = new int[rolls.length]; - - int newIndex = newRolls.length; - for (int index = 0; index < rolls.length; index++) { - newRolls[newIndex--] = rolls[index]; - } - - return newRolls; - } else { - return rolls; - } - } - - @Override - public Die[] contained() { - return pool.contained(); - } - - @Override - public String toString() { - return String.format("%s (sorted %s)", pool, - isDescending ? " descending" : "ascending"); - } - - @Override - public int hashCode() { - return Objects.hash(isDescending, pool); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - SortedDiePool other = (SortedDiePool) obj; - - return isDescending == other.isDescending - && Objects.equals(pool, other.pool); - } -}
\ No newline at end of file diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/TimesDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/TimesDiePool.java index 1b34247..56fe0e5 100644 --- a/dice/src/main/java/bjc/dicelang/neodice/diepool/TimesDiePool.java +++ b/dice/src/main/java/bjc/dicelang/neodice/diepool/TimesDiePool.java @@ -1,35 +1,31 @@ package bjc.dicelang.neodice.diepool; import java.util.*; +import java.util.stream.*; import bjc.dicelang.neodice.*; -public class TimesDiePool implements DiePool { - private final Die contained; +public class TimesDiePool<SideType> implements IDiePool<SideType> { + private final IDie<SideType> contained; private final int numDice; - public TimesDiePool(Die contained, int numDice) { + public TimesDiePool(IDie<SideType> contained, int numDice) { this.contained = contained; this.numDice = numDice; } @Override - public int[] roll(Random rng) { - int[] results = new int[numDice]; - - for (int index = 0; index < numDice; index++) { - results[index] = contained.roll(rng); - } - - return results; + public Stream<SideType> roll(Random rng) { + return Stream.generate(() -> contained.roll(rng)) + .limit(numDice); } @Override - public Die[] contained() { - Die[] results = new Die[numDice]; + public List<IDie<SideType>> contained() { + List<IDie<SideType>> results = new ArrayList<>(numDice); for (int index = 0; index < numDice; index++) { - results[index] = contained; + results.add(contained); } return results; @@ -51,7 +47,7 @@ public class TimesDiePool implements DiePool { if (obj == null) return false; if (getClass() != obj.getClass()) return false; - TimesDiePool other = (TimesDiePool) obj; + TimesDiePool<?> other = (TimesDiePool<?>) obj; return Objects.equals(contained, other.contained) && numDice == other.numDice; } diff --git a/dice/src/main/java/bjc/dicelang/neodice/diepool/TransformDiePool.java b/dice/src/main/java/bjc/dicelang/neodice/diepool/TransformDiePool.java new file mode 100644 index 0000000..f590e91 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/neodice/diepool/TransformDiePool.java @@ -0,0 +1,52 @@ +package bjc.dicelang.neodice.diepool; + +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import bjc.dicelang.neodice.*; + +public class TransformDiePool<SideType> implements IDiePool<SideType> { + private final IDiePool<SideType> contained; + + private UnaryOperator<Stream<SideType>> transform; + + public TransformDiePool(IDiePool<SideType> contained, + UnaryOperator<Stream<SideType>> transform) { + super(); + this.contained = contained; + this.transform = transform; + } + + @Override + public Stream<SideType> roll(Random rng) { + return transform.apply(contained.roll(rng)); + } + + @Override + public List<IDie<SideType>> contained() { + return contained.contained(); + } + + @Override + public int hashCode() { + return Objects.hash(contained, transform); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + + TransformDiePool<?> other = (TransformDiePool<?>) obj; + + return Objects.equals(contained, other.contained) + && Objects.equals(transform, other.transform); + } + + @Override + public String toString() { + return contained.toString() + "(transformed)"; + } +} diff --git a/dice/src/test/java/bjc/dicelang/neodice/DiePoolTest.java b/dice/src/test/java/bjc/dicelang/neodice/DiePoolTest.java index 3614cd3..fda1505 100644 --- a/dice/src/test/java/bjc/dicelang/neodice/DiePoolTest.java +++ b/dice/src/test/java/bjc/dicelang/neodice/DiePoolTest.java @@ -10,24 +10,5 @@ import org.junit.*; public class DiePoolTest { private static final Random rng = new Random(); - @Test - public void containedOneDieYieldsOneDie() { - Die oneSidedDie = Die.polyhedral(1); - DiePool pool = DiePool.containing(oneSidedDie); - - assertArrayEquals( - "A contained pool created with one die, yields that die", - new Die[] {oneSidedDie}, pool.contained()); - } - - @Test - public void containedOneDieRollsOneDie() { - Die oneSidedDie = Die.polyhedral(1); - DiePool pool = DiePool.containing(oneSidedDie); - - for (int i = 0; i < 10; i++) { - assertArrayEquals("One-die pools roll one die", - new int[] {1}, pool.roll(rng)); - } - } + // Some tests, I suppose } diff --git a/dice/src/test/java/bjc/dicelang/neodice/DieTest.java b/dice/src/test/java/bjc/dicelang/neodice/DieTest.java index 202688b..baee4cc 100644 --- a/dice/src/test/java/bjc/dicelang/neodice/DieTest.java +++ b/dice/src/test/java/bjc/dicelang/neodice/DieTest.java @@ -10,37 +10,5 @@ import org.junit.*; public class DieTest { private final static Random rng = new Random(); - @Test - public void onesidedDiceReturnOne() { - Die die = Die.polyhedral(1); - - for (int i = 0; i < 10; i++) { - assertEquals("One-sided dice always return 1", 1, die.roll(rng)); - } - } - - @Test - public void polyhedralDiceStayInRange() { - Die die = Die.polyhedral(6); - - for (int i = 0; i < 50; i++) { - int result = die.roll(rng); - - boolean inRange = result <= 6 && result >= 1; - - assertTrue("Six-sided dice always return a value from 1 to 6", inRange); - } - } - - @Test - public void polyhedralDiceEqualityFunctionsProperly() { - Die dieA1 = Die.polyhedral(1); - Die dieA2 = Die.polyhedral(1); - Die dieB1 = Die.polyhedral(2); - - assertEquals("Polyhedral dice with the same number of sides are equal", - dieA1, dieA2); - assertNotEquals("Polyhedral dice with a diffeent number of sides aren't equal", - dieA1, dieB1); - } + // Some tests, I guess } |
