neural networks fully set up for velocity
This commit is contained in:
@@ -5,11 +5,12 @@ import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.server.v1_16_R3.Chunk;
|
||||
import net.minecraft.server.v1_16_R3.*;
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.ai.bot.event.BotFallDamageEvent;
|
||||
import net.nuggetmc.ai.utils.BotUtils;
|
||||
import net.nuggetmc.ai.utils.MathUtils;
|
||||
import net.nuggetmc.ai.utils.MojangAPI;
|
||||
import net.nuggetmc.ai.utils.StringUtils;
|
||||
import net.nuggetmc.ai.utils.StringUtilities;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.SoundCategory;
|
||||
import org.bukkit.World;
|
||||
@@ -32,12 +33,27 @@ import java.util.UUID;
|
||||
|
||||
public class Bot extends EntityPlayer {
|
||||
|
||||
public boolean item; // eventually make this not garbage lol
|
||||
private NeuralNetwork network;
|
||||
|
||||
public Vector velocity;
|
||||
public NeuralNetwork getNeuralNetwork() {
|
||||
return network;
|
||||
}
|
||||
|
||||
public void setNeuralNetwork(NeuralNetwork network) {
|
||||
this.network = network;
|
||||
}
|
||||
|
||||
public boolean hasNeuralNetwork() {
|
||||
return network != null;
|
||||
}
|
||||
|
||||
public boolean item; // eventually make this not garbage lol
|
||||
public boolean shield;
|
||||
|
||||
private Vector velocity;
|
||||
private Vector oldVelocity;
|
||||
|
||||
private boolean removeOnDeath;
|
||||
private final boolean removeOnDeath;
|
||||
|
||||
private byte aliveTicks;
|
||||
private byte fireTicks;
|
||||
@@ -55,22 +71,23 @@ public class Bot extends EntityPlayer {
|
||||
this.oldVelocity = velocity.clone();
|
||||
this.noFallTicks = 60;
|
||||
this.fireTicks = 0;
|
||||
this.removeOnDeath = true;
|
||||
this.offset = MathUtils.circleOffset(3);
|
||||
|
||||
datawatcher.set(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 0xFF);
|
||||
}
|
||||
|
||||
public static Bot createBot(Location loc, String name) {
|
||||
return createBot(loc, name, MojangAPI.getSkin(name), true);
|
||||
return createBot(loc, name, MojangAPI.getSkin(name));
|
||||
}
|
||||
|
||||
public static Bot createBot(Location loc, String name, String[] skin, boolean removeOnDeath) {
|
||||
public static Bot createBot(Location loc, String name, String[] skin) {
|
||||
MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
WorldServer nmsWorld = ((CraftWorld) Objects.requireNonNull(loc.getWorld())).getHandle();
|
||||
|
||||
UUID uuid = BotUtils.randomSteveUUID();
|
||||
|
||||
CustomGameProfile profile = new CustomGameProfile(uuid, StringUtils.trim16(name), skin);
|
||||
CustomGameProfile profile = new CustomGameProfile(uuid, StringUtilities.trim16(name), skin);
|
||||
PlayerInteractManager interactManager = new PlayerInteractManager(nmsWorld);
|
||||
|
||||
Bot bot = new Bot(nmsServer, nmsWorld, profile, interactManager);
|
||||
@@ -78,7 +95,6 @@ public class Bot extends EntityPlayer {
|
||||
bot.playerConnection = new PlayerConnection(nmsServer, new NetworkManager(EnumProtocolDirection.CLIENTBOUND), bot);
|
||||
bot.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
|
||||
bot.getBukkitEntity().setNoDamageTicks(0);
|
||||
bot.removeOnDeath = removeOnDeath;
|
||||
nmsWorld.addEntity(bot);
|
||||
|
||||
bot.renderAll();
|
||||
@@ -268,6 +284,10 @@ public class Bot extends EntityPlayer {
|
||||
return velocity.getY() < -0.8;
|
||||
}
|
||||
|
||||
public void block() {
|
||||
// block for 10 ticks, cooldown for 10 extra ticks (20 total)
|
||||
}
|
||||
|
||||
private void updateLocation() {
|
||||
double y;
|
||||
|
||||
@@ -321,6 +341,15 @@ public class Bot extends EntityPlayer {
|
||||
jump(new Vector(0, 0.5, 0));
|
||||
}
|
||||
|
||||
public void walk(Vector vel) {
|
||||
double max = 0.4;
|
||||
|
||||
Vector sum = velocity.clone().add(vel);
|
||||
if (sum.length() > max) sum.normalize().multiply(max);
|
||||
|
||||
velocity = sum;
|
||||
}
|
||||
|
||||
public void attack(org.bukkit.entity.Entity entity) {
|
||||
faceLocation(entity.getLocation());
|
||||
punch();
|
||||
@@ -407,9 +436,11 @@ public class Bot extends EntityPlayer {
|
||||
private void dieCheck() {
|
||||
if (removeOnDeath) {
|
||||
TerminatorPlus plugin = TerminatorPlus.getInstance();
|
||||
plugin.getManager().remove(this);
|
||||
this.removeTab();
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> plugin.getManager().remove(this));
|
||||
Bukkit.getScheduler().runTaskLater(plugin, this::setDead, 30);
|
||||
|
||||
this.removeTab();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,12 +508,9 @@ public class Bot extends EntityPlayer {
|
||||
}
|
||||
|
||||
private void kb(Location loc1, Location loc2) {
|
||||
double kbUp = 0.3;
|
||||
|
||||
Vector vel = loc1.toVector().subtract(loc2.toVector()).setY(0).normalize().multiply(0.3);
|
||||
|
||||
if (isOnGround()) vel.multiply(0.8).setY(0.4);
|
||||
else if (vel.getY() > kbUp) vel.setY(kbUp);
|
||||
|
||||
velocity = vel;
|
||||
kbTicks = 10;
|
||||
|
||||
@@ -3,6 +3,8 @@ package net.nuggetmc.ai.bot;
|
||||
import net.minecraft.server.v1_16_R3.PlayerConnection;
|
||||
import net.nuggetmc.ai.bot.agent.Agent;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.LegacyAgent;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.NetworkType;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.ai.utils.MojangAPI;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
|
||||
@@ -59,6 +61,10 @@ public class BotManager implements Listener {
|
||||
}
|
||||
|
||||
public void createBots(Player sender, String name, String skinName, int n) {
|
||||
createBots(sender, name, skinName, n, null);
|
||||
}
|
||||
|
||||
public void createBots(Player sender, String name, String skinName, int n, NetworkType type) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
if (n < 1) n = 1;
|
||||
@@ -78,8 +84,15 @@ public class BotManager implements Listener {
|
||||
String[] skin = MojangAPI.getSkin(skinName);
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
Bot bot = Bot.createBot(loc, name.replace("%", String.valueOf(i)), skin, removeOnDeath);
|
||||
if (i > 1) bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f));
|
||||
Bot bot = Bot.createBot(loc, name.replace("%", String.valueOf(i)), skin);
|
||||
|
||||
if (i > 1) {
|
||||
bot.setVelocity(new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize().multiply(f));
|
||||
}
|
||||
|
||||
if (type == NetworkType.RANDOM) {
|
||||
bot.setNeuralNetwork(NeuralNetwork.generateRandomNetwork());
|
||||
}
|
||||
}
|
||||
|
||||
world.spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5);
|
||||
|
||||
@@ -48,16 +48,11 @@ public abstract class Agent {
|
||||
}
|
||||
|
||||
public void stopAllTasks() {
|
||||
taskList.forEach(t -> {
|
||||
if (!t.isCancelled()) {
|
||||
t.cancel();
|
||||
}
|
||||
});
|
||||
taskList.stream().filter(t -> !t.isCancelled()).forEach(BukkitRunnable::cancel);
|
||||
taskList.clear();
|
||||
}
|
||||
|
||||
protected abstract void tick();
|
||||
|
||||
public void onFallDamage(BotFallDamageEvent event) {
|
||||
}
|
||||
public void onFallDamage(BotFallDamageEvent event) { }
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class BotAgent extends Agent {
|
||||
bot.setItem(null); // method to check item in main hand, bot.getItemInHand()
|
||||
|
||||
try {
|
||||
vel.add(bot.velocity);
|
||||
vel.add(bot.getVelocity());
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (MathUtils.isNotFinite(vel)) {
|
||||
MathUtils.clean(vel);
|
||||
|
||||
@@ -5,6 +5,9 @@ import net.minecraft.server.v1_16_R3.PacketPlayOutBlockBreakAnimation;
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import net.nuggetmc.ai.bot.BotManager;
|
||||
import net.nuggetmc.ai.bot.agent.Agent;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.BotData;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.BotNode;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.ai.bot.event.BotFallDamageEvent;
|
||||
import net.nuggetmc.ai.utils.MathUtils;
|
||||
import net.nuggetmc.ai.utils.PlayerUtils;
|
||||
@@ -19,14 +22,17 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
// Yes, this code is very unoptimized, I know.
|
||||
public class LegacyAgent extends Agent {
|
||||
|
||||
private final LegacyBlockCheck blockCheck;
|
||||
|
||||
private boolean useAIManipulators;
|
||||
public boolean offsets = true;
|
||||
|
||||
public LegacyAgent(BotManager manager) {
|
||||
super(manager);
|
||||
@@ -55,13 +61,7 @@ public class LegacyAgent extends Agent {
|
||||
|
||||
@Override
|
||||
protected void tick() {
|
||||
try {
|
||||
manager.fetch().forEach(this::tickBot);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
// Yes this is a really bad way to deal with this issue, but in the future I will have a thing
|
||||
// where when bots die they will be added to a cleanup cache that will be ticked after this (which will be refactored
|
||||
// to the BotManager) and will be removed separately from the set.
|
||||
}
|
||||
manager.fetch().forEach(this::tickBot);
|
||||
}
|
||||
|
||||
private void center(Bot bot) {
|
||||
@@ -69,24 +69,24 @@ public class LegacyAgent extends Agent {
|
||||
return;
|
||||
}
|
||||
|
||||
final Player playerBot = bot.getBukkitEntity();
|
||||
final Player botPlayer = bot.getBukkitEntity();
|
||||
|
||||
Location prev = null;
|
||||
if (btList.containsKey(playerBot)) {
|
||||
prev = btList.get(playerBot);
|
||||
if (btList.containsKey(botPlayer)) {
|
||||
prev = btList.get(botPlayer);
|
||||
}
|
||||
|
||||
Location loc = playerBot.getLocation();
|
||||
Location loc = botPlayer.getLocation();
|
||||
|
||||
if (prev != null) {
|
||||
if (loc.getBlockX() == prev.getBlockX() && loc.getBlockZ() == prev.getBlockZ()) {
|
||||
btCheck.put(playerBot, true);
|
||||
btCheck.put(botPlayer, true);
|
||||
} else {
|
||||
btCheck.put(playerBot, false);
|
||||
btCheck.put(botPlayer, false);
|
||||
}
|
||||
}
|
||||
|
||||
btList.put(playerBot, loc);
|
||||
btList.put(botPlayer, loc);
|
||||
}
|
||||
|
||||
private void tickBot(Bot bot) {
|
||||
@@ -110,15 +110,34 @@ public class LegacyAgent extends Agent {
|
||||
fallDamageCheck(bot);
|
||||
miscellaneousChecks(bot, player);
|
||||
|
||||
Player playerBot = bot.getBukkitEntity();
|
||||
Location target = player.getLocation().add(bot.getOffset());
|
||||
Player botPlayer = bot.getBukkitEntity();
|
||||
|
||||
if (bot.tickDelay(3) && !miningAnim.containsKey(playerBot)) {
|
||||
Location a = playerBot.getEyeLocation();
|
||||
Location target = offsets ? player.getLocation().add(bot.getOffset()) : player.getLocation();
|
||||
|
||||
NeuralNetwork network;
|
||||
boolean ai = bot.hasNeuralNetwork();
|
||||
|
||||
if (ai) {
|
||||
BotData data = BotData.generate(bot, player);
|
||||
|
||||
network = bot.getNeuralNetwork();
|
||||
network.feed(data);
|
||||
} else {
|
||||
network = null;
|
||||
}
|
||||
|
||||
if (bot.tickDelay(3) && !miningAnim.containsKey(botPlayer)) {
|
||||
Location a = botPlayer.getEyeLocation();
|
||||
Location b = player.getEyeLocation();
|
||||
Location c1 = player.getLocation();
|
||||
|
||||
if (!LegacyUtils.checkIfBlocksOnVector(a, b) || !LegacyUtils.checkIfBlocksOnVector(a, c1)) {
|
||||
if (ai) { // force unable to block if they are more than 6/7 blocks away
|
||||
if (network.check(BotNode.BLOCK) && loc.distance(player.getLocation()) < 6) {
|
||||
bot.block();
|
||||
}
|
||||
}
|
||||
|
||||
if (LegacyUtils.checkFreeSpace(a, b) || LegacyUtils.checkFreeSpace(a, c1)) {
|
||||
attack(bot, player, loc);
|
||||
}
|
||||
}
|
||||
@@ -126,17 +145,17 @@ public class LegacyAgent extends Agent {
|
||||
boolean waterGround = (LegacyMats.WATER.contains(loc.clone().add(0, -0.1, 0).getBlock().getType())
|
||||
&& !LegacyMats.AIR.contains(loc.clone().add(0, -0.6, 0).getBlock().getType()));
|
||||
|
||||
boolean c = false, lc = false;
|
||||
boolean withinTargetXZ = false, sameXZ = false;
|
||||
|
||||
if (btCheck.containsKey(playerBot)) lc = btCheck.get(playerBot);
|
||||
if (btCheck.containsKey(botPlayer)) sameXZ = btCheck.get(botPlayer);
|
||||
|
||||
if (waterGround || bot.isOnGround() || onBoat(playerBot)) {
|
||||
byte j = 1;
|
||||
if (waterGround || bot.isOnGround() || onBoat(botPlayer)) {
|
||||
byte sideResult = 1;
|
||||
|
||||
if (towerList.containsKey(playerBot)) {
|
||||
if (towerList.containsKey(botPlayer)) {
|
||||
if (loc.getBlockY() > player.getLocation().getBlockY()) {
|
||||
towerList.remove(playerBot);
|
||||
resetHand(bot, player, playerBot);
|
||||
towerList.remove(botPlayer);
|
||||
resetHand(bot, player, botPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,47 +163,100 @@ public class LegacyAgent extends Agent {
|
||||
|
||||
if (Math.abs(loc.getBlockX() - target.getBlockX()) <= 3 &&
|
||||
Math.abs(loc.getBlockZ() - target.getBlockZ()) <= 3) {
|
||||
c = true;
|
||||
withinTargetXZ = true;
|
||||
}
|
||||
|
||||
boolean bc = c || lc;
|
||||
boolean bothXZ = withinTargetXZ || sameXZ;
|
||||
|
||||
// make this not destroy in scenarios where the bot can move out of the place
|
||||
if (checkAt(bot, block, playerBot)) {
|
||||
return;
|
||||
if (checkAt(bot, block, botPlayer)) return;
|
||||
|
||||
if (checkFence(bot, loc.getBlock(), botPlayer)) return;
|
||||
|
||||
if (checkDown(bot, botPlayer, player.getLocation(), bothXZ)) return;
|
||||
|
||||
if ((withinTargetXZ || sameXZ) && checkUp(bot, player, botPlayer, target, withinTargetXZ)) return;
|
||||
|
||||
if (bothXZ) sideResult = checkSide(bot, player, botPlayer);
|
||||
|
||||
switch (sideResult) {
|
||||
case 1:
|
||||
resetHand(bot, player, botPlayer);
|
||||
if (!noJump.contains(botPlayer) && !waterGround) move(bot, player, loc, target, ai);
|
||||
return;
|
||||
|
||||
case 2:
|
||||
if (!waterGround) move(bot, player, loc, target, ai);
|
||||
}
|
||||
|
||||
else if (checkFence(bot, loc.getBlock(), playerBot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
else if (checkDown(bot, playerBot, player.getLocation(), bc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
else if ((c || lc) && checkUp(bot, player, playerBot, target, c)) {
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
if (bc) j = checkSide(bot, player, playerBot);
|
||||
|
||||
switch (j) {
|
||||
case 1:
|
||||
resetHand(bot, player, playerBot);
|
||||
if (!noJump.contains(playerBot)) {
|
||||
if (!waterGround) move(bot, player, loc, target);
|
||||
}
|
||||
return;
|
||||
|
||||
case 2:
|
||||
if (!waterGround) move(bot, player, loc, target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (LegacyMats.WATER.contains(loc.getBlock().getType())) {
|
||||
swim(bot, target, playerBot, player, LegacyMats.WATER.contains(loc.clone().add(0, -1, 0).getBlock().getType()));
|
||||
}
|
||||
|
||||
else if (LegacyMats.WATER.contains(loc.getBlock().getType())) {
|
||||
swim(bot, target, botPlayer, player, LegacyMats.WATER.contains(loc.clone().add(0, -1, 0).getBlock().getType()));
|
||||
}
|
||||
}
|
||||
|
||||
private void move(Bot bot, Player player, Location loc, Location target, boolean ai) {
|
||||
Vector position = loc.toVector();
|
||||
Vector vel = target.toVector().subtract(position).normalize();
|
||||
|
||||
if (bot.tickDelay(5)) bot.faceLocation(player.getLocation());
|
||||
if (!bot.isOnGround()) return; // calling this a second time later on
|
||||
|
||||
bot.stand(); // eventually create a memory system so packets do not have to be sent every tick
|
||||
bot.setItem(null); // method to check item in main hand, bot.getItemInHand()
|
||||
|
||||
try {
|
||||
vel.add(bot.getVelocity());
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (MathUtils.isNotFinite(vel)) {
|
||||
MathUtils.clean(vel);
|
||||
}
|
||||
}
|
||||
|
||||
if (vel.length() > 1) vel.normalize();
|
||||
|
||||
double distance = loc.distance(target);
|
||||
|
||||
if (distance <= 5) {
|
||||
vel.multiply(0.3);
|
||||
} else {
|
||||
vel.multiply(0.4);
|
||||
}
|
||||
|
||||
if (slow.contains(bot)) {
|
||||
vel.setY(0).multiply(0.5);
|
||||
} else {
|
||||
vel.setY(0.4);
|
||||
}
|
||||
|
||||
vel.setY(vel.getY() - Math.random() * 0.05);
|
||||
|
||||
if (ai) {
|
||||
NeuralNetwork network = bot.getNeuralNetwork();
|
||||
|
||||
boolean left = network.check(BotNode.LEFT);
|
||||
boolean right = network.check(BotNode.RIGHT);
|
||||
|
||||
if (left != right && distance <= 6) {
|
||||
if (left) {
|
||||
vel.rotateAroundY(Math.PI / 3);
|
||||
}
|
||||
|
||||
if (right) {
|
||||
vel.rotateAroundY(-Math.PI / 3);
|
||||
}
|
||||
|
||||
if (network.check(BotNode.JUMP)) {
|
||||
bot.jump(vel);
|
||||
} else {
|
||||
bot.walk(vel.clone().setY(0));
|
||||
scheduler.runTaskLater(plugin, () -> bot.jump(vel), 10);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bot.jump(vel);
|
||||
}
|
||||
|
||||
private void fallDamageCheck(Bot bot) {
|
||||
@@ -295,57 +367,21 @@ public class LegacyAgent extends Agent {
|
||||
}
|
||||
}
|
||||
|
||||
private void move(Bot bot, Player player, Location loc, Location target) {
|
||||
Vector vel = target.toVector().subtract(loc.toVector()).normalize();
|
||||
|
||||
if (bot.tickDelay(5)) bot.faceLocation(player.getLocation());
|
||||
if (!bot.isOnGround()) return; // calling this a second time later on
|
||||
|
||||
bot.stand(); // eventually create a memory system so packets do not have to be sent every tick
|
||||
bot.setItem(null); // method to check item in main hand, bot.getItemInHand()
|
||||
|
||||
try {
|
||||
vel.add(bot.velocity);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (MathUtils.isNotFinite(vel)) {
|
||||
MathUtils.clean(vel);
|
||||
}
|
||||
}
|
||||
|
||||
if (vel.length() > 1) vel.normalize();
|
||||
|
||||
if (loc.distance(target) <= 5) {
|
||||
vel.multiply(0.3);
|
||||
} else {
|
||||
vel.multiply(0.4);
|
||||
}
|
||||
|
||||
if (slow.contains(bot)) {
|
||||
vel.setY(0).multiply(0.5);
|
||||
} else {
|
||||
vel.setY(0.4);
|
||||
}
|
||||
|
||||
vel.setY(vel.getY() - Math.random() * 0.05);
|
||||
|
||||
bot.jump(vel);
|
||||
}
|
||||
|
||||
private byte checkSide(Bot npc, Player player, Player playerNPC) { // make it so they don't jump when checking side
|
||||
Location a = playerNPC.getEyeLocation();
|
||||
Location b = player.getLocation().add(0, 1, 0);
|
||||
|
||||
if (npc.getLocation().distance(player.getLocation()) < 2.9 && !LegacyUtils.checkIfBlocksOnVector(a, b)) {
|
||||
if (npc.getLocation().distance(player.getLocation()) < 2.9 && LegacyUtils.checkFreeSpace(a, b)) {
|
||||
resetHand(npc, player, playerNPC);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LegacyLevel h = checkNearby(player, npc);
|
||||
LegacyLevel level = checkNearby(player, npc);
|
||||
|
||||
if (h == null) {
|
||||
if (level == null) {
|
||||
resetHand(npc, player, playerNPC);
|
||||
return 1;
|
||||
} else if (h.isSide()) {
|
||||
} else if (level.isSide()) {
|
||||
return 0;
|
||||
} else {
|
||||
return 2;
|
||||
@@ -560,7 +596,7 @@ public class LegacyAgent extends Agent {
|
||||
|
||||
private boolean checkDown(Bot npc, Player player, Location loc, boolean c) { // possibly a looser check for c
|
||||
|
||||
if (!LegacyUtils.checkIfBlocksOnVector(npc.getLocation(), loc) || !LegacyUtils.checkIfBlocksOnVector(player.getEyeLocation(), loc)) return false;
|
||||
if (LegacyUtils.checkFreeSpace(npc.getLocation(), loc) || LegacyUtils.checkFreeSpace(player.getEyeLocation(), loc)) return false;
|
||||
|
||||
if (c && npc.getLocation().getBlockY() > loc.getBlockY() + 1) {
|
||||
Block block = npc.getLocation().add(0, -1, 0).getBlock();
|
||||
@@ -839,8 +875,8 @@ public class LegacyAgent extends Agent {
|
||||
}
|
||||
|
||||
private void miscellaneousChecks(Bot bot, Player target) {
|
||||
Player playerBot = bot.getBukkitEntity();
|
||||
World world = playerBot.getWorld();
|
||||
Player botPlayer = bot.getBukkitEntity();
|
||||
World world = botPlayer.getWorld();
|
||||
String worldName = world.getName();
|
||||
Location loc = bot.getLocation();
|
||||
|
||||
@@ -914,15 +950,15 @@ public class LegacyAgent extends Agent {
|
||||
}
|
||||
}
|
||||
|
||||
if (playerBot.getLocation().getBlockY() <= target.getLocation().getBlockY() + 1) {
|
||||
if (!miningAnim.containsKey(playerBot)) {
|
||||
Vector vel = playerBot.getVelocity();
|
||||
if (botPlayer.getLocation().getBlockY() <= target.getLocation().getBlockY() + 1) {
|
||||
if (!miningAnim.containsKey(botPlayer)) {
|
||||
Vector vel = botPlayer.getVelocity();
|
||||
double y = vel.getY();
|
||||
|
||||
if (y >= -0.6) {
|
||||
if (loc.clone().add(0, -0.6, 0).getBlock().getType() == Material.WATER
|
||||
&& !LegacyMats.NO_CRACK.contains(under2Type)
|
||||
&& playerBot.getEyeLocation().getBlock().getType().isAir()) {
|
||||
&& botPlayer.getEyeLocation().getBlock().getType().isAir()) {
|
||||
|
||||
Block place = loc.clone().add(0, -1, 0).getBlock();
|
||||
if (LegacyMats.WATER.contains(place.getType())) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.lang.reflect.Field;
|
||||
|
||||
public class LegacyUtils {
|
||||
|
||||
public static boolean checkIfBlocksOnVector(Location a, Location b) {
|
||||
public static boolean checkFreeSpace(Location a, Location b) {
|
||||
Vector v = b.toVector().subtract(a.toVector());
|
||||
|
||||
int n = 32;
|
||||
@@ -20,16 +20,18 @@ public class LegacyUtils {
|
||||
double j = Math.floor(v.length() * n);
|
||||
v.multiply(m / v.length());
|
||||
|
||||
for (int i = 0; i <= j; i++) {
|
||||
org.bukkit.World world = a.getWorld();
|
||||
if (world == null) return false;
|
||||
|
||||
Block block = a.getWorld().getBlockAt((a.toVector().add(v.clone().multiply(i))).toLocation(a.getWorld()));
|
||||
for (int i = 0; i <= j; i++) {
|
||||
Block block = world.getBlockAt((a.toVector().add(v.clone().multiply(i))).toLocation(world));
|
||||
|
||||
if (!LegacyMats.AIR.contains(block.getType())) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Sound breakBlockSound(Block block) {
|
||||
|
||||
@@ -2,46 +2,51 @@ package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import net.nuggetmc.ai.utils.MathUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.Location;
|
||||
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 float health;
|
||||
|
||||
private final double distXZ;
|
||||
private final double distY;
|
||||
|
||||
private final boolean enemyBlocking;
|
||||
private final Map<BotDataType, Double> values;
|
||||
|
||||
private BotData(Bot bot, Player target) {
|
||||
this.values = new HashMap<>();
|
||||
|
||||
Location a = bot.getLocation();
|
||||
Location b = target.getLocation();
|
||||
|
||||
this.health = bot.getHealth();
|
||||
this.distXZ = Math.sqrt(MathUtils.square(a.getX() - b.getX()) + MathUtils.square(a.getZ() - b.getZ()));
|
||||
this.distY = b.getY() - a.getY();
|
||||
this.enemyBlocking = target.isBlocking();
|
||||
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.isBlocking() ? 1D : 0);
|
||||
}
|
||||
|
||||
public static BotData generate(Bot bot, Player target) {
|
||||
return new BotData(bot, target);
|
||||
}
|
||||
|
||||
public float getHealth() {
|
||||
return health;
|
||||
public Map<BotDataType, Double> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public double getDistXZ() {
|
||||
return distXZ;
|
||||
public double getValue(BotDataType dataType) {
|
||||
return values.get(dataType);
|
||||
}
|
||||
|
||||
public double getDistY() {
|
||||
return distY;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> strings = new ArrayList<>();
|
||||
|
||||
public boolean getEnemyBlocking() {
|
||||
return enemyBlocking;
|
||||
values.forEach((type, value) -> strings.add(type.name() + "=" + MathUtils.round2Dec(value)));
|
||||
|
||||
Collections.sort(strings);
|
||||
|
||||
return "BotData{" + StringUtils.join(strings, ",") + "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
public enum BotDataType {
|
||||
CRITICAL_HEALTH,
|
||||
DISTANCE_XZ,
|
||||
DISTANCE_Y,
|
||||
ENEMY_BLOCKING
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.nuggetmc.ai.bot.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)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
public enum NetworkType {
|
||||
RANDOM
|
||||
}
|
||||
@@ -1,20 +1,64 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.nuggetmc.ai.utils.MathUtils;
|
||||
import net.nuggetmc.ai.utils.StringUtilities;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class NeuralNetwork {
|
||||
|
||||
private final NodeConnections nodeL; // left strafe
|
||||
private final NodeConnections nodeR; // right strafe (if L and R are opposite, move forward)
|
||||
private final NodeConnections nodeB; // block
|
||||
private final NodeConnections nodeJ; // jump
|
||||
// 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
|
||||
|
||||
public NeuralNetwork() {
|
||||
this.nodeL = new NodeConnections();
|
||||
this.nodeR = new NodeConnections();
|
||||
this.nodeB = new NodeConnections();
|
||||
this.nodeJ = new NodeConnections();
|
||||
private final Map<BotNode, NodeConnections> nodes;
|
||||
|
||||
private NeuralNetwork(BotNode... nodes) {
|
||||
this.nodes = new HashMap<>();
|
||||
|
||||
Arrays.stream(nodes).forEach(n -> this.nodes.put(n, new NodeConnections()));
|
||||
}
|
||||
|
||||
public static NeuralNetwork generateRandomNetwork() {
|
||||
return new NeuralNetwork(BotNode.values());
|
||||
}
|
||||
|
||||
public NodeConnections fetch(BotNode node) {
|
||||
return nodes.get(node);
|
||||
}
|
||||
|
||||
public boolean check(BotNode node) {
|
||||
return nodes.get(node).check();
|
||||
}
|
||||
|
||||
public void feed(BotData data) {
|
||||
nodes.values().forEach(n -> n.test(data));
|
||||
}
|
||||
|
||||
public String output() {
|
||||
return generateString(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return generateString(true);
|
||||
}
|
||||
|
||||
private String generateString(boolean values) {
|
||||
List<String> strings = new ArrayList<>();
|
||||
|
||||
if (values) {
|
||||
nodes.forEach((type, node) -> {
|
||||
double value = node.value();
|
||||
strings.add(type.name().toLowerCase() + "=" + (value >= 0.5 ? StringUtilities.ON : StringUtilities.OFF) + MathUtils.round2Dec(value) + ChatColor.RESET);
|
||||
});
|
||||
} else {
|
||||
nodes.forEach((type, node) -> strings.add(type.name().toLowerCase() + "=" + (node.check() ? StringUtilities.ON + "1" : StringUtilities.OFF + "0") + ChatColor.RESET));
|
||||
}
|
||||
|
||||
Collections.sort(strings);
|
||||
|
||||
return "[" + StringUtils.join(strings, ", ") + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,60 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NodeConnections {
|
||||
|
||||
private final double connectionX; // horizontal distance
|
||||
private final double connectionY; // vertical distance
|
||||
private final double connectionB; // enemy blocking
|
||||
private final double connectionH; // health
|
||||
/*
|
||||
* 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.connectionX = generate();
|
||||
this.connectionY = generate();
|
||||
this.connectionB = generate();
|
||||
this.connectionH = generate();
|
||||
this.connections = new HashMap<>();
|
||||
|
||||
Arrays.stream(BotDataType.values()).forEach(type -> connections.put(type, generateValue()));
|
||||
}
|
||||
|
||||
public NodeConnections(double y, double b, double t, double h) {
|
||||
this.connectionX = t;
|
||||
this.connectionY = y;
|
||||
this.connectionB = b;
|
||||
this.connectionH = h;
|
||||
}
|
||||
|
||||
private double generate() {
|
||||
private double generateValue() {
|
||||
return Math.random() * 20 - 10;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return connectionX;
|
||||
public boolean check() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return connectionY;
|
||||
public double value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public double getB() {
|
||||
return connectionB;
|
||||
public double getValue(BotDataType dataType) {
|
||||
return connections.get(dataType);
|
||||
}
|
||||
|
||||
public double getH() {
|
||||
return connectionH;
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
public boolean test(double y, double b, double t, double h) {
|
||||
return Math.tanh(y * connectionX + b * connectionY + t * connectionB + h * connectionH) >= 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import com.jonahseguin.drink.annotation.OptArg;
|
||||
import com.jonahseguin.drink.annotation.Sender;
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
import net.nuggetmc.ai.bot.BotManager;
|
||||
import net.nuggetmc.ai.bot.agent.Agent;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.NetworkType;
|
||||
import net.nuggetmc.ai.command.CommandHandler;
|
||||
import net.nuggetmc.ai.command.CommandInstance;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@@ -13,11 +15,13 @@ import org.bukkit.entity.Player;
|
||||
public class AICommand extends CommandInstance {
|
||||
|
||||
private final BotManager manager;
|
||||
private final Agent agent;
|
||||
|
||||
public AICommand(CommandHandler commandHandler) {
|
||||
super(commandHandler);
|
||||
|
||||
this.manager = TerminatorPlus.getInstance().getManager();
|
||||
this.agent = manager.getAgent();
|
||||
}
|
||||
|
||||
@Command(
|
||||
@@ -30,9 +34,9 @@ public class AICommand extends CommandInstance {
|
||||
@Command(
|
||||
name = "random",
|
||||
desc = "Create bots with random neural networks, collecting feed data.",
|
||||
usage = "<name> [skin]"
|
||||
usage = "<amount> <name> [skin]"
|
||||
)
|
||||
public void create(@Sender Player sender, String name, @OptArg String skin) {
|
||||
manager.createBots(sender, name, skin, 1);
|
||||
public void random(@Sender Player sender, int n, String name, @OptArg String skin) {
|
||||
manager.createBots(sender, name, skin, n, NetworkType.RANDOM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import net.nuggetmc.ai.bot.BotManager;
|
||||
import net.nuggetmc.ai.command.CommandHandler;
|
||||
import net.nuggetmc.ai.command.CommandInstance;
|
||||
import net.nuggetmc.ai.utils.Debugger;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
|
||||
@@ -96,6 +96,21 @@ public class Debugger {
|
||||
* DEBUGGER METHODS
|
||||
*/
|
||||
|
||||
public void offsets(boolean b) {
|
||||
Agent agent = TerminatorPlus.getInstance().getManager().getAgent();
|
||||
if (!(agent instanceof LegacyAgent)) {
|
||||
print("This method currently only supports " + ChatColor.AQUA + "LegacyAgent" + ChatColor.RESET + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
LegacyAgent legacyAgent = (LegacyAgent) agent;
|
||||
legacyAgent.offsets = b;
|
||||
|
||||
print("Bot target offsets are now "
|
||||
+ (legacyAgent.offsets ? ChatColor.GREEN + "ENABLED" : ChatColor.RED + "DISABLED")
|
||||
+ ChatColor.RESET + ".");
|
||||
}
|
||||
|
||||
public void confuse(int n) {
|
||||
if (!(sender instanceof Player)) return;
|
||||
|
||||
|
||||
@@ -3,12 +3,15 @@ package net.nuggetmc.ai.utils;
|
||||
import org.bukkit.util.NumberConversions;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
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();
|
||||
@@ -79,4 +82,12 @@ public class MathUtils {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
13
src/main/java/net/nuggetmc/ai/utils/StringUtilities.java
Normal file
13
src/main/java/net/nuggetmc/ai/utils/StringUtilities.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.nuggetmc.ai.utils;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
public class StringUtilities {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package net.nuggetmc.ai.utils;
|
||||
|
||||
public class StringUtils {
|
||||
public static String trim16(String str) {
|
||||
return str.length() > 16 ? str.substring(0, 16) : str;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user