From fc63cee4c0284635c601889ce49a892cf73082ff Mon Sep 17 00:00:00 2001 From: HoleFish <48403212+HoleFish@users.noreply.github.com> Date: Thu, 4 Jan 2024 06:13:42 +0800 Subject: Update ParallelHelper (#2427) * Update GT_ParallelHelper.java * Update GT_Recipe.java * fix and comment * unit test --- .../java/gregtech/api/util/GT_ParallelHelper.java | 100 ++++++++++++++------- src/main/java/gregtech/api/util/GT_Recipe.java | 20 ++--- 2 files changed, 80 insertions(+), 40 deletions(-) (limited to 'src/main') diff --git a/src/main/java/gregtech/api/util/GT_ParallelHelper.java b/src/main/java/gregtech/api/util/GT_ParallelHelper.java index e836122f37..6ec736b15f 100644 --- a/src/main/java/gregtech/api/util/GT_ParallelHelper.java +++ b/src/main/java/gregtech/api/util/GT_ParallelHelper.java @@ -1,7 +1,9 @@ package gregtech.api.util; +import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; +import java.util.Random; import java.util.function.Function; import javax.annotation.Nonnull; @@ -373,12 +375,12 @@ public class GT_ParallelHelper { double tickTimeAfterOC = calculator.setParallel(originalMaxParallel) .calculateDurationUnderOneTick(); if (tickTimeAfterOC < 1) { - maxParallel = (int) (maxParallel / tickTimeAfterOC); + maxParallel = GT_Utility.safeInt((long) (maxParallel / tickTimeAfterOC), 0); } int maxParallelBeforeBatchMode = maxParallel; if (batchMode) { - maxParallel *= batchModifier; + maxParallel = GT_Utility.safeInt((long) maxParallel * batchModifier, 0); } final ItemStack[] truncatedItemOutputs = recipe.mOutputs != null @@ -511,31 +513,42 @@ public class GT_ParallelHelper { itemOutputs = customItemOutputCalculation.apply(currentParallel); return; } - itemOutputs = new ItemStack[truncatedItemOutputs.length]; + ArrayList itemOutputsList = new ArrayList<>(); + Random rand = new Random(); for (int i = 0; i < truncatedItemOutputs.length; i++) { - if (recipe.getOutputChance(i) >= 10000) { - ItemStack item = recipe.getOutput(i) - .copy(); - item.stackSize *= currentParallel; - itemOutputs[i] = item; - continue; - } - int items = 0; - int itemStackSize = recipe.getOutput(i).stackSize; - for (int roll = 0; roll < currentParallel; roll++) { - if (recipe.getOutputChance(i) > XSTR.XSTR_INSTANCE.nextInt(10000)) { - items += itemStackSize; - } - } - ItemStack item = recipe.getOutput(i) + if (recipe.getOutput(i) == null) continue; + long items = 0; + ItemStack origin = recipe.getOutput(i) .copy(); - if (items == 0) { - item = null; + final long itemStackSize = origin.stackSize; + + if (recipe.getOutputChance(i) >= 10000) { + items = itemStackSize * currentParallel; } else { - item.stackSize = items; + double chance = (double) recipe.getOutputChance(i) / 10000; + double mean = currentParallel * chance; + double stdDev = Math.sqrt(currentParallel * chance * (1 - chance)); + // Check if everything within 3 standard deviations of mean is within the range + // of possible values (0 ~ currentParallel) + boolean isSuitableForFittingWithNormalDistribution = mean - 3 * stdDev >= 0 + && mean + 3 * stdDev <= currentParallel; + if (isSuitableForFittingWithNormalDistribution) { + // Use Normal Distribution to fit Binomial Distribution + items = (long) Math.ceil(itemStackSize * (stdDev * rand.nextGaussian() + mean)); + items = Math.max(Math.min(items, itemStackSize * currentParallel), 0); + } else { + // Do Binomial Distribution by loop + for (int roll = 0; roll < currentParallel; roll++) { + if (recipe.getOutputChance(i) > XSTR.XSTR_INSTANCE.nextInt(10000)) { + items += itemStackSize; + } + } + } } - itemOutputs[i] = item; + + addItemsLong(itemOutputsList, origin, items); } + itemOutputs = itemOutputsList.toArray(new ItemStack[0]); } private void calculateFluidOutputs(FluidStack[] truncatedFluidOutputs) { @@ -543,16 +556,43 @@ public class GT_ParallelHelper { fluidOutputs = customFluidOutputCalculation.apply(currentParallel); return; } - fluidOutputs = new FluidStack[truncatedFluidOutputs.length]; + ArrayList fluidOutputsList = new ArrayList<>(); for (int i = 0; i < truncatedFluidOutputs.length; i++) { - if (recipe.getFluidOutput(i) == null) { - fluidOutputs[i] = null; - } else { - FluidStack tFluid = recipe.getFluidOutput(i) - .copy(); - tFluid.amount *= currentParallel; - fluidOutputs[i] = tFluid; + if (recipe.getFluidOutput(i) == null) continue; + FluidStack origin = recipe.getFluidOutput(i) + .copy(); + long fluids = (long) origin.amount * currentParallel; + + addFluidsLong(fluidOutputsList, origin, fluids); + } + fluidOutputs = fluidOutputsList.toArray(new FluidStack[0]); + } + + public static void addItemsLong(ArrayList itemList, ItemStack origin, long amount) { + if (amount >= 0) { + while (amount > Integer.MAX_VALUE) { + ItemStack item = origin.copy(); + item.stackSize = Integer.MAX_VALUE; + itemList.add(item); + amount -= Integer.MAX_VALUE; + } + ItemStack item = origin.copy(); + item.stackSize = (int) amount; + itemList.add(item); + } + } + + public static void addFluidsLong(ArrayList fluidList, FluidStack origin, long amount) { + if (amount >= 0) { + while (amount > Integer.MAX_VALUE) { + FluidStack fluid = origin.copy(); + fluid.amount = Integer.MAX_VALUE; + fluidList.add(fluid); + amount -= Integer.MAX_VALUE; } + FluidStack fluid = origin.copy(); + fluid.amount = (int) amount; + fluidList.add(fluid); } } diff --git a/src/main/java/gregtech/api/util/GT_Recipe.java b/src/main/java/gregtech/api/util/GT_Recipe.java index 57f619eb7e..159ac1bbd1 100644 --- a/src/main/java/gregtech/api/util/GT_Recipe.java +++ b/src/main/java/gregtech/api/util/GT_Recipe.java @@ -415,12 +415,12 @@ public class GT_Recipe implements Comparable { public void consumeInput(int amountMultiplier, FluidStack[] aFluidInputs, ItemStack... aInputs) { if (amountMultiplier <= 0) return; - int remainingCost; + long remainingCost; if (aFluidInputs != null) { for (FluidStack recipeFluidCost : mFluidInputs) { if (recipeFluidCost != null) { - remainingCost = recipeFluidCost.amount * amountMultiplier; + remainingCost = (long) recipeFluidCost.amount * amountMultiplier; for (FluidStack providedFluid : aFluidInputs) { if (providedFluid != null && providedFluid.isFluidEqual(recipeFluidCost)) { @@ -441,7 +441,7 @@ public class GT_Recipe implements Comparable { for (ItemStack recipeItemCost : mInputs) { ItemStack unifiedItemCost = GT_OreDictUnificator.get_nocopy(true, recipeItemCost); if (unifiedItemCost != null) { - remainingCost = recipeItemCost.stackSize * amountMultiplier; + remainingCost = (long) recipeItemCost.stackSize * amountMultiplier; for (ItemStack providedItem : aInputs) { if (isNBTSensitive && !GT_Utility.areStacksEqual(providedItem, unifiedItemCost, false)) { @@ -484,23 +484,23 @@ public class GT_Recipe implements Comparable { if (aFluidInputs != null) { // Create map for fluid -> stored amount - Map fluidMap = new HashMap<>(); - Map fluidCost = new HashMap<>(); + Map fluidMap = new HashMap<>(); + Map fluidCost = new HashMap<>(); for (FluidStack fluidStack : aFluidInputs) { if (fluidStack == null) continue; - fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); + fluidMap.merge(fluidStack.getFluid(), (long) fluidStack.amount, Long::sum); } for (FluidStack fluidStack : mFluidInputs) { if (fluidStack == null) continue; - fluidCost.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); + fluidCost.merge(fluidStack.getFluid(), (long) fluidStack.amount, Long::sum); } // Check how many parallels can it perform for each fluid - for (Map.Entry costEntry : fluidCost.entrySet()) { + for (Map.Entry costEntry : fluidCost.entrySet()) { if (costEntry.getValue() > 0) { currentParallel = Math.min( currentParallel, - (double) fluidMap.getOrDefault(costEntry.getKey(), 0) / costEntry.getValue()); + (double) fluidMap.getOrDefault(costEntry.getKey(), 0L) / costEntry.getValue()); } if (currentParallel <= 0) { return 0; @@ -509,7 +509,7 @@ public class GT_Recipe implements Comparable { } double remainingCost; - int providedAmount; + long providedAmount; if (aInputs != null) { nextRecipeItemCost: for (ItemStack recipeItemCost : mInputs) { -- cgit