From 6aa1024508358b168ac6964f96a0c122b3c09b95 Mon Sep 17 00:00:00 2001
From: Chris Kearney <chris@kearneymail.com>
Date: Sun, 7 Aug 2016 11:20:46 -0700
Subject: [PATCH] npc test harness and necessary changes to make it work
 without having to spin up the entire server

---
 pom.xml                                       |   6 +
 .../com/comandante/creeper/ConfigureNpc.java  |  12 +-
 .../com/comandante/creeper/Items/Loot.java    |   7 +-
 .../comandante/creeper/command/Command.java   |   3 +-
 .../creeper/command/CountdownCommand.java     |   5 +-
 .../creeper/managers/GameManager.java         |  11 +-
 .../creeper/managers/SentryManager.java       |   9 +-
 .../comandante/creeper/merchant/Wizard.java   |  41 ++++++
 .../merchant/bank/commands/BankCommand.java   |   3 +-
 .../merchant/lockers/LockerCommand.java       |   3 +-
 .../java/com/comandante/creeper/npc/Npc.java  |   5 +
 .../server/ChannelCommunicationUtils.java     |  13 ++
 .../creeper/server/ChannelUtils.java          |   2 +-
 .../creeper/world/WorldExporter.java          |  14 ++
 .../creeper/player/NpcTestHarness.java        | 136 ++++++++++++++++++
 world/npcs/treeberserker.json                 |  14 +-
 world/world.json                              |   8 +-
 17 files changed, 260 insertions(+), 32 deletions(-)
 create mode 100644 src/main/java/com/comandante/creeper/merchant/Wizard.java
 create mode 100644 src/main/java/com/comandante/creeper/server/ChannelCommunicationUtils.java
 create mode 100644 src/test/com/comandante/creeper/player/NpcTestHarness.java

diff --git a/pom.xml b/pom.xml
index e65414bc..11d70363 100755
--- a/pom.xml
+++ b/pom.xml
@@ -112,6 +112,12 @@
             <version>4.11</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>1.9.5</version>
+        </dependency>
+
         <dependency>
             <groupId>com.google.http-client</groupId>
             <artifactId>google-http-client</artifactId>
