diff --git a/common/src/main/java/io/github/gaming32/bingo/BingoCommand.java b/common/src/main/java/io/github/gaming32/bingo/BingoCommand.java index 48c0508f..35846fee 100644 --- a/common/src/main/java/io/github/gaming32/bingo/BingoCommand.java +++ b/common/src/main/java/io/github/gaming32/bingo/BingoCommand.java @@ -487,7 +487,7 @@ private static int startGame(CommandContext context) throws try { board = BingoBoard.generate( size, - difficulty.difficulty().number(), + difficulty.difficulty(), teams.size(), RandomSource.create(seed), gamemode::isGoalAllowed, diff --git a/common/src/main/java/io/github/gaming32/bingo/data/BingoDifficulty.java b/common/src/main/java/io/github/gaming32/bingo/data/BingoDifficulty.java index 087c9e54..1922533c 100644 --- a/common/src/main/java/io/github/gaming32/bingo/data/BingoDifficulty.java +++ b/common/src/main/java/io/github/gaming32/bingo/data/BingoDifficulty.java @@ -11,6 +11,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import io.github.gaming32.bingo.Bingo; import io.github.gaming32.bingo.util.ResourceLocations; +import it.unimi.dsi.fastutil.floats.FloatList; import net.minecraft.core.HolderLookup; import net.minecraft.network.chat.Component; import net.minecraft.resources.RegistryOps; @@ -29,20 +30,22 @@ import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; +import java.util.List; -public record BingoDifficulty(int number, @Nullable String fallbackName) { +public record BingoDifficulty(int number, @Nullable String fallbackName, @Nullable List distribution) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( ExtraCodecs.NON_NEGATIVE_INT.fieldOf("number").forGetter(BingoDifficulty::number), - Codec.STRING.optionalFieldOf("fallback_name").forGetter(d -> Optional.ofNullable(d.fallbackName)) + Codec.STRING.optionalFieldOf("fallback_name").forGetter(d -> Optional.ofNullable(d.fallbackName)), + Codec.FLOAT.listOf().optionalFieldOf("distribution").forGetter(dist -> Optional.ofNullable(dist.distribution)) ).apply(instance, BingoDifficulty::new) ); private static Map byId = Map.of(); private static NavigableMap byNumber = ImmutableSortedMap.of(); - private BingoDifficulty(int number, Optional fallbackName) { - this(number, fallbackName.orElse(null)); + private BingoDifficulty(int number, Optional fallbackName, Optional> distribution) { + this(number, fallbackName.orElse(null), distribution.orElse(null)); } public static Builder builder(ResourceLocation id) { @@ -96,6 +99,7 @@ public static final class Builder { private final ResourceLocation id; private Integer number; private String fallbackName; + private List distribution; private Builder(ResourceLocation id) { this.id = id; @@ -111,10 +115,24 @@ public Builder fallbackName(String name) { return this; } + public Builder distribution(int... scaledBy5x5) { + final float[] unscaled = new float[scaledBy5x5.length]; + for (int i = 0; i < scaledBy5x5.length; i++) { + unscaled[i] = scaledBy5x5[i] / 25f; + } + return this.distribution(unscaled); + } + + public Builder distribution(float... unscaledDistribution) { + this.distribution = FloatList.of(unscaledDistribution); + return this; + } + public Holder build() { return new Holder(id, new BingoDifficulty( Objects.requireNonNull(number, "number"), - fallbackName + fallbackName, + distribution )); } diff --git a/common/src/main/java/io/github/gaming32/bingo/game/BingoBoard.java b/common/src/main/java/io/github/gaming32/bingo/game/BingoBoard.java index e9cb1da3..837f52f6 100644 --- a/common/src/main/java/io/github/gaming32/bingo/game/BingoBoard.java +++ b/common/src/main/java/io/github/gaming32/bingo/game/BingoBoard.java @@ -74,7 +74,7 @@ private static BingoBoard create(int size, Teams[] states, ActiveGoal[] goals) { public static BingoBoard generate( int size, - int difficulty, + BingoDifficulty difficulty, int teamCount, RandomSource rand, BiPredicate isAllowedGoal, @@ -109,7 +109,7 @@ public static BingoBoard generate( public static BingoGoal.Holder[] generateGoals( int size, - int difficulty, + BingoDifficulty difficulty, RandomSource rand, BiPredicate isAllowedGoal, List requiredGoals, @@ -131,7 +131,7 @@ public static BingoGoal.Holder[] generateGoals( final Set catalysts = new HashSet<>(); for (final BingoTag.Holder tag : excludedTags) { - tagCount.put(tag.id(), tag.tag().getMaxForDifficulty(difficulty, size)); + tagCount.put(tag.id(), tag.tag().getMaxForDifficulty(difficulty.number(), size)); } for (int i = 0; i < size * size; i++) { @@ -188,7 +188,7 @@ public static BingoGoal.Holder[] generateGoals( if (!goalCandidate.goal().getTags().isEmpty()) { for (final BingoTag.Holder tag : goalCandidate.goal().getTags()) { - if (tagCount.getInt(tag.id()) >= tag.tag().getMaxForDifficulty(difficulty, size)) { + if (tagCount.getInt(tag.id()) >= tag.tag().getMaxForDifficulty(difficulty.number(), size)) { continue goalGen; } } @@ -267,25 +267,35 @@ private static boolean isOnSameLine(int size, int a, int b) { return false; } - private static int[] generateDifficulty(int size, int difficulty, RandomSource rand) { + private static int[] generateDifficulty(int size, BingoDifficulty difficulty, RandomSource rand) { final int[] layout = new int[size * size]; - final Iterator available = BingoDifficulty.getNumbers() - .headSet(difficulty, true) - .descendingIterator(); - if (!available.hasNext()) { - throw new IllegalArgumentException("No difficulty exists with number " + difficulty); - } - final int difficulty1 = available.next(); - if (!available.hasNext()) { - Arrays.fill(layout, difficulty1); + if (difficulty.distribution() != null) { + List scaledDistribution = difficulty.distribution().stream().map(f -> Math.round(f * size * size)).toList(); + int p = 0; + for (int difficultyLevel = 0; difficultyLevel < scaledDistribution.size(); ++difficultyLevel) { + for (int i = 0; i < scaledDistribution.get(difficultyLevel) && p < layout.length; ++i) + layout[p++] = difficultyLevel; + } + BingoUtil.shuffle(layout, rand); } else { - final int difficulty2 = available.next(); - final int amountOf1 = rand.nextInt(size * size * 3 / 5, size * size * 3 / 5 + size); - final int[] indices = BingoUtil.shuffle(BingoUtil.generateIntArray(size * size), rand); - Arrays.fill(layout, difficulty2); - for (int i = 0; i < amountOf1; i++) { - layout[indices[i]] = difficulty1; + final Iterator available = BingoDifficulty.getNumbers() + .headSet(difficulty.number(), true) + .descendingIterator(); + if (!available.hasNext()) { + throw new IllegalArgumentException("No difficulty exists with number " + difficulty); + } + final int difficulty1 = available.next(); + if (!available.hasNext()) { + Arrays.fill(layout, difficulty1); + } else { + final int difficulty2 = available.next(); + final int amountOf1 = rand.nextInt(size * size * 3 / 5, size * size * 3 / 5 + size); + final int[] indices = BingoUtil.shuffle(BingoUtil.generateIntArray(size * size), rand); + Arrays.fill(layout, difficulty2); + for (int i = 0; i < amountOf1; i++) { + layout[indices[i]] = difficulty1; + } } } return layout;