diff options
| author | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2018-06-02 17:32:24 -0300 |
|---|---|---|
| committer | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2018-06-02 17:32:24 -0300 |
| commit | 775394cf6b4a3c6a7e7345affeb45bb34f6e4517 (patch) | |
| tree | a193472620f796b3701fa979cba84d4361eef0c0 /dice/src/main/java/bjc/dicelang/dicev2 | |
| parent | 09f161528619f9396aa07c784c15bc1d33f5c047 (diff) | |
Finish die rewrite
This adds all of the remaining missing die modifiers, as well as a few
base die types that were missing.
Diffstat (limited to 'dice/src/main/java/bjc/dicelang/dicev2')
14 files changed, 786 insertions, 22 deletions
diff --git a/dice/src/main/java/bjc/dicelang/dicev2/CompositeDie.java b/dice/src/main/java/bjc/dicelang/dicev2/CompositeDie.java new file mode 100644 index 0000000..e9bb585 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/CompositeDie.java @@ -0,0 +1,78 @@ +package bjc.dicelang.dicev2; + +import java.util.Random; + +public class CompositeDie extends Die { + public final Die numDice; + public final Die numSides; + + public final boolean rerollSides; + + public CompositeDie(Die numDice, Die numSides) { + this(numDice, numSides, false); + } + + public CompositeDie(Die numDice, Die numSides, boolean rerollSides) { + super(); + + this.numDice = numDice; + this.numSides = numSides; + + this.rerollSides = rerollSides; + } + + public CompositeDie(Random rnd, Die numDice, Die numSides) { + this(rnd, numDice, numSides, false); + } + + public CompositeDie(Random rnd, Die numDice, Die numSides, boolean rerollSides) { + super(rnd); + + this.numDice = numDice; + this.numSides = numSides; + + this.rerollSides = rerollSides; + } + + public long[] roll() { + int target = (int)numDice.rollSingle(); + int sides = (int)numSides.rollSingle(); + + long[] res = new long[target]; + + for(int i = 0; i < target; i++) { + res[i] = rng.nextInt(sides) + 1; + + if(rerollSides) sides = (int)numSides.rollSingle(); + } + + return res; + } + + public long rollSingle() { + return rng.nextInt((int)numSides.rollSingle()); + } + + public boolean canOptimize() { + if(numSides.canOptimize()) { + if(numSides.optimize() <= 1) { + return true; + } + } + + if(numDice.canOptimize()) { + if(numDice.optimize() == 0) { + return true; + } + } + + return false; + } + + public long optimize() { + if(numDice.canOptimize()) return 0; + if(numSides.canOptimize() && numSides.optimize() == 0) return 0; + + return numDice.rollSingle(); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/CompoundDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/CompoundDieMod.java new file mode 100644 index 0000000..6b752f2 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/CompoundDieMod.java @@ -0,0 +1,80 @@ +package bjc.dicelang.dicev2; + +import bjc.utils.funcutils.ListUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.LongPredicate; + +public class CompoundDieMod extends Die { + public final Die[] dice; + + public final LongPredicate compound; + + public final boolean penetrate; + + public CompoundDieMod(LongPredicate compound, Die... dice) { + this(compound, false, dice); + } + + public CompoundDieMod(LongPredicate compound, boolean penetrate, Die... dice) { + super(); + + this.dice = dice; + + this.compound = compound; + + this.penetrate = penetrate; + } + + public long[] roll() { + List<Long> lst = new ArrayList<>(5); + + for(Die die : dice) { + for(long val : die.roll()) { + long res = val; + + long newVal = die.rollSingle(); + + while(compound.test(newVal)) { + if(penetrate) newVal -= 1; + + res += newVal; + + newVal = die.rollSingle(); + } + + lst.add(res); + } + } + + return ListUtils.toPrimitive(lst); + } + + public long rollSingle() { + Die die = dice[0]; + + long newVal = die.rollSingle(); + + long res = newVal; + + while(compound.test(newVal)) { + newVal = die.rollSingle(); + + if(penetrate) newVal -= 1; + + res += newVal; + } + + return res; + } + + /* :UnoptimizableDice */ + public boolean canOptimize() { + return false; + } + + public long optimize() { + throw new UnsupportedOperationException("Exploding dice can't be optimized"); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/ComputedDie.java b/dice/src/main/java/bjc/dicelang/dicev2/ComputedDie.java new file mode 100644 index 0000000..89405fe --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/ComputedDie.java @@ -0,0 +1,75 @@ +package bjc.dicelang.dicev2; + +import java.util.Random; +import java.util.function.IntSupplier; + +public class ComputedDie extends Die { + public final IntSupplier numDice; + public final IntSupplier numSides; + + public final boolean rerollSides; + + public ComputedDie(IntSupplier numDice, IntSupplier numSides) { + this(numDice, numSides, false); + } + + public ComputedDie(IntSupplier numDice, IntSupplier numSides, boolean rerollSides) { + super(); + + this.numDice = numDice; + this.numSides = numSides; + + this.rerollSides = rerollSides; + } + + public ComputedDie(Random rnd, IntSupplier numDice, IntSupplier numSides) { + this(rnd, numDice, numSides, false); + } + + public ComputedDie(Random rnd, IntSupplier numDice, IntSupplier numSides, boolean rerollSides) { + super(rnd); + + this.numDice = numDice; + this.numSides = numSides; + + this.rerollSides = rerollSides; + } + + public long[] roll() { + int target = numDice.getAsInt(); + int sides = numSides.getAsInt(); + + long[] res = new long[target]; + + for(int i = 0; i < target; i++) { + res[i] = rng.nextInt(sides) + 1; + + if(rerollSides) sides = numSides.getAsInt(); + } + + return res; + } + + public long rollSingle() { + return rng.nextInt(numSides.getAsInt()); + } + + /* + * @NOTE + * + * :UnoptimizableDice + * + * Here, we assume that we can't optimize because we have no way + * of knowing that our suppliers will always return the same + * thing. As a matter of fact, they almost always will have that + * behavior, otherwise you wouldn't need ComputedDie. + */ + + public boolean canOptimize() { + return false; + } + + public long optimize() { + throw new UnsupportedOperationException("ComputedDie cannot be optimized"); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/SumDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/ConcatDieMod.java index 54ae3d0..94815d8 100644 --- a/dice/src/main/java/bjc/dicelang/dicev2/SumDieMod.java +++ b/dice/src/main/java/bjc/dicelang/dicev2/ConcatDieMod.java @@ -1,9 +1,9 @@ package bjc.dicelang.dicev2; -public class SumDieMod extends Die { +public class ConcatDieMod extends Die { public final Die[] dice; - public SumDieMod(Die... dice) { + public ConcatDieMod(Die... dice) { super(); this.dice = dice; @@ -14,15 +14,15 @@ public class SumDieMod extends Die { } public long rollSingle() { - long res = 0; + StringBuilder sb = new StringBuilder(); for(Die die : dice) { for(long val : die.roll()) { - res += val; + sb.append(val); } } - return res; + return Long.parseLong(sb.toString()); } public boolean canOptimize() { @@ -32,14 +32,14 @@ public class SumDieMod extends Die { return true; } - + public long optimize() { - long res = 0; + StringBuilder sb = new StringBuilder(); for(Die die : dice) { - res += die.optimize(); + sb.append(die.optimize()); } - return res; + return Long.parseLong(sb.toString()); } } diff --git a/dice/src/main/java/bjc/dicelang/dicev2/CountDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/CountDieMod.java new file mode 100644 index 0000000..0b5ec5b --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/CountDieMod.java @@ -0,0 +1,52 @@ +package bjc.dicelang.dicev2; + +import java.util.function.LongPredicate; + +public class CountDieMod extends Die { + public final Die[] dice; + + public final LongPredicate success; + + public LongPredicate failure; + + public CountDieMod(LongPredicate success, Die... dice) { + this(success, null, dice); + } + + public CountDieMod(LongPredicate success, LongPredicate failure, Die... dice) { + super(); + + this.success = success; + this.failure = failure; + + this.dice = dice; + } + + public long[] roll() { + return new long[] { rollSingle() }; + } + + public long rollSingle() { + long count = 0; + + for(Die die : dice) { + for(long val : die.roll()) { + if(success.test(val)) count += 1; + + if(failure != null && failure.test(val)) count -= 1; + } + } + + return count; + } + + /* :UnoptimizableDice */ + + public boolean canOptimize() { + return false; + } + + public long optimize() { + throw new UnsupportedOperationException("Counted dice can't be optimized"); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/DieMods.java b/dice/src/main/java/bjc/dicelang/dicev2/DieMods.java index afdb604..929cd6e 100644 --- a/dice/src/main/java/bjc/dicelang/dicev2/DieMods.java +++ b/dice/src/main/java/bjc/dicelang/dicev2/DieMods.java @@ -1,7 +1,156 @@ package bjc.dicelang.dicev2; +import bjc.utils.data.GeneratingIterator; + +import java.util.Comparator; +import java.util.Iterator; + +import java.util.function.LongPredicate; +import java.util.function.LongBinaryOperator; +import java.util.function.LongUnaryOperator; + +/* + * @NOTE + * + * :SyntheticMod + * + * These mods are less efficent than if they were hard-coded, involving + * additional function calls and object allocations. If this ends up causing + * performance issues, replace these with custom classes. + */ + public class DieMods { + public Die reduce(LongBinaryOperator fold, long initial, Die... dice) { + return new ReduceDieMod(fold, initial, dice); + } + + public Die concat(Die... dice) { + return new ConcatDieMod(dice); + } + + public Die counted(LongPredicate success, Die... dice) { + return new CountDieMod(success, dice); + } + + public Die counted(LongPredicate success, LongPredicate failure, Die... dice) { + return new CountDieMod(success, failure, dice); + } + + public Die explode(LongPredicate explode, Die... dice) { + return new ExplodingDieMod(explode, dice); + } + + public Die explode(LongPredicate explode, boolean penetrate, Die... dice) { + return new ExplodingDieMod(explode, penetrate, dice); + } + + public Die compound(LongPredicate compound, Die... dice) { + return new CompoundDieMod(compound, dice); + } + + public Die compound(LongPredicate compound, boolean penetrate, Die... dice) { + return new CompoundDieMod(compound, penetrate, dice); + } + + public Die pool(Die... dice) { + return new PoolDiceMod(dice); + } + + public Die filter(LongPredicate filter, Die... dice) { + return new FilterDieMod(filter, dice); + } + + public Die sort(Comparator<Long> sorter, Die die) { + return new SortDieMod(sorter, die); + } + + public Die map(LongUnaryOperator map, Die die) { + return new MapDieMod(map, die); + } + + /* :SyntheticMod */ public Die sum(Die... dice) { - return new SumDieMod(dice); + return reduce((l, r) -> l + r, 0, dice); + } + + /* :SyntheticMod */ + public Die subtract(Die... dice) { + return reduce((l, r) -> l - r, 0, dice); + } + + /* :SyntheticMod */ + public Die multiply(Die... dice) { + return reduce((l, r) -> l * r, 1, dice); + } + + /* :SyntheticMod */ + public Die divide(Die... dice) { + return reduce((l, r) -> l / r, 1, dice); + } + + /* :SyntheticMod */ + public Die ascending(Die die) { + return new SortDieMod(Comparator.naturalOrder(), die); + } + + /* :SyntheticMod */ + public Die descending(Die die) { + Comparator<Long> cmp = (Comparator<Long>)Comparator.naturalOrder().reversed(); + + return new SortDieMod(cmp, die); + } + + /* :SyntheticMod */ + public Die take(int num, Die die) { + GeneratingIterator<Integer> itr = new GeneratingIterator<>(num, (val) -> { + return val - 1; + }, (val) -> val == 0); + + return filter((val) -> { + if(itr.hasNext()) { + itr.next(); + return true; + } + + return false; + }, die); + } + + /* :SyntheticMod */ + public Die drop(int num, Die die) { + GeneratingIterator<Integer> itr = new GeneratingIterator<>(num, (val) -> { + return val - 1; + }, (val) -> val == 0); + + return filter((val) -> { + if(itr.hasNext()) { + itr.next(); + return false; + } + + return true; + }, die); + } + + /* :SyntheticMod */ + public Die rerollOnce(LongPredicate reroll, Die die) { + return map((val) -> { + if(reroll.test(val)) return die.rollSingle(); + + return val; + }, die); + } + + /* :SyntheticMod */ + public Die reroll(LongPredicate reroll, Die die) { + return map((val) -> { + long nVal = val; + + while(reroll.test(nVal)) { + nVal = die.rollSingle(); + } + + return nVal; + }, die); } } diff --git a/dice/src/main/java/bjc/dicelang/dicev2/Dies.java b/dice/src/main/java/bjc/dicelang/dicev2/Dies.java index 98ffbaa..1d29b55 100644 --- a/dice/src/main/java/bjc/dicelang/dicev2/Dies.java +++ b/dice/src/main/java/bjc/dicelang/dicev2/Dies.java @@ -1,16 +1,13 @@ package bjc.dicelang.dicev2; import java.util.Random; +import java.util.function.IntSupplier; public class Dies { - public static Die scalar(int val) { + public static Die scalar(long val) { return new ScalarDie(val); } - public static Die scalar(Random rnd, int val) { - return new ScalarDie(rnd, val); - } - public static Die polyhedral(int dice, int sides) { return new PolyhedralDie(dice, sides); } @@ -26,4 +23,36 @@ public class Dies { public static Die fudge(Random rnd, int dice) { return new FudgeDie(rnd, dice); } + + public static Die composite(Die numDice, Die numSides) { + return new CompositeDie(numDice, numSides); + } + + public static Die composite(Die numDice, Die numSides, boolean rerollSides) { + return new CompositeDie(numDice, numSides, rerollSides); + } + + public static Die composite(Random rnd, Die numDice, Die numSides) { + return new CompositeDie(rnd, numDice, numSides); + } + + public static Die composite(Random rnd, Die numDice, Die numSides, boolean rerollSides) { + return new CompositeDie(rnd, numDice, numSides, rerollSides); + } + + public static Die computed(IntSupplier numDice, IntSupplier numSides) { + return new ComputedDie(numDice, numSides); + } + + public static Die computed(IntSupplier numDice, IntSupplier numSides, boolean rerollSides) { + return new ComputedDie(numDice, numSides, rerollSides); + } + + public static Die computed(Random rnd, IntSupplier numDice, IntSupplier numSides) { + return new ComputedDie(rnd, numDice, numSides); + } + + public static Die computed(Random rnd, IntSupplier numDice, IntSupplier numSides, boolean rerollSides) { + return new ComputedDie(rnd, numDice, numSides, rerollSides); + } } diff --git a/dice/src/main/java/bjc/dicelang/dicev2/ExplodingDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/ExplodingDieMod.java new file mode 100644 index 0000000..885e02d --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/ExplodingDieMod.java @@ -0,0 +1,72 @@ +package bjc.dicelang.dicev2; + +import bjc.utils.funcutils.ListUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.LongPredicate; + +public class ExplodingDieMod extends Die { + public final Die[] dice; + + public final LongPredicate explode; + + public final boolean penetrate; + + public ExplodingDieMod(LongPredicate explode, Die... dice) { + this(explode, false, dice); + } + + public ExplodingDieMod(LongPredicate explode, boolean penetrate, Die... dice) { + super(); + + this.dice = dice; + + this.explode = explode; + + this.penetrate = penetrate; + } + + public long[] roll() { + List<Long> lst = new ArrayList<>(dice.length); + + for(Die die : dice) { + for(long val : die.roll()) { + lst.add(val); + + long newVal = val; + + while(explode.test(newVal)) { + newVal = die.rollSingle(); + + if(penetrate) newVal -= 1; + + lst.add(newVal); + } + } + } + + return ListUtils.toPrimitive(lst); + } + + /* + * @NOTE + * + * :NoSingleRolls + * + * It makes no sense to roll a 'single' exploding dice, since it + * exploding adds another die. Use compounding dice for that case. + */ + public long rollSingle() { + throw new UnsupportedOperationException("Exploding dice can't be rolled singly"); + } + + /* :UnoptimizableDice */ + public boolean canOptimize() { + return false; + } + + public long optimize() { + throw new UnsupportedOperationException("Exploding dice can't be optimized"); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/FilterDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/FilterDieMod.java new file mode 100644 index 0000000..47917c4 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/FilterDieMod.java @@ -0,0 +1,48 @@ +package bjc.dicelang.dicev2; + +import bjc.utils.funcutils.ListUtils; + +import java.util.ArrayList; +import java.util.List; + +import java.util.function.LongPredicate; + +public class FilterDieMod extends Die { + public final Die[] dice; + + public final LongPredicate filter; + + public FilterDieMod(LongPredicate filter, Die[] dice) { + super(); + + this.filter = filter; + + this.dice = dice; + } + + public long[] roll() { + List<Long> lst = new ArrayList<>(dice.length); + + for(Die die : dice) { + for(long val : die.roll()) { + if(filter.test(val)) lst.add(val); + } + } + + return ListUtils.toPrimitive(lst); + } + + public long rollSingle() { + throw new UnsupportedOperationException("Filtered dice can't be rolled singly"); + } + + /* :UnoptimizableDice */ + + public boolean canOptimize() { + return false; + } + + public long optimize() { + throw new UnsupportedOperationException("Filtered dice can't be optimized"); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/MapDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/MapDieMod.java new file mode 100644 index 0000000..d6d5dd0 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/MapDieMod.java @@ -0,0 +1,39 @@ +package bjc.dicelang.dicev2; + +import java.util.function.LongUnaryOperator; + +public class MapDieMod extends Die { + public final Die die; + + public final LongUnaryOperator map; + + public MapDieMod(LongUnaryOperator map, Die die) { + super(); + + this.die = die; + this.map = map; + } + + public long[] roll() { + long[] res = die.roll(); + + for(int i = 0; i < res.length; i++) { + res[i] = map.applyAsLong(res[i]); + } + + return res; + } + + public long rollSingle() { + return map.applyAsLong(die.rollSingle()); + } + + /* :UnoptimizableDice */ + public boolean canOptimize() { + return false; + } + + public long optimize() { + throw new UnsupportedOperationException("Mapped dice can't be optimized"); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/PoolDiceMod.java b/dice/src/main/java/bjc/dicelang/dicev2/PoolDiceMod.java new file mode 100644 index 0000000..8733908 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/PoolDiceMod.java @@ -0,0 +1,42 @@ +package bjc.dicelang.dicev2; + +import bjc.utils.funcutils.ListUtils; + +import java.util.ArrayList; +import java.util.List; + +public class PoolDiceMod extends Die { + public Die[] dice; + + public PoolDiceMod(Die... dice) { + super(); + + this.dice = dice; + } + + public long[] roll() { + List<Long> lst = new ArrayList<>(dice.length); + + for(Die die : dice) { + for(long val : die.roll()) { + lst.add(val); + } + } + + return ListUtils.toPrimitive(lst); + } + + /* :NoSingleRolls */ + public long rollSingle() { + throw new UnsupportedOperationException("Pooled dice can't be rolled singly"); + } + + /* :UnoptimizableDice */ + public boolean canOptimize() { + return false; + } + + public long optimize() { + throw new UnsupportedOperationException("Pooled dice can't be optimized"); + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/ReduceDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/ReduceDieMod.java new file mode 100644 index 0000000..266e270 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/ReduceDieMod.java @@ -0,0 +1,53 @@ +package bjc.dicelang.dicev2; + +import java.util.function.LongBinaryOperator; + +public class ReduceDieMod extends Die { + public final Die[] dice; + + public final LongBinaryOperator fold; + public final long initial; + + public ReduceDieMod(LongBinaryOperator fold, long initial, Die... dice) { + super(); + + this.dice = dice; + + this.fold = fold; + this.initial = initial; + } + + public long[] roll() { + return new long[] { rollSingle() }; + } + + public long rollSingle() { + long res = initial; + + for(Die die : dice) { + for(long val : die.roll()) { + res = fold.applyAsLong(res, val); + } + } + + return res; + } + + public boolean canOptimize() { + for(Die die : dice) { + if(!die.canOptimize()) return false; + } + + return true; + } + + public long optimize() { + long res = 0; + + for(Die die : dice) { + res = fold.applyAsLong(res, die.optimize()); + } + + return res; + } +} diff --git a/dice/src/main/java/bjc/dicelang/dicev2/ScalarDie.java b/dice/src/main/java/bjc/dicelang/dicev2/ScalarDie.java index 548b4e9..c206c25 100644 --- a/dice/src/main/java/bjc/dicelang/dicev2/ScalarDie.java +++ b/dice/src/main/java/bjc/dicelang/dicev2/ScalarDie.java @@ -11,14 +11,8 @@ public class ScalarDie extends Die { this.val = val; } - public ScalarDie(Random rnd, long val) { - super(rnd); - - this.val = val; - } - public long[] roll() { - return new long[] { val }; + return new long[] { rollSingle() }; } public long rollSingle() { diff --git a/dice/src/main/java/bjc/dicelang/dicev2/SortDieMod.java b/dice/src/main/java/bjc/dicelang/dicev2/SortDieMod.java new file mode 100644 index 0000000..50963e1 --- /dev/null +++ b/dice/src/main/java/bjc/dicelang/dicev2/SortDieMod.java @@ -0,0 +1,53 @@ +package bjc.dicelang.dicev2; + +import bjc.utils.funcutils.ListUtils; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.Comparator; + +public class SortDieMod extends Die { + public final Die die; + + public Comparator<Long> sorter; + + public SortDieMod(Comparator<Long> sorter, Die die) { + super(); + + this.sorter = sorter; + + this.die = die; + } + + public long[] roll() { + /* + * @NOTE + * + * This is likely quite a bit slower than using Arrays.sort, but + * that only sorts in ascending numeric order. If this ends up + * being a performance issue, add another sort that does that. + */ + List<Long> lst = new ArrayList<>(); + + for(long val : die.roll()) { + lst.add(val); + } + + lst.sort(sorter); + + return ListUtils.toPrimitive(lst); + } + + public long rollSingle() { + return die.rollSingle(); + } + + public boolean canOptimize() { + return die.canOptimize(); + } + + public long optimize() { + return die.optimize(); + } +} |