diff --git a/src/main/java/com/comandante/creeper/ConfigureNpc.java b/src/main/java/com/comandante/creeper/ConfigureNpc.java
index 6d93d315..b6053341 100755
--- a/src/main/java/com/comandante/creeper/ConfigureNpc.java
+++ b/src/main/java/com/comandante/creeper/ConfigureNpc.java
@@ -16,13 +16,14 @@ import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 public class ConfigureNpc {
 
-    public static void configureAllNpcs(GameManager gameManager) throws FileNotFoundException {
+    public static void configureAllNpcs(GameManager gameManager) throws IOException {
         EntityManager entityManager = gameManager.getEntityManager();
         List<Npc> npcsFromFile = NpcExporter.getNpcsFromFile(gameManager);
         for (Npc npc: npcsFromFile) {
@@ -35,7 +36,7 @@ public class ConfigureNpc {
         }
     }
 
-    public static void configure(EntityManager entityManager, GameManager gameManager) throws FileNotFoundException {
+    public static void configure(EntityManager entityManager, GameManager gameManager) throws IOException {
 
         configureAllNpcs(gameManager);
 
@@ -71,12 +72,17 @@ public class ConfigureNpc {
         blacksmithItems.put(4, new MerchantItemForSale(ItemType.IRON_HELMET, 500));
         blacksmithItems.put(5, new MerchantItemForSale(ItemType.IRON_CHEST_PLATE, 1500));
         blacksmithItems.put(6, new MerchantItemForSale(ItemType.IRON_LEGGINGS, 1100));
-        blacksmithItems.put(7, new MerchantItemForSale(ItemType.LIGHTNING_SPELLBOOKNG, 1100));
 
         Blacksmith blacksmith = new Blacksmith(gameManager, new Loot(18, 26, Sets.<ItemType>newHashSet()), blacksmithItems);
         gameManager.getRoomManager().addMerchant(66, blacksmith);
         gameManager.getRoomManager().addMerchant(253, blacksmith);
 
+        Map<Integer, MerchantItemForSale> wizarditems = Maps.newHashMap();
+        wizarditems.put(1, new MerchantItemForSale(ItemType.LIGHTNING_SPELLBOOKNG, 5000));
+
+        Wizard wizard = new Wizard(gameManager, new Loot(18, 26, Sets.<ItemType>newHashSet()), wizarditems);
+        gameManager.getRoomManager().addMerchant(98, wizard);
+
         JimBanker jimBanker = new JimBanker(gameManager, new Loot(18, 26, Sets.<ItemType>newHashSet()), null);
         gameManager.getRoomManager().addMerchant(65, jimBanker);
         gameManager.getRoomManager().addMerchant(209, jimBanker);
diff --git a/src/main/java/com/comandante/creeper/Items/Loot.java b/src/main/java/com/comandante/creeper/Items/Loot.java
index c45ea8e9..82e672a0 100644
--- a/src/main/java/com/comandante/creeper/Items/Loot.java
+++ b/src/main/java/com/comandante/creeper/Items/Loot.java
@@ -1,5 +1,8 @@
 package com.comandante.creeper.Items;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
 import java.io.Serializable;
 import java.util.Set;
 
@@ -9,7 +12,8 @@ public class Loot implements Serializable {
     private final long lootGoldMax;
     private final long lootGoldMin;
 
-    public Loot(long lootGoldMin, long lootGoldMax, Set<ItemType> items) {
+    @JsonCreator
+    public Loot(@JsonProperty("lootGoldMin") long lootGoldMin, @JsonProperty("lootGoldMax") long lootGoldMax, @JsonProperty("items") Set<ItemType> items) {
         this.items = items;
         this.lootGoldMax = lootGoldMax;
         this.lootGoldMin = lootGoldMin;
@@ -26,4 +30,5 @@ public class Loot implements Serializable {
     public long getLootGoldMin() {
         return lootGoldMin;
     }
+
 }
diff --git a/src/main/java/com/comandante/creeper/command/Command.java b/src/main/java/com/comandante/creeper/command/Command.java
index 43a19015..1a0af609 100644
--- a/src/main/java/com/comandante/creeper/command/Command.java
+++ b/src/main/java/com/comandante/creeper/command/Command.java
@@ -7,6 +7,7 @@ import com.comandante.creeper.managers.GameManager;
 import com.comandante.creeper.player.Player;
 import com.comandante.creeper.player.PlayerManager;
 import com.comandante.creeper.player.PlayerRole;
+import com.comandante.creeper.server.ChannelCommunicationUtils;
 import com.comandante.creeper.server.ChannelUtils;
 import com.comandante.creeper.server.CreeperSession;
 import com.comandante.creeper.world.*;
@@ -31,7 +32,7 @@ public abstract class Command extends SimpleChannelUpstreamHandler {
     public final EntityManager entityManager;
     public final RoomManager roomManager;
     public final PlayerManager playerManager;
-    public final ChannelUtils channelUtils;
+    public final ChannelCommunicationUtils channelUtils;
     public final LootManager lootManager;
     public final String correctUsage;
     public CreeperSession creeperSession;
diff --git a/src/main/java/com/comandante/creeper/command/CountdownCommand.java b/src/main/java/com/comandante/creeper/command/CountdownCommand.java
index bf6a26ca..f098192a 100644
--- a/src/main/java/com/comandante/creeper/command/CountdownCommand.java
+++ b/src/main/java/com/comandante/creeper/command/CountdownCommand.java
@@ -3,6 +3,7 @@ package com.comandante.creeper.command;
 import com.comandante.creeper.managers.GameManager;
 import com.comandante.creeper.player.Player;
 import com.comandante.creeper.player.PlayerManager;
+import com.comandante.creeper.server.ChannelCommunicationUtils;
 import com.comandante.creeper.server.ChannelUtils;
 import com.comandante.creeper.server.Color;
 import com.google.common.collect.Lists;
@@ -34,9 +35,9 @@ public class CountdownCommand extends Command {
     public static class PrintCountdown implements Runnable {
 
         private PlayerManager playerManager;
-        private ChannelUtils channelUtils;
+        private ChannelCommunicationUtils channelUtils;
 
-        public PrintCountdown(PlayerManager playerManager, ChannelUtils channelUtils) {
+        public PrintCountdown(PlayerManager playerManager, ChannelCommunicationUtils channelUtils) {
             this.playerManager = playerManager;
             this.channelUtils = channelUtils;
         }
diff --git a/src/main/java/com/comandante/creeper/managers/GameManager.java b/src/main/java/com/comandante/creeper/managers/GameManager.java
index 24b6af54..374a2ba6 100755
--- a/src/main/java/com/comandante/creeper/managers/GameManager.java
+++ b/src/main/java/com/comandante/creeper/managers/GameManager.java
@@ -13,10 +13,7 @@ import com.comandante.creeper.merchant.Merchant;
 import com.comandante.creeper.npc.Npc;
 import com.comandante.creeper.npc.NpcMover;
 import com.comandante.creeper.player.*;
-import com.comandante.creeper.server.ChannelUtils;
-import com.comandante.creeper.server.Color;
-import com.comandante.creeper.server.GossipCache;
-import com.comandante.creeper.server.MultiLineInputManager;
+import com.comandante.creeper.server.*;
 import com.comandante.creeper.spawner.NpcSpawner;
 import com.comandante.creeper.spells.Effect;
 import com.comandante.creeper.spells.EffectsManager;
@@ -46,7 +43,7 @@ public class GameManager {
 
     private final RoomManager roomManager;
     private final PlayerManager playerManager;
-    private final ChannelUtils channelUtils;
+    private final ChannelCommunicationUtils channelUtils;
     private final NewUserRegistrationManager newUserRegistrationManager;
     private final EntityManager entityManager;
     private final ItemDecayManager itemDecayManager;
@@ -68,7 +65,7 @@ public class GameManager {
     private final ItemUseHandler itemUseHandler;
     private final NpcMover npcMover;
 
-    public GameManager(CreeperConfiguration creeperConfiguration, RoomManager roomManager, PlayerManager playerManager, EntityManager entityManager, MapsManager mapsManager, ChannelUtils channelUtils) {
+    public GameManager(CreeperConfiguration creeperConfiguration, RoomManager roomManager, PlayerManager playerManager, EntityManager entityManager, MapsManager mapsManager, ChannelCommunicationUtils channelUtils) {
         this.roomManager = roomManager;
         this.playerManager = playerManager;
         this.entityManager = entityManager;
@@ -154,7 +151,7 @@ public class GameManager {
         return newUserRegistrationManager;
     }
 
-    public ChannelUtils getChannelUtils() {
+    public ChannelCommunicationUtils getChannelUtils() {
         return channelUtils;
     }
 
diff --git a/src/main/java/com/comandante/creeper/managers/SentryManager.java b/src/main/java/com/comandante/creeper/managers/SentryManager.java
index 1c4a80f4..5e9d0d97 100644
--- a/src/main/java/com/comandante/creeper/managers/SentryManager.java
+++ b/src/main/java/com/comandante/creeper/managers/SentryManager.java
@@ -9,20 +9,21 @@ import net.kencochrane.raven.event.interfaces.ExceptionInterface;
 
 public class SentryManager {
 
-    private static Raven raven = RavenFactory.ravenInstance(new Dsn("https://f6888b8b68384ae8a6e5809f8fb08a6d:e2e3dbaaaa8d40639c87d7b16a1539f9@app.getsentry.com/47336"));
+   // private static Raven raven = RavenFactory.ravenInstance(new Dsn("https://f6888b8b68384ae8a6e5809f8fb08a6d:e2e3dbaaaa8d40639c87d7b16a1539f9@app.getsentry.com/47336"));
 
     public static void logSentry(Class c, Exception e, String msg) {
-        EventBuilder eventBuilder = new EventBuilder()
+      /*  EventBuilder eventBuilder = new EventBuilder()
                 .setMessage(msg)
                 .setLevel(Event.Level.ERROR)
                 .setLogger(c.getName())
                 .addSentryInterface(new ExceptionInterface(e));
 
         raven.runBuilderHelpers(eventBuilder);
-        raven.sendEvent(eventBuilder.build());
+        raven.sendEvent(eventBuilder.build());*/
     }
 
     public static void logSentry(Class c, Throwable e, String msg) {
+        /*
         EventBuilder eventBuilder = new EventBuilder()
                 .setMessage(msg)
                 .setLevel(Event.Level.ERROR)
@@ -30,6 +31,6 @@ public class SentryManager {
                 .addSentryInterface(new ExceptionInterface(e));
 
         raven.runBuilderHelpers(eventBuilder);
-        raven.sendEvent(eventBuilder.build());
+        raven.sendEvent(eventBuilder.build());*/
     }
 }
diff --git a/src/main/java/com/comandante/creeper/merchant/Wizard.java b/src/main/java/com/comandante/creeper/merchant/Wizard.java
new file mode 100644
index 00000000..00a27a7e
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/merchant/Wizard.java
@@ -0,0 +1,41 @@
+package com.comandante.creeper.merchant;
+
+import com.comandante.creeper.Items.Loot;
+import com.comandante.creeper.managers.GameManager;
+import com.comandante.creeper.server.Color;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static com.comandante.creeper.server.Color.BOLD_ON;
+
+public class Wizard extends Merchant {
+    private final static long phraseIntervalMs = 300000;
+    private final static String NAME = "willy the wizard";
+    private final static String welcomeMessage = "  ____                                        \n" +
+            " 6MMMMb\\                                      \n" +
+            "6M'    `   /                                  \n" +
+            "MM        /M       _____     _____   __ ____  \n" +
+            "YM.      /MMMMM   6MMMMMb   6MMMMMb  `M6MMMMb \n" +
+            " YMMMMb   MM     6M'   `Mb 6M'   `Mb  MM'  `Mb\n" +
+            "     `Mb  MM     MM     MM MM     MM  MM    MM\n" +
+            "      MM  MM     MM     MM MM     MM  MM    MM\n" +
+            "      MM  MM     MM     MM MM     MM  MM    MM\n" +
+            "L    ,M9  YM.  , YM.   ,M9 YM.   ,M9  MM.  ,M9\n" +
+            "MYMMMM9    YMMM9  YMMMMM9   YMMMMM9   MMYMMM9 \n" +
+            "                                      MM      \n" +
+            "                                      MM      \n" +
+            "                                     _MM_     \n" +
+            "\n";
+    private final static Set<String> validTriggers = new HashSet<String>(Arrays.asList(new String[]
+            {"wizard", "willy the wizard", "willy", NAME}
+    ));
+
+    private final static String colorName = BOLD_ON + Color.BLUE + NAME  + Color.RESET ;
+
+    public Wizard(GameManager gameManager, Loot loot, Map<Integer, MerchantItemForSale> merchantItemForSales) {
+        super(gameManager, NAME, colorName, validTriggers, merchantItemForSales, welcomeMessage);
+    }
+}
diff --git a/src/main/java/com/comandante/creeper/merchant/bank/commands/BankCommand.java b/src/main/java/com/comandante/creeper/merchant/bank/commands/BankCommand.java
index 9921b8b6..ab154cc4 100644
--- a/src/main/java/com/comandante/creeper/merchant/bank/commands/BankCommand.java
+++ b/src/main/java/com/comandante/creeper/merchant/bank/commands/BankCommand.java
@@ -4,6 +4,7 @@ import com.comandante.creeper.Main;
 import com.comandante.creeper.managers.GameManager;
 import com.comandante.creeper.player.Player;
 import com.comandante.creeper.player.PlayerManager;
+import com.comandante.creeper.server.ChannelCommunicationUtils;
 import com.comandante.creeper.server.ChannelUtils;
 import com.comandante.creeper.server.Color;
 import com.comandante.creeper.server.CreeperSession;
@@ -23,7 +24,7 @@ public class BankCommand extends SimpleChannelUpstreamHandler {
     public final List<String> validTriggers;
     public final GameManager gameManager;
     public final PlayerManager playerManager;
-    public final ChannelUtils channelUtils;
+    public final ChannelCommunicationUtils channelUtils;
     public CreeperSession creeperSession;
     public Player player;
     public String playerId;
diff --git a/src/main/java/com/comandante/creeper/merchant/lockers/LockerCommand.java b/src/main/java/com/comandante/creeper/merchant/lockers/LockerCommand.java
index 3cf0b00b..aa35dbc4 100644
--- a/src/main/java/com/comandante/creeper/merchant/lockers/LockerCommand.java
+++ b/src/main/java/com/comandante/creeper/merchant/lockers/LockerCommand.java
@@ -4,6 +4,7 @@ import com.comandante.creeper.Main;
 import com.comandante.creeper.managers.GameManager;
 import com.comandante.creeper.player.Player;
 import com.comandante.creeper.player.PlayerManager;
+import com.comandante.creeper.server.ChannelCommunicationUtils;
 import com.comandante.creeper.server.ChannelUtils;
 import com.comandante.creeper.server.Color;
 import com.comandante.creeper.server.CreeperSession;
@@ -23,7 +24,7 @@ public class LockerCommand extends SimpleChannelUpstreamHandler {
     public final List<String> validTriggers;
     public final GameManager gameManager;
     public final PlayerManager playerManager;
-    public final ChannelUtils channelUtils;
+    public final ChannelCommunicationUtils channelUtils;
     public CreeperSession creeperSession;
     public Player player;
     public String playerId;
diff --git a/src/main/java/com/comandante/creeper/npc/Npc.java b/src/main/java/com/comandante/creeper/npc/Npc.java
index b718543d..17154718 100644
--- a/src/main/java/com/comandante/creeper/npc/Npc.java
+++ b/src/main/java/com/comandante/creeper/npc/Npc.java
@@ -19,6 +19,7 @@ import com.comandante.creeper.stat.StatsBuilder;
 import com.comandante.creeper.stat.StatsHelper;
 import com.comandante.creeper.world.Area;
 import com.comandante.creeper.world.Room;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.google.api.client.util.Sets;
 import com.google.common.base.Optional;
 import com.google.common.collect.Interner;
@@ -252,6 +253,10 @@ public class Npc extends CreeperEntity {
         return currentRoom;
     }
 
+    public AtomicBoolean getIsAlive() {
+        return isAlive;
+    }
+
     public void setCurrentRoom(Room currentRoom) {
         this.currentRoom = currentRoom;
     }
diff --git a/src/main/java/com/comandante/creeper/server/ChannelCommunicationUtils.java b/src/main/java/com/comandante/creeper/server/ChannelCommunicationUtils.java
new file mode 100644
index 00000000..092ab5b4
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/server/ChannelCommunicationUtils.java
@@ -0,0 +1,13 @@
+package com.comandante.creeper.server;
+
+import com.comandante.creeper.player.Player;
+import com.comandante.creeper.player.PlayerManager;
+import com.comandante.creeper.world.RoomManager;
+
+public interface ChannelCommunicationUtils {
+
+        void write(String playerId, String message);
+
+        void write(String playerId, String message, boolean leadingBlankLine);
+
+}
diff --git a/src/main/java/com/comandante/creeper/server/ChannelUtils.java b/src/main/java/com/comandante/creeper/server/ChannelUtils.java
index 231925d1..177c9a27 100644
--- a/src/main/java/com/comandante/creeper/server/ChannelUtils.java
+++ b/src/main/java/com/comandante/creeper/server/ChannelUtils.java
@@ -4,7 +4,7 @@ import com.comandante.creeper.player.Player;
 import com.comandante.creeper.player.PlayerManager;
 import com.comandante.creeper.world.RoomManager;
 
-public class ChannelUtils {
+public class ChannelUtils implements ChannelCommunicationUtils {
 
     private final PlayerManager playerManager;
     private final RoomManager roomManager;
diff --git a/src/main/java/com/comandante/creeper/world/WorldExporter.java b/src/main/java/com/comandante/creeper/world/WorldExporter.java
index f7705289..eaafcb87 100644
--- a/src/main/java/com/comandante/creeper/world/WorldExporter.java
+++ b/src/main/java/com/comandante/creeper/world/WorldExporter.java
@@ -205,4 +205,18 @@ public class WorldExporter {
         }
     }
 
+    public void buildTestworld() {
+        WorldModel worldModel = new GsonBuilder().create().fromJson("{\n" +
+                "  \"floorModelList\": [\n" +
+                "    {\n" +
+                "      \"name\": \"main\",\n" +
+                "      \"id\": 0,\n" +
+                "      \"rawMatrixCsv\": \"0,1\"\n" +
+                "}]\n" +
+                "}", WorldModel.class);
+        for (FloorModel next : worldModel.getFloorModelList()) {
+            buildFloor(next);
+        }
+    }
+
 }
diff --git a/src/test/com/comandante/creeper/player/NpcTestHarness.java b/src/test/com/comandante/creeper/player/NpcTestHarness.java
new file mode 100644
index 00000000..84a1e36f
--- /dev/null
+++ b/src/test/com/comandante/creeper/player/NpcTestHarness.java
@@ -0,0 +1,136 @@
+package com.comandante.creeper.player;
+
+import com.comandante.creeper.ConfigureCommands;
+import com.comandante.creeper.CreeperConfiguration;
+import com.comandante.creeper.Items.ItemUseRegistry;
+import com.comandante.creeper.Main;
+import com.comandante.creeper.entity.EntityManager;
+import com.comandante.creeper.managers.GameManager;
+import com.comandante.creeper.managers.SessionManager;
+import com.comandante.creeper.npc.Npc;
+import com.comandante.creeper.npc.NpcBuilder;
+import com.comandante.creeper.npc.NpcExporter;
+import com.comandante.creeper.server.ChannelCommunicationUtils;
+import com.comandante.creeper.server.CreeperSession;
+import com.comandante.creeper.world.MapsManager;
+import com.comandante.creeper.world.RoomManager;
+import com.comandante.creeper.world.WorldExporter;
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.commons.configuration.MapConfiguration;
+import org.jboss.netty.channel.Channel;
+import org.junit.Before;
+import org.junit.Test;
+import org.mapdb.DB;
+import org.mapdb.DBMaker;
+
+import java.io.FileNotFoundException;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static org.mockito.Mockito.mock;
+
+public class NpcTestHarness {
+
+    private GameManager gameManager;
+    private EntityManager entityManager;
+    private int totalFightRounds = 0;
+
+    @Before
+    public void setUp() throws Exception {
+        ChannelCommunicationUtils channelUtils = new ChannelCommunicationUtils() {
+            @Override
+            public void write(String playerId, String message) {}
+
+            @Override
+            public void write(String playerId, String message, boolean leadingBlankLine) {}
+        };
+        CreeperConfiguration creeperConfiguration = new CreeperConfiguration(new MapConfiguration(Maps.newHashMap()));
+        DB db = DBMaker.newMemoryDB().closeOnJvmShutdown().make();
+        PlayerManager playerManager = new PlayerManager(db, new SessionManager());
+        RoomManager roomManager = new RoomManager(playerManager);
+        MapsManager mapsManager = new MapsManager(creeperConfiguration, roomManager);
+        EntityManager entityManager = new EntityManager(roomManager, playerManager, db);
+        GameManager gameManager = new GameManager(creeperConfiguration, roomManager, playerManager, entityManager, mapsManager, channelUtils);
+        WorldExporter worldExporter = new WorldExporter(roomManager, mapsManager, gameManager.getFloorManager(), entityManager, gameManager);
+        worldExporter.buildTestworld();
+        ConfigureCommands.configure(gameManager);
+        ItemUseRegistry.configure();
+        this.entityManager = entityManager;
+        this.gameManager = gameManager;
+    }
+
+    @Test
+    public void testCombat() throws Exception {
+        List<Npc> npcsFromFile = NpcExporter.getNpcsFromFile(gameManager);
+        Npc treeBerseker = npcsFromFile.stream().filter(npc -> npc.getName().equals("tree berserker")).collect(Collectors.toList()).get(0);
+
+        int playerWins = 0;
+        int npcWins = 0;
+
+        totalFightRounds = 0;
+        int totalIterations = 100000;
+        for (int i = 0; i < totalIterations; i++) {
+            String username = UUID.randomUUID().toString();
+            Player player = createRandomPlayer(username);
+            Npc npc = new NpcBuilder(treeBerseker).createNpc();
+            gameManager.getEntityManager().addEntity(npc);
+            player.getCurrentRoom().addPresentNpc(npc.getEntityId());
+            player.addActiveFight(npc);
+            if (conductFight(player, npc)) {
+                playerWins++;
+            } else {
+                npcWins++;
+            }
+            player.getCurrentRoom().removePresentNpc(npc.getEntityId());
+            entityManager.deleteNpcEntity(npc.getEntityId());
+            player.getCurrentRoom().removePresentPlayer(player.getPlayerId());
+            if (i%100==0) {
+                System.out.println("Fight iterations: " + i);
+            }
+        }
+        System.out.println("Player Wins: " + playerWins);
+        System.out.println("Npc Wins: " + npcWins);
+        System.out.println("Average rounds: " + totalFightRounds / totalIterations);
+    }
+
+    private boolean conductFight(Player player, Npc npc) {
+        int i = 0;
+        try {
+            for (i = 0; i < 1000; i++) {
+                player.run();
+                npc.run();
+                if (!npc.getIsAlive().get()) {
+                    return true;
+                }
+                if (player.isActive(CoolDownType.DEATH)) {
+                    return false;
+                }
+            }
+            return false;
+        } finally {
+            totalFightRounds += i;
+        }
+    }
+
+    private Player createRandomPlayer(String username) throws FileNotFoundException {
+        createUser(username, "3333333");
+        Player player = new Player(username, gameManager);
+        Channel mockChannel = mock(Channel.class);
+        CreeperSession creeperSession = new CreeperSession();
+        creeperSession.setUsername(Optional.of(username));
+        player.setChannel(mockChannel);
+        gameManager.getPlayerManager().addPlayer(player);
+        gameManager.placePlayerInLobby(player);
+        gameManager.getPlayerManager().getSessionManager().putSession(creeperSession);
+        return player;
+    }
+
+    private void createUser(String username, String password) {
+        PlayerMetadata playerMetadata = new PlayerMetadata(username, password, Main.createPlayerId(username), PlayerStats.DEFAULT_PLAYER.createStats(), 0, Sets.newHashSet(PlayerRole.MORTAL), new String[0], 0, new String[0]);
+        gameManager.getPlayerManager().savePlayerMetadata(playerMetadata);
+    }
+
+}
diff --git a/world/npcs/treeberserker.json b/world/npcs/treeberserker.json
index 099e28c6..eb59d5f2 100755
--- a/world/npcs/treeberserker.json
+++ b/world/npcs/treeberserker.json
@@ -11,19 +11,19 @@
     "north1_zone"
   ],
   "stats": {
-    "agile": 1,
+    "agile": 3,
     "aim": 1,
-    "armorRating": 8,
-    "currentHealth": 200,
+    "armorRating": 16,
+    "currentHealth": 100,
     "currentMana": 100,
     "experience": 22600,
     "maxHealth": 150,
     "maxMana": 100,
-    "meleSkill": 6,
+    "meleSkill": 12,
     "numberOfWeaponRolls": 1,
-    "strength": 5,
-    "weaponRatingMax": 13,
-    "weaponRatingMin": 8,
+    "strength": 10,
+    "weaponRatingMax": 9,
+    "weaponRatingMin": 6,
     "willPower": 1
   },
   "spawnAreas": {
diff --git a/world/world.json b/world/world.json
index dadec184..582ef008 100644
--- a/world/world.json
+++ b/world/world.json
@@ -2931,8 +2931,8 @@
         {
           "roomId": 98,
           "floorId": 16,
-          "roomDescription": "Dried and cured meats hang from the ceiling and along the walls. A rack of sharp knives can be seen behind the counter, and leather and cloth aprons hang from several hooks on the wall.",
-          "roomTitle": "BUTCHER SHOP",
+          "roomDescription": "Dust covered books and alchemy bottles line the wall.  Everything here is for sale.",
+          "roomTitle": "Wizards Stoop",
           "roomTags": [],
           "areaNames": [
             "house_zone"
@@ -6143,7 +6143,7 @@
         {
           "roomId": 2,
           "floorId": 0,
-          "roomDescription": "Flanking the town square on its northern side, this cobblestone lane is home to a small butcher shop and a blacksmith. The simple stone buildings are covered in a layer of soot from the constant burning of coal, needed to forge the tools and weaponry required by the townspeople. To the north, the town gates can be seen in the distance.",
+          "roomDescription": "Flanking the town square on its northern side, this cobblestone lane is home to a small wizards shop and a blacksmith. The simple stone buildings are covered in a layer of soot from the constant burning of coal, needed to forge the tools and weaponry required by the townspeople. To the north, the town gates can be seen in the distance.",
           "roomTitle": "CRAIGAVON LANE NORTH",
           "roomTags": [],
           "areaNames": [
@@ -6151,7 +6151,7 @@
           ],
           "enterExitNames": {
             "66": "Blacksmith",
-            "98": "Butcher"
+            "98": "Wizard"
           },
           "notables": {}
         },
-- 
GitLab