Skip to content

Instantly share code, notes, and snippets.

@OLibutzki
Last active September 8, 2020 15:36
Show Gist options
  • Save OLibutzki/c973ba781f922d30271f5d4ee8fb8d93 to your computer and use it in GitHub Desktop.
Save OLibutzki/c973ba781f922d30271f5d4ee8fb8d93 to your computer and use it in GitHub Desktop.
//usr/bin/env jbang "$0" "$@" ; exit $?
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class FirstOrchard {
private static final int iterations = 1000000;
public static void main(String... args) throws InterruptedException {
run(Strategy.MINIMUM_FIRST);
run(Strategy.MAXIMUM_FIRST);
}
private static void run(Strategy strategy) throws InterruptedException {
final AtomicInteger ravenVictories = new AtomicInteger();
final AtomicInteger ravenDefeats = new AtomicInteger();
final int availableProcessors = Runtime.getRuntime().availableProcessors();
ExecutorService executorService = Executors.newFixedThreadPool(availableProcessors);
try {
CountDownLatch latch = new CountDownLatch(iterations);
for (long i = 0; i < iterations; i++) {
executorService.execute(() -> {
SimulationResult result = new FirstOrchard().simulate(strategy);
switch (result) {
case RAVEN_WINS:
ravenVictories.incrementAndGet();
break;
case RAVEN_LOSES:
ravenDefeats.incrementAndGet();
break;
}
latch.countDown();
});
}
latch.await();
} finally {
executorService.shutdown();
}
BigDecimal winRate = BigDecimal.valueOf(ravenVictories.get()).divide(BigDecimal.valueOf(iterations)).multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP);
System.out.printf("%s: Raven victories: %s (%s%%), defeats: %s%n", strategy, ravenVictories, winRate, ravenDefeats);
}
private final Die die = new Die();
private int remainingStepsRaven = 6;
private int[] remainingFruits = { 4, 4, 4, 4 };
private SimulationResult simulate(Strategy strategy) {
while (remainingStepsRaven > 0 && atLeastOneBasketContainsFruits()) {
RollResult rollResult = die.roll();
switch (rollResult) {
case FRUIT1:
case FRUIT2:
case FRUIT3:
case FRUIT4:
if (remainingFruits[rollResult.index] > 0) {
remainingFruits[rollResult.index]--;
}
break;
case RAVEN:
remainingStepsRaven--;
break;
case BASKET:
switch (strategy) {
case MINIMUM_FIRST:
reduceMinimum();
break;
case MAXIMUM_FIRST:
reduceMaximum();
break;
}
break;
}
}
if (remainingStepsRaven == 0) {
return SimulationResult.RAVEN_WINS;
} else {
return SimulationResult.RAVEN_LOSES;
}
}
private void reduceMaximum() {
int maxIdx = IntStream.range(0, remainingFruits.length)
.reduce((i, j) -> remainingFruits[i] < remainingFruits[j] ? j : i).getAsInt();
remainingFruits[maxIdx]--;
}
private void reduceMinimum() {
int minIdx = IntStream.range(0, remainingFruits.length)
.reduce((i, j) -> remainingFruits[j] > 0 && remainingFruits[i] > remainingFruits[j] ? j : i).getAsInt();
remainingFruits[minIdx]--;
}
private boolean atLeastOneBasketContainsFruits() {
return Arrays.stream(remainingFruits).anyMatch(i -> i > 0);
}
private static enum Strategy {
MAXIMUM_FIRST, MINIMUM_FIRST;
}
private static enum SimulationResult {
RAVEN_WINS, RAVEN_LOSES
}
private static enum RollResult {
FRUIT1(0), FRUIT2(1), FRUIT3(2), FRUIT4(3), BASKET(4), RAVEN(5);
private final int index;
private RollResult(int index) {
this.index = index;
}
private static final Map<Integer, RollResult> lookup = new HashMap<>();
static {
for (RollResult dieResult : RollResult.values()) {
lookup.put(dieResult.index, dieResult);
}
}
private static RollResult get(int index) {
return lookup.get(index);
}
}
private static class Die {
private final Random random = new Random();
private RollResult roll() {
return RollResult.get(random.nextInt(6));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment