Moved to gradle & started work on separating API and plugin

This commit is contained in:
Badbird-5907
2022-06-17 00:22:47 -04:00
parent d0bfbb966a
commit b052a04ddb
55 changed files with 273 additions and 233 deletions

View File

@@ -0,0 +1,91 @@
package net.nuggetmc.tplus.api;
import com.mojang.authlib.GameProfile;
import net.kyori.adventure.text.Component;
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
public interface Terminator {
String getBotName();
GameProfile getGameProfile();
NeuralNetwork getNeuralNetwork();
void setNeuralNetwork(NeuralNetwork neuralNetwork);
boolean hasNeuralNetwork();
Location getLocation();
float getHealth();
float getMaxHealth();
void ignite();
boolean isOnFire();
boolean isFalling();
boolean isBlocking();
void block(int length, int cooldown);
boolean isInWater();
void jump(Vector velocity);
void jump();
void walk(Vector velocity);
void look(BlockFace face);
void attack(Entity target);
void attemptBlockPlace(Location loc, Material type, boolean down);
void punch();
void swim();
void sneak();
void stand();
void addFriction(double factor);
void removeVisually();
int getKills();
void incrementKills();
void setItem(ItemStack item);
void setItem(ItemStack item, EquipmentSlot slot);
void setItemOffhand(ItemStack item);
void setDefaultItem(ItemStack item);
Vector getOffset();
Vector getVelocity();
void setVelocity(Vector velocity);
void addVelocity(Vector velocity);
int getAliveTicks();
boolean tickDelay(int ticks);
}

View File

@@ -0,0 +1,9 @@
package net.nuggetmc.tplus.api.agent.legacyagent.ai;
public enum ActivationType {
TANH,
SIN_X,
SIN_X2,
COS_X,
COS_X2
}

View File

@@ -0,0 +1,53 @@
package net.nuggetmc.tplus.api.agent.legacyagent.ai;
import net.nuggetmc.tplus.api.Terminator;
import net.nuggetmc.tplus.utils.MathUtils;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import java.util.*;
// If this is laggy, try only instantiating this once and update it instead of creating a new instance every tick
public class BotData {
private final Map<BotDataType, Double> values;
private BotData(Terminator bot, LivingEntity target) {
this.values = new HashMap<>();
Location a = bot.getLocation();
Location b = target.getLocation();
float health = bot.getHealth();
values.put(BotDataType.CRITICAL_HEALTH, health >= 5 ? 0 : 5D - health);
values.put(BotDataType.DISTANCE_XZ, Math.sqrt(MathUtils.square(a.getX() - b.getX()) + MathUtils.square(a.getZ() - b.getZ())));
values.put(BotDataType.DISTANCE_Y, b.getY() - a.getY());
values.put(BotDataType.ENEMY_BLOCKING, target instanceof Player && ((Player) target).isBlocking() ? 1D : 0);
}
public static BotData generate(Terminator bot, LivingEntity target) {
return new BotData(bot, target);
}
public Map<BotDataType, Double> getValues() {
return values;
}
public double getValue(BotDataType dataType) {
return values.get(dataType);
}
@Override
public String toString() {
List<String> strings = new ArrayList<>();
values.forEach((type, value) -> strings.add(type.name() + "=" + MathUtils.round2Dec(value)));
Collections.sort(strings);
return "BotData{" + StringUtils.join(strings, ",") + "}";
}
}

View File

@@ -0,0 +1,18 @@
package net.nuggetmc.tplus.api.agent.legacyagent.ai;
public enum BotDataType {
CRITICAL_HEALTH("h"),
DISTANCE_XZ("xz"),
DISTANCE_Y("y"),
ENEMY_BLOCKING("b");
private final String shorthand;
BotDataType(String shorthand) {
this.shorthand = shorthand;
}
public String getShorthand() {
return shorthand;
}
}

View File

@@ -0,0 +1,8 @@
package net.nuggetmc.tplus.api.agent.legacyagent.ai;
public enum BotNode {
BLOCK, // block (can't attack while blocking)
JUMP, // jump
LEFT, // left strafe
RIGHT // right strafe (if L and R are opposite, move forward)
}

View File

@@ -0,0 +1,94 @@
package net.nuggetmc.tplus.api.agent.legacyagent.ai;
import net.md_5.bungee.api.ChatColor;
import net.nuggetmc.tplus.utils.MathUtils;
import net.nuggetmc.tplus.utils.ChatUtils;
import org.apache.commons.lang.StringUtils;
import java.util.*;
public class NeuralNetwork {
// thinking about making an enum called BotNode, and have a map here, .check(Node.L) or fetch
// also randomize activation point between 0 and 0.5
// randomize the blocking length and cooldown
// also the XZ offset randomizers!! (or maybe just turn them off entirely, that works too)
// b, j, l, r, ox, oz, av, bl, bc, dlr
private final Map<BotNode, NodeConnections> nodes;
private final boolean dynamicLR;
public static final NeuralNetwork RANDOM = new NeuralNetwork(new HashMap<>());
private NeuralNetwork(Map<BotNode, Map<BotDataType, Double>> profile) {
this.nodes = new HashMap<>();
this.dynamicLR = true;
if (profile == null) {
Arrays.stream(BotNode.values()).forEach(n -> this.nodes.put(n, new NodeConnections()));
} else {
profile.forEach((nodeType, map) -> nodes.put(nodeType, new NodeConnections(map)));
}
}
public static NeuralNetwork createNetworkFromProfile(Map<BotNode, Map<BotDataType, Double>> profile) {
return new NeuralNetwork(profile);
}
public static NeuralNetwork generateRandomNetwork() {
return new NeuralNetwork(null);
}
public NodeConnections fetch(BotNode node) {
return nodes.get(node);
}
public boolean check(BotNode node) {
return nodes.get(node).check();
}
public double value(BotNode node) {
return nodes.get(node).value();
}
public void feed(BotData data) {
nodes.values().forEach(n -> n.test(data));
}
public Map<BotNode, NodeConnections> nodes() {
return nodes;
}
public boolean dynamicLR() {
return dynamicLR;
}
public Map<BotNode, Map<BotDataType, Double>> values() {
Map<BotNode, Map<BotDataType, Double>> output = new HashMap<>();
nodes.forEach((nodeType, node) -> output.put(nodeType, node.getValues()));
return output;
}
public String output() {
List<String> strings = new ArrayList<>();
nodes.forEach((type, node) -> strings.add(type.name().toLowerCase() + "=" + (node.check() ? ChatUtils.ON + "1" : ChatUtils.OFF + "0") + ChatColor.RESET));
Collections.sort(strings);
return "[" + StringUtils.join(strings, ", ") + "]";
}
@Override
public String toString() {
List<String> strings = new ArrayList<>();
nodes.forEach((nodeType, node) -> {
List<String> values = new ArrayList<>();
values.add("name=\"" + nodeType.name().toLowerCase() + "\"");
node.getValues().forEach((dataType, value) -> values.add(dataType.getShorthand() + "=" + MathUtils.round2Dec(value)));
strings.add("{" + StringUtils.join(values, ",") + "}");
});
return "NeuralNetwork{nodes:[" + StringUtils.join(strings, ",") + "]}";
}
}

View File

@@ -0,0 +1,69 @@
package net.nuggetmc.tplus.api.agent.legacyagent.ai;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
public class NodeConnections {
/*
* more node ideas
* how much the bot is to the left or right of target (not horizontal distance)
* bot velocity?
* if the player is temporarily invincible (has damage ticks > 0)
*/
private final Map<BotDataType, Double> connections;
private boolean active;
private double value;
public NodeConnections() {
this.connections = new HashMap<>();
Arrays.stream(BotDataType.values()).forEach(type -> connections.put(type, generateValue()));
}
public NodeConnections(Map<BotDataType, Double> values) {
this.connections = values;
}
private double generateValue() {
return ThreadLocalRandom.current().nextDouble(-10, 10);
}
public boolean check() {
return active;
}
public double value() {
return value;
}
public Map<BotDataType, Double> getValues() {
return connections;
}
public double getValue(BotDataType dataType) {
return connections.get(dataType);
}
/*
* maybe a sinusoidal activation function?
* maybe generate a random activation function?
* definitely something less.. broad
*/
public void test(BotData data) {
this.activationFunction(data);
this.active = this.value >= 0.5;
}
/*
* try sin, sin x^2, cos, cos x^2
*/
private void activationFunction(BotData data) {
this.value = Math.tanh(data.getValues().entrySet().stream().mapToDouble(entry -> connections.get(entry.getKey()) * entry.getValue()).sum());
}
}

View File

@@ -0,0 +1,36 @@
package net.nuggetmc.tplus.utils;
import net.md_5.bungee.api.ChatColor;
import java.text.NumberFormat;
import java.util.Locale;
public class ChatUtils {
public static final String LINE = ChatColor.GRAY + "------------------------------------------------";
public static final String BULLET = "";
public static final String BULLET_FORMATTED = ChatColor.GRAY + "" + ChatColor.RESET;
public static final String EXCEPTION_MESSAGE = ChatColor.RED + "An exception has occured. Please try again.";
public static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance(Locale.US);
public static final String ON = ChatColor.GREEN.toString();
public static final String OFF = ChatColor.GRAY.toString();
public static String trim16(String str) {
return str.length() > 16 ? str.substring(0, 16) : str;
}
public static String camelToDashed(String input) {
StringBuilder result = new StringBuilder();
for (char ch : input.toCharArray()) {
if (Character.isUpperCase(ch)) {
result.append("-").append(Character.toLowerCase(ch));
} else {
result.append(ch);
}
}
return result.toString();
}
}

