Introduce ThingPicker.

Things work best as hardwired, atomic pieces of "stuff", so fitting RNG
into the framework is a little difficult. We could have a RandomThing,
but the issue with that is that the entropy has to live inside of it for
it to be random every time it is given, taken, or checked. This makes it
impossible to make a well-defined, atomic Thing with entropy involved.

Enter the pickers.

The ThingPicker is an early stage extension to the Things framework that
encapsulates the possibilities of entropy and grouping _around_ Things,
a type of Factory pattern.

This introductory stage consists of a "leaf picker" that can only ever
pick a single Thing, a Composite Pattern "group picker" that wraps a
list of other pickers and shoves their resulting Thing instances into a
ThingGroup (introduced in the previous commit).

These simple constructs should now make it possible to introduce entropy
without placing the responsibility on the Thing framework itself. Using
this extension requires adapting code to using the ThingPicker interface
instead of using Thing directly.
This commit is contained in:
Andreas Troelsen
2020-08-22 16:10:07 +02:00
parent e1784552de
commit e8bb8a9e4d
6 changed files with 190 additions and 0 deletions
@@ -0,0 +1,31 @@
package com.garbagemule.MobArena.things;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class RandomThingPicker implements ThingPicker {
private final List<ThingPicker> pickers;
private final Random random;
public RandomThingPicker(List<ThingPicker> pickers, Random random) {
this.pickers = pickers;
this.random = random;
}
@Override
public Thing pick() {
int index = random.nextInt(pickers.size());
return pickers.get(index).pick();
}
@Override
public String toString() {
String list = pickers.stream()
.map(ThingPicker::toString)
.collect(Collectors.joining(" or "));
return "(" + list + ")";
}
}
@@ -0,0 +1,21 @@
package com.garbagemule.MobArena.things;
public class SingleThingPicker implements ThingPicker {
private final Thing thing;
public SingleThingPicker(Thing thing) {
this.thing = thing;
}
@Override
public Thing pick() {
return thing;
}
@Override
public String toString() {
return thing.toString();
}
}
@@ -0,0 +1,31 @@
package com.garbagemule.MobArena.things;
import java.util.List;
import java.util.stream.Collectors;
public class ThingGroupPicker implements ThingPicker {
private final List<ThingPicker> pickers;
public ThingGroupPicker(List<ThingPicker> pickers) {
this.pickers = pickers;
}
@Override
public Thing pick() {
List<Thing> things = pickers.stream()
.map(ThingPicker::pick)
.collect(Collectors.toList());
return new ThingGroup(things);
}
@Override
public String toString() {
String list = pickers.stream()
.map(ThingPicker::toString)
.collect(Collectors.joining(" and "));
return "(" + list + ")";
}
}
@@ -0,0 +1,22 @@
package com.garbagemule.MobArena.things;
/**
* Pickers encapsulate a type of highly specific Factory pattern that revolves
* around choosing from an arbitrary pool of Thing instances without directly
* giving them to or taking them from players.
* <p>
* The interface exposes a single method, {@link #pick()}. When invoked, the
* implementation is expected to resolve or create a {@link Thing} instance
* or null. The how and why is entirely up to the implementation, but callers
* must not depend on the return value being stable, even though it may be.
*/
public interface ThingPicker {
/**
* Pick a thing.
*
* @return a {@link Thing} instance, or null
*/
Thing pick();
}