neural networks fully set up for velocity

This commit is contained in:
batchprogrammer314
2021-07-23 03:47:11 -05:00
parent aa12ea0e71
commit 95be5d06a1
18 changed files with 396 additions and 204 deletions

View File

@@ -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.Chunk;
import net.minecraft.server.v1_16_R3.*; import net.minecraft.server.v1_16_R3.*;
import net.nuggetmc.ai.TerminatorPlus; 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.bot.event.BotFallDamageEvent;
import net.nuggetmc.ai.utils.BotUtils; import net.nuggetmc.ai.utils.BotUtils;
import net.nuggetmc.ai.utils.MathUtils; import net.nuggetmc.ai.utils.MathUtils;
import net.nuggetmc.ai.utils.MojangAPI; 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.Material;
import org.bukkit.SoundCategory; import org.bukkit.SoundCategory;
import org.bukkit.World; import org.bukkit.World;
@@ -32,12 +33,27 @@ import java.util.UUID;
public class Bot extends EntityPlayer { 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 Vector oldVelocity;
private boolean removeOnDeath; private final boolean removeOnDeath;
private byte aliveTicks; private byte aliveTicks;
private byte fireTicks; private byte fireTicks;
@@ -55,22 +71,23 @@ public class Bot extends EntityPlayer {
this.oldVelocity = velocity.clone(); this.oldVelocity = velocity.clone();
this.noFallTicks = 60; this.noFallTicks = 60;
this.fireTicks = 0; this.fireTicks = 0;
this.removeOnDeath = true;
this.offset = MathUtils.circleOffset(3); this.offset = MathUtils.circleOffset(3);
datawatcher.set(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 0xFF); datawatcher.set(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 0xFF);
} }
public static Bot createBot(Location loc, String name) { 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(); MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
WorldServer nmsWorld = ((CraftWorld) Objects.requireNonNull(loc.getWorld())).getHandle(); WorldServer nmsWorld = ((CraftWorld) Objects.requireNonNull(loc.getWorld())).getHandle();
UUID uuid = BotUtils.randomSteveUUID(); 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); PlayerInteractManager interactManager = new PlayerInteractManager(nmsWorld);
Bot bot = new Bot(nmsServer, nmsWorld, profile, interactManager); 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.playerConnection = new PlayerConnection(nmsServer, new NetworkManager(EnumProtocolDirection.CLIENTBOUND), bot);
bot.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); bot.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
bot.getBukkitEntity().setNoDamageTicks(0); bot.getBukkitEntity().setNoDamageTicks(0);
bot.removeOnDeath = removeOnDeath;
nmsWorld.addEntity(bot); nmsWorld.addEntity(bot);
bot.renderAll(); bot.renderAll();
@@ -268,6 +284,10 @@ public class Bot extends EntityPlayer {
return velocity.getY() < -0.8; return velocity.getY() < -0.8;
} }
public void block() {
// block for 10 ticks, cooldown for 10 extra ticks (20 total)
}
private void updateLocation() { private void updateLocation() {
double y; double y;
@@ -321,6 +341,15 @@ public class Bot extends EntityPlayer {
jump(new Vector(0, 0.5, 0)); 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) { public void attack(org.bukkit.entity.Entity entity) {
faceLocation(entity.getLocation()); faceLocation(entity.getLocation());
punch(); punch();
@@ -407,9 +436,11 @@ public class Bot extends EntityPlayer {
private void dieCheck() { private void dieCheck() {
if (removeOnDeath) { if (removeOnDeath) {
TerminatorPlus plugin = TerminatorPlus.getInstance(); 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); 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) { private void kb(Location loc1, Location loc2) {
double kbUp = 0.3;
Vector vel = loc1.toVector().subtract(loc2.toVector()).setY(0).normalize().multiply(0.3); Vector vel = loc1.toVector().subtract(loc2.toVector()).setY(0).normalize().multiply(0.3);
if (isOnGround()) vel.multiply(0.8).setY(0.4); if (isOnGround()) vel.multiply(0.8).setY(0.4);
else if (vel.getY() > kbUp) vel.setY(kbUp);
velocity = vel; velocity = vel;
kbTicks = 10; kbTicks = 10;

View File

@@ -3,6 +3,8 @@ package net.nuggetmc.ai.bot;
import net.minecraft.server.v1_16_R3.PlayerConnection; import net.minecraft.server.v1_16_R3.PlayerConnection;
import net.nuggetmc.ai.bot.agent.Agent; import net.nuggetmc.ai.bot.agent.Agent;
import net.nuggetmc.ai.bot.agent.legacyagent.LegacyAgent; 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 net.nuggetmc.ai.utils.MojangAPI;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; 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) { 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(); long timestamp = System.currentTimeMillis();
if (n < 1) n = 1; if (n < 1) n = 1;
@@ -78,8 +84,15 @@ public class BotManager implements Listener {
String[] skin = MojangAPI.getSkin(skinName); String[] skin = MojangAPI.getSkin(skinName);
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
Bot bot = Bot.createBot(loc, name.replace("%", String.valueOf(i)), skin, removeOnDeath); 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 (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); world.spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5);

View File

@@ -48,16 +48,11 @@ public abstract class Agent {
} }
public void stopAllTasks() { public void stopAllTasks() {
taskList.forEach(t -> { taskList.stream().filter(t -> !t.isCancelled()).forEach(BukkitRunnable::cancel);
if (!t.isCancelled()) {
t.cancel();
}
});
taskList.clear(); taskList.clear();
} }
protected abstract void tick(); protected abstract void tick();
public void onFallDamage(BotFallDamageEvent event) { public void onFallDamage(BotFallDamageEvent event) { }
}
} }

View File

@@ -100,7 +100,7 @@ public class BotAgent extends Agent {
bot.setItem(null); // method to check item in main hand, bot.getItemInHand() bot.setItem(null); // method to check item in main hand, bot.getItemInHand()
try { try {
vel.add(bot.velocity); vel.add(bot.getVelocity());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
if (MathUtils.isNotFinite(vel)) { if (MathUtils.isNotFinite(vel)) {
MathUtils.clean(vel); MathUtils.clean(vel);

View File

@@ -5,6 +5,9 @@ import net.minecraft.server.v1_16_R3.PacketPlayOutBlockBreakAnimation;
import net.nuggetmc.ai.bot.Bot; import net.nuggetmc.ai.bot.Bot;
import net.nuggetmc.ai.bot.BotManager; import net.nuggetmc.ai.bot.BotManager;
import net.nuggetmc.ai.bot.agent.Agent; 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.bot.event.BotFallDamageEvent;
import net.nuggetmc.ai.utils.MathUtils; import net.nuggetmc.ai.utils.MathUtils;
import net.nuggetmc.ai.utils.PlayerUtils; import net.nuggetmc.ai.utils.PlayerUtils;
@@ -19,14 +22,17 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector; 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. // Yes, this code is very unoptimized, I know.
public class LegacyAgent extends Agent { public class LegacyAgent extends Agent {
private final LegacyBlockCheck blockCheck; private final LegacyBlockCheck blockCheck;
private boolean useAIManipulators; public boolean offsets = true;
public LegacyAgent(BotManager manager) { public LegacyAgent(BotManager manager) {
super(manager); super(manager);
@@ -55,13 +61,7 @@ public class LegacyAgent extends Agent {
@Override @Override
protected void tick() { protected void tick() {
try {
manager.fetch().forEach(this::tickBot); 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.
}
} }
private void center(Bot bot) { private void center(Bot bot) {
@@ -69,24 +69,24 @@ public class LegacyAgent extends Agent {
return; return;
} }
final Player playerBot = bot.getBukkitEntity(); final Player botPlayer = bot.getBukkitEntity();
Location prev = null; Location prev = null;
if (btList.containsKey(playerBot)) { if (btList.containsKey(botPlayer)) {
prev = btList.get(playerBot); prev = btList.get(botPlayer);
} }
Location loc = playerBot.getLocation(); Location loc = botPlayer.getLocation();
if (prev != null) { if (prev != null) {
if (loc.getBlockX() == prev.getBlockX() && loc.getBlockZ() == prev.getBlockZ()) { if (loc.getBlockX() == prev.getBlockX() && loc.getBlockZ() == prev.getBlockZ()) {
btCheck.put(playerBot, true); btCheck.put(botPlayer, true);
} else { } else {
btCheck.put(playerBot, false); btCheck.put(botPlayer, false);
} }
} }
btList.put(playerBot, loc); btList.put(botPlayer, loc);
} }
private void tickBot(Bot bot) { private void tickBot(Bot bot) {
@@ -110,15 +110,34 @@ public class LegacyAgent extends Agent {
fallDamageCheck(bot); fallDamageCheck(bot);
miscellaneousChecks(bot, player); miscellaneousChecks(bot, player);
Player playerBot = bot.getBukkitEntity(); Player botPlayer = bot.getBukkitEntity();
Location target = player.getLocation().add(bot.getOffset());
if (bot.tickDelay(3) && !miningAnim.containsKey(playerBot)) { Location target = offsets ? player.getLocation().add(bot.getOffset()) : player.getLocation();
Location a = playerBot.getEyeLocation();
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 b = player.getEyeLocation();
Location c1 = player.getLocation(); 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); 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()) 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())); && !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)) { if (waterGround || bot.isOnGround() || onBoat(botPlayer)) {
byte j = 1; byte sideResult = 1;
if (towerList.containsKey(playerBot)) { if (towerList.containsKey(botPlayer)) {
if (loc.getBlockY() > player.getLocation().getBlockY()) { if (loc.getBlockY() > player.getLocation().getBlockY()) {
towerList.remove(playerBot); towerList.remove(botPlayer);
resetHand(bot, player, playerBot); resetHand(bot, player, botPlayer);
} }
} }
@@ -144,47 +163,100 @@ public class LegacyAgent extends Agent {
if (Math.abs(loc.getBlockX() - target.getBlockX()) <= 3 && if (Math.abs(loc.getBlockX() - target.getBlockX()) <= 3 &&
Math.abs(loc.getBlockZ() - target.getBlockZ()) <= 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, botPlayer)) return;
if (checkAt(bot, block, playerBot)) {
return;
}
else if (checkFence(bot, loc.getBlock(), playerBot)) { if (checkFence(bot, loc.getBlock(), botPlayer)) return;
return;
}
else if (checkDown(bot, playerBot, player.getLocation(), bc)) { if (checkDown(bot, botPlayer, player.getLocation(), bothXZ)) return;
return;
}
else if ((c || lc) && checkUp(bot, player, playerBot, target, c)) { if ((withinTargetXZ || sameXZ) && checkUp(bot, player, botPlayer, target, withinTargetXZ)) return;
return;
}
else { if (bothXZ) sideResult = checkSide(bot, player, botPlayer);
if (bc) j = checkSide(bot, player, playerBot);
switch (j) { switch (sideResult) {
case 1: case 1:
resetHand(bot, player, playerBot); resetHand(bot, player, botPlayer);
if (!noJump.contains(playerBot)) { if (!noJump.contains(botPlayer) && !waterGround) move(bot, player, loc, target, ai);
if (!waterGround) move(bot, player, loc, target);
}
return; return;
case 2: case 2:
if (!waterGround) move(bot, player, loc, target); if (!waterGround) move(bot, player, loc, target, ai);
}
}
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; 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())); bot.jump(vel);
}
} }
private void fallDamageCheck(Bot bot) { 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 private byte checkSide(Bot npc, Player player, Player playerNPC) { // make it so they don't jump when checking side
Location a = playerNPC.getEyeLocation(); Location a = playerNPC.getEyeLocation();
Location b = player.getLocation().add(0, 1, 0); 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); resetHand(npc, player, playerNPC);
return 1; return 1;
} }
LegacyLevel h = checkNearby(player, npc); LegacyLevel level = checkNearby(player, npc);
if (h == null) { if (level == null) {
resetHand(npc, player, playerNPC); resetHand(npc, player, playerNPC);
return 1; return 1;
} else if (h.isSide()) { } else if (level.isSide()) {
return 0; return 0;
} else { } else {
return 2; 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 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) { if (c && npc.getLocation().getBlockY() > loc.getBlockY() + 1) {
Block block = npc.getLocation().add(0, -1, 0).getBlock(); 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) { private void miscellaneousChecks(Bot bot, Player target) {
Player playerBot = bot.getBukkitEntity(); Player botPlayer = bot.getBukkitEntity();
World world = playerBot.getWorld(); World world = botPlayer.getWorld();
String worldName = world.getName(); String worldName = world.getName();
Location loc = bot.getLocation(); Location loc = bot.getLocation();
@@ -914,15 +950,15 @@ public class LegacyAgent extends Agent {
} }
} }
if (playerBot.getLocation().getBlockY() <= target.getLocation().getBlockY() + 1) { if (botPlayer.getLocation().getBlockY() <= target.getLocation().getBlockY() + 1) {
if (!miningAnim.containsKey(playerBot)) { if (!miningAnim.containsKey(botPlayer)) {
Vector vel = playerBot.getVelocity(); Vector vel = botPlayer.getVelocity();
double y = vel.getY(); double y = vel.getY();
if (y >= -0.6) { if (y >= -0.6) {
if (loc.clone().add(0, -0.6, 0).getBlock().getType() == Material.WATER if (loc.clone().add(0, -0.6, 0).getBlock().getType() == Material.WATER
&& !LegacyMats.NO_CRACK.contains(under2Type) && !LegacyMats.NO_CRACK.contains(under2Type)
&& playerBot.getEyeLocation().getBlock().getType().isAir()) { && botPlayer.getEyeLocation().getBlock().getType().isAir()) {
Block place = loc.clone().add(0, -1, 0).getBlock(); Block place = loc.clone().add(0, -1, 0).getBlock();
if (LegacyMats.WATER.contains(place.getType())) { if (LegacyMats.WATER.contains(place.getType())) {

View File

@@ -11,7 +11,7 @@ import java.lang.reflect.Field;
public class LegacyUtils { 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()); Vector v = b.toVector().subtract(a.toVector());
int n = 32; int n = 32;
@@ -20,16 +20,18 @@ public class LegacyUtils {
double j = Math.floor(v.length() * n); double j = Math.floor(v.length() * n);
v.multiply(m / v.length()); 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())) { if (!LegacyMats.AIR.contains(block.getType())) {
return true; return false;
} }
} }
return false; return true;
} }
public static Sound breakBlockSound(Block block) { public static Sound breakBlockSound(Block block) {

View File

@@ -2,46 +2,51 @@ package net.nuggetmc.ai.bot.agent.legacyagent.ai;
import net.nuggetmc.ai.bot.Bot; import net.nuggetmc.ai.bot.Bot;
import net.nuggetmc.ai.utils.MathUtils; import net.nuggetmc.ai.utils.MathUtils;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; 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 // If this is laggy, try only instantiating this once and update it instead of creating a new instance every tick
public class BotData { public class BotData {
private final float health; private final Map<BotDataType, Double> values;
private final double distXZ;
private final double distY;
private final boolean enemyBlocking;
private BotData(Bot bot, Player target) { private BotData(Bot bot, Player target) {
this.values = new HashMap<>();
Location a = bot.getLocation(); Location a = bot.getLocation();
Location b = target.getLocation(); Location b = target.getLocation();
this.health = bot.getHealth(); float 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(); values.put(BotDataType.CRITICAL_HEALTH, health >= 5 ? 0 : 5D - health);
this.enemyBlocking = target.isBlocking(); 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) { public static BotData generate(Bot bot, Player target) {
return new BotData(bot, target); return new BotData(bot, target);
} }
public float getHealth() { public Map<BotDataType, Double> getValues() {
return health; return values;
} }
public double getDistXZ() { public double getValue(BotDataType dataType) {
return distXZ; return values.get(dataType);
} }
public double getDistY() { @Override
return distY; public String toString() {
} List<String> strings = new ArrayList<>();
public boolean getEnemyBlocking() { values.forEach((type, value) -> strings.add(type.name() + "=" + MathUtils.round2Dec(value)));
return enemyBlocking;
Collections.sort(strings);
return "BotData{" + StringUtils.join(strings, ",") + "}";
} }
} }

View File

@@ -0,0 +1,8 @@
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
public enum BotDataType {
CRITICAL_HEALTH,
DISTANCE_XZ,
DISTANCE_Y,
ENEMY_BLOCKING
}

View File

@@ -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)
}

View File

@@ -0,0 +1,5 @@
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
public enum NetworkType {
RANDOM
}

View File

@@ -1,20 +1,64 @@
package net.nuggetmc.ai.bot.agent.legacyagent.ai; 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 { public class NeuralNetwork {
private final NodeConnections nodeL; // left strafe // thinking about making an enum called BotNode, and have a map here, .check(Node.L) or fetch
private final NodeConnections nodeR; // right strafe (if L and R are opposite, move forward) // also randomize activation point between 0 and 0.5
private final NodeConnections nodeB; // block
private final NodeConnections nodeJ; // jump
public NeuralNetwork() { private final Map<BotNode, NodeConnections> nodes;
this.nodeL = new NodeConnections();
this.nodeR = new NodeConnections(); private NeuralNetwork(BotNode... nodes) {
this.nodeB = new NodeConnections(); this.nodes = new HashMap<>();
this.nodeJ = new NodeConnections();
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) { 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, ", ") + "]";
} }
} }

View File

@@ -1,47 +1,60 @@
package net.nuggetmc.ai.bot.agent.legacyagent.ai; package net.nuggetmc.ai.bot.agent.legacyagent.ai;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class NodeConnections { public class NodeConnections {
private final double connectionX; // horizontal distance /*
private final double connectionY; // vertical distance * more node ideas
private final double connectionB; // enemy blocking * how much the bot is to the left or right of target (not horizontal distance)
private final double connectionH; // health * 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() { public NodeConnections() {
this.connectionX = generate(); this.connections = new HashMap<>();
this.connectionY = generate();
this.connectionB = generate(); Arrays.stream(BotDataType.values()).forEach(type -> connections.put(type, generateValue()));
this.connectionH = generate();
} }
public NodeConnections(double y, double b, double t, double h) { private double generateValue() {
this.connectionX = t;
this.connectionY = y;
this.connectionB = b;
this.connectionH = h;
}
private double generate() {
return Math.random() * 20 - 10; return Math.random() * 20 - 10;
} }
public double getX() { public boolean check() {
return connectionX; return active;
} }
public double getY() { public double value() {
return connectionY; return value;
} }
public double getB() { public double getValue(BotDataType dataType) {
return connectionB; 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());
} }
} }

View File

@@ -5,6 +5,8 @@ import com.jonahseguin.drink.annotation.OptArg;
import com.jonahseguin.drink.annotation.Sender; import com.jonahseguin.drink.annotation.Sender;
import net.nuggetmc.ai.TerminatorPlus; import net.nuggetmc.ai.TerminatorPlus;
import net.nuggetmc.ai.bot.BotManager; 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.CommandHandler;
import net.nuggetmc.ai.command.CommandInstance; import net.nuggetmc.ai.command.CommandInstance;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -13,11 +15,13 @@ import org.bukkit.entity.Player;
public class AICommand extends CommandInstance { public class AICommand extends CommandInstance {
private final BotManager manager; private final BotManager manager;
private final Agent agent;
public AICommand(CommandHandler commandHandler) { public AICommand(CommandHandler commandHandler) {
super(commandHandler); super(commandHandler);
this.manager = TerminatorPlus.getInstance().getManager(); this.manager = TerminatorPlus.getInstance().getManager();
this.agent = manager.getAgent();
} }
@Command( @Command(
@@ -30,9 +34,9 @@ public class AICommand extends CommandInstance {
@Command( @Command(
name = "random", name = "random",
desc = "Create bots with random neural networks, collecting feed data.", 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) { public void random(@Sender Player sender, int n, String name, @OptArg String skin) {
manager.createBots(sender, name, skin, 1); manager.createBots(sender, name, skin, n, NetworkType.RANDOM);
} }
} }

View File

@@ -11,7 +11,6 @@ import net.nuggetmc.ai.bot.BotManager;
import net.nuggetmc.ai.command.CommandHandler; import net.nuggetmc.ai.command.CommandHandler;
import net.nuggetmc.ai.command.CommandInstance; import net.nuggetmc.ai.command.CommandInstance;
import net.nuggetmc.ai.utils.Debugger; import net.nuggetmc.ai.utils.Debugger;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;

View File

@@ -96,6 +96,21 @@ public class Debugger {
* DEBUGGER METHODS * 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) { public void confuse(int n) {
if (!(sender instanceof Player)) return; if (!(sender instanceof Player)) return;

View File

@@ -3,12 +3,15 @@ package net.nuggetmc.ai.utils;
import org.bukkit.util.NumberConversions; import org.bukkit.util.NumberConversions;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.text.DecimalFormat;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
public class MathUtils { public class MathUtils {
public static final Random RANDOM = new Random(); 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) { public static float[] fetchYawPitch(Vector dir) {
double x = dir.getX(); double x = dir.getX();
@@ -79,4 +82,12 @@ public class MathUtils {
public static double square(double n) { public static double square(double n) {
return n * 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);
}
} }

View 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;
}
}

View File

@@ -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;
}
}