View File

@@ -0,0 +1,179 @@
package net.nuggetmc.tplus.utils;
import net.nuggetmc.tplus.api.Terminator;
import org.bukkit.util.NumberConversions;
import org.bukkit.util.Vector;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;
public class MathUtils {
public static final Random RANDOM = new Random();
public static final DecimalFormat FORMATTER_1 = new DecimalFormat("0.#");
public static final DecimalFormat FORMATTER_2 = new DecimalFormat("0.##");
public static float[] fetchYawPitch(Vector dir) {
double x = dir.getX();
double z = dir.getZ();
float[] out = new float[2];
if (x == 0.0D && z == 0.0D) {
out[1] = (float) (dir.getY() > 0.0D ? -90 : 90);
} else {
double theta = Math.atan2(-x, z);
out[0] = (float) Math.toDegrees((theta + 6.283185307179586D) % 6.283185307179586D);
double x2 = NumberConversions.square(x);
double z2 = NumberConversions.square(z);
double xz = Math.sqrt(x2 + z2);
out[1] = (float) Math.toDegrees(Math.atan(-dir.getY() / xz));
}
return out;
}
public static float fetchPitch(Vector dir) {
double x = dir.getX();
double z = dir.getZ();
float result;
if (x == 0.0D && z == 0.0D) {
result = (float) (dir.getY() > 0.0D ? -90 : 90);
} else {
double x2 = NumberConversions.square(x);
double z2 = NumberConversions.square(z);
double xz = Math.sqrt(x2 + z2);
result = (float) Math.toDegrees(Math.atan(-dir.getY() / xz));
}
return result;
}
public static Vector circleOffset(double r) {
double rad = 2 * Math.random() * Math.PI;
double x = r * Math.random() * Math.cos(rad);
double z = r * Math.random() * Math.sin(rad);
return new Vector(x, 0, z);
}
public static boolean isNotFinite(Vector vector) {
return !NumberConversions.isFinite(vector.getX()) || !NumberConversions.isFinite(vector.getY()) || !NumberConversions.isFinite(vector.getZ());
}
public static void clean(Vector vector) {
if (!NumberConversions.isFinite(vector.getX())) vector.setX(0);
if (!NumberConversions.isFinite(vector.getY())) vector.setY(0);
if (!NumberConversions.isFinite(vector.getZ())) vector.setZ(0);
}
public static <E> E getRandomSetElement(Set<E> set) {
return set.isEmpty() ? null : set.stream().skip(RANDOM.nextInt(set.size())).findFirst().orElse(null);
}
public static double square(double n) {
return n * n;
}
public static String round1Dec(double n) {
return FORMATTER_1.format(n);
}
public static String round2Dec(double n) {
return FORMATTER_2.format(n);
}
public static List<Map.Entry<Terminator, Integer>> sortByValue(HashMap<Terminator, Integer> hm) {
List<Map.Entry<Terminator, Integer>> list = new LinkedList<>(hm.entrySet());
list.sort(Map.Entry.comparingByValue());
Collections.reverse(list);
return list;
}
public static double generateConnectionValue(List<Double> list, double mutationSize) {
double[] bounds = getBounds(list, mutationSize);
return random(bounds[0], bounds[1]);
}
public static double generateConnectionValue(List<Double> list) {
return generateConnectionValue(list, 0);
}
public static double random(double low, double high) {
return Math.random() * (high - low) + low;
}
public static double sum(List<Double> list) {
return list.stream().mapToDouble(n -> n).sum();
}
public static double min(List<Double> list) {
if (list.isEmpty()) {
return 0;
}
double min = Double.MAX_VALUE;
for (double n : list) {
if (n < min) {
min = n;
}
}
return min;
}
public static double max(List<Double> list) {
if (list.isEmpty()) {
return 0;
}
double max = 0;
for (double n : list) {
if (n > max) {
max = n;
}
}
return max;
}
public static double getMidValue(List<Double> list) {
return (min(list) + max(list)) / 2D;
}
public static double distribution(List<Double> list, double mid) {
return Math.sqrt(sum(list.stream().map(n -> Math.pow(n - mid, 2)).collect(Collectors.toList())) / list.size());
}
public static double[] getBounds(List<Double> list, double mutationSize) {
double mid = getMidValue(list);
double dist = distribution(list, mid);
double p = mutationSize * dist / Math.sqrt(list.size());
return new double[]{
mid - p,
mid + p
};
}
public static double getMutationSize(int generation) {
int shift = 4;
if (generation <= shift + 1) {
return 7.38905609893;
}
double a = 0.8;
double b = -8.5 - shift;
double c = 2;
return Math.pow(a, generation + b) + c;
}
}