package slimeknights.tconstruct.debug;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.item.ItemStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.text.TextComponentString;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import slimeknights.tconstruct.library.TinkerRegistry;
import slimeknights.tconstruct.library.materials.Material;
import slimeknights.tconstruct.library.materials.MaterialTypes;
import slimeknights.tconstruct.library.tools.ToolCore;
import slimeknights.tconstruct.library.utils.ToolHelper;
import slimeknights.tconstruct.tools.harvest.TinkerHarvestTools;
public class FindBestTool extends CommandBase {
@Override
public int getRequiredPermissionLevel() {
return 0;
}
@Override
public String getName() {
return "findBestTool";
}
@Override
public String getUsage(ICommandSender sender) {
return "/findBestTool [number of tools to display] <filter type>";
}
@Override
public void execute(@Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args)
throws CommandException {
int num, filtertype;
if(args.length < 1) {
throw new CommandException("Too few arguments");
}
else if(args.length < 2) {
num = 100;
filtertype = Integer.valueOf(args[0]);
}
else {
num = Integer.valueOf(args[0]);
filtertype = Integer.valueOf(args[1]);
}
Predicate<Triple<ItemStack, ImmutableList<Material>, Object[]>> filter[] = new Predicate[3];
if(num < 0 || filtertype < 0 || filtertype > filter.length) {
throw new CommandException("Inavlid arguments");
}
ToolCore tool = TinkerHarvestTools.pickaxe;
List<Triple<ItemStack, ImmutableList<Material>, Object[]>> results = Lists.newArrayList();
@SuppressWarnings("unchecked")
Function<ItemStack, ?> functions[] = new Function[]{
new Function<ItemStack, Integer>() {
@Override
public Integer apply(ItemStack itemStack) {
return ToolHelper.getDurabilityStat(itemStack);
}
},
new Function<ItemStack, Float>() {
@Override
public Float apply(ItemStack itemStack) {
return ToolHelper.getMiningSpeedStat(itemStack);
}
},
new Function<ItemStack, Float>() {
@Override
public Float apply(ItemStack itemStack) {
return ToolHelper.getAttackStat(itemStack);
}
}
};
recurse(tool, ImmutableList.<Material>of(), results, functions);
//Collections.sort(results, Comp.INSTANCE);
List<Integer> durabilities = Lists.transform(results, new com.google.common.base.Function<Triple<ItemStack, ImmutableList<Material>, Object[]>, Integer>() {
@Nullable
@Override
public Integer apply(Triple<ItemStack, ImmutableList<Material>, Object[]> input) {
return (Integer) input.getRight()[0];
}
});
List<Float> speeds = Lists.transform(results, new com.google.common.base.Function<Triple<ItemStack, ImmutableList<Material>, Object[]>, Float>() {
@Nullable
@Override
public Float apply(Triple<ItemStack, ImmutableList<Material>, Object[]> input) {
return (Float) input.getRight()[1];
}
});
List<Float> attacks = Lists.transform(results, new com.google.common.base.Function<Triple<ItemStack, ImmutableList<Material>, Object[]>, Float>() {
@Nullable
@Override
public Float apply(Triple<ItemStack, ImmutableList<Material>, Object[]> input) {
return (Float) input.getRight()[2];
}
});
Collection<Triple<ItemStack, ImmutableList<Material>, Object[]>> best;
float percentile = 0.5f;
int durPercentile = (int) getPercentile(Ordering.natural().reverse().sortedCopy(durabilities), percentile);
float speedPercentile = (float) getPercentile(Ordering.natural().reverse().sortedCopy(speeds), percentile);
float attackPercentile = (float) getPercentile(Ordering.natural().reverse().sortedCopy(attacks), percentile);
do {
percentile /= 2f;
// calculate upper quartile of durability
final int durPercentile2 = (int) getPercentile(Ordering.natural().reverse().sortedCopy(durabilities), percentile);
final float speedPercentile2 = (float) getPercentile(Ordering.natural().reverse().sortedCopy(speeds), percentile);
final float attackPercentile2 = (float) getPercentile(Ordering.natural().reverse().sortedCopy(attacks), percentile);
durPercentile = durPercentile2;
speedPercentile = speedPercentile2;
attackPercentile = attackPercentile2;
filter[0] = new Predicate<Triple<ItemStack, ImmutableList<Material>, Object[]>>() {
@Override
public boolean apply(@Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> entry) {
return ((Integer) entry.getRight()[0]) > durPercentile2
&& ((Float) entry.getRight()[1]) > speedPercentile2;
}
};
filter[1] = new Predicate<Triple<ItemStack, ImmutableList<Material>, Object[]>>() {
@Override
public boolean apply(@Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> entry) {
return ((Integer) entry.getRight()[0]) > durPercentile2
&& ((Float) entry.getRight()[2]) > attackPercentile2;
}
};
filter[2] = new Predicate<Triple<ItemStack, ImmutableList<Material>, Object[]>>() {
@Override
public boolean apply(@Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> entry) {
return ((Integer) entry.getRight()[0]) > durPercentile2
&& ((Float) entry.getRight()[1]) > speedPercentile2
&& ((Float) entry.getRight()[2]) > attackPercentile2;
}
};
// get all tools that are above in both quartiles
best = Collections2.filter(results, filter[filtertype]);
} while(best.size() > num);
sender.sendMessage(new TextComponentString(String.format("%d are in the top %d percentile of stats (%d; %f; %f)", best.size(), (int) (percentile * 100f), durPercentile, speedPercentile, attackPercentile)));
Collection<Triple<ItemStack, ImmutableList<Material>, Object[]>> sortedDurability = new Ordering<Triple<ItemStack, ImmutableList<Material>, Object[]>>() {
@Override
public int compare(@Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> left, @Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> right) {
return (Integer) right.getRight()[0] - (Integer) left.getRight()[0];
}
}.sortedCopy(best);
Collection<Triple<ItemStack, ImmutableList<Material>, Object[]>> sortedSpeed = new Ordering<Triple<ItemStack, ImmutableList<Material>, Object[]>>() {
@Override
public int compare(@Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> left, @Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> right) {
return (int) ((Float) right.getRight()[1] * 10f) - (int) ((Float) left.getRight()[1] * 10f);
}
}.sortedCopy(best);
Collection<Triple<ItemStack, ImmutableList<Material>, Object[]>> sortedAttack = new Ordering<Triple<ItemStack, ImmutableList<Material>, Object[]>>() {
@Override
public int compare(@Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> left, @Nullable Triple<ItemStack, ImmutableList<Material>, Object[]> right) {
return (int) ((Float) right.getRight()[2] * 10f) - (int) ((Float) left.getRight()[2] * 10f);
}
}.sortedCopy(best);
for(Triple<ItemStack, ImmutableList<Material>, Object[]> foo : best) {
StringBuilder text = new StringBuilder();
text.append("Materials: ");
for(Material mat : foo.getMiddle()) {
text.append(mat.getIdentifier());
text.append(" ");
}
text.append("- ");
text.append("Dur: ");
text.append(foo.getRight()[0]);
text.append(" Speed: ");
text.append((Float) foo.getRight()[1] * tool.miningSpeedModifier());
text.append(" Dmg: ");
text.append((Float) foo.getRight()[2] * tool.damagePotential());
sender.sendMessage(foo.getLeft().getTextComponent().appendSibling(new TextComponentString(text.toString())));
//System.out.println(text.toString());
}
sender.sendMessage(new TextComponentString("Top 5 Durability:"));
Iterator<Triple<ItemStack, ImmutableList<Material>, Object[]>> iter = sortedDurability.iterator();
for(int i = 0; i < 5 && iter.hasNext(); i++) {
Triple<ItemStack, ImmutableList<Material>, Object[]> foo = iter.next();
StringBuilder text = new StringBuilder();
text.append(foo.getRight()[0]);
text.append(" - ");
for(Material mat : foo.getMiddle()) {
text.append(mat.getIdentifier());
text.append(" ");
}
sender.sendMessage(foo.getLeft().getTextComponent().appendSibling(new TextComponentString(text.toString())));
}
sender.sendMessage(new TextComponentString("Top 5 Speed:"));
iter = sortedSpeed.iterator();
for(int i = 0; i < 5 && iter.hasNext(); i++) {
Triple<ItemStack, ImmutableList<Material>, Object[]> foo = iter.next();
StringBuilder text = new StringBuilder();
text.append(foo.getRight()[1]);
text.append(" - ");
for(Material mat : foo.getMiddle()) {
text.append(mat.getIdentifier());
text.append(" ");
}
sender.sendMessage(foo.getLeft().getTextComponent().appendSibling(new TextComponentString(text.toString())));
}
sender.sendMessage(new TextComponentString("Top 5 Attack:"));
iter = sortedAttack.iterator();
for(int i = 0; i < 5 && iter.hasNext(); i++) {
Triple<ItemStack, ImmutableList<Material>, Object[]> foo = iter.next();
StringBuilder text = new StringBuilder();
text.append(foo.getRight()[2]);
text.append(" - ");
for(Material mat : foo.getMiddle()) {
text.append(mat.getIdentifier());
text.append(" ");
}
sender.sendMessage(foo.getLeft().getTextComponent().appendSibling(new TextComponentString(text.toString())));
}
}
public void recurse(ToolCore tool, ImmutableList<Material> materials, List<Triple<ItemStack, ImmutableList<Material>, Object[]>> results, Function<ItemStack, ?> fns[]) {
// not enough materials yet, recurse
if(tool.getRequiredComponents().size() > materials.size()) {
for(Material mat : TinkerRegistry.getAllMaterials()) {
if(!mat.hasStats(MaterialTypes.HEAD)) {
continue;
}
ImmutableList.Builder<Material> mats = ImmutableList.builder();
mats.addAll(materials);
mats.add(mat);
recurse(tool, mats.build(), results, fns);
}
}
// enough materials, build it and do stuff with it!
else {
ItemStack stack = tool.buildItem(materials);
Object[] values = new Object[fns.length];
for(int i = 0; i < fns.length; i++) {
values[i] = fns[i].apply(stack);
}
results.add(Triple.of(stack, materials, values));
}
}
private enum Comp implements Comparator<Pair<ImmutableList<Material>, Integer>> {
INSTANCE;
@Override
public int compare(Pair<ImmutableList<Material>, Integer> o1, Pair<ImmutableList<Material>, Integer> o2) {
return o2.getRight() - o1.getRight();
}
}
private <T extends Number> double getPercentile(List<T> entries, float percentile) {
int coeff = (int) (1f / percentile);
if(entries.size() % 2 == 1) {
return entries.get(entries.size() / coeff).doubleValue();
}
T v1 = entries.get(entries.size() / coeff);
T v2 = entries.get(entries.size() / coeff + 1);
return ((v1.doubleValue() + v2.doubleValue()) / 2d);
}
}