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.*;
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;

View File

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

View File

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

View File

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

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.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())) {

View File

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

View File

@@ -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, ",") + "}";
}
}

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;
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, ", ") + "]";
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

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