intelligence pvp thing done
This commit is contained in:
@@ -13,6 +13,7 @@ public class TerminatorPlus extends JavaPlugin {
|
||||
private static String version;
|
||||
|
||||
private BotManager manager;
|
||||
private CommandHandler handler;
|
||||
|
||||
public static TerminatorPlus getInstance() {
|
||||
return instance;
|
||||
@@ -26,6 +27,10 @@ public class TerminatorPlus extends JavaPlugin {
|
||||
return manager;
|
||||
}
|
||||
|
||||
public CommandHandler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
@@ -33,7 +38,7 @@ public class TerminatorPlus extends JavaPlugin {
|
||||
|
||||
// Create Instances
|
||||
this.manager = new BotManager();
|
||||
new CommandHandler(this);
|
||||
this.handler = new CommandHandler(this);
|
||||
|
||||
// Register event listeners
|
||||
this.registerEvents(manager);
|
||||
|
||||
@@ -5,9 +5,11 @@ 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.Agent;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.ai.bot.event.BotDamageByPlayerEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotFallDamageEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotKilledByPlayerEvent;
|
||||
import net.nuggetmc.ai.utils.*;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.SoundCategory;
|
||||
@@ -21,6 +23,7 @@ import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Damageable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -33,6 +36,7 @@ public class Bot extends EntityPlayer {
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final BukkitScheduler scheduler;
|
||||
private final Agent agent;
|
||||
|
||||
private NeuralNetwork network;
|
||||
|
||||
@@ -48,7 +52,7 @@ public class Bot extends EntityPlayer {
|
||||
return network != null;
|
||||
}
|
||||
|
||||
public boolean item; // eventually make this not garbage lol
|
||||
public ItemStack defaultItem;
|
||||
|
||||
private boolean shield;
|
||||
private boolean blocking;
|
||||
@@ -57,9 +61,11 @@ public class Bot extends EntityPlayer {
|
||||
private Vector velocity;
|
||||
private Vector oldVelocity;
|
||||
|
||||
private final boolean removeOnDeath;
|
||||
private boolean removeOnDeath;
|
||||
|
||||
private int aliveTicks;
|
||||
private int kills;
|
||||
|
||||
private byte aliveTicks;
|
||||
private byte fireTicks;
|
||||
private byte groundTicks;
|
||||
private byte jumpTicks;
|
||||
@@ -72,6 +78,8 @@ public class Bot extends EntityPlayer {
|
||||
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
this.agent = plugin.getManager().getAgent();
|
||||
this.defaultItem = new ItemStack(Material.AIR);
|
||||
this.velocity = new Vector(0, 0, 0);
|
||||
this.oldVelocity = velocity.clone();
|
||||
this.noFallTicks = 60;
|
||||
@@ -139,6 +147,10 @@ public class Bot extends EntityPlayer {
|
||||
};
|
||||
}
|
||||
|
||||
public void setDefaultItem(ItemStack item) {
|
||||
this.defaultItem = item;
|
||||
}
|
||||
|
||||
public Vector getOffset() {
|
||||
return offset;
|
||||
}
|
||||
@@ -160,6 +172,10 @@ public class Bot extends EntityPlayer {
|
||||
velocity.add(vector);
|
||||
}
|
||||
|
||||
public int getAliveTicks() {
|
||||
return aliveTicks;
|
||||
}
|
||||
|
||||
public boolean tickDelay(int i) {
|
||||
return aliveTicks % i == 0;
|
||||
}
|
||||
@@ -385,7 +401,7 @@ public class Bot extends EntityPlayer {
|
||||
punch();
|
||||
|
||||
if (entity instanceof Damageable) {
|
||||
((Damageable) entity).damage(item ? 2.5 : 0.25, getBukkitEntity()); // fist damage is 0.25
|
||||
((Damageable) entity).damage(ItemUtils.getLegacyAttackDamage(defaultItem), getBukkitEntity());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,10 +458,6 @@ public class Bot extends EntityPlayer {
|
||||
velocity.setZ(Math.abs(z) < frictionMin ? 0 : z * factor);
|
||||
}
|
||||
|
||||
public void despawn() {
|
||||
getBukkitEntity().remove();
|
||||
}
|
||||
|
||||
public void removeVisually() {
|
||||
this.removeTab();
|
||||
this.setDead();
|
||||
@@ -455,6 +467,10 @@ public class Bot extends EntityPlayer {
|
||||
sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, this));
|
||||
}
|
||||
|
||||
public void setRemoveOnDeath(boolean enabled) {
|
||||
this.removeOnDeath = enabled;
|
||||
}
|
||||
|
||||
private void setDead() {
|
||||
this.dead = true;
|
||||
this.defaultContainer.b(this);
|
||||
@@ -523,11 +539,16 @@ public class Bot extends EntityPlayer {
|
||||
|
||||
float damage;
|
||||
|
||||
if (attacker instanceof EntityPlayer) {
|
||||
boolean playerInstance = attacker instanceof EntityPlayer;
|
||||
|
||||
BotDamageByPlayerEvent event = new BotDamageByPlayerEvent(this, ((EntityPlayer) attacker).getBukkitEntity(), f);
|
||||
Player killer;
|
||||
|
||||
plugin.getManager().getAgent().onPlayerDamage(event);
|
||||
if (playerInstance) {
|
||||
killer = ((EntityPlayer) attacker).getBukkitEntity();
|
||||
|
||||
BotDamageByPlayerEvent event = new BotDamageByPlayerEvent(this, killer, f);
|
||||
|
||||
agent.onPlayerDamage(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
@@ -535,6 +556,7 @@ public class Bot extends EntityPlayer {
|
||||
|
||||
damage = event.getDamage();
|
||||
} else {
|
||||
killer = null;
|
||||
damage = f;
|
||||
}
|
||||
|
||||
@@ -545,6 +567,10 @@ public class Bot extends EntityPlayer {
|
||||
}
|
||||
|
||||
if (damaged && attacker != null) {
|
||||
if (playerInstance && !isAlive()) {
|
||||
agent.onBotKilledByPlayer(new BotKilledByPlayerEvent(this, killer));
|
||||
}
|
||||
|
||||
kb(getLocation(), attacker.getBukkitEntity().getLocation());
|
||||
}
|
||||
|
||||
@@ -559,6 +585,14 @@ public class Bot extends EntityPlayer {
|
||||
velocity = vel;
|
||||
}
|
||||
|
||||
public int getKills() {
|
||||
return kills;
|
||||
}
|
||||
|
||||
public void incrementKills() {
|
||||
kills++;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return getBukkitEntity().getLocation();
|
||||
}
|
||||
@@ -615,8 +649,8 @@ public class Bot extends EntityPlayer {
|
||||
setItem(item, EnumItemSlot.OFFHAND);
|
||||
}
|
||||
|
||||
public void setItem(org.bukkit.inventory.ItemStack item, EnumItemSlot slot) {
|
||||
if (item == null) item = new org.bukkit.inventory.ItemStack(this.item ? Material.WOODEN_SHOVEL : Material.AIR);
|
||||
private void setItem(org.bukkit.inventory.ItemStack item, EnumItemSlot slot) {
|
||||
if (item == null) item = defaultItem;
|
||||
|
||||
if (slot == EnumItemSlot.MAINHAND) {
|
||||
getBukkitEntity().getInventory().setItemInMainHand(item);
|
||||
|
||||
@@ -3,22 +3,22 @@ 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.bot.event.BotDeathEvent;
|
||||
import net.nuggetmc.ai.utils.MojangAPI;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BotManager implements Listener {
|
||||
|
||||
@@ -27,7 +27,6 @@ public class BotManager implements Listener {
|
||||
private final NumberFormat numberFormat;
|
||||
|
||||
public boolean joinMessages = false;
|
||||
public boolean removeOnDeath = true;
|
||||
|
||||
public BotManager() {
|
||||
this.agent = new LegacyAgent(this);
|
||||
@@ -57,6 +56,10 @@ public class BotManager implements Listener {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> fetchNames() {
|
||||
return bots.stream().map(Bot::getName).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Agent getAgent() {
|
||||
return agent;
|
||||
}
|
||||
@@ -65,44 +68,73 @@ public class BotManager implements Listener {
|
||||
createBots(sender, name, skinName, n, null);
|
||||
}
|
||||
|
||||
public void createBots(Player sender, String name, String skinName, int n, NetworkType type) {
|
||||
public void createBots(Player sender, String name, String skinName, int n, NeuralNetwork network) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
if (n < 1) n = 1;
|
||||
|
||||
World world = sender.getWorld();
|
||||
Location loc = sender.getLocation();
|
||||
|
||||
sender.sendMessage("Creating " + (n == 1 ? "new bot" : ChatColor.RED + numberFormat.format(n) + ChatColor.RESET + " new bots")
|
||||
+ " with name " + ChatColor.GREEN + name
|
||||
+ " with name " + ChatColor.GREEN + name.replace("%", ChatColor.LIGHT_PURPLE + "%" + ChatColor.RESET)
|
||||
+ (skinName == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + skinName)
|
||||
+ ChatColor.RESET + "...");
|
||||
|
||||
skinName = skinName == null ? name : skinName;
|
||||
|
||||
double f = n < 100 ? .004 * n : .4;
|
||||
|
||||
String[] skin = MojangAPI.getSkin(skinName);
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
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());
|
||||
bot.setShield(true);
|
||||
bot.item = true;
|
||||
}
|
||||
}
|
||||
|
||||
world.spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5);
|
||||
createBots(sender.getLocation(), name, MojangAPI.getSkin(skinName), n, network);
|
||||
|
||||
sender.sendMessage("Process completed (" + ChatColor.RED + ((System.currentTimeMillis() - timestamp) / 1000D) + "s" + ChatColor.RESET + ").");
|
||||
}
|
||||
|
||||
public Set<Bot> createBots(Location loc, String name, String[] skin, int n, NeuralNetwork network) {
|
||||
List<NeuralNetwork> networks = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
networks.add(network);
|
||||
}
|
||||
|
||||
return createBots(loc, name, skin, networks);
|
||||
}
|
||||
|
||||
public Set<Bot> createBots(Location loc, String name, String[] skin, List<NeuralNetwork> networks) {
|
||||
Set<Bot> bots = new HashSet<>();
|
||||
World world = loc.getWorld();
|
||||
|
||||
int n = networks.size();
|
||||
int i = 1;
|
||||
|
||||
double f = n < 100 ? .004 * n : .4;
|
||||
|
||||
for (NeuralNetwork network : networks) {
|
||||
Bot bot = Bot.createBot(loc, name.replace("%", String.valueOf(i)), skin);
|
||||
|
||||
if (network != null) {
|
||||
bot.setNeuralNetwork(network == NeuralNetwork.RANDOM ? NeuralNetwork.generateRandomNetwork() : network);
|
||||
bot.setShield(true);
|
||||
bot.setDefaultItem(new ItemStack(Material.WOODEN_AXE));
|
||||
bot.setRemoveOnDeath(false);
|
||||
}
|
||||
|
||||
if (network != null) {
|
||||
bot.setVelocity(randomVelocity());
|
||||
} else if (i > 1) {
|
||||
bot.setVelocity(randomVelocity().multiply(f));
|
||||
}
|
||||
|
||||
bots.add(bot);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (world != null) {
|
||||
world.spawnParticle(Particle.CLOUD, loc, 100, 1, 1, 1, 0.5);
|
||||
}
|
||||
|
||||
return bots;
|
||||
}
|
||||
|
||||
private Vector randomVelocity() {
|
||||
return new Vector(Math.random() - 0.5, 0.5, Math.random() - 0.5).normalize();
|
||||
}
|
||||
|
||||
public void remove(Bot bot) {
|
||||
bots.remove(bot);
|
||||
}
|
||||
@@ -111,6 +143,23 @@ public class BotManager implements Listener {
|
||||
bots.forEach(Bot::removeVisually);
|
||||
bots.clear(); // Not always necessary, but a good security measure
|
||||
agent.stopAllTasks();
|
||||
|
||||
System.gc();
|
||||
}
|
||||
|
||||
public Bot getBot(Player player) { // potentially memory intensive
|
||||
Bot bot = null;
|
||||
|
||||
int id = player.getEntityId();
|
||||
|
||||
for (Bot b : bots) {
|
||||
if (id == b.getId()) {
|
||||
bot = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -120,11 +169,12 @@ public class BotManager implements Listener {
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEntityDamage(EntityDamageByEntityEvent event) {
|
||||
if (!(event.getEntity() instanceof Player)) return;
|
||||
if (!(event.getDamager() instanceof Player)) return;
|
||||
public void onDeath(PlayerDeathEvent event) {
|
||||
Player player = event.getEntity();
|
||||
Bot bot = getBot(player);
|
||||
|
||||
Player player = (Player) event.getEntity();
|
||||
Player damager = (Player) event.getDamager();
|
||||
if (bot != null) {
|
||||
agent.onBotDeath(new BotDeathEvent(event, bot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package net.nuggetmc.ai.bot.agent;
|
||||
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import net.nuggetmc.ai.bot.BotManager;
|
||||
import net.nuggetmc.ai.bot.event.BotDamageByPlayerEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotDeathEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotFallDamageEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotKilledByPlayerEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
@@ -23,6 +27,8 @@ public abstract class Agent {
|
||||
protected boolean enabled;
|
||||
protected int taskID;
|
||||
|
||||
protected boolean drops;
|
||||
|
||||
public Agent(BotManager manager) {
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.manager = manager;
|
||||
@@ -53,9 +59,27 @@ public abstract class Agent {
|
||||
taskList.clear();
|
||||
}
|
||||
|
||||
public void setDrops(boolean enabled) {
|
||||
this.drops = enabled;
|
||||
}
|
||||
|
||||
protected abstract void tick();
|
||||
|
||||
public void onFallDamage(BotFallDamageEvent event) { }
|
||||
|
||||
public void onPlayerDamage(BotDamageByPlayerEvent event) { }
|
||||
|
||||
public void onBotDeath(BotDeathEvent event) { }
|
||||
|
||||
public void onBotKilledByPlayer(BotKilledByPlayerEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
scheduler.runTaskAsynchronously(plugin, () -> {
|
||||
Bot bot = manager.getBot(player);
|
||||
|
||||
if (bot != null) {
|
||||
bot.incrementKills();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ public enum EnumTargetGoal { // TODO USE ORDINAL!!!!!
|
||||
NEAREST_REAL_PLAYER,
|
||||
NEAREST_BOT_DIFFER,
|
||||
NEAREST_BOT,
|
||||
NEAREST_BOT_DIFFER_ALPHA,
|
||||
NONE;
|
||||
|
||||
public static EnumTargetGoal of(int n) {
|
||||
@@ -23,6 +24,9 @@ public enum EnumTargetGoal { // TODO USE ORDINAL!!!!!
|
||||
|
||||
case 4:
|
||||
return NEAREST_BOT;
|
||||
|
||||
case 5:
|
||||
return NEAREST_BOT_DIFFER_ALPHA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ 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.BotDamageByPlayerEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotDeathEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotFallDamageEvent;
|
||||
import net.nuggetmc.ai.bot.event.BotKilledByPlayerEvent;
|
||||
import net.nuggetmc.ai.utils.MathUtils;
|
||||
import net.nuggetmc.ai.utils.PlayerUtils;
|
||||
import org.bukkit.*;
|
||||
@@ -234,30 +236,58 @@ public class LegacyAgent extends Agent {
|
||||
if (ai) {
|
||||
NeuralNetwork network = bot.getNeuralNetwork();
|
||||
|
||||
boolean left = network.check(BotNode.LEFT);
|
||||
boolean right = network.check(BotNode.RIGHT);
|
||||
if (network.dynamicLR()) {
|
||||
if (bot.isBlocking()) {
|
||||
vel.multiply(0.6);
|
||||
}
|
||||
|
||||
if (bot.isBlocking()) {
|
||||
vel.multiply(0.6);
|
||||
if (distance <= 6) {
|
||||
|
||||
// positive y rotation means left, negative means right
|
||||
// if left > right, value will be positive
|
||||
|
||||
double value = network.value(BotNode.LEFT) - network.value(BotNode.RIGHT);
|
||||
|
||||
vel.rotateAroundY(value * Math.PI / 8);
|
||||
|
||||
if (network.check(BotNode.JUMP)) {
|
||||
bot.jump(vel);
|
||||
} else {
|
||||
bot.walk(vel.clone().setY(0));
|
||||
scheduler.runTaskLater(plugin, () -> bot.jump(vel), 10);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (left != right && distance <= 6) {
|
||||
if (left) {
|
||||
vel.rotateAroundY(Math.PI / 4);
|
||||
else {
|
||||
boolean left = network.check(BotNode.LEFT);
|
||||
boolean right = network.check(BotNode.RIGHT);
|
||||
|
||||
if (bot.isBlocking()) {
|
||||
vel.multiply(0.6);
|
||||
}
|
||||
|
||||
if (right) {
|
||||
vel.rotateAroundY(-Math.PI / 4);
|
||||
}
|
||||
if (left != right && distance <= 6) {
|
||||
|
||||
if (network.check(BotNode.JUMP)) {
|
||||
bot.jump(vel);
|
||||
} else {
|
||||
bot.walk(vel.clone().setY(0));
|
||||
scheduler.runTaskLater(plugin, () -> bot.jump(vel), 10);
|
||||
}
|
||||
if (left) {
|
||||
vel.rotateAroundY(Math.PI / 4);
|
||||
}
|
||||
|
||||
return;
|
||||
if (right) {
|
||||
vel.rotateAroundY(-Math.PI / 4);
|
||||
}
|
||||
|
||||
if (network.check(BotNode.JUMP)) {
|
||||
bot.jump(vel);
|
||||
} else {
|
||||
bot.walk(vel.clone().setY(0));
|
||||
scheduler.runTaskLater(plugin, () -> bot.jump(vel), 10);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +310,13 @@ public class LegacyAgent extends Agent {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBotDeath(BotDeathEvent event) {
|
||||
if (!drops) {
|
||||
event.getDrops().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerDamage(BotDamageByPlayerEvent event) {
|
||||
Bot bot = event.getBot();
|
||||
@@ -1023,9 +1060,9 @@ public class LegacyAgent extends Agent {
|
||||
Vector vector = targetLoc.toVector().subtract(bot.getLocation().toVector()).normalize();
|
||||
vector.multiply(0.8);
|
||||
|
||||
Vector move = bot.getVelocity().add(vector);
|
||||
Vector move = bot.getVelocity().add(vector).setY(0);
|
||||
if (move.length() > 1) move = move.normalize();
|
||||
move.multiply(0.75);
|
||||
move.multiply(0.5);
|
||||
move.setY(0.42);
|
||||
bot.setVelocity(move);
|
||||
|
||||
@@ -1104,6 +1141,9 @@ public class LegacyAgent extends Agent {
|
||||
case NEAREST_BOT_DIFFER:
|
||||
return nearestBotDiffer(bot, loc);
|
||||
|
||||
case NEAREST_BOT_DIFFER_ALPHA:
|
||||
return nearestBotDifferAlpha(bot, loc);
|
||||
|
||||
case NEAREST_BOT:
|
||||
return nearestBot(bot, loc);
|
||||
|
||||
@@ -1116,7 +1156,7 @@ public class LegacyAgent extends Agent {
|
||||
Player result = null;
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (loc.getWorld() != player.getWorld()) continue;
|
||||
if (loc.getWorld() != player.getWorld() || player.isDead()) continue;
|
||||
|
||||
if (result == null || loc.distance(player.getLocation()) < loc.distance(result.getLocation())) {
|
||||
result = player;
|
||||
@@ -1130,7 +1170,7 @@ public class LegacyAgent extends Agent {
|
||||
Player result = null;
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (PlayerUtils.isInvincible(player.getGameMode()) || loc.getWorld() != player.getWorld()) continue;
|
||||
if (PlayerUtils.isInvincible(player.getGameMode()) || loc.getWorld() != player.getWorld() || player.isDead()) continue;
|
||||
|
||||
if (result == null || loc.distance(player.getLocation()) < loc.distance(result.getLocation())) {
|
||||
result = player;
|
||||
@@ -1148,7 +1188,7 @@ public class LegacyAgent extends Agent {
|
||||
|
||||
Player player = otherBot.getBukkitEntity();
|
||||
|
||||
if (loc.getWorld() != player.getWorld()) continue;
|
||||
if (loc.getWorld() != player.getWorld() || player.isDead()) continue;
|
||||
|
||||
if (result == null || loc.distance(player.getLocation()) < loc.distance(result.getLocation())) {
|
||||
result = player;
|
||||
@@ -1167,7 +1207,27 @@ public class LegacyAgent extends Agent {
|
||||
Player player = otherBot.getBukkitEntity();
|
||||
|
||||
if (!bot.getName().equals(otherBot.getName())) {
|
||||
if (loc.getWorld() != player.getWorld()) continue;
|
||||
if (loc.getWorld() != player.getWorld() || player.isDead()) continue;
|
||||
|
||||
if (result == null || loc.distance(player.getLocation()) < loc.distance(result.getLocation())) {
|
||||
result = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Player nearestBotDifferAlpha(Bot bot, Location loc) {
|
||||
Player result = null;
|
||||
|
||||
for (Bot otherBot : manager.fetch()) {
|
||||
if (bot == otherBot) continue;
|
||||
|
||||
Player player = otherBot.getBukkitEntity();
|
||||
|
||||
if (!bot.getName().replaceAll("[^A-Za-z]+", "").equals(otherBot.getName().replaceAll("[^A-Za-z]+", ""))) {
|
||||
if (loc.getWorld() != player.getWorld() || player.isDead()) continue;
|
||||
|
||||
if (result == null || loc.distance(player.getLocation()) < loc.distance(result.getLocation())) {
|
||||
result = player;
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.nuggetmc.ai.bot.agent.legacyagent;
|
||||
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import net.nuggetmc.ai.utils.Debugger;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
@@ -27,6 +28,11 @@ public class LegacyBlockCheck {
|
||||
for (Player all : Bukkit.getOnlinePlayers()) all.playSound(loc, Sound.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1, 1);
|
||||
bot.setItem(new ItemStack(Material.COBBLESTONE));
|
||||
loc.getBlock().setType(Material.COBBLESTONE);
|
||||
|
||||
Block under = loc.clone().add(0, -1, 0).getBlock();
|
||||
if (under.getType() == Material.LAVA) {
|
||||
under.setType(Material.COBBLESTONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +47,7 @@ public class LegacyBlockCheck {
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
placeFinal(bot, player, block.getLocation());
|
||||
}, 2);
|
||||
}/* else {
|
||||
placeFinal(player, block.getLocation());
|
||||
return;
|
||||
}*/
|
||||
}
|
||||
|
||||
Set<Block> face = new HashSet<>(Arrays.asList(loc.clone().add(1, 0, 0).getBlock(),
|
||||
loc.clone().add(-1, 0, 0).getBlock(),
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
public enum ActivationType {
|
||||
TANH,
|
||||
SIN_X,
|
||||
SIN_X2,
|
||||
COS_X,
|
||||
COS_X2
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
public enum BotDataType {
|
||||
CRITICAL_HEALTH,
|
||||
DISTANCE_XZ,
|
||||
DISTANCE_Y,
|
||||
ENEMY_BLOCKING
|
||||
CRITICAL_HEALTH("h"),
|
||||
DISTANCE_XZ("xz"),
|
||||
DISTANCE_Y("y"),
|
||||
ENEMY_BLOCKING("b");
|
||||
|
||||
private final String shorthand;
|
||||
|
||||
BotDataType(String shorthand) {
|
||||
this.shorthand = shorthand;
|
||||
}
|
||||
|
||||
public String getShorthand() {
|
||||
return shorthand;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
import com.jonahseguin.drink.utils.ChatUtils;
|
||||
import net.minecraft.server.v1_16_R3.EntityLiving;
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import net.nuggetmc.ai.bot.BotManager;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.EnumTargetGoal;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.LegacyAgent;
|
||||
import net.nuggetmc.ai.command.commands.AICommand;
|
||||
import net.nuggetmc.ai.utils.MathUtils;
|
||||
import net.nuggetmc.ai.utils.MojangAPI;
|
||||
import net.nuggetmc.ai.utils.PlayerUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class IntelligenceAgent {
|
||||
|
||||
/*
|
||||
* export all agent data to the plugin folder as separate folder things
|
||||
* commands /ai stop and /ai pause
|
||||
* if a session with name already exists keep adding underscores
|
||||
* /ai conclude or /ai finish
|
||||
*/
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final BotManager manager;
|
||||
private final AICommand aiManager;
|
||||
private final BukkitScheduler scheduler;
|
||||
|
||||
private LegacyAgent agent;
|
||||
private Thread thread;
|
||||
private boolean active;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String botName;
|
||||
private final String botSkin;
|
||||
private final int cutoff;
|
||||
|
||||
private final Map<String, Bot> bots;
|
||||
|
||||
private int populationSize;
|
||||
private int generation;
|
||||
|
||||
private Player primary;
|
||||
|
||||
private final Set<CommandSender> users;
|
||||
|
||||
private final Map<Integer, Set<Map<BotNode, Map<BotDataType, Double>>>> genProfiles;
|
||||
|
||||
public IntelligenceAgent(AICommand aiManager, int populationSize, String name, String skin) {
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.manager = plugin.getManager();
|
||||
this.aiManager = aiManager;
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
this.name = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(Calendar.getInstance().getTime());
|
||||
this.botName = name;
|
||||
this.botSkin = skin;
|
||||
this.bots = new HashMap<>();
|
||||
this.users = new HashSet<>(Collections.singletonList(Bukkit.getConsoleSender()));
|
||||
this.cutoff = 5;
|
||||
this.genProfiles = new HashMap<>();
|
||||
this.populationSize = populationSize;
|
||||
this.active = true;
|
||||
|
||||
scheduler.runTaskAsynchronously(plugin, () -> {
|
||||
thread = Thread.currentThread();
|
||||
|
||||
try {
|
||||
task();
|
||||
} catch (Exception e) {
|
||||
print(e);
|
||||
print("The thread has been interrupted.");
|
||||
print("The session will now close.");
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void task() throws InterruptedException {
|
||||
setup();
|
||||
sleep(1000);
|
||||
|
||||
while (active) {
|
||||
runGeneration();
|
||||
}
|
||||
|
||||
sleep(5000);
|
||||
close();
|
||||
}
|
||||
|
||||
private void runGeneration() throws InterruptedException {
|
||||
generation++;
|
||||
|
||||
print("Starting generation " + ChatColor.RED + generation + ChatColor.RESET + "...");
|
||||
|
||||
sleep(2000);
|
||||
|
||||
String skinName = botSkin == null ? this.botName : botSkin;
|
||||
|
||||
print("Fetching skin data for " + ChatColor.GREEN + skinName + ChatColor.RESET + "...");
|
||||
|
||||
String[] skinData = MojangAPI.getSkin(skinName);
|
||||
|
||||
String botName = this.botName.endsWith("%") ? this.botName : this.botName + "%";
|
||||
|
||||
print("Creating " + (populationSize == 1 ? "new bot" : ChatColor.RED + NumberFormat.getInstance(Locale.US).format(populationSize) + ChatColor.RESET + " new bots")
|
||||
+ " with name " + ChatColor.GREEN + botName.replace("%", ChatColor.LIGHT_PURPLE + "%" + ChatColor.RESET)
|
||||
+ (botSkin == null ? "" : ChatColor.RESET + " and skin " + ChatColor.GREEN + botSkin)
|
||||
+ ChatColor.RESET + "...");
|
||||
|
||||
Set<Map<BotNode, Map<BotDataType, Double>>> loadedProfiles = genProfiles.get(generation);
|
||||
Location loc = PlayerUtils.findAbove(primary.getLocation(), 20);
|
||||
|
||||
scheduler.runTask(plugin, () -> {
|
||||
Set<Bot> bots;
|
||||
|
||||
if (loadedProfiles == null) {
|
||||
bots = manager.createBots(loc, botName, skinData, populationSize, NeuralNetwork.RANDOM);
|
||||
} else {
|
||||
List<NeuralNetwork> networks = new ArrayList<>();
|
||||
loadedProfiles.forEach(profile -> networks.add(NeuralNetwork.createNetworkFromProfile(profile)));
|
||||
|
||||
if (populationSize != networks.size()) {
|
||||
print("An exception has occured.");
|
||||
print("The stored population size differs from the size of the stored networks.");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
bots = manager.createBots(loc, botName, skinData, networks);
|
||||
}
|
||||
|
||||
bots.forEach(bot -> {
|
||||
String name = bot.getName();
|
||||
|
||||
while (this.bots.containsKey(name)) {
|
||||
name += "_";
|
||||
}
|
||||
|
||||
this.bots.put(name, bot);
|
||||
});
|
||||
});
|
||||
|
||||
while (bots.size() != populationSize) {
|
||||
sleep(1000);
|
||||
}
|
||||
|
||||
sleep(2000);
|
||||
print("The bots will now attack each other.");
|
||||
|
||||
agent.setTargetType(EnumTargetGoal.NEAREST_BOT);
|
||||
|
||||
while (aliveCount() > 1) {
|
||||
sleep(1000);
|
||||
}
|
||||
|
||||
print("Generation " + ChatColor.RED + generation + ChatColor.RESET + " has ended.");
|
||||
|
||||
HashMap<Bot, Integer> values = new HashMap<>();
|
||||
|
||||
for (Bot bot : bots.values()) {
|
||||
values.put(bot, bot.getAliveTicks());
|
||||
}
|
||||
|
||||
List<Map.Entry<Bot, Integer>> sorted = MathUtils.sortByValue(values);
|
||||
Set<Bot> winners = new HashSet<>();
|
||||
|
||||
int i = 1;
|
||||
|
||||
for (Map.Entry<Bot, Integer> entry : sorted) {
|
||||
Bot bot = entry.getKey();
|
||||
boolean check = i <= cutoff;
|
||||
if (check) {
|
||||
print(ChatColor.GRAY + "[" + ChatColor.YELLOW + "#" + i + ChatColor.GRAY + "] " + ChatColor.GREEN + bot.getName()
|
||||
+ ChatUtils.BULLET_FORMATTED + ChatColor.RED + bot.getKills() + " kills");
|
||||
winners.add(bot);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
sleep(3000);
|
||||
|
||||
Map<BotNode, Map<BotDataType, List<Double>>> lists = new HashMap<>();
|
||||
|
||||
winners.forEach(bot -> {
|
||||
Map<BotNode, Map<BotDataType, Double>> data = bot.getNeuralNetwork().values();
|
||||
|
||||
data.forEach((nodeType, node) -> {
|
||||
if (!lists.containsKey(nodeType)) {
|
||||
lists.put(nodeType, new HashMap<>());
|
||||
}
|
||||
|
||||
Map<BotDataType, List<Double>> nodeValues = lists.get(nodeType);
|
||||
|
||||
node.forEach((dataType, value) -> {
|
||||
if (!nodeValues.containsKey(dataType)) {
|
||||
nodeValues.put(dataType, new ArrayList<>());
|
||||
}
|
||||
|
||||
nodeValues.get(dataType).add(value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Set<Map<BotNode, Map<BotDataType, Double>>> profiles = new HashSet<>();
|
||||
|
||||
double mutationSize = MathUtils.getMutationSize(generation);
|
||||
|
||||
for (int j = 0; j < populationSize; j++) {
|
||||
Map<BotNode, Map<BotDataType, Double>> profile = new HashMap<>();
|
||||
|
||||
lists.forEach((nodeType, map) -> {
|
||||
Map<BotDataType, Double> points = new HashMap<>();
|
||||
|
||||
map.forEach((dataType, dataPoints) -> {
|
||||
double value = ((int) (10 * MathUtils.generateConnectionValue(dataPoints, mutationSize))) / 10D;
|
||||
|
||||
points.put(dataType, value);
|
||||
});
|
||||
|
||||
profile.put(nodeType, points);
|
||||
});
|
||||
|
||||
profiles.add(profile);
|
||||
}
|
||||
|
||||
genProfiles.put(generation + 1, profiles);
|
||||
|
||||
sleep(2000);
|
||||
|
||||
clearBots();
|
||||
|
||||
agent.setTargetType(EnumTargetGoal.NONE);
|
||||
}
|
||||
|
||||
private int aliveCount() {
|
||||
return (int) bots.values().stream().filter(EntityLiving::isAlive).count();
|
||||
}
|
||||
|
||||
private void close() {
|
||||
aiManager.clearSession();
|
||||
stop(); // safety call
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.active) {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
if (!thread.isInterrupted()) {
|
||||
this.thread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void sleep(long millis) throws InterruptedException {
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void addUser(CommandSender sender) {
|
||||
users.add(sender);
|
||||
print(sender.getName() + " has been added to the userlist.");
|
||||
|
||||
if (primary == null && sender instanceof Player) {
|
||||
setPrimary((Player) sender);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPrimary(Player player) {
|
||||
this.primary = player;
|
||||
print(player.getName() + " has been set as the primary user.");
|
||||
}
|
||||
|
||||
private void print(Object... objects) {
|
||||
String message = ChatColor.DARK_GREEN + "[REINFORCEMENT] " + ChatColor.RESET + String.join(" ", Arrays.stream(objects).map(String::valueOf).toArray(String[]::new));
|
||||
users.forEach(u -> u.sendMessage(message));
|
||||
// log -> ChatColor.stripColor(message);
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
clearBots();
|
||||
|
||||
if (populationSize < cutoff) {
|
||||
populationSize = cutoff;
|
||||
print("The input value for the population size is lower than the cutoff (" + ChatColor.RED + cutoff + ChatColor.RESET + ")!"
|
||||
+ " The new population size is " + ChatColor.RED + populationSize + ChatColor.RESET + ".");
|
||||
}
|
||||
|
||||
if (!(manager.getAgent() instanceof LegacyAgent)) {
|
||||
print("The AI manager currently only supports " + ChatColor.AQUA + "LegacyAgent" + ChatColor.RESET + ".");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
agent = (LegacyAgent) manager.getAgent();
|
||||
agent.setTargetType(EnumTargetGoal.NONE);
|
||||
|
||||
print("The bot target goal has been set to " + ChatColor.YELLOW + EnumTargetGoal.NONE.name() + ChatColor.RESET + ".");
|
||||
print("Disabling target offsets...");
|
||||
|
||||
agent.offsets = false;
|
||||
|
||||
print("Disabling bot drops...");
|
||||
|
||||
agent.setDrops(false);
|
||||
|
||||
print(ChatColor.GREEN + "Setup is now complete.");
|
||||
}
|
||||
|
||||
private void clearBots() {
|
||||
print("Removing all current bots...");
|
||||
|
||||
int size = manager.fetch().size();
|
||||
manager.reset();
|
||||
|
||||
String formatted = NumberFormat.getNumberInstance(Locale.US).format(size);
|
||||
print("Removed " + ChatColor.RED + formatted + ChatColor.RESET + " entit" + (size == 1 ? "y" : "ies") + ".");
|
||||
|
||||
bots.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package net.nuggetmc.ai.bot.agent.legacyagent.ai;
|
||||
|
||||
public enum NetworkType {
|
||||
RANDOM
|
||||
}
|
||||
@@ -14,16 +14,31 @@ public class NeuralNetwork {
|
||||
// randomize the blocking length and cooldown
|
||||
// also the XZ offset randomizers!! (or maybe just turn them off entirely, that works too)
|
||||
|
||||
// b, j, l, r, ox, oz, av, bl, bc, dlr
|
||||
|
||||
private final Map<BotNode, NodeConnections> nodes;
|
||||
|
||||
private NeuralNetwork(BotNode... nodes) {
|
||||
this.nodes = new HashMap<>();
|
||||
private final boolean dynamicLR;
|
||||
|
||||
Arrays.stream(nodes).forEach(n -> this.nodes.put(n, new NodeConnections()));
|
||||
public static final NeuralNetwork RANDOM = new NeuralNetwork(new HashMap<>());
|
||||
|
||||
private NeuralNetwork(Map<BotNode, Map<BotDataType, Double>> profile) {
|
||||
this.nodes = new HashMap<>();
|
||||
this.dynamicLR = true;
|
||||
|
||||
if (profile == null) {
|
||||
Arrays.stream(BotNode.values()).forEach(n -> this.nodes.put(n, new NodeConnections()));
|
||||
} else {
|
||||
profile.forEach((nodeType, map) -> nodes.put(nodeType, new NodeConnections(map)));
|
||||
}
|
||||
}
|
||||
|
||||
public static NeuralNetwork createNetworkFromProfile(Map<BotNode, Map<BotDataType, Double>> profile) {
|
||||
return new NeuralNetwork(profile);
|
||||
}
|
||||
|
||||
public static NeuralNetwork generateRandomNetwork() {
|
||||
return new NeuralNetwork(BotNode.values());
|
||||
return new NeuralNetwork(null);
|
||||
}
|
||||
|
||||
public NodeConnections fetch(BotNode node) {
|
||||
@@ -34,33 +49,46 @@ public class NeuralNetwork {
|
||||
return nodes.get(node).check();
|
||||
}
|
||||
|
||||
public double value(BotNode node) {
|
||||
return nodes.get(node).value();
|
||||
}
|
||||
|
||||
public void feed(BotData data) {
|
||||
nodes.values().forEach(n -> n.test(data));
|
||||
}
|
||||
|
||||
public Map<BotNode, NodeConnections> nodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public boolean dynamicLR() {
|
||||
return dynamicLR;
|
||||
}
|
||||
|
||||
public Map<BotNode, Map<BotDataType, Double>> values() {
|
||||
Map<BotNode, Map<BotDataType, Double>> output = new HashMap<>();
|
||||
nodes.forEach((nodeType, node) -> output.put(nodeType, node.getValues()));
|
||||
return output;
|
||||
}
|
||||
|
||||
public String output() {
|
||||
return generateString(false);
|
||||
List<String> strings = new ArrayList<>();
|
||||
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, ", ") + "]";
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
nodes.forEach((nodeType, node) -> {
|
||||
List<String> values = new ArrayList<>();
|
||||
values.add("name=\"" + nodeType.name().toLowerCase() + "\"");
|
||||
node.getValues().forEach((dataType, value) -> values.add(dataType.getShorthand() + "=" + MathUtils.round2Dec(value)));
|
||||
strings.add("{" + StringUtils.join(values, ",") + "}");
|
||||
});
|
||||
|
||||
Collections.sort(strings);
|
||||
|
||||
return "[" + StringUtils.join(strings, ", ") + "]";
|
||||
return "NeuralNetwork{nodes:[" + StringUtils.join(strings, ",") + "]}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ public class NodeConnections {
|
||||
Arrays.stream(BotDataType.values()).forEach(type -> connections.put(type, generateValue()));
|
||||
}
|
||||
|
||||
public NodeConnections(Map<BotDataType, Double> values) {
|
||||
this.connections = values;
|
||||
}
|
||||
|
||||
private double generateValue() {
|
||||
return Math.random() * 20 - 10;
|
||||
}
|
||||
@@ -37,6 +41,10 @@ public class NodeConnections {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Map<BotDataType, Double> getValues() {
|
||||
return connections;
|
||||
}
|
||||
|
||||
public double getValue(BotDataType dataType) {
|
||||
return connections.get(dataType);
|
||||
}
|
||||
|
||||
18
src/main/java/net/nuggetmc/ai/bot/event/BotDeathEvent.java
Normal file
18
src/main/java/net/nuggetmc/ai/bot/event/BotDeathEvent.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package net.nuggetmc.ai.bot.event;
|
||||
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
|
||||
public class BotDeathEvent extends PlayerDeathEvent {
|
||||
|
||||
private final Bot bot;
|
||||
|
||||
public BotDeathEvent(PlayerDeathEvent event, Bot bot) {
|
||||
super(event.getEntity(), event.getDrops(), event.getDroppedExp(), event.getDeathMessage());
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
public Bot getBot() {
|
||||
return bot;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.nuggetmc.ai.bot.event;
|
||||
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class BotKilledByPlayerEvent {
|
||||
|
||||
// eventually also call this event for deaths from other damage causes within combat time
|
||||
// (like hitting the ground too hard)
|
||||
|
||||
private final Bot bot;
|
||||
private final Player player;
|
||||
|
||||
public BotKilledByPlayerEvent(Bot bot, Player player) {
|
||||
this.bot = bot;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Bot getBot() {
|
||||
return bot;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,13 @@ public class CommandHandler {
|
||||
|
||||
private final DrinkCommandService drink;
|
||||
private final Map<Class<? extends CommandInstance>, List<String>> help;
|
||||
private final Map<String, CommandInstance> commandMap;
|
||||
|
||||
public CommandHandler(TerminatorPlus plugin) {
|
||||
this.plugin = plugin;
|
||||
this.drink = (DrinkCommandService) Drink.get(plugin);
|
||||
this.help = new HashMap<>();
|
||||
this.commandMap = new HashMap<>();
|
||||
this.registerCommands();
|
||||
this.drink.registerCommands();
|
||||
}
|
||||
@@ -45,10 +47,15 @@ public class CommandHandler {
|
||||
|
||||
private void registerCommand(@Nonnull CommandInstance handler, @Nonnull String name, @Nullable String... aliases) {
|
||||
handler.setName(name);
|
||||
commandMap.put(name, handler);
|
||||
drink.register(handler, MANAGE_PERMISSION, name, aliases);
|
||||
setHelp(handler.getClass());
|
||||
}
|
||||
|
||||
public CommandInstance getComand(String name) {
|
||||
return commandMap.get(name);
|
||||
}
|
||||
|
||||
public void sendRootInfo(CommandInstance commandInstance, CommandSender sender) {
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.GOLD + plugin.getName() + ChatUtils.BULLET_FORMATTED + ChatColor.GRAY
|
||||
|
||||
@@ -3,25 +3,44 @@ package net.nuggetmc.ai.command.commands;
|
||||
import com.jonahseguin.drink.annotation.Command;
|
||||
import com.jonahseguin.drink.annotation.OptArg;
|
||||
import com.jonahseguin.drink.annotation.Sender;
|
||||
import com.jonahseguin.drink.utils.ChatUtils;
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
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.NetworkType;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.IntelligenceAgent;
|
||||
import net.nuggetmc.ai.bot.agent.legacyagent.ai.NeuralNetwork;
|
||||
import net.nuggetmc.ai.command.CommandHandler;
|
||||
import net.nuggetmc.ai.command.CommandInstance;
|
||||
import net.nuggetmc.ai.utils.MathUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AICommand extends CommandInstance {
|
||||
|
||||
/*
|
||||
* ideas
|
||||
* ability to export neural network data to a text file, and also load from them
|
||||
* maybe also have a custom extension like .tplus and encrypt it in base64
|
||||
*/
|
||||
|
||||
private final TerminatorPlus plugin;
|
||||
private final BotManager manager;
|
||||
private final Agent agent;
|
||||
private final BukkitScheduler scheduler;
|
||||
|
||||
private IntelligenceAgent agent;
|
||||
|
||||
public AICommand(CommandHandler commandHandler) {
|
||||
super(commandHandler);
|
||||
|
||||
this.manager = TerminatorPlus.getInstance().getManager();
|
||||
this.agent = manager.getAgent();
|
||||
this.plugin = TerminatorPlus.getInstance();
|
||||
this.manager = plugin.getManager();
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
}
|
||||
|
||||
@Command(
|
||||
@@ -37,6 +56,112 @@ public class AICommand extends CommandInstance {
|
||||
usage = "<amount> <name> [skin]"
|
||||
)
|
||||
public void random(@Sender Player sender, int n, String name, @OptArg String skin) {
|
||||
manager.createBots(sender, name, skin, n, NetworkType.RANDOM);
|
||||
manager.createBots(sender, name, skin, n, NeuralNetwork.RANDOM);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "reinforcement",
|
||||
desc = "Begin an AI training session.",
|
||||
usage = "<population-size> <name> [skin]"
|
||||
)
|
||||
public void reinforcement(@Sender Player sender, int populationSize, String name, @OptArg String skin) {
|
||||
// automatically do the -% thing, store values in map
|
||||
// for now only 1 session at a time, have a set of commandsenders to see output, including console
|
||||
// automatically reset all existing bots at the start, set targets towards each other
|
||||
// also in the future make this a subcommand, with /ai reinforcement defaults, /ai reinforcement begin/start
|
||||
// or just make /ai defaults with reinforcement options
|
||||
|
||||
if (agent != null) {
|
||||
sender.sendMessage("A session is already active.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Starting a new session...");
|
||||
|
||||
agent = new IntelligenceAgent(this, populationSize, name, skin);
|
||||
agent.addUser(sender);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "stop",
|
||||
desc = "End a currently running AI training session."
|
||||
)
|
||||
public void stop(@Sender CommandSender sender) {
|
||||
if (agent == null) {
|
||||
sender.sendMessage("No session is currently active.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Stopping the current session...");
|
||||
String name = agent.getName();
|
||||
clearSession();
|
||||
|
||||
scheduler.runTaskLater(plugin, () -> sender.sendMessage("The session " + ChatColor.YELLOW + name + ChatColor.RESET + " has been closed."), 10);
|
||||
}
|
||||
|
||||
public void clearSession() {
|
||||
if (agent != null) {
|
||||
agent.stop();
|
||||
agent = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasActiveSession() {
|
||||
return agent != null;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "info",
|
||||
desc = "Display neural network information about a bot.",
|
||||
usage = "<name>",
|
||||
autofill = "infoAutofill"
|
||||
)
|
||||
public void info(@Sender CommandSender sender, String name) {
|
||||
sender.sendMessage("Processing request...");
|
||||
|
||||
scheduler.runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
Bot bot = manager.getFirst(name);
|
||||
|
||||
if (bot == null) {
|
||||
sender.sendMessage("Could not find bot " + ChatColor.GREEN + name + ChatColor.RESET + "!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bot.hasNeuralNetwork()) {
|
||||
sender.sendMessage("The bot " + ChatColor.GREEN + name + ChatColor.RESET + " does not have a neural network!");
|
||||
return;
|
||||
}
|
||||
|
||||
NeuralNetwork network = bot.getNeuralNetwork();
|
||||
List<String> strings = new ArrayList<>();
|
||||
|
||||
network.nodes().forEach((nodeType, node) -> {
|
||||
strings.add("");
|
||||
strings.add(ChatColor.YELLOW + "\"" + nodeType.name().toLowerCase() + "\"" + ChatColor.RESET + ":");
|
||||
List<String> values = new ArrayList<>();
|
||||
node.getValues().forEach((dataType, value) -> values.add(ChatUtils.BULLET_FORMATTED + "node"
|
||||
+ dataType.getShorthand().toUpperCase() + ": " + ChatColor.RED + MathUtils.round2Dec(value)));
|
||||
strings.addAll(values);
|
||||
});
|
||||
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "NeuralNetwork" + ChatUtils.BULLET_FORMATTED + ChatColor.GRAY + "[" + ChatColor.GREEN + name + ChatColor.GRAY + "]");
|
||||
strings.forEach(sender::sendMessage);
|
||||
sender.sendMessage(ChatUtils.LINE);
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
sender.sendMessage(ChatUtils.EXCEPTION_MESSAGE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<String> infoAutofill(CommandSender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
return manager.fetchNames();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ public class BotCommand extends CommandInstance {
|
||||
private final BukkitScheduler scheduler;
|
||||
private final DecimalFormat formatter;
|
||||
|
||||
private AICommand aiManager;
|
||||
|
||||
public BotCommand(CommandHandler commandHandler) {
|
||||
super(commandHandler);
|
||||
|
||||
@@ -39,6 +41,8 @@ public class BotCommand extends CommandInstance {
|
||||
this.manager = plugin.getManager();
|
||||
this.scheduler = Bukkit.getScheduler();
|
||||
this.formatter = new DecimalFormat("0.##");
|
||||
|
||||
scheduler.runTask(plugin, () -> aiManager = (AICommand) plugin.getHandler().getComand("ai"));
|
||||
}
|
||||
|
||||
@Command(
|
||||
@@ -69,7 +73,8 @@ public class BotCommand extends CommandInstance {
|
||||
@Command(
|
||||
name = "info",
|
||||
desc = "Information about loaded bots.",
|
||||
usage = "[name]"
|
||||
usage = "[name]",
|
||||
autofill = "infoAutofill"
|
||||
)
|
||||
public void info(@Sender CommandSender sender, @OptArg String name) {
|
||||
if (name == null) {
|
||||
@@ -115,11 +120,19 @@ public class BotCommand extends CommandInstance {
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
sender.sendMessage(ChatColor.RED + "An exception has occured. Please try again.");
|
||||
sender.sendMessage(ChatUtils.EXCEPTION_MESSAGE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<String> infoAutofill(CommandSender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
return manager.fetchNames();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "reset",
|
||||
desc = "Remove all loaded bots."
|
||||
@@ -132,6 +145,10 @@ public class BotCommand extends CommandInstance {
|
||||
|
||||
String formatted = NumberFormat.getNumberInstance(Locale.US).format(size);
|
||||
sender.sendMessage("Removed " + ChatColor.RED + formatted + ChatColor.RESET + " entit" + (size == 1 ? "y" : "ies") + ".");
|
||||
|
||||
if (aiManager.hasActiveSession()) {
|
||||
Bukkit.dispatchCommand(sender, "ai stop");
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.nuggetmc.ai.utils;
|
||||
|
||||
import net.minecraft.server.v1_16_R3.EntityLiving;
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import net.nuggetmc.ai.bot.agent.Agent;
|
||||
@@ -10,11 +11,13 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.permissions.ServerOperator;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.beans.Statement;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Debugger {
|
||||
|
||||
@@ -191,7 +194,7 @@ public class Debugger {
|
||||
}
|
||||
|
||||
public void item() {
|
||||
TerminatorPlus.getInstance().getManager().fetch().forEach(b -> b.item = true);
|
||||
TerminatorPlus.getInstance().getManager().fetch().forEach(b -> b.setDefaultItem(new ItemStack(Material.IRON_SWORD)));
|
||||
}
|
||||
|
||||
public void j(boolean b) {
|
||||
@@ -215,7 +218,7 @@ public class Debugger {
|
||||
}
|
||||
|
||||
public void tp() {
|
||||
Bot bot = MathUtils.getRandomSetElement(TerminatorPlus.getInstance().getManager().fetch());
|
||||
Bot bot = MathUtils.getRandomSetElement(TerminatorPlus.getInstance().getManager().fetch().stream().filter(EntityLiving::isAlive).collect(Collectors.toSet()));
|
||||
|
||||
if (bot == null) {
|
||||
print("Failed to locate a bot.");
|
||||
|
||||
59
src/main/java/net/nuggetmc/ai/utils/ItemUtils.java
Normal file
59
src/main/java/net/nuggetmc/ai/utils/ItemUtils.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package net.nuggetmc.ai.utils;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class ItemUtils {
|
||||
|
||||
public static double getLegacyAttackDamage(ItemStack item) {
|
||||
switch (item.getType()) {
|
||||
default:
|
||||
return 0.25;
|
||||
|
||||
case WOODEN_SHOVEL:
|
||||
case GOLDEN_SHOVEL:
|
||||
case WOODEN_HOE:
|
||||
case GOLDEN_HOE:
|
||||
case STONE_HOE:
|
||||
case IRON_HOE:
|
||||
case DIAMOND_HOE:
|
||||
case NETHERITE_HOE:
|
||||
return 1;
|
||||
|
||||
case WOODEN_PICKAXE:
|
||||
case GOLDEN_PICKAXE:
|
||||
case STONE_SHOVEL:
|
||||
return 2;
|
||||
|
||||
case WOODEN_AXE:
|
||||
case GOLDEN_AXE:
|
||||
case STONE_PICKAXE:
|
||||
case IRON_SHOVEL:
|
||||
return 3;
|
||||
|
||||
case WOODEN_SWORD:
|
||||
case GOLDEN_SWORD:
|
||||
case STONE_AXE:
|
||||
case IRON_PICKAXE:
|
||||
case DIAMOND_SHOVEL:
|
||||
return 4;
|
||||
|
||||
case STONE_SWORD:
|
||||
case IRON_AXE:
|
||||
case DIAMOND_PICKAXE:
|
||||
case NETHERITE_SHOVEL:
|
||||
return 5;
|
||||
|
||||
case IRON_SWORD:
|
||||
case DIAMOND_AXE:
|
||||
case NETHERITE_PICKAXE:
|
||||
return 6;
|
||||
|
||||
case DIAMOND_SWORD:
|
||||
case NETHERITE_AXE:
|
||||
return 7;
|
||||
|
||||
case NETHERITE_SWORD:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package net.nuggetmc.ai.utils;
|
||||
|
||||
import net.nuggetmc.ai.bot.Bot;
|
||||
import org.bukkit.util.NumberConversions;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
@@ -90,4 +91,93 @@ public class MathUtils {
|
||||
public static String round2Dec(double n) {
|
||||
return FORMATTER_2.format(n);
|
||||
}
|
||||
|
||||
public static List<Map.Entry<Bot, Integer>> sortByValue(HashMap<Bot, Integer> hm) {
|
||||
List<Map.Entry<Bot, Integer>> list = new LinkedList<>(hm.entrySet());
|
||||
list.sort(Map.Entry.comparingByValue());
|
||||
Collections.reverse(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static double generateConnectionValue(List<Double> list, double mutationSize) {
|
||||
double[] bounds = getBounds(list, mutationSize);
|
||||
return random(bounds[0], bounds[1]);
|
||||
}
|
||||
|
||||
public static double generateConnectionValue(List<Double> list) {
|
||||
return generateConnectionValue(list, 0);
|
||||
}
|
||||
|
||||
public static double random(double low, double high) {
|
||||
return Math.random() * (high - low) + low;
|
||||
}
|
||||
|
||||
public static double sum(List<Double> list) {
|
||||
return list.stream().mapToDouble(n -> n).sum();
|
||||
}
|
||||
|
||||
public static double min(List<Double> list) {
|
||||
if (list.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double min = Double.MAX_VALUE;
|
||||
|
||||
for (double n : list) {
|
||||
if (n < min) {
|
||||
min = n;
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
public static double max(List<Double> list) {
|
||||
if (list.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double max = 0;
|
||||
|
||||
for (double n : list) {
|
||||
if (n > max) {
|
||||
max = n;
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
public static double getMidValue(List<Double> list) {
|
||||
return (min(list) + max(list)) / 2D;
|
||||
}
|
||||
|
||||
public static double distribution(List<Double> list, double mid) {
|
||||
return Math.sqrt(sum(list.stream().map(n -> Math.pow(n - mid, 2)).collect(Collectors.toList())) / list.size());
|
||||
}
|
||||
|
||||
public static double[] getBounds(List<Double> list, double mutationSize) {
|
||||
double mid = getMidValue(list);
|
||||
double dist = distribution(list, mid);
|
||||
double p = mutationSize * dist / Math.sqrt(list.size());
|
||||
|
||||
return new double[] {
|
||||
mid - p,
|
||||
mid + p
|
||||
};
|
||||
}
|
||||
|
||||
public static double getMutationSize(int generation) {
|
||||
int shift = 4;
|
||||
|
||||
if (generation <= shift + 1) {
|
||||
return 7.38905609893;
|
||||
}
|
||||
|
||||
double a = 0.8;
|
||||
double b = -8.5 - shift;
|
||||
double c = 2;
|
||||
|
||||
return Math.pow(a, generation + b) + c;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.nuggetmc.ai.utils;
|
||||
|
||||
import net.nuggetmc.ai.TerminatorPlus;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
@@ -49,4 +50,21 @@ public class PlayerUtils {
|
||||
Debugger.log("Failed to fetch from the usercache.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Location findAbove(Location loc, int amount) {
|
||||
boolean check = false;
|
||||
|
||||
for (int i = 0; i <= amount; i++) {
|
||||
if (loc.clone().add(0, i, 0).getBlock().getType().isSolid()) {
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (check) {
|
||||
return loc;
|
||||
} else {
|
||||
return loc.clone().add(0, amount, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user