summaryrefslogtreecommitdiff
path: root/dice/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'dice/src/main/java')
-rw-r--r--dice/src/main/java/bjc/dicelang/neodice/Die.java267
-rw-r--r--dice/src/main/java/bjc/dicelang/neodice/DieFactory.java63
-rw-r--r--dice/src/main/java/bjc/dicelang/neodice/DiePool.java582
-rw-r--r--dice/src/main/java/bjc/dicelang/neodice/DiePoolFactory.java82
4 files changed, 425 insertions, 569 deletions
diff --git a/dice/src/main/java/bjc/dicelang/neodice/Die.java b/dice/src/main/java/bjc/dicelang/neodice/Die.java
index a986ca2..56e4327 100644
--- a/dice/src/main/java/bjc/dicelang/neodice/Die.java
+++ b/dice/src/main/java/bjc/dicelang/neodice/Die.java
@@ -9,10 +9,12 @@ import bjc.esodata.*;
/**
* Represents a single polyhedral die.
* @author Ben Culkin
+ *
+ * @param <SideType> The type of value represented by this die.
*
*/
@FunctionalInterface
-public interface Die {
+public interface Die<SideType> {
/**
* Rolls this die.
*
@@ -20,7 +22,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.
@@ -29,143 +31,202 @@ 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 DiePool<SideType> times(int numTimes) {
+ return new ExpandDiePool<>(this, (die, rng) -> {
+ return Stream.generate(() -> die.roll(rng))
+ .limit(numTimes);
+ });
};
/**
* Returns a die which will reroll this die as long as the provided condition is true.
*
+ * @param comparer The thing to use to compare die values.
* @param condition The condition to reroll the die on.
*
* @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 Die<SideType> reroll(
+ Comparator<SideType> comparer,
+ Predicate<SideType> condition) {
+ return RerollDie.create(comparer, this, condition,
+ (list) -> list.get(list.size()));
}
/**
* Returns a die which will reroll this die up to a specified number of times,
* as long as the provided condition is true.
*
+ * @param comparer The thing to use to compare die values.
* @param condition The condition to reroll the die on.
* @param limit The maximum number of times to reroll the 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 Die<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));
+ }
+
+ /**
+ * Create a new transforming die.
+ *
+ * @param <NewType> The new type of the die.
+ *
+ * @param mapper The function to use for mapping.
+ *
+ * @return A die that transforms with the given mapping.
+ */
+ default <NewType> Die<NewType> transform(Function<SideType, NewType> mapper) {
+ return (rng) -> mapper.apply(this.roll(rng));
+ }
+
+ /**
+ * Create a simple polyhedral die with a fixed number of sides.
+ *
+ * @param sides The number of sides for the die.
+ *
+ * @return A die which returns a result from 1 to sides.
+ */
+ static Die<Integer> polyhedral(int sides) {
+ return new PolyhedralDie(sides);
}
}
-final class TimesDiePool implements DiePool {
- private final Die contained;
- private final int numDice;
+class PolyhedralDie implements Die<Integer> {
+ private final int sides;
- public TimesDiePool(Die contained, int numDice) {
- this.contained = contained;
- this.numDice = numDice;
- }
+ public PolyhedralDie(int sides) {
+ this.sides = sides;
+ }
- @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;
- }
-
- @Override
- public Die[] contained() {
- Die[] results = new Die[numDice];
-
- for (int index = 0; index < numDice; index++) {
- results[index] = contained;
- }
-
- return results;
- }
+ @Override
+ public Integer roll(Random rng) {
+ // Dice are one-based, not zero-based.
+ return rng.nextInt(sides) + 1;
+ }
- @Override
- public String toString() {
- return String.format("%d%s", numDice, contained);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(contained, numDice);
- }
+ @Override
+ public String toString() {
+ return String.format("d%d", sides);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + sides;
+ return result;
+ }
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
-
- TimesDiePool other = (TimesDiePool) obj;
-
- return Objects.equals(contained, other.contained) && numDice == other.numDice;
- }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ PolyhedralDie other = (PolyhedralDie) obj;
+
+ if (sides != other.sides) return false;
+ else return true;
+ }
}
-final class RerollDie implements Die {
- private final Die contained;
-
- private final IntPredicate condition;
- private final Function<MinMaxList<Integer>, Integer> chooser;
-
- private int limit = Integer.MAX_VALUE;
-
-
- public RerollDie(Die contained, IntPredicate condition,
- Function<MinMaxList<Integer>, Integer> chooser) {
- 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;
-
- this.limit = limit;
- }
-
- @Override
- public int roll(Random rng) {
- int roll = contained.roll(rng);
-
- MinMaxList<Integer> newRolls = new MinMaxList<Integer>(
- Comparator.naturalOrder(), roll);
-
- int rerollCount = 0;
- while (condition.test(roll) && rerollCount < limit) {
- roll = contained.roll(rng);
- newRolls.add(roll);
-
- rerollCount += 1;
- }
-
- return chooser.apply(newRolls);
- }
+class RerollDie<SideType> implements Die<SideType> {
+ private final Die<SideType> contained;
+
+ private final Predicate<SideType> condition;
+ private final Function<MinMaxList<SideType>, SideType> chooser;
+
+ private final Comparator<SideType> comparer;
+
+ private int limit = Integer.MAX_VALUE;
+
+ private RerollDie(
+ Comparator<SideType> comparer,
+ Die<SideType> contained,
+ Predicate<SideType> condition,
+ Function<MinMaxList<SideType>, SideType> chooser) {
+ this.comparer = comparer;
+
+ this.contained = contained;
+
+ this.condition = condition;
+ this.chooser = chooser;
+ }
+
+ private RerollDie(
+ Comparator<SideType> comparer,
+ Die<SideType> contained,
+ Predicate<SideType> condition,
+ Function<MinMaxList<SideType>, SideType> chooser,
+ int limit) {
+ this(comparer, contained, condition, chooser);
+
+ this.limit = limit;
+ }
+
+ @Override
+ 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) {
+ roll = contained.roll(rng);
+ newRolls.add(roll);
+
+ rerollCount += 1;
+ }
+
+ return chooser.apply(newRolls);
+ }
+
+ public static <Side extends Comparable<Side>> Die<Side> create(
+ Die<Side> contained,
+ Predicate<Side> condition,
+ Function<MinMaxList<Side>, Side> chooser) {
+ return new RerollDie<>(Comparator.naturalOrder(), contained, condition, chooser);
+ }
+
+ public static <Side extends Comparable<Side>> Die<Side> create(
+ Die<Side> contained,
+ Predicate<Side> condition,
+ Function<MinMaxList<Side>, Side> chooser,
+ int limit) {
+ return new RerollDie<>(Comparator.naturalOrder(), contained, condition, chooser, limit);
+ }
+
- // No toString, because IntPredicate can't be converted to a string
+ public static <Side> Die<Side> create(
+ Comparator<Side> comparer,
+ Die<Side> contained,
+ Predicate<Side> condition,
+ Function<MinMaxList<Side>, Side> chooser) {
+ return new RerollDie<>(comparer, contained, condition, chooser);
+ }
+
+ public static <Side> Die<Side> create(
+ Comparator<Side> comparer,
+ Die<Side> contained,
+ Predicate<Side> condition,
+ Function<MinMaxList<Side>, Side> chooser,
+ int limit) {
+ return new RerollDie<>(comparer, contained, condition, chooser, limit);
+ }
} \ No newline at end of file
diff --git a/dice/src/main/java/bjc/dicelang/neodice/DieFactory.java b/dice/src/main/java/bjc/dicelang/neodice/DieFactory.java
deleted file mode 100644
index d9bae7e..0000000
--- a/dice/src/main/java/bjc/dicelang/neodice/DieFactory.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package bjc.dicelang.neodice;
-
-import java.util.*;
-
-/**
- * Various static functions which create instances of Die.
- *
- * @author Ben Culkin
- *
- */
-public class DieFactory {
- /**
- * Create a simple polyhedral die with a fixed number of sides.
- *
- * @param sides The number of sides for the die.
- *
- * @return A die which returns a result from 1 to sides.
- */
- public static Die polyhedral(int sides) {
- return new PolyhedralDie(sides);
- }
-}
-
-final class PolyhedralDie implements Die {
- private final int sides;
-
- public PolyhedralDie(int sides) {
- this.sides = sides;
- }
-
- @Override
- public int roll(Random rng) {
- // Dice are one-based, not zero-based.
- return rng.nextInt(sides) + 1;
- }
-
- @Override
- public String toString() {
- return String.format("d%d", sides);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + sides;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
-
- PolyhedralDie other = (PolyhedralDie) obj;
-
- if (sides != other.sides) return false;
- else return true;
- }
-
-
-} \ 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/DiePool.java
index 70dfa50..16bec48 100644
--- a/dice/src/main/java/bjc/dicelang/neodice/DiePool.java
+++ b/dice/src/main/java/bjc/dicelang/neodice/DiePool.java
@@ -2,38 +2,40 @@ package bjc.dicelang.neodice;
import java.util.*;
import java.util.function.*;
+import java.util.stream.*;
/**
* Represents a pool of dice.
*
* @author Ben Culkin
- *
+ *
+ * @param <SideType> The type of the sides of the contained dice.
*/
@FunctionalInterface
-public interface DiePool {
+public interface DiePool<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.
@@ -42,7 +44,7 @@ public interface DiePool {
*
* @throws UnsupportedOperationException If the composite dice can't be retrieved.
*/
- default Die[] contained() {
+ default List<Die<SideType>> contained() {
throw new UnsupportedOperationException("Can't get composite dice");
}
@@ -54,16 +56,19 @@ 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 comparer The comparator to use for the dice.
* @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 DiePool<SideType> sorted(
+ Comparator<SideType> comparer,
+ boolean isDescending) {
+ return new TransformDiePool<>(this,
+ (pool) -> pool.sorted(
+ isDescending
+ ? comparer.reversed()
+ : comparer));
}
/**
@@ -74,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 DiePool<SideType> filtered(Predicate<SideType> matcher) {
+ return new TransformDiePool<>(this,
+ (pool) -> pool.filter(matcher));
}
/**
@@ -85,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 DiePool<SideType> dropFirst(int number) {
+ return new TransformDiePool<>(this,
+ (pool) -> pool.skip(number));
}
/**
@@ -96,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 DiePool<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();
+ });
}
/**
@@ -107,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 DiePool<SideType> keepFirst(int number) {
+ return new TransformDiePool<>(this,
+ (pool) -> pool.limit(number));
}
/**
@@ -118,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 DiePool<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();
+ });
}
/*
@@ -130,45 +154,49 @@ public interface DiePool {
/**
* Return a die pool which rolls this one, then drops a number of the lowest values.
*
+ * @param comparer The comparer to use for the sides.
* @param number The number of lowest values to drop.
*
* @return A die pool which has the lowest entries dropped.
*/
- default DiePool dropLowest(int number) {
- return this.sorted(false).dropFirst(number);
+ default DiePool<SideType> dropLowest(Comparator<SideType> comparer, int number) {
+ return this.sorted(comparer, false).dropFirst(number);
}
/**
* Return a die pool which rolls this one, then drops a number of the lowest values.
*
+ * @param comparer The comparer to use for the sides.
* @param number The number of lowest values to drop.
*
* @return A die pool which has the lowest entries dropped.
*/
- default DiePool dropHighest(int number) {
- return this.sorted(false).dropLast(number);
+ default DiePool<SideType> dropHighest(Comparator<SideType> comparer,int number) {
+ return this.sorted(comparer, false).dropLast(number);
}
/**
* Return a die pool which rolls this one, then keeps a number of the lowest values.
*
+ * @param comparer The comparer to use for the sides.
* @param number The number of lowest values to keep.
*
* @return A die pool which has the lowest entries kept.
*/
- default DiePool keepLowest(int number) {
- return this.sorted(false).keepFirst(number);
+ default DiePool<SideType> keepLowest(Comparator<SideType> comparer,int number) {
+ return this.sorted(comparer, false).keepFirst(number);
}
/**
* Return a die pool which rolls this one, then keeps a number of the highest values.
*
+ * @param comparer The comparer to use for the sides.
* @param number The number of highest values to keep.
*
* @return A die pool which has the highest entries kept.
*/
- default DiePool keepHighest(int number) {
- return this.sorted(false).keepLast(number);
+ default DiePool<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. */
@@ -180,318 +208,230 @@ 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();
- }
-}
-
-final class SortedDiePool implements DiePool {
- private final boolean isDescending;
- private final DiePool pool;
-
- public SortedDiePool(DiePool pool, boolean isDescending) {
- this.pool = pool;
- this.isDescending = isDescending;
+ default Iterator<SideType> iterator(Random rng) {
+ return this.roll(rng).iterator();
}
- @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;
- }
+ /**
+ * Create a die pool containing the provided dice.
+ *
+ * @param <Side> The type of the sides.
+ *
+ * @param dice The dice to put into the pool.
+ *
+ * @return A pool which contains the provided dice.
+ */
+ @SafeVarargs
+ static <Side> DiePool<Side> containing(Die<Side>... dice) {
+ return new FixedDiePool<>(dice);
}
- @Override
- public Die[] contained() {
- return pool.contained();
+ /**
+ * Create an expanding die pool
+ *
+ * @param <Side> The type of the sides.
+ *
+ * @param contained The contained die.
+ * @param expander The expanding function.
+ *
+ * @return A die pool that expands the result given the provided function.
+ */
+ static <Side> DiePool<Side> expanding(Die<Side> contained,
+ BiFunction<Die<Side>, Random, Stream<Side>> expander)
+ {
+ return new ExpandDiePool<>(contained, expander);
}
+}
- @Override
- public String toString() {
- return String.format("%s (sorted %s)", pool,
- isDescending ? " descending" : "ascending");
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(isDescending, pool);
- }
+/**
+ * A die pool that can expand dice.
+ * @author Ben Culkin
+ *
+ * @param <SideType> The type the die uses.
+ */
+class ExpandDiePool<SideType> implements DiePool<SideType> {
+ private final Die<SideType> contained;
+
+ private final BiFunction<Die<SideType>, Random, Stream<SideType>> expander;
- @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);
- }
+ /**
+ * Create a new expanding die pool.
+ *
+ * @param contained The die to expand.
+ * @param expander The function to use for expanding.
+ */
+ public ExpandDiePool(Die<SideType> contained,
+ BiFunction<Die<SideType>, Random, Stream<SideType>> expander) {
+ this.contained = contained;
+ this.expander = expander;
+ }
+
+ @Override
+ public Stream<SideType> roll(Random rng) {
+ return expander.apply(contained, rng);
+ }
+
+ @Override
+ public List<Die<SideType>> contained()
+ {
+ return Arrays.asList(contained);
+ }
}
-final 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();
- }
+/**
+ * A die pool that has a fixed size.
+ *
+ * @author Ben Culkin
+ *
+ * @param <SideType> The type of the sides of the dice.
+ */
+class FixedDiePool<SideType> implements DiePool<SideType> {
+ private final List<Die<SideType>> dice;
- @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);
- }
+ /**
+ * Create a new fixed dice pool.
+ * @param dice The dice to put into the pool.
+ */
+ public FixedDiePool(List<Die<SideType>> dice) {
+ this.dice = dice;
+ }
+
+ /**
+ * Create a new fixed dice pool from an array of dice.
+ * @param dice The dice to put into the pool.
+ */
+ @SafeVarargs
+ public FixedDiePool(Die<SideType>...dice) {
+ this.dice = new ArrayList<>(dice.length);
+ for (Die<SideType> die : dice) {
+ this.dice.add(die);
+ }
+ }
- @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);
- }
-}
+ @Override
+ public Stream<SideType> roll(Random rng) {
+ return dice.stream().map((die) -> die.roll(rng));
+ }
-final 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 List<Die<SideType>> contained() {
+ return dice;
+ }
- @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 dice.stream()
+ .map(Die<SideType>::toString)
+ .collect(Collectors.joining(", "));
+ }
- @Override
- public String toString() {
- return String.format("%sdF%d", pool, number);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(number, pool);
- }
+ @Override
+ public int hashCode() {
+ return Objects.hash(dice);
+ }
- @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);
- }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ FixedDiePool<?> other = (FixedDiePool<?>) obj;
+
+ return Objects.equals(dice, other.dice);
+ }
}
-final class DropLastPool implements DiePool {
- private final int number;
- private final DiePool pool;
-
- public DropLastPool(DiePool pool, int number) {
- this.pool = pool;
- this.number = number;
- }
+class TimesDiePool<SideType> implements DiePool<SideType> {
+ private final Die<SideType> contained;
+ private final int numDice;
- @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();
- }
+ public TimesDiePool(Die<SideType> contained, int numDice) {
+ this.contained = contained;
+ this.numDice = numDice;
+ }
- @Override
- public String toString() {
- return String.format("%sdL%d", pool, number);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(number, pool);
- }
+ @Override
+ public Stream<SideType> roll(Random rng) {
+ return Stream.generate(() -> contained.roll(rng))
+ .limit(numDice);
+ }
+
+ @Override
+ public List<Die<SideType>> contained() {
+ List<Die<SideType>> results = new ArrayList<>(numDice);
+
+ for (int index = 0; index < numDice; index++) {
+ results.add(contained);
+ }
+
+ return results;
+ }
- @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);
- }
-}
+ @Override
+ public String toString() {
+ return String.format("%d%s", numDice, contained);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(contained, numDice);
+ }
-final 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 boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ TimesDiePool<?> other = (TimesDiePool<?>) obj;
+
+ return Objects.equals(contained, other.contained) && numDice == other.numDice;
+ }
+}
- @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);
- }
+class TransformDiePool<SideType> implements DiePool<SideType> {
+ private final DiePool<SideType> contained;
+
+ private UnaryOperator<Stream<SideType>> transform;
- @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);
- }
-}
+ public TransformDiePool(DiePool<SideType> contained,
+ UnaryOperator<Stream<SideType>> transform) {
+ super();
+ this.contained = contained;
+ this.transform = transform;
+ }
-final 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 Stream<SideType> roll(Random rng) {
+ return transform.apply(contained.roll(rng));
+ }
+
+ @Override
+ public List<Die<SideType>> contained() {
+ return contained.contained();
+ }
- @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 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;
-
- KeepLastDiePool other = (KeepLastDiePool) obj;
-
- return number == other.number && Objects.equals(pool, other.pool);
- }
+ @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)";
+ }
} \ No newline at end of file
diff --git a/dice/src/main/java/bjc/dicelang/neodice/DiePoolFactory.java b/dice/src/main/java/bjc/dicelang/neodice/DiePoolFactory.java
deleted file mode 100644
index 6d9314d..0000000
--- a/dice/src/main/java/bjc/dicelang/neodice/DiePoolFactory.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package bjc.dicelang.neodice;
-
-import java.util.*;
-
-/**
- * Various static functions which create instances of DiePool.
- *
- * @author Ben Culkin
- *
- */
-public class DiePoolFactory {
- /**
- * Create a die pool containing the provided dice.
- *
- * @param dice The dice to put into the pool.
- *
- * @return A pool which contains the provided dice.
- */
- public static DiePool containing(Die... dice) {
- return new FixedDiePool(dice);
- }
-}
-
-final class FixedDiePool implements DiePool {
- private final Die[] dice;
-
- public FixedDiePool(Die[] dice) {
- this.dice = dice;
- }
-
- @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;
- }
-
- @Override
- public Die[] 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();
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Arrays.hashCode(dice);
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
-
- FixedDiePool other = (FixedDiePool) obj;
-
- return Arrays.equals(dice, other.dice);
- }
-} \ No newline at end of file