diff options
Diffstat (limited to 'dice/src/main/java/bjc')
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 |
