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 1a798203..b1b6d669 100644 --- a/common/src/main/java/io/github/gaming32/bingo/BingoCommand.java +++ b/common/src/main/java/io/github/gaming32/bingo/BingoCommand.java @@ -433,7 +433,7 @@ private static int startGame(CommandContext context, int tea try { board = BingoBoard.generate( size, - difficulty.value().number(), + difficulty.value(), 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 07e6fe27..18b2591e 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 @@ -9,17 +9,42 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.util.ExtraCodecs; +import org.jetbrains.annotations.Nullable; +import java.util.Arrays; +import java.util.List; import java.util.NavigableSet; +import java.util.Optional; -public record BingoDifficulty(Component description, int number) { +public record BingoDifficulty(Component description, int number, @Nullable List distribution) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( ComponentSerialization.CODEC.fieldOf("description").forGetter(BingoDifficulty::description), - ExtraCodecs.NON_NEGATIVE_INT.fieldOf("number").forGetter(BingoDifficulty::number) + ExtraCodecs.NON_NEGATIVE_INT.fieldOf("number").forGetter(BingoDifficulty::number), + Codec.FLOAT.listOf().optionalFieldOf("distribution").forGetter(dist -> Optional.ofNullable(dist.distribution)) ).apply(instance, BingoDifficulty::new) ); + private BingoDifficulty(Component description, int number, Optional> distribution) { + this(description, number, distribution.orElse(null)); + } + + public BingoDifficulty(Component description, int number) { + this(description, number, Optional.empty()); + } + + private static List unscale5x5(int[] scaledBy5x5) { + final Float[] unscaled = new Float[scaledBy5x5.length]; + for (int i = 0; i < scaledBy5x5.length; i++) { + unscaled[i] = scaledBy5x5[i] / 25f; + } + return Arrays.asList(unscaled); + } + + public BingoDifficulty(Component description, int number, int[] scaledBy5x5) { + this(description, number, unscale5x5(scaledBy5x5)); + } + public static NavigableSet getNumbers(HolderLookup lookup) { return lookup.listElements() .map(Holder.Reference::value) 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 fa71a7f3..78f2fee5 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 @@ -80,7 +80,7 @@ private static BingoBoard create(PartiallyParsed parsed) { public static BingoBoard generate( int size, - int difficulty, + BingoDifficulty difficulty, int teamCount, RandomSource rand, Predicate isAllowedGoal, @@ -120,7 +120,7 @@ public static BingoBoard generate( public static GoalHolder[] generateGoals( HolderLookup difficultyLookup, int size, - int difficulty, + BingoDifficulty difficulty, RandomSource rand, Predicate isAllowedGoal, Collection requiredGoals, @@ -142,7 +142,7 @@ public static GoalHolder[] generateGoals( final Set catalysts = new HashSet<>(); for (final var tag : excludedTags) { - tagCount.put(tag, tag.value().getMaxForDifficulty(difficulty, size)); + tagCount.put(tag, tag.value().getMaxForDifficulty(difficulty.number(), size)); } for (int i = 0; i < size * size; i++) { @@ -197,7 +197,7 @@ public static GoalHolder[] generateGoals( if (goalCandidate.goal().getTags().size() != 0) { for (final var tag : goalCandidate.goal().getTags()) { - if (tagCount.getInt(tag) >= tag.value().getMaxForDifficulty(difficulty, size)) { + if (tagCount.getInt(tag) >= tag.value().getMaxForDifficulty(difficulty.number(), size)) { continue goalGen; } } @@ -263,23 +263,43 @@ private static boolean isOnSameLine(int size, int a, int b) { return false; } - private static int[] generateDifficulty(NavigableSet difficulties, int size, int difficulty, RandomSource rand) { + private static int[] generateDifficulty(NavigableSet difficulties, int size, BingoDifficulty difficulty, RandomSource rand) { final int[] layout = new int[size * size]; - final Iterator available = difficulties.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) { + Arrays.fill(layout, difficulty.number()); + float[] cumDistribution = new float[difficulty.distribution().size()]; + for (int d = 0; d < cumDistribution.length; d++) { + cumDistribution[d] = difficulty.distribution().get(d) * layout.length; + if (d > 0) { + cumDistribution[d] += cumDistribution[d - 1]; + } + } + for (int i = 0; i < layout.length; ++i) { + for (int d = 0; d < cumDistribution.length; d++) { + if ((i + 1) <= cumDistribution[d]) { + layout[i] = d; + break; + } + } + } + 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 = difficulties.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;