summaryrefslogtreecommitdiff
path: root/dice/src/main/java/bjc/dicelang/neodice/Die.java
diff options
context:
space:
mode:
Diffstat (limited to 'dice/src/main/java/bjc/dicelang/neodice/Die.java')
-rw-r--r--dice/src/main/java/bjc/dicelang/neodice/Die.java267
1 files changed, 164 insertions, 103 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