commit 2ee9eb1b655e1186d8a10adb2eb8a073bdc2cc79
Author: Hymmel
Date: Fri Oct 10 15:32:29 2025 +0200
init
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c14674c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/target
+/project/target
+/project/project
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..b872a00
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,29 @@
+# Stage 1: Build the application using SBT
+FROM sbtscala/sbt:11.0.11_1.5.5_2.13.6 as builder
+
+WORKDIR /app
+
+# Copy project definition files and resolve dependencies first for better layer caching
+COPY project/ ./project/
+COPY build.sbt .
+
+# This step will download all the dependencies
+RUN sbt update
+
+# Copy the rest of the source code
+COPY . .
+
+# Package the application into a WAR file
+RUN sbt package
+
+# Stage 2: Create the runtime image with Tomcat
+FROM tomcat:9.0-jdk11-openjdk-slim
+
+# Remove the default webapps
+RUN rm -rf /usr/local/tomcat/webapps/*
+
+# Copy the WAR file from the builder stage to Tomcat's webapps directory
+# The WAR file will be automatically deployed by Tomcat on startup
+COPY --from=builder /app/target/scala-2.11/coc-base-analyser_2.11-0.1.war /usr/local/tomcat/webapps/ROOT.war
+
+EXPOSE 8080
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4a875ed
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Daniel Holmes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2a67bc3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,82 @@
+# Clash of Clans Base Analyser
+
+Analyses base layouts against sets of war base rules. It runs this analysis in bulk and provides tabulated results for
+the current war bases for each member in a clan. Additional to this you can click through to an individual's base to
+see a more in depth analysis.
+
+Analysis is only allowed for pre-configured clans in the app which are assigned an alias for convenience.
+
+
+## Dependencies
+
+ - SBT
+ - JDK 8+
+ - Scala 2.11
+
+To find available SBT dependency updates run `sbt dependencyUpdates`
+
+
+## Tests
+
+ - All: `sbt test`
+ - Individual: `sbt "test-only org.danielholmes.coc.baseanalyser.baseparser.VillageJsonParserSpec"`
+ - Individual continuous: `sbt ~"test-only org.danielholmes.coc.baseanalyser.baseparser.VillageJsonParserSpec"`
+
+
+## Command Line Utils
+
+`sbt 'run-main org.danielholmes.coc.baseanalyser.PrintVillage alpha "I AM SPARTA!!1!"'`
+`sbt 'run-main org.danielholmes.coc.baseanalyser.PrintAttackPlacements alpha "I AM SPARTA!!1!"'`
+`sbt 'run-main org.danielholmes.coc.baseanalyser.ProfileAnalysis alpha "I AM SPARTA!!1!"'`
+
+
+## Running dev version of site (automatically reloads on changes)
+
+`sbt ~tomcat:start`
+
+Available at [http://localhost:8080](http://localhost:8080)
+
+Accessing a clan e.g. [http://localhost:8080/clans/alpha](http://localhost:8080/clans/alpha)
+
+
+## Production Build
+
+`sbt package`
+
+then deploy the war as required:
+
+`target/scala-2.11/coc-base-analyser_2.11-0.1.war`
+
+
+## Game Connection
+
+The app requires a connection to the Supercell servers to query the village json and other clan members. Note that this
+isn't referring to the official [Clash of Clans API](https://developer.clashofclans.com/), but a direct connection to
+the game servers how the game does. Once upon a time this project used a product called "Clan Seeker" which has since
+been discontinued. It's trivial however to write an agent for the app if there's another such service out there:
+
+ 1. Write a new game connection agent that implements the
+ [GameConnection Trait](src/main/scala/org/danielholmes/coc/baseanalyser/gameconnection/GameConnection.scala)
+ 2. Wire in the game connection in the
+ [Services Trait](src/main/scala/org/danielholmes/coc/baseanalyser/Services.scala)
+
+At the moment there's a hardcoded stub/testing GameConnection with dummy data
+
+
+## Rules
+
+The project is very much in a WIP state. Town Hall 8 is pretty fleshed out but TH9 and above don't have many rules.
+Also these were modelled off of attack and base building meta from around February 2016. Some newer buildings are also
+not present such as the Bomb Tower.
+
+Rules are pretty quick to build due to the building blocks and infrastructure of a modelled base being in place.
+
+
+## Screenshots
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..0dab6c2
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,100 @@
+# TODO
+
+## General TODO
+ - Obstacles anywhere but outer 3 border tiles rule
+ - just use generic obstacle render atm
+
+ - Once api up again
+ - store skeleton trap mode once can see and verify on live data
+ - store xbow mode once can see and verify on live data
+ - double check sweeper angles being rendered same as in game once clan seeker up
+
+ - own connection
+ - purchase from alex
+ - set up on own small EB app
+
+ - trap access (if leadership go for it) - new, dedicated credentials of own
+ - Privileged vs unprivileged analysis - warnings if not using traps
+ - asterixes against rules which traps have an effect
+
+ - password protection (in the wrong hands opposition would see our trap locations)
+
+ - analysis performance, currently too slow
+ - MapCoordinate trait with underlying FloatMapCoordinate, TileCoordinate, Tile - encourage integer math where possible and prevent widening
+ - Views of tile sets (e.g. TileBlock returned from matrix
+ - Redo ranges - should include underlying cached set of tiles + tilecoordinates contained
+
+ - Some TH9 rules
+ - Queen Charge into wall breakable compartment shouldnt get to 2 air defs
+ - It should require either a jump spell or 2 wall breaker groups in order to access the queen.
+
+ - on equidistant hog lure, mark it as such or ignore it all together (maybe a pink line showing equidistant, non luring alternative path
+
+ - TH10s without infernos should go under TH 9.5 rules?
+
+ - separate hole in the base rule - just to highlight really bad issues (due to ignoring some others)
+
+ - expand possible trap locations for channel bases. e.g. see spandan and vicious 2.0 an sparta home base
+
+ - Begin on DGB:
+ - class PossibleDoubleGiantBomb(anchors: (Either[Defense, PossibleTrapLocation], Either[Defense, PossibleTrapLocation]), gbs: (PossibleTrapLocation, PossibleTrapLocation))
+
+ - BK Trigger rule further tweaks. should show red for all non-compartment tiles floodfilled from triggered
+
+ - clarify AQ range - see iphoto screenshot of greg raid. possibly shown on ppetes war base
+
+ - TH11 rendering - new levels and warden + eagle
+
+ - sbt deploy task
+ - 3d render
+ - split map display 2d apart. New inner stage container - drawLine(tile1, tile2) which transforms to 2d or 3d view
+ - separate rule groups for farm vs arranged
+ - pass, warning, fail levels (e.g. for minion anchors)
+ - integration tests
+
+
+## TH8 TODO Rules
+ - air defs should be a minimum distance apart
+ - SAMs not next to each other - one kills a dragon
+ - loon pathing
+ - must be >= 3 defenses to go through to path to air defs
+ - OR must be > certain distance
+ - should also consider air trap placement
+ - should also consider air sweeper placement
+ - minimum 3 DGB possible spots (including diagonal)
+ - minion anchors (warning only, no hard fail, once that functionality is built)
+ - wb t junction warning
+
+
+## TH8 TODO Rules once traps available
+ - spring trap locations (resting on defenses)
+ - skele traps should be on ground and not triggerable during cc lure
+ - skele traps + air traps not within dgb positions (gives info for cleanup if first hit was with air)
+ - 3 viable DGB spots (more difficult)
+ - farm wars - teslas in diff compartment for gowipe
+ - trash buildings in front of all outer ring defenses
+
+
+## TH9 Rules
+ - EQ cant connect >2 GB/DGB positions + AQ
+ - Jump doesnt connect too many AQ, GB
+ - queen needs to be protected from “suicide dragons”
+ Specifically, an air sweeper pointed to protect the queen, or (more commonly) a black mine between the queen and the likely dragon entry point
+ - black bombs within range of queen or air def - to get hounds or suicide drags. red bombs out of range of air defs
+ - The defenses around your DGB should be more than 4 tiles from an exterior wall
+ Ensures the defenses aren’t eliminated using a queen walk
+
+
+## TH10 Rules
+ - cant get 2 infernos with one freeze
+
+
+## Expansion ideas
+ - Hog pathing analysis - start paths from each tile and be able to select/see individual paths from defense to defense
+ to show DGB issues
+ - multiple goals/rulesets:
+ - farming (protected loot, give away easy shield - one star, but no value for more than)
+ - war - depending on clan and level, this might be to prevent 1 star, prevent 2 star, or just prevent 3 star
+ - trophy?
+ - provide weaknesses for attack types. e.g. drags doesnt consider DGB locations, hogs dont consider air def high hp.
+ - queen walk pathing from drop point
diff --git a/build.sbt b/build.sbt
new file mode 100644
index 0000000..54a45b0
--- /dev/null
+++ b/build.sbt
@@ -0,0 +1,68 @@
+name := "coc-base-analyser"
+
+version := "0.1"
+
+scalaVersion := "2.11.8"
+
+lazy val root = (project in file(".")).enablePlugins(SbtWeb).enablePlugins(TomcatPlugin)
+
+libraryDependencies ++= {
+ val akkaV = "2.4.4"
+ val sprayV = "1.3.3"
+ Seq(
+ "org.apache.commons" % "commons-math3" % "3.6.1",
+ "io.spray" %% "spray-json" % "1.3.2",
+
+ "io.spray" %% "spray-can" % sprayV,
+ "io.spray" %% "spray-servlet" % sprayV,
+ "io.spray" %% "spray-routing" % sprayV,
+ "io.spray" %% "spray-client" % sprayV,
+ "com.typesafe.akka" %% "akka-actor" % akkaV,
+
+ "org.scalactic" %% "scalactic" % "3.0.0-SNAP13",
+
+ "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
+
+ "com.softwaremill.macwire" %% "macros" % "2.2.2" % "provided",
+ "com.softwaremill.macwire" %% "util" % "2.2.2",
+ "com.softwaremill.macwire" %% "proxy" % "2.2.2",
+
+ "com.github.spullara.mustache.java" % "scala-extensions-2.11" % "0.9.1",
+ "com.google.guava" % "guava" % "19.0",
+
+ "org.scalatest" % "scalatest_2.11" % "2.2.6" % "test"
+ )
+}
+
+lazy val testScalastyle = taskKey[Unit]("testScalastyle")
+testScalastyle := org.scalastyle.sbt.ScalastylePlugin.scalastyle.in(Test).toTask("").value
+(test in Test) <<= (test in Test) dependsOn testScalastyle
+
+lazy val compileScalastyle = taskKey[Unit]("compileScalastyle")
+compileScalastyle := org.scalastyle.sbt.ScalastylePlugin.scalastyle.in(Compile).toTask("").value
+(compile in Compile) <<= (compile in Compile) dependsOn compileScalastyle
+
+// Runs with tomcat:start, only want for war task
+//(packageBin in Compile) <<= (packageBin in Compile) dependsOn (test in Test)
+
+/*
+webappPostProcess := {
+ webappDir: File => Unit;
+
+ def listFiles(level: Int)(f: File): Unit = {
+ val indent = ((1 until level) map { _ => " " }).mkString
+ if (f.isDirectory) {
+ streams.value.log.info(indent + f.getName + "/")
+ f.listFiles foreach { listFiles(level + 1) }
+ } else streams.value.log.info(indent + f.getName)
+ }
+ listFiles(1)(webappDir)
+}
+
+pipelineStages in Assets := Seq(concat)
+// Doesnt work but need something like this so can copy the staged files in the PostProcess above
+packageBin <<= packageBin.dependsOn(WebKeys.stage)
+
+Concat.groups := Seq(
+ "js/script-group.js" -> group(Seq("js/script1.js", "js/script2.js"))
+)*/
diff --git a/docs/images/base-1.png b/docs/images/base-1.png
new file mode 100644
index 0000000..f34795f
Binary files /dev/null and b/docs/images/base-1.png differ
diff --git a/docs/images/base-2.png b/docs/images/base-2.png
new file mode 100644
index 0000000..5be0f3f
Binary files /dev/null and b/docs/images/base-2.png differ
diff --git a/docs/images/base-3.png b/docs/images/base-3.png
new file mode 100644
index 0000000..442afcb
Binary files /dev/null and b/docs/images/base-3.png differ
diff --git a/docs/images/base-4.png b/docs/images/base-4.png
new file mode 100644
index 0000000..d809a0b
Binary files /dev/null and b/docs/images/base-4.png differ
diff --git a/docs/images/bulk-1.png b/docs/images/bulk-1.png
new file mode 100644
index 0000000..97c9d64
Binary files /dev/null and b/docs/images/bulk-1.png differ
diff --git a/docs/images/home-1.png b/docs/images/home-1.png
new file mode 100644
index 0000000..698fd55
Binary files /dev/null and b/docs/images/home-1.png differ
diff --git a/project/build.properties b/project/build.properties
new file mode 100644
index 0000000..43b8278
--- /dev/null
+++ b/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.11
diff --git a/project/plugins.sbt b/project/plugins.sbt
new file mode 100644
index 0000000..1cd9e59
--- /dev/null
+++ b/project/plugins.sbt
@@ -0,0 +1,5 @@
+resolvers += Resolver.sonatypeRepo("releases")
+
+addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.1.0")
+addSbtPlugin("net.ground5hark.sbt" % "sbt-concat" % "0.1.8")
+addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")
\ No newline at end of file
diff --git a/project/sbt-updates.sbt b/project/sbt-updates.sbt
new file mode 100644
index 0000000..b26bdc4
--- /dev/null
+++ b/project/sbt-updates.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
\ No newline at end of file
diff --git a/scalastyle-config.xml b/scalastyle-config.xml
new file mode 100644
index 0000000..9a8a9f2
--- /dev/null
+++ b/scalastyle-config.xml
@@ -0,0 +1,107 @@
+
+ Scalastyle standard configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf
new file mode 100644
index 0000000..c4373ab
--- /dev/null
+++ b/src/main/resources/application.conf
@@ -0,0 +1,7 @@
+akka {
+ log-dead-letters-during-shutdown = off
+}
+
+spray.servlet {
+ boot-class = "org.danielholmes.coc.baseanalyser.web.WebAppServlet"
+}
\ No newline at end of file
diff --git a/src/main/resources/examples/th5-sample-1.json b/src/main/resources/examples/th5-sample-1.json
new file mode 100644
index 0000000..399699f
--- /dev/null
+++ b/src/main/resources/examples/th5-sample-1.json
@@ -0,0 +1 @@
+{"wave_num":6,"exp_ver":1,"active_layout":0,"layout_state":[0,0,0,0,0,0],"buildings":[{"data":1000001,"id":500000000,"lvl":5,"x":30,"y":30,"l1x":25,"l1y":19,"l2x":13,"l2y":26,"l3x":29,"l3y":20,"l4x":29,"l4y":20,"l5x":13,"l5y":26},{"data":1000004,"id":500000001,"lvl":11,"x":22,"y":33,"res_time":87169,"l1x":11,"l1y":31,"l2x":24,"l2y":40,"l3x":41,"l3y":33,"l4x":41,"l4y":33,"l5x":24,"l5y":40},{"data":1000000,"id":500000002,"lvl":5,"x":35,"y":15,"units":[[4000008,2]],"l1x":29,"l1y":39,"l2x":38,"l2y":14,"l3x":40,"l3y":19,"l4x":40,"l4y":19,"l5x":38,"l5y":14},{"data":1000015,"id":500000003,"lvl":0,"x":41,"y":41,"l1x":14,"l1y":39,"l2x":9,"l2y":33,"l3x":36,"l3y":12,"l4x":36,"l4y":12,"l5x":41,"l5y":34},{"data":1000014,"id":500000004,"lvl":3,"x":27,"y":27,"l1x":25,"l1y":24,"l2x":23,"l2y":23,"l3x":25,"l3y":25,"l4x":25,"l4y":25,"l5x":23,"l5y":23},{"data":1000008,"id":500000005,"lvl":9,"x":37,"y":37,"l1x":36,"l1y":25,"l2x":33,"l2y":37,"l3x":34,"l3y":39,"l4x":34,"l4y":39,"l5x":33,"l5y":37},{"data":1000015,"id":500000006,"lvl":0,"x":38,"y":41,"l1x":16,"l1y":39,"l2x":37,"l2y":12,"l3x":32,"l3y":9,"l4x":32,"l4y":9,"l5x":37,"l5y":12},{"data":1000002,"id":500000007,"lvl":11,"x":37,"y":22,"res_time":87173,"l1x":21,"l1y":9,"l2x":41,"l2y":24,"l3x":8,"l3y":20,"l4x":8,"l4y":20,"l5x":41,"l5y":24},{"data":1000003,"id":500000008,"lvl":10,"x":39,"y":30,"l1x":16,"l1y":20,"l2x":26,"l2y":15,"l3x":29,"l3y":34,"l4x":29,"l4y":33,"l5x":26,"l5y":15},{"data":1000005,"id":500000009,"lvl":10,"x":30,"y":39,"l1x":24,"l1y":33,"l2x":27,"l2y":33,"l3x":16,"l3y":32,"l4x":16,"l4y":32,"l5x":27,"l5y":33},{"data":1000006,"id":500000010,"lvl":9,"x":8,"y":27,"unit_prod":{"unit_type":0},"l1x":40,"l1y":27,"l2x":21,"l2y":39,"l3x":30,"l3y":39,"l4x":30,"l4y":39,"l5x":21,"l5y":39},{"data":1000004,"id":500000011,"lvl":11,"x":33,"y":22,"res_time":65399,"l1x":18,"l1y":8,"l2x":36,"l2y":24,"l3x":42,"l3y":25,"l4x":42,"l4y":25,"l5x":36,"l5y":24},{"data":1000006,"id":500000012,"lvl":9,"x":21,"y":10,"unit_prod":{"unit_type":0},"l1x":39,"l1y":36,"l2x":18,"l2y":39,"l3x":7,"l3y":28,"l4x":7,"l4y":28,"l5x":18,"l5y":39},{"data":1000002,"id":500000013,"lvl":11,"x":31,"y":14,"res_time":61037,"l1x":34,"l1y":11,"l2x":41,"l2y":19,"l3x":8,"l3y":17,"l4x":8,"l4y":17,"l5x":41,"l5y":19},{"data":1000009,"id":500000014,"lvl":9,"x":24,"y":30,"l1x":15,"l1y":16,"l2x":12,"l2y":15,"l3x":24,"l3y":35,"l4x":24,"l4y":35,"l5x":12,"l5y":15},{"data":1000008,"id":500000015,"lvl":9,"x":10,"y":32,"l1x":30,"l1y":26,"l2x":9,"l2y":22,"l3x":38,"l3y":25,"l4x":38,"l4y":25,"l5x":9,"l5y":22},{"data":1000000,"id":500000016,"lvl":5,"x":15,"y":35,"units":[[4000008,2]],"l1x":24,"l1y":8,"l2x":32,"l2y":9,"l3x":20,"l3y":5,"l4x":20,"l4y":5,"l5x":32,"l5y":9},{"data":1000002,"id":500000017,"lvl":11,"x":10,"y":17,"res_time":87181,"l1x":37,"l1y":15,"l2x":40,"l2y":35,"l3x":14,"l3y":22,"l4x":14,"l4y":22,"l5x":9,"l5y":34},{"data":1000004,"id":500000018,"lvl":11,"x":14,"y":31,"res_time":61057,"l1x":40,"l1y":24,"l2x":15,"l2y":11,"l3x":40,"l3y":29,"l4x":40,"l4y":29,"l5x":15,"l5y":11},{"data":1000003,"id":500000019,"lvl":10,"x":39,"y":33,"l1x":35,"l1y":22,"l2x":19,"l2y":14,"l3x":22,"l3y":14,"l4x":22,"l4y":14,"l5x":20,"l5y":14},{"data":1000005,"id":500000020,"lvl":10,"x":33,"y":39,"l1x":19,"l1y":16,"l2x":30,"l2y":19,"l3x":18,"l3y":23,"l4x":18,"l4y":23,"l5x":30,"l5y":18},{"data":1000010,"id":500000021,"lvl":7,"x":40,"y":37,"l1x":24,"l1y":39,"l2x":23,"l2y":35,"l3x":36,"l3y":35,"l4x":36,"l4y":35,"l5x":24,"l5y":34},{"data":1000010,"id":500000022,"lvl":7,"x":42,"y":35,"l1x":25,"l1y":39,"l2x":23,"l2y":33,"l3x":15,"l3y":16,"l4x":15,"l4y":16,"l5x":24,"l5y":33},{"data":1000010,"id":500000023,"lvl":7,"x":42,"y":36,"l1x":26,"l1y":39,"l2x":24,"l2y":32,"l3x":15,"l3y":15,"l4x":15,"l4y":15,"l5x":24,"l5y":32},{"data":1000010,"id":500000024,"lvl":7,"x":41,"y":36,"l1x":27,"l1y":39,"l2x":15,"l2y":19,"l3x":15,"l3y":14,"l4x":15,"l4y":14,"l5x":13,"l5y":20}],"decos":[],"respawnVars":{"secondsFromLastRespawn":231386,"respawnSeed":1914935487,"obstacleClearCounter":5,"time_to_gembox_drop":196966,"time_in_gembox_period":228665},"cooldowns":[],"newShopBuildings":[4,0,6,3,6,3,4,1,5,5,225,3,3,4,1,5,0,0,0,3,1,0,1,2,1,0,2,0,1,1,0,0],"newShopTraps":[6,6,3,0,0,4,2,0,2],"newShopDecos":[1,4,0,1,1,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"last_league_rank":9,"last_alliance_level":7,"last_league_shuffle":1,"last_season_seen":0,"last_news_seen":221,"troop_req_msg":"max hogs and max poison","war_req_msg":"2 witches, 1 barb","war_tutorials_seen":0,"war_base":false,"account_flags":14,"bool_layout_edit_shown_erase":true}
\ No newline at end of file
diff --git a/src/main/resources/examples/th8-sample-1.json b/src/main/resources/examples/th8-sample-1.json
new file mode 100644
index 0000000..273bd4f
--- /dev/null
+++ b/src/main/resources/examples/th8-sample-1.json
@@ -0,0 +1,18 @@
+{"wave_num":6,"exp_ver":1,"active_layout":0,"war_layout":1,"layout_state":[0,0,0,0,0,0],"buildings":[{"data":1000001,"id":500000000,"lvl":7,"x":30,"y":30,"l1x":25,"l1y":19,"l2x":13,"l2y":26,"l3x":29,"l3y":20,"l4x":29,"l4y":20,"l5x":13,"l5y":26},{"data":1000004,"id":500000001,"lvl":11,"x":22,"y":33,"res_time":87169,"l1x":11,"l1y":31,"l2x":24,"l2y":40,"l3x":41,"l3y":33,"l4x":41,"l4y":33,"l5x":24,"l5y":40},{"data":1000000,"id":500000002,"lvl":5,"x":35,"y":15,"units":[[4000008,2]],"l1x":29,"l1y":39,"l2x":38,"l2y":14,"l3x":40,"l3y":19,"l4x":40,"l4y":19,"l5x":38,"l5y":14},{"data":1000015,"id":500000003,"lvl":0,"x":41,"y":41,"l1x":14,"l1y":39,"l2x":9,"l2y":33,"l3x":36,"l3y":12,"l4x":36,"l4y":12,"l5x":41,"l5y":34},{"data":1000014,"id":500000004,"lvl":3,"x":27,"y":27,"l1x":25,"l1y":24,"l2x":23,"l2y":23,"l3x":25,"l3y":25,"l4x":25,"l4y":25,"l5x":23,"l5y":23},{"data":1000008,"id":500000005,"lvl":9,"x":37,"y":37,"l1x":36,"l1y":25,"l2x":33,"l2y":37,"l3x":34,"l3y":39,"l4x":34,"l4y":39,"l5x":33,"l5y":37},{"data":1000015,"id":500000006,"lvl":0,"x":38,"y":41,"l1x":16,"l1y":39,"l2x":37,"l2y":12,"l3x":32,"l3y":9,"l4x":32,"l4y":9,"l5x":37,"l5y":12},{"data":1000002,"id":500000007,"lvl":11,"x":37,"y":22,"res_time":87173,"l1x":21,"l1y":9,"l2x":41,"l2y":24,"l3x":8,"l3y":20,"l4x":8,"l4y":20,"l5x":41,"l5y":24},{"data":1000003,"id":500000008,"lvl":10,"x":39,"y":30,"l1x":16,"l1y":20,"l2x":26,"l2y":15,"l3x":29,"l3y":34,"l4x":29,"l4y":33,"l5x":26,"l5y":15},{"data":1000005,"id":500000009,"lvl":10,"x":30,"y":39,"l1x":24,"l1y":33,"l2x":27,"l2y":33,"l3x":16,"l3y":32,"l4x":16,"l4y":32,"l5x":27,"l5y":33},{"data":1000006,"id":500000010,"lvl":9,"x":8,"y":27,"unit_prod":{"unit_type":0},"l1x":40,"l1y":27,"l2x":21,"l2y":39,"l3x":30,"l3y":39,"l4x":30,"l4y":39,"l5x":21,"l5y":39},{"data":1000004,"id":500000011,"lvl":11,"x":33,"y":22,"res_time":65399,"l1x":18,"l1y":8,"l2x":36,"l2y":24,"l3x":42,"l3y":25,"l4x":42,"l4y":25,"l5x":36,"l5y":24},{"data":1000006,"id":500000012,"lvl":9,"x":21,"y":10,"unit_prod":{"unit_type":0},"l1x":39,"l1y":36,"l2x":18,"l2y":39,"l3x":7,"l3y":28,"l4x":7,"l4y":28,"l5x":18,"l5y":39},{"data":1000002,"id":500000013,"lvl":11,"x":31,"y":14,"res_time":61037,"l1x":34,"l1y":11,"l2x":41,"l2y":19,"l3x":8,"l3y":17,"l4x":8,"l4y":17,"l5x":41,"l5y":19},{"data":1000009,"id":500000014,"lvl":9,"x":24,"y":30,"l1x":15,"l1y":16,"l2x":12,"l2y":15,"l3x":24,"l3y":35,"l4x":24,"l4y":35,"l5x":12,"l5y":15},{"data":1000008,"id":500000015,"lvl":9,"x":10,"y":32,"l1x":30,"l1y":26,"l2x":9,"l2y":22,"l3x":38,"l3y":25,"l4x":38,"l4y":25,"l5x":9,"l5y":22},{"data":1000000,"id":500000016,"lvl":5,"x":15,"y":35,"units":[[4000008,2]],"l1x":24,"l1y":8,"l2x":32,"l2y":9,"l3x":20,"l3y":5,"l4x":20,"l4y":5,"l5x":32,"l5y":9},{"data":1000002,"id":500000017,"lvl":11,"x":10,"y":17,"res_time":87181,"l1x":37,"l1y":15,"l2x":40,"l2y":35,"l3x":14,"l3y":22,"l4x":14,"l4y":22,"l5x":9,"l5y":34},{"data":1000004,"id":500000018,"lvl":11,"x":14,"y":31,"res_time":61057,"l1x":40,"l1y":24,"l2x":15,"l2y":11,"l3x":40,"l3y":29,"l4x":40,"l4y":29,"l5x":15,"l5y":11},{"data":1000003,"id":500000019,"lvl":10,"x":39,"y":33,"l1x":35,"l1y":22,"l2x":19,"l2y":14,"l3x":22,"l3y":14,"l4x":22,"l4y":14,"l5x":20,"l5y":14},{"data":1000005,"id":500000020,"lvl":10,"x":33,"y":39,"l1x":19,"l1y":16,"l2x":30,"l2y":19,"l3x":18,"l3y":23,"l4x":18,"l4y":23,"l5x":30,"l5y":18},{"data":1000010,"id":500000021,"lvl":7,"x":40,"y":37,"l1x":24,"l1y":39,"l2x":23,"l2y":35,"l3x":36,"l3y":35,"l4x":36,"l4y":35,"l5x":24,"l5y":34},{"data":1000010,"id":500000022,"lvl":7,"x":42,"y":35,"l1x":25,"l1y":39,"l2x":23,"l2y":33,"l3x":15,"l3y":16,"l4x":15,"l4y":16,"l5x":24,"l5y":33},{"data":1000010,"id":500000023,"lvl":7,"x":42,"y":36,"l1x":26,"l1y":39,"l2x":24,"l2y":32,"l3x":15,"l3y":15,"l4x":15,"l4y":15,"l5x":24,"l5y":32},{"data":1000010,"id":500000024,"lvl":7,"x":41,"y":36,"l1x":27,"l1y":39,"l2x":15,"l2y":19,"l3x":15,"l3y":14,"l4x":15,"l4y":14,"l5x":13,"l5y":20},{"data":1000010,"id":500000025,"lvl":7,"x":34,"y":42,"l1x":28,"l1y":39,"l2x":14,"l2y":19,"l3x":22,"l3y":18,"l4x":19,"l4y":18,"l5x":15,"l5y":20},{"data":1000010,"id":500000026,"lvl":7,"x":35,"y":42,"l1x":26,"l1y":23,"l2x":13,"l2y":19,"l3x":41,"l3y":28,"l4x":41,"l4y":28,"l5x":34,"l5y":20},{"data":1000010,"id":500000027,"lvl":7,"x":36,"y":42,"l1x":29,"l1y":38,"l2x":17,"l2y":31,"l3x":15,"l3y":13,"l4x":15,"l4y":13,"l5x":17,"l5y":31},{"data":1000010,"id":500000028,"lvl":7,"x":37,"y":42,"l1x":30,"l1y":35,"l2x":17,"l2y":32,"l3x":16,"l3y":13,"l4x":16,"l4y":13,"l5x":17,"l5y":32},{"data":1000010,"id":500000029,"lvl":7,"x":37,"y":41,"l1x":31,"l1y":38,"l2x":17,"l2y":33,"l3x":17,"l3y":13,"l4x":17,"l4y":13,"l5x":17,"l5y":33},{"data":1000010,"id":500000030,"lvl":7,"x":40,"y":26,"l1x":32,"l1y":38,"l2x":16,"l2y":33,"l3x":18,"l3y":13,"l4x":18,"l4y":13,"l5x":16,"l5y":33},{"data":1000010,"id":500000031,"lvl":7,"x":37,"y":40,"l1x":34,"l1y":38,"l2x":15,"l2y":33,"l3x":19,"l3y":13,"l4x":19,"l4y":13,"l5x":15,"l5y":33},{"data":1000010,"id":500000032,"lvl":7,"x":38,"y":40,"l1x":35,"l1y":38,"l2x":14,"l2y":33,"l3x":36,"l3y":38,"l4x":36,"l4y":38,"l5x":14,"l5y":33},{"data":1000010,"id":500000033,"lvl":7,"x":39,"y":40,"l1x":36,"l1y":38,"l2x":13,"l2y":33,"l3x":41,"l3y":27,"l4x":41,"l4y":27,"l5x":13,"l5y":33},
+
+ {"data":1000019, "lvl":5, "x":31, "y": 33,"l1x":31, "l1y":33},
+ {"data":12000002, "lvl":2, "x":24, "y":28,"l1x":24,"l1y":28},
+
+ {"data":12000000, "lvl":5, "x":3, "y":3,"l1x":24,"l1y":30},
+ {"data":12000000, "lvl":5, "x":3, "y":4,"l1x":25,"l1y":30},
+ {"data":12000000, "lvl":5, "x":3, "y":5,"l1x":24,"l1y":31},
+ {"data":12000000, "lvl":5, "x":3, "y":6,"l1x":25,"l1y":31},
+
+ {"data":12000001, "lvl":5, "x":3, "y":7,"l1x":16,"l1y":15},
+ {"data":12000008, "lvl":2, "x":3, "y":8,"l1x":24,"l1y":24},
+ {"data":12000005, "lvl":1, "x":3, "y":9,"l1x":24,"l1y":25},
+ {"data":12000006, "lvl":3, "x":3, "y":10,"l1x":24,"l1y":26},
+
+ {"data":8000021, "lvl":0, "x":3, "y":11,"l1x":38,"l1y":9},
+
+ {"data":1000010,"id":500000034,"lvl":7,"x":40,"y":36,"l1x":37,"l1y":38,"l2x":12,"l2y":33,"l3x":35,"l3y":38,"l4x":35,"l4y":38,"l5x":12,"l5y":33},{"data":1000010,"id":500000035,"lvl":7,"x":42,"y":34,"l1x":38,"l1y":38,"l2x":11,"l2y":33,"l3x":34,"l3y":38,"l4x":34,"l4y":38,"l5x":11,"l5y":33},{"data":1000010,"id":500000036,"lvl":7,"x":40,"y":38,"l1x":38,"l1y":37,"l2x":11,"l2y":32,"l3x":33,"l3y":38,"l4x":33,"l4y":38,"l5x":11,"l5y":32},{"data":1000010,"id":500000037,"lvl":7,"x":40,"y":39,"l1x":38,"l1y":36,"l2x":11,"l2y":31,"l3x":32,"l3y":38,"l4x":32,"l4y":38,"l5x":11,"l5y":31},{"data":1000010,"id":500000038,"lvl":7,"x":39,"y":26,"l1x":32,"l1y":21,"l2x":11,"l2y":30,"l3x":31,"l3y":38,"l4x":31,"l4y":38,"l5x":11,"l5y":30},{"data":1000010,"id":500000039,"lvl":7,"x":38,"y":26,"l1x":29,"l1y":19,"l2x":11,"l2y":29,"l3x":31,"l3y":37,"l4x":31,"l4y":37,"l5x":11,"l5y":29},{"data":1000010,"id":500000040,"lvl":7,"x":38,"y":25,"l1x":38,"l1y":35,"l2x":12,"l2y":29,"l3x":36,"l3y":34,"l4x":36,"l4y":34,"l5x":12,"l5y":29},{"data":1000010,"id":500000041,"lvl":7,"x":37,"y":25,"l1x":38,"l1y":34,"l2x":12,"l2y":28,"l3x":36,"l3y":33,"l4x":36,"l4y":33,"l5x":12,"l5y":28},{"data":1000010,"id":500000042,"lvl":7,"x":36,"y":25,"l1x":27,"l1y":13,"l2x":12,"l2y":27,"l3x":36,"l3y":36,"l4x":36,"l4y":36,"l5x":12,"l5y":27},{"data":1000010,"id":500000043,"lvl":7,"x":36,"y":24,"l1x":23,"l1y":13,"l2x":12,"l2y":26,"l3x":23,"l3y":37,"l4x":22,"l4y":38,"l5x":12,"l5y":26},{"data":1000010,"id":500000044,"lvl":7,"x":36,"y":23,"l1x":24,"l1y":13,"l2x":12,"l2y":19,"l3x":30,"l3y":37,"l4x":30,"l4y":37,"l5x":34,"l5y":22},{"data":1000010,"id":500000045,"lvl":7,"x":36,"y":22,"l1x":25,"l1y":13,"l2x":12,"l2y":20,"l3x":27,"l3y":38,"l4x":27,"l4y":38,"l5x":12,"l5y":20},{"data":1000010,"id":500000046,"lvl":7,"x":36,"y":21,"l1x":26,"l1y":13,"l2x":12,"l2y":21,"l3x":33,"l3y":32,"l4x":33,"l4y":32,"l5x":12,"l5y":21},{"data":1000010,"id":500000047,"lvl":7,"x":35,"y":21,"l1x":28,"l1y":13,"l2x":12,"l2y":22,"l3x":34,"l3y":32,"l4x":34,"l4y":32,"l5x":12,"l5y":22},{"data":1000010,"id":500000048,"lvl":7,"x":40,"y":40,"l1x":32,"l1y":12,"l2x":12,"l2y":23,"l3x":35,"l3y":32,"l4x":35,"l4y":32,"l5x":12,"l5y":23},{"data":1000010,"id":500000049,"lvl":7,"x":12,"y":8,"l1x":29,"l1y":14,"l2x":12,"l2y":24,"l3x":36,"l3y":32,"l4x":36,"l4y":32,"l5x":12,"l5y":24},{"data":1000010,"id":500000050,"lvl":7,"x":26,"y":39,"l1x":29,"l1y":16,"l2x":12,"l2y":25,"l3x":37,"l3y":32,"l4x":37,"l4y":32,"l5x":12,"l5y":25},{"data":1000010,"id":500000051,"lvl":7,"x":26,"y":38,"l1x":29,"l1y":15,"l2x":18,"l2y":16,"l3x":37,"l3y":31,"l4x":37,"l4y":31,"l5x":18,"l5y":16},{"data":1000010,"id":500000052,"lvl":7,"x":25,"y":38,"l1x":29,"l1y":17,"l2x":18,"l2y":15,"l3x":37,"l3y":30,"l4x":37,"l4y":30,"l5x":18,"l5y":15},{"data":1000010,"id":500000053,"lvl":7,"x":25,"y":37,"l1x":31,"l1y":12,"l2x":18,"l2y":14,"l3x":37,"l3y":29,"l4x":37,"l4y":29,"l5x":18,"l5y":14},{"data":1000010,"id":500000054,"lvl":7,"x":25,"y":36,"l1x":36,"l1y":16,"l2x":18,"l2y":13,"l3x":37,"l3y":28,"l4x":37,"l4y":28,"l5x":18,"l5y":13},{"data":1000010,"id":500000055,"lvl":7,"x":24,"y":36,"l1x":29,"l1y":12,"l2x":18,"l2y":12,"l3x":38,"l3y":28,"l4x":38,"l4y":28,"l5x":18,"l5y":12},{"data":1000010,"id":500000056,"lvl":7,"x":23,"y":36,"l1x":35,"l1y":14,"l2x":18,"l2y":11,"l3x":40,"l3y":28,"l4x":40,"l4y":28,"l5x":18,"l5y":11},{"data":1000010,"id":500000057,"lvl":7,"x":22,"y":36,"l1x":36,"l1y":14,"l2x":18,"l2y":10,"l3x":41,"l3y":25,"l4x":41,"l4y":25,"l5x":18,"l5y":10},{"data":1000010,"id":500000058,"lvl":7,"x":21,"y":36,"l1x":29,"l1y":20,"l2x":18,"l2y":9,"l3x":41,"l3y":24,"l4x":41,"l4y":24,"l5x":18,"l5y":9},{"data":1000010,"id":500000059,"lvl":7,"x":8,"y":35,"l1x":30,"l1y":20,"l2x":18,"l2y":8,"l3x":34,"l3y":23,"l4x":34,"l4y":23,"l5x":18,"l5y":8},{"data":1000010,"id":500000060,"lvl":7,"x":21,"y":35,"l1x":31,"l1y":20,"l2x":30,"l2y":13,"l3x":35,"l3y":23,"l4x":35,"l4y":23,"l5x":30,"l5y":13},{"data":1000010,"id":500000061,"lvl":7,"x":7,"y":35,"l1x":31,"l1y":21,"l2x":19,"l2y":8,"l3x":36,"l3y":23,"l4x":36,"l4y":23,"l5x":19,"l5y":8},{"data":1000010,"id":500000062,"lvl":7,"x":7,"y":34,"l1x":34,"l1y":24,"l2x":20,"l2y":8,"l3x":37,"l3y":23,"l4x":37,"l4y":23,"l5x":20,"l5y":8},{"data":1000010,"id":500000063,"lvl":7,"x":7,"y":33,"l1x":34,"l1y":23,"l2x":21,"l2y":8,"l3x":38,"l3y":23,"l4x":38,"l4y":23,"l5x":21,"l5y":8},{"data":1000010,"id":500000064,"lvl":7,"x":7,"y":32,"l1x":34,"l1y":22,"l2x":22,"l2y":8,"l3x":39,"l3y":24,"l4x":39,"l4y":24,"l5x":22,"l5y":8},{"data":1000010,"id":500000065,"lvl":7,"x":7,"y":31,"l1x":33,"l1y":21,"l2x":23,"l2y":8,"l3x":39,"l3y":23,"l4x":39,"l4y":23,"l5x":23,"l5y":8},{"data":1000010,"id":500000066,"lvl":7,"x":7,"y":30,"l1x":34,"l1y":21,"l2x":24,"l2y":8,"l3x":39,"l3y":22,"l4x":39,"l4y":22,"l5x":24,"l5y":8},{"data":1000010,"id":500000067,"lvl":7,"x":7,"y":29,"l1x":30,"l1y":25,"l2x":25,"l2y":8,"l3x":39,"l3y":21,"l4x":39,"l4y":21,"l5x":25,"l5y":8},{"data":1000010,"id":500000068,"lvl":7,"x":7,"y":28,"l1x":31,"l1y":25,"l2x":26,"l2y":8,"l3x":39,"l3y":20,"l4x":39,"l4y":20,"l5x":26,"l5y":8},{"data":1000010,"id":500000069,"lvl":7,"x":7,"y":27,"l1x":32,"l1y":25,"l2x":27,"l2y":8,"l3x":39,"l3y":19,"l4x":39,"l4y":19,"l5x":27,"l5y":8},{"data":1000010,"id":500000070,"lvl":7,"x":7,"y":26,"l1x":19,"l1y":22,"l2x":28,"l2y":8,"l3x":39,"l3y":18,"l4x":39,"l4y":18,"l5x":28,"l5y":8},{"data":1000013,"id":500000071,"lvl":5,"x":18,"y":25,"l1x":35,"l1y":34,"l2x":15,"l2y":14,"l3x":36,"l3y":15,"l4x":36,"l4y":15,"l5x":15,"l5y":14},{"data":1000007,"id":500000072,"lvl":5,"x":21,"y":37,"l1x":10,"l1y":14,"l2x":8,"l2y":14,"l3x":26,"l3y":10,"l4x":26,"l4y":10,"l5x":8,"l5y":14},{"data":1000010,"id":500000073,"lvl":7,"x":9,"y":35,"l1x":19,"l1y":21,"l2x":29,"l2y":8,"l3x":39,"l3y":17,"l4x":39,"l4y":17,"l5x":29,"l5y":8},{"data":1000010,"id":500000074,"lvl":7,"x":35,"y":8,"l1x":19,"l1y":23,"l2x":30,"l2y":8,"l3x":39,"l3y":16,"l4x":39,"l4y":16,"l5x":30,"l5y":8},{"data":1000010,"id":500000075,"lvl":7,"x":35,"y":7,"l1x":20,"l1y":23,"l2x":31,"l2y":8,"l3x":39,"l3y":15,"l4x":39,"l4y":15,"l5x":31,"l5y":8},{"data":1000010,"id":500000076,"lvl":7,"x":34,"y":7,"l1x":33,"l1y":25,"l2x":31,"l2y":9,"l3x":39,"l3y":14,"l4x":39,"l4y":14,"l5x":31,"l5y":9},{"data":1000010,"id":500000077,"lvl":7,"x":33,"y":7,"l1x":28,"l1y":25,"l2x":31,"l2y":10,"l3x":38,"l3y":14,"l4x":38,"l4y":14,"l5x":31,"l5y":10},{"data":1000010,"id":500000078,"lvl":7,"x":32,"y":7,"l1x":29,"l1y":25,"l2x":23,"l2y":13,"l3x":37,"l3y":14,"l4x":37,"l4y":14,"l5x":35,"l5y":26},{"data":1000010,"id":500000079,"lvl":7,"x":31,"y":7,"l1x":28,"l1y":24,"l2x":23,"l2y":15,"l3x":36,"l3y":14,"l4x":36,"l4y":14,"l5x":25,"l5y":15},{"data":1000010,"id":500000080,"lvl":7,"x":30,"y":7,"l1x":19,"l1y":20,"l2x":24,"l2y":13,"l3x":35,"l3y":14,"l4x":35,"l4y":14,"l5x":34,"l5y":26},{"data":1000010,"id":500000081,"lvl":7,"x":29,"y":7,"l1x":34,"l1y":25,"l2x":23,"l2y":14,"l3x":34,"l3y":14,"l4x":34,"l4y":14,"l5x":25,"l5y":16},{"data":1000010,"id":500000082,"lvl":7,"x":28,"y":7,"l1x":19,"l1y":19,"l2x":25,"l2y":13,"l3x":33,"l3y":14,"l4x":33,"l4y":14,"l5x":25,"l5y":13},{"data":1000010,"id":500000083,"lvl":7,"x":27,"y":7,"l1x":22,"l1y":19,"l2x":26,"l2y":13,"l3x":32,"l3y":14,"l4x":32,"l4y":14,"l5x":26,"l5y":13},{"data":1000010,"id":500000084,"lvl":7,"x":26,"y":7,"l1x":21,"l1y":19,"l2x":27,"l2y":13,"l3x":31,"l3y":14,"l4x":31,"l4y":14,"l5x":27,"l5y":13},{"data":1000010,"id":500000085,"lvl":7,"x":25,"y":7,"l1x":20,"l1y":19,"l2x":29,"l2y":13,"l3x":30,"l3y":14,"l4x":30,"l4y":14,"l5x":29,"l5y":13},{"data":1000010,"id":500000086,"lvl":7,"x":35,"y":9,"l1x":25,"l1y":18,"l2x":28,"l2y":13,"l3x":27,"l3y":14,"l4x":27,"l4y":14,"l5x":28,"l5y":13},{"data":1000010,"id":500000087,"lvl":7,"x":35,"y":10,"l1x":24,"l1y":18,"l2x":31,"l2y":12,"l3x":25,"l3y":13,"l4x":25,"l4y":13,"l5x":31,"l5y":12},{"data":1000010,"id":500000088,"lvl":7,"x":35,"y":11,"l1x":24,"l1y":19,"l2x":31,"l2y":13,"l3x":25,"l3y":12,"l4x":25,"l4y":12,"l5x":31,"l5y":13},{"data":1000010,"id":500000089,"lvl":7,"x":35,"y":12,"l1x":23,"l1y":19,"l2x":31,"l2y":14,"l3x":25,"l3y":10,"l4x":25,"l4y":10,"l5x":31,"l5y":14},{"data":1000010,"id":500000090,"lvl":7,"x":35,"y":13,"l1x":29,"l1y":18,"l2x":32,"l2y":14,"l3x":24,"l3y":10,"l4x":24,"l4y":10,"l5x":32,"l5y":14},{"data":1000010,"id":500000091,"lvl":7,"x":7,"y":25,"l1x":28,"l1y":18,"l2x":33,"l2y":14,"l3x":23,"l3y":10,"l4x":23,"l4y":10,"l5x":33,"l5y":14},{"data":1000010,"id":500000092,"lvl":7,"x":10,"y":35,"l1x":27,"l1y":18,"l2x":34,"l2y":14,"l3x":22,"l3y":10,"l4x":22,"l4y":10,"l5x":34,"l5y":14},{"data":1000010,"id":500000093,"lvl":7,"x":11,"y":35,"l1x":26,"l1y":18,"l2x":35,"l2y":14,"l3x":21,"l3y":10,"l4x":21,"l4y":10,"l5x":35,"l5y":14},{"data":1000010,"id":500000094,"lvl":7,"x":12,"y":35,"l1x":27,"l1y":23,"l2x":36,"l2y":14,"l3x":20,"l3y":10,"l4x":20,"l4y":10,"l5x":36,"l5y":14},{"data":1000010,"id":500000095,"lvl":7,"x":13,"y":35,"l1x":28,"l1y":23,"l2x":37,"l2y":14,"l3x":20,"l3y":11,"l4x":20,"l4y":11,"l5x":37,"l5y":14},{"data":1000010,"id":500000096,"lvl":7,"x":26,"y":11,"l1x":25,"l1y":23,"l2x":37,"l2y":15,"l3x":20,"l3y":12,"l4x":20,"l4y":12,"l5x":37,"l5y":15},{"data":1000010,"id":500000097,"lvl":7,"x":27,"y":11,"l1x":24,"l1y":23,"l2x":37,"l2y":16,"l3x":20,"l3y":13,"l4x":20,"l4y":13,"l5x":37,"l5y":16},{"data":1000002,"id":500000098,"lvl":11,"x":17,"y":10,"res_time":87175,"l1x":23,"l1y":15,"l2x":35,"l2y":41,"l3x":16,"l3y":19,"l4x":16,"l4y":19,"l5x":35,"l5y":41},{"data":1000006,"id":500000099,"lvl":9,"x":27,"y":8,"unit_prod":{"unit_type":0,"t":0,"slots":[{"id":4000008,"cnt":1}]},"l1x":37,"l1y":18,"l2x":14,"l2y":38,"l3x":7,"l3y":23,"l4x":7,"l4y":23,"l5x":14,"l5y":38},{"data":1000004,"id":500000100,"lvl":11,"x":28,"y":18,"res_time":61043,"l1x":39,"l1y":33,"l2x":11,"l2y":11,"l3x":34,"l3y":42,"l4x":34,"l4y":42,"l5x":11,"l5y":11},{"data":1000009,"id":500000101,"lvl":9,"x":30,"y":24,"l1x":25,"l1y":36,"l2x":12,"l2y":30,"l3x":36,"l3y":20,"l4x":36,"l4y":20,"l5x":12,"l5y":30},{"data":1000015,"id":500000102,"lvl":0,"x":28,"y":40,"l1x":12,"l1y":18,"l2x":22,"l2y":37,"l3x":30,"l3y":9,"l4x":30,"l4y":9,"l5x":41,"l5y":36},{"data":1000012,"id":500000103,"lvl":5,"x":27,"y":30,"l1x":21,"l1y":20,"l2x":30,"l2y":29,"l3x":25,"l3y":20,"l4x":25,"l4y":20,"l5x":30,"l5y":29},{"data":1000015,"id":500000104,"lvl":0,"x":26,"y":40,"l1x":37,"l1y":13,"l2x":18,"l2y":23,"l3x":8,"l3y":26,"l4x":8,"l4y":26,"l5x":18,"l5y":23},{"data":1000000,"id":500000105,"lvl":5,"x":12,"y":26,"units":[[4000008,3]],"l1x":10,"l1y":20,"l2x":6,"l2y":28,"l3x":23,"l3y":39,"l4x":23,"l4y":39,"l5x":6,"l5y":28},{"data":1000020,"id":500000106,"lvl":2,"x":41,"y":37,"units":[[26000001,1],[26000000,2]],"unit_prod":{"unit_type":1,"t":0,"slots":[{"id":26000001,"cnt":1}]},"l1x":11,"l1y":27,"l2x":9,"l2y":25,"l3x":17,"l3y":10,"l4x":17,"l4y":10,"l5x":9,"l5y":25},{"data":1000011,"id":500000107,"lvl":5,"x":21,"y":21,"l1x":16,"l1y":24,"l2x":25,"l2y":29,"l3x":30,"l3y":11,"l4x":30,"l4y":11,"l5x":25,"l5y":28},{"data":1000008,"id":500000108,"lvl":9,"x":32,"y":10,"l1x":14,"l1y":36,"l2x":26,"l2y":10,"l3x":18,"l3y":37,"l4x":18,"l4y":37,"l5x":26,"l5y":10},{"data":1000010,"id":500000109,"lvl":7,"x":28,"y":11,"l1x":36,"l1y":15,"l2x":37,"l2y":18,"l3x":20,"l3y":14,"l4x":20,"l4y":14,"l5x":37,"l5y":18},{"data":1000010,"id":500000110,"lvl":7,"x":29,"y":11,"l1x":30,"l1y":12,"l2x":37,"l2y":17,"l3x":20,"l3y":15,"l4x":20,"l4y":15,"l5x":37,"l5y":17},{"data":1000010,"id":500000111,"lvl":7,"x":30,"y":11,"l1x":36,"l1y":17,"l2x":39,"l2y":34,"l3x":20,"l3y":16,"l4x":20,"l4y":16,"l5x":39,"l5y":34},{"data":1000010,"id":500000112,"lvl":7,"x":31,"y":11,"l1x":34,"l1y":14,"l2x":38,"l2y":34,"l3x":28,"l3y":14,"l4x":28,"l4y":14,"l5x":38,"l5y":34},{"data":1000010,"id":500000113,"lvl":7,"x":31,"y":12,"l1x":36,"l1y":18,"l2x":37,"l2y":34,"l3x":25,"l3y":14,"l4x":25,"l4y":14,"l5x":37,"l5y":34},{"data":1000010,"id":500000114,"lvl":7,"x":31,"y":13,"l1x":36,"l1y":19,"l2x":35,"l2y":34,"l3x":26,"l3y":14,"l4x":26,"l4y":14,"l5x":35,"l5y":34},{"data":1000010,"id":500000115,"lvl":7,"x":32,"y":13,"l1x":33,"l1y":13,"l2x":35,"l2y":33,"l3x":29,"l3y":14,"l4x":29,"l4y":14,"l5x":35,"l5y":33},{"data":1000010,"id":500000116,"lvl":7,"x":33,"y":13,"l1x":35,"l1y":21,"l2x":36,"l2y":34,"l3x":29,"l3y":15,"l4x":29,"l4y":15,"l5x":36,"l5y":34},{"data":1000010,"id":500000117,"lvl":7,"x":34,"y":13,"l1x":33,"l1y":38,"l2x":36,"l2y":35,"l3x":29,"l3y":16,"l4x":29,"l4y":16,"l5x":36,"l5y":35},{"data":1000010,"id":500000118,"lvl":7,"x":34,"y":14,"l1x":36,"l1y":20,"l2x":36,"l2y":36,"l3x":29,"l3y":17,"l4x":29,"l4y":17,"l5x":36,"l5y":36},{"data":1000010,"id":500000119,"lvl":7,"x":34,"y":15,"l1x":37,"l1y":21,"l2x":36,"l2y":37,"l3x":28,"l3y":37,"l4x":28,"l4y":37,"l5x":36,"l5y":37},{"data":1000010,"id":500000120,"lvl":7,"x":34,"y":16,"l1x":38,"l1y":21,"l2x":36,"l2y":38,"l3x":41,"l3y":26,"l4x":41,"l4y":26,"l5x":36,"l5y":38},{"data":1000010,"id":500000121,"lvl":7,"x":11,"y":26,"l1x":39,"l1y":21,"l2x":36,"l2y":39,"l3x":25,"l3y":17,"l4x":25,"l4y":17,"l5x":36,"l5y":39},{"data":1000010,"id":500000122,"lvl":7,"x":11,"y":27,"l1x":39,"l1y":22,"l2x":23,"l2y":16,"l3x":23,"l3y":17,"l4x":23,"l4y":17,"l5x":25,"l5y":14},{"data":1000010,"id":500000123,"lvl":7,"x":11,"y":28,"l1x":39,"l1y":23,"l2x":36,"l2y":40,"l3x":21,"l3y":17,"l4x":21,"l4y":17,"l5x":36,"l5y":40},{"data":1000010,"id":500000124,"lvl":7,"x":11,"y":29,"l1x":39,"l1y":24,"l2x":35,"l2y":40,"l3x":20,"l3y":17,"l4x":20,"l4y":17,"l5x":35,"l5y":40},{"data":1000010,"id":500000125,"lvl":7,"x":11,"y":30,"l1x":39,"l1y":25,"l2x":34,"l2y":40,"l3x":19,"l3y":17,"l4x":19,"l4y":17,"l5x":34,"l5y":40},{"data":1000010,"id":500000126,"lvl":7,"x":11,"y":31,"l1x":39,"l1y":26,"l2x":33,"l2y":40,"l3x":18,"l3y":17,"l4x":18,"l4y":17,"l5x":33,"l5y":40},{"data":1000010,"id":500000127,"lvl":7,"x":12,"y":31,"l1x":39,"l1y":27,"l2x":32,"l2y":40,"l3x":16,"l3y":17,"l4x":16,"l4y":17,"l5x":32,"l5y":40},{"data":1000010,"id":500000128,"lvl":7,"x":13,"y":31,"l1x":39,"l1y":28,"l2x":24,"l2y":35,"l3x":15,"l3y":17,"l4x":15,"l4y":17,"l5x":24,"l5y":35},{"data":1000010,"id":500000129,"lvl":7,"x":13,"y":32,"l1x":39,"l1y":29,"l2x":24,"l2y":36,"l3x":14,"l3y":17,"l4x":14,"l4y":17,"l5x":24,"l5y":36},{"data":1000010,"id":500000130,"lvl":7,"x":13,"y":33,"l1x":22,"l1y":23,"l2x":24,"l2y":37,"l3x":13,"l3y":17,"l4x":13,"l4y":17,"l5x":24,"l5y":37},{"data":1000010,"id":500000131,"lvl":7,"x":13,"y":34,"l1x":39,"l1y":30,"l2x":33,"l2y":21,"l3x":12,"l3y":17,"l4x":12,"l4y":17,"l5x":33,"l5y":21},{"data":1000010,"id":500000132,"lvl":7,"x":14,"y":34,"l1x":39,"l1y":31,"l2x":33,"l2y":20,"l3x":11,"l3y":17,"l4x":11,"l4y":17,"l5x":30,"l5y":21},{"data":1000010,"id":500000133,"lvl":7,"x":15,"y":34,"l1x":39,"l1y":32,"l2x":33,"l2y":19,"l3x":11,"l3y":18,"l4x":11,"l4y":18,"l5x":34,"l5y":19},{"data":1000002,"id":500000134,"lvl":11,"x":17,"y":14,"res_time":87179,"l1x":11,"l1y":11,"l2x":29,"l2y":36,"l3x":34,"l3y":25,"l4x":34,"l4y":25,"l5x":29,"l5y":36},{"data":1000004,"id":500000135,"lvl":11,"x":18,"y":28,"res_time":61053,"l1x":25,"l1y":40,"l2x":11,"l2y":34,"l3x":37,"l3y":39,"l4x":37,"l4y":39,"l5x":12,"l5y":34},{"data":1000009,"id":500000136,"lvl":9,"x":14,"y":14,"l1x":15,"l1y":28,"l2x":34,"l2y":15,"l3x":22,"l3y":11,"l4x":22,"l4y":11,"l5x":34,"l5y":15},{"data":1000002,"id":500000137,"lvl":11,"x":14,"y":17,"res_time":87173,"l1x":14,"l1y":8,"l2x":6,"l2y":20,"l3x":26,"l3y":15,"l4x":26,"l4y":15,"l5x":6,"l5y":20},{"data":1000004,"id":500000138,"lvl":11,"x":18,"y":22,"res_time":61055,"l1x":11,"l1y":8,"l2x":13,"l2y":21,"l3x":33,"l3y":11,"l4x":33,"l4y":11,"l5x":13,"l5y":21},{"data":1000013,"id":500000139,"lvl":5,"x":25,"y":18,"l1x":15,"l1y":12,"l2x":37,"l2y":35,"l3x":16,"l3y":14,"l4x":16,"l4y":14,"l5x":38,"l5y":35},{"data":1000010,"id":500000140,"lvl":7,"x":9,"y":25,"l1x":21,"l1y":23,"l2x":34,"l2y":19,"l3x":11,"l3y":19,"l4x":11,"l4y":19,"l5x":36,"l5y":18},{"data":1000010,"id":500000141,"lvl":7,"x":10,"y":25,"l1x":23,"l1y":23,"l2x":35,"l2y":19,"l3x":11,"l3y":20,"l4x":11,"l4y":20,"l5x":35,"l5y":18},{"data":1000010,"id":500000142,"lvl":7,"x":11,"y":25,"l1x":20,"l1y":26,"l2x":36,"l2y":19,"l3x":11,"l3y":21,"l4x":11,"l4y":21,"l5x":34,"l5y":18},{"data":1000010,"id":500000143,"lvl":7,"x":12,"y":25,"l1x":20,"l1y":36,"l2x":37,"l2y":19,"l3x":11,"l3y":22,"l4x":11,"l4y":22,"l5x":37,"l5y":19},{"data":1000010,"id":500000144,"lvl":7,"x":25,"y":9,"l1x":37,"l1y":33,"l2x":38,"l2y":19,"l3x":12,"l3y":22,"l4x":12,"l4y":22,"l5x":38,"l5y":19},{"data":1000010,"id":500000145,"lvl":7,"x":25,"y":10,"l1x":36,"l1y":33,"l2x":39,"l2y":19,"l3x":27,"l3y":33,"l4x":27,"l4y":33,"l5x":39,"l5y":19},{"data":1000010,"id":500000146,"lvl":7,"x":25,"y":11,"l1x":35,"l1y":33,"l2x":40,"l2y":19,"l3x":27,"l3y":34,"l4x":27,"l4y":34,"l5x":40,"l5y":19},{"data":1000010,"id":500000147,"lvl":7,"x":25,"y":12,"l1x":34,"l1y":33,"l2x":40,"l2y":20,"l3x":27,"l3y":35,"l4x":27,"l4y":35,"l5x":40,"l5y":20},{"data":1000010,"id":500000148,"lvl":7,"x":25,"y":8,"l1x":15,"l1y":26,"l2x":40,"l2y":21,"l3x":27,"l3y":36,"l4x":27,"l4y":36,"l5x":40,"l5y":21},{"data":1000010,"id":500000149,"lvl":7,"x":24,"y":8,"l1x":15,"l1y":25,"l2x":40,"l2y":22,"l3x":27,"l3y":37,"l4x":27,"l4y":37,"l5x":40,"l5y":22},{"data":1000010,"id":500000150,"lvl":7,"x":23,"y":8,"l1x":15,"l1y":24,"l2x":32,"l2y":39,"l3x":23,"l3y":38,"l4x":23,"l4y":38,"l5x":32,"l5y":39},{"data":1000010,"id":500000151,"lvl":7,"x":22,"y":8,"l1x":15,"l1y":23,"l2x":31,"l2y":39,"l3x":24,"l3y":38,"l4x":24,"l4y":38,"l5x":31,"l5y":39},{"data":1000010,"id":500000152,"lvl":7,"x":21,"y":8,"l1x":15,"l1y":21,"l2x":30,"l2y":39,"l3x":25,"l3y":38,"l4x":25,"l4y":38,"l5x":30,"l5y":39},{"data":1000010,"id":500000153,"lvl":7,"x":20,"y":8,"l1x":15,"l1y":22,"l2x":29,"l2y":39,"l3x":22,"l3y":37,"l4x":21,"l4y":38,"l5x":29,"l5y":39},{"data":1000010,"id":500000154,"lvl":7,"x":19,"y":8,"l1x":14,"l1y":19,"l2x":28,"l2y":39,"l3x":26,"l3y":38,"l4x":26,"l4y":38,"l5x":28,"l5y":39},{"data":1000010,"id":500000155,"lvl":7,"x":18,"y":8,"l1x":14,"l1y":18,"l2x":27,"l2y":39,"l3x":33,"l3y":18,"l4x":29,"l4y":19,"l5x":27,"l5y":39},{"data":1000010,"id":500000156,"lvl":7,"x":17,"y":8,"l1x":14,"l1y":17,"l2x":26,"l2y":39,"l3x":32,"l3y":18,"l4x":32,"l4y":19,"l5x":26,"l5y":39},{"data":1000010,"id":500000157,"lvl":7,"x":16,"y":8,"l1x":14,"l1y":16,"l2x":25,"l2y":39,"l3x":31,"l3y":18,"l4x":31,"l4y":19,"l5x":25,"l5y":39},{"data":1000010,"id":500000158,"lvl":7,"x":16,"y":34,"l1x":14,"l1y":15,"l2x":39,"l2y":33,"l3x":33,"l3y":19,"l4x":33,"l4y":19,"l5x":39,"l5y":33},{"data":1000010,"id":500000159,"lvl":7,"x":8,"y":25,"l1x":14,"l1y":14,"l2x":39,"l2y":27,"l3x":33,"l3y":20,"l4x":33,"l4y":20,"l5x":39,"l5y":27},{"data":1000010,"id":500000160,"lvl":7,"x":8,"y":24,"l1x":14,"l1y":13,"l2x":39,"l2y":28,"l3x":33,"l3y":21,"l4x":33,"l4y":21,"l5x":39,"l5y":28},{"data":1000010,"id":500000161,"lvl":7,"x":8,"y":23,"l1x":14,"l1y":12,"l2x":39,"l2y":29,"l3x":33,"l3y":22,"l4x":33,"l4y":22,"l5x":39,"l5y":29},{"data":1000010,"id":500000162,"lvl":7,"x":8,"y":22,"l1x":14,"l1y":11,"l2x":22,"l2y":32,"l3x":33,"l3y":23,"l4x":33,"l4y":23,"l5x":22,"l5y":32},{"data":1000010,"id":500000163,"lvl":7,"x":8,"y":21,"l1x":15,"l1y":11,"l2x":22,"l2y":31,"l3x":33,"l3y":24,"l4x":33,"l4y":24,"l5x":22,"l5y":31},{"data":1000010,"id":500000164,"lvl":7,"x":8,"y":20,"l1x":16,"l1y":11,"l2x":23,"l2y":32,"l3x":33,"l3y":25,"l4x":33,"l4y":25,"l5x":23,"l5y":32},{"data":1000011,"id":500000165,"lvl":5,"x":36,"y":30,"l1x":31,"l1y":35,"l2x":17,"l2y":19,"l3x":19,"l3y":19,"l4x":38,"l4y":33,"l5x":17,"l5y":19},{"data":1000015,"id":500000166,"lvl":0,"x":39,"y":27,"l1x":23,"l1y":40,"l2x":26,"l2y":25,"l3x":25,"l3y":8,"l4x":25,"l4y":8,"l5x":27,"l5y":25},{"data":1000024,"id":500000167,"lvl":3,"x":22,"y":18,"l1x":21,"l1y":24,"l2x":32,"l2y":34,"l3x":24,"l3y":29,"l4x":14,"l4y":26,"l5x":32,"l5y":33},{"data":1000006,"id":500000168,"lvl":9,"x":10,"y":21,"unit_prod":{"unit_type":0,"t":0,"slots":[{"id":4000008,"cnt":1}]},"l1x":37,"l1y":39,"l2x":9,"l2y":18,"l3x":10,"l3y":27,"l4x":10,"l4y":27,"l5x":9,"l5y":18},{"data":1000000,"id":500000169,"lvl":5,"x":26,"y":12,"units":[[4000008,3]],"l1x":18,"l1y":37,"l2x":40,"l2y":29,"l3x":7,"l3y":31,"l4x":7,"l4y":31,"l5x":40,"l5y":29},{"data":1000010,"id":500000170,"lvl":7,"x":8,"y":19,"l1x":17,"l1y":11,"l2x":23,"l2y":34,"l3x":33,"l3y":26,"l4x":33,"l4y":26,"l5x":24,"l5y":17},{"data":1000010,"id":500000171,"lvl":7,"x":8,"y":18,"l1x":18,"l1y":11,"l2x":25,"l2y":32,"l3x":30,"l3y":18,"l4x":30,"l4y":19,"l5x":25,"l5y":32},{"data":1000010,"id":500000172,"lvl":7,"x":8,"y":17,"l1x":33,"l1y":14,"l2x":26,"l2y":32,"l3x":29,"l3y":18,"l4x":29,"l4y":18,"l5x":26,"l5y":32},{"data":1000010,"id":500000173,"lvl":7,"x":8,"y":16,"l1x":19,"l1y":12,"l2x":27,"l2y":32,"l3x":28,"l3y":18,"l4x":28,"l4y":18,"l5x":27,"l5y":32},{"data":1000010,"id":500000174,"lvl":7,"x":9,"y":16,"l1x":20,"l1y":12,"l2x":28,"l2y":32,"l3x":27,"l3y":18,"l4x":27,"l4y":18,"l5x":28,"l5y":32},{"data":1000010,"id":500000175,"lvl":7,"x":10,"y":16,"l1x":21,"l1y":12,"l2x":29,"l2y":32,"l3x":26,"l3y":18,"l4x":26,"l4y":18,"l5x":29,"l5y":32},{"data":1000010,"id":500000176,"lvl":7,"x":11,"y":16,"l1x":22,"l1y":12,"l2x":30,"l2y":32,"l3x":25,"l3y":18,"l4x":25,"l4y":18,"l5x":30,"l5y":32},{"data":1000010,"id":500000177,"lvl":7,"x":12,"y":16,"l1x":22,"l1y":13,"l2x":31,"l2y":32,"l3x":22,"l3y":17,"l4x":22,"l4y":17,"l5x":31,"l5y":32},{"data":1000010,"id":500000178,"lvl":7,"x":16,"y":9,"l1x":22,"l1y":14,"l2x":32,"l2y":32,"l3x":24,"l3y":17,"l4x":24,"l4y":17,"l5x":32,"l5y":32},{"data":1000010,"id":500000179,"lvl":7,"x":16,"y":10,"l1x":22,"l1y":15,"l2x":33,"l2y":32,"l3x":22,"l3y":20,"l4x":19,"l4y":21,"l5x":33,"l5y":32},{"data":1000010,"id":500000180,"lvl":7,"x":16,"y":11,"l1x":22,"l1y":18,"l2x":34,"l2y":32,"l3x":22,"l3y":19,"l4x":19,"l4y":20,"l5x":34,"l5y":32},{"data":1000010,"id":500000181,"lvl":7,"x":14,"y":9,"l1x":22,"l1y":17,"l2x":35,"l2y":32,"l3x":22,"l3y":21,"l4x":19,"l4y":19,"l5x":35,"l5y":32},{"data":1000010,"id":500000182,"lvl":7,"x":9,"y":14,"l1x":15,"l1y":19,"l2x":35,"l2y":31,"l3x":21,"l3y":37,"l4x":21,"l4y":37,"l5x":35,"l5y":31},{"data":1000010,"id":500000183,"lvl":7,"x":16,"y":12,"l1x":20,"l1y":25,"l2x":35,"l2y":30,"l3x":21,"l3y":36,"l4x":21,"l4y":36,"l5x":35,"l5y":30},{"data":1000010,"id":500000184,"lvl":7,"x":8,"y":12,"l1x":29,"l1y":30,"l2x":35,"l2y":29,"l3x":20,"l3y":36,"l4x":20,"l4y":36,"l5x":35,"l5y":29},{"data":1000010,"id":500000185,"lvl":7,"x":9,"y":9,"l1x":18,"l1y":19,"l2x":35,"l2y":28,"l3x":19,"l3y":36,"l4x":19,"l4y":36,"l5x":35,"l5y":28},{"data":1000010,"id":500000186,"lvl":7,"x":13,"y":14,"l1x":30,"l1y":32,"l2x":39,"l2y":31,"l3x":18,"l3y":36,"l4x":18,"l4y":36,"l5x":39,"l5y":31},{"data":1000010,"id":500000187,"lvl":7,"x":13,"y":13,"l1x":29,"l1y":27,"l2x":35,"l2y":27,"l3x":17,"l3y":36,"l4x":17,"l4y":36,"l5x":35,"l5y":27},{"data":1000010,"id":500000188,"lvl":7,"x":14,"y":13,"l1x":29,"l1y":31,"l2x":17,"l2y":23,"l3x":16,"l3y":36,"l4x":16,"l4y":36,"l5x":17,"l5y":23},{"data":1000010,"id":500000189,"lvl":7,"x":15,"y":13,"l1x":29,"l1y":26,"l2x":17,"l2y":24,"l3x":15,"l3y":36,"l4x":15,"l4y":36,"l5x":17,"l5y":24},{"data":1000010,"id":500000190,"lvl":7,"x":16,"y":13,"l1x":18,"l1y":12,"l2x":17,"l2y":25,"l3x":15,"l3y":35,"l4x":15,"l4y":35,"l5x":17,"l5y":25},{"data":1000010,"id":500000191,"lvl":7,"x":17,"y":13,"l1x":29,"l1y":28,"l2x":17,"l2y":26,"l3x":15,"l3y":34,"l4x":15,"l4y":34,"l5x":17,"l5y":26},{"data":1000010,"id":500000192,"lvl":7,"x":18,"y":13,"l1x":29,"l1y":29,"l2x":17,"l2y":27,"l3x":15,"l3y":33,"l4x":15,"l4y":33,"l5x":17,"l5y":27},{"data":1000010,"id":500000193,"lvl":7,"x":19,"y":13,"l1x":20,"l1y":24,"l2x":17,"l2y":28,"l3x":15,"l3y":32,"l4x":15,"l4y":32,"l5x":17,"l5y":28},{"data":1000010,"id":500000194,"lvl":7,"x":13,"y":15,"l1x":22,"l1y":33,"l2x":17,"l2y":29,"l3x":13,"l3y":23,"l4x":13,"l4y":23,"l5x":17,"l5y":29},{"data":1000010,"id":500000195,"lvl":7,"x":13,"y":16,"l1x":20,"l1y":31,"l2x":39,"l2y":30,"l3x":13,"l3y":24,"l4x":13,"l4y":24,"l5x":39,"l5y":30},{"data":1000010,"id":500000196,"lvl":7,"x":13,"y":17,"l1x":14,"l1y":30,"l2x":17,"l2y":30,"l3x":13,"l3y":25,"l4x":13,"l4y":25,"l5x":17,"l5y":30},{"data":1000010,"id":500000197,"lvl":7,"x":13,"y":18,"l1x":14,"l1y":29,"l2x":18,"l2y":30,"l3x":13,"l3y":26,"l4x":13,"l4y":26,"l5x":18,"l5y":30},{"data":1000010,"id":500000198,"lvl":7,"x":13,"y":19,"l1x":14,"l1y":28,"l2x":19,"l2y":30,"l3x":13,"l3y":27,"l4x":13,"l4y":27,"l5x":19,"l5y":30},{"data":1000010,"id":500000199,"lvl":7,"x":20,"y":13,"l1x":14,"l1y":27,"l2x":20,"l2y":30,"l3x":13,"l3y":28,"l4x":13,"l4y":28,"l5x":20,"l5y":30},{"data":1000010,"id":500000200,"lvl":7,"x":21,"y":13,"l1x":14,"l1y":33,"l2x":21,"l2y":30,"l3x":13,"l3y":29,"l4x":13,"l4y":29,"l5x":21,"l5y":30},{"data":1000010,"id":500000201,"lvl":7,"x":22,"y":13,"l1x":23,"l1y":33,"l2x":22,"l2y":30,"l3x":13,"l3y":30,"l4x":13,"l4y":30,"l5x":22,"l5y":30},{"data":1000010,"id":500000202,"lvl":7,"x":23,"y":13,"l1x":14,"l1y":31,"l2x":22,"l2y":29,"l3x":13,"l3y":31,"l4x":13,"l4y":31,"l5x":22,"l5y":29},{"data":1000010,"id":500000203,"lvl":7,"x":24,"y":13,"l1x":16,"l1y":35,"l2x":22,"l2y":28,"l3x":24,"l3y":23,"l4x":24,"l4y":23,"l5x":22,"l5y":28},{"data":1000010,"id":500000204,"lvl":7,"x":13,"y":20,"l1x":15,"l1y":35,"l2x":16,"l2y":17,"l3x":24,"l3y":22,"l4x":24,"l4y":22,"l5x":16,"l5y":17},{"data":1000010,"id":500000205,"lvl":7,"x":13,"y":21,"l1x":14,"l1y":32,"l2x":17,"l2y":17,"l3x":23,"l3y":22,"l4x":23,"l4y":22,"l5x":17,"l5y":17},{"data":1000010,"id":500000206,"lvl":7,"x":13,"y":22,"l1x":20,"l1y":27,"l2x":18,"l2y":17,"l3x":22,"l3y":22,"l4x":22,"l4y":22,"l5x":18,"l5y":17},{"data":1000010,"id":500000207,"lvl":7,"x":13,"y":23,"l1x":17,"l1y":27,"l2x":19,"l2y":17,"l3x":21,"l3y":22,"l4x":21,"l4y":22,"l5x":19,"l5y":17},{"data":1000010,"id":500000208,"lvl":7,"x":13,"y":24,"l1x":18,"l1y":27,"l2x":20,"l2y":17,"l3x":20,"l3y":22,"l4x":20,"l4y":22,"l5x":20,"l5y":17},{"data":1000010,"id":500000209,"lvl":7,"x":13,"y":25,"l1x":30,"l1y":34,"l2x":21,"l2y":17,"l3x":19,"l3y":22,"l4x":19,"l4y":22,"l5x":21,"l5y":17},{"data":1000010,"id":500000210,"lvl":7,"x":14,"y":25,"l1x":33,"l1y":12,"l2x":22,"l2y":17,"l3x":18,"l3y":22,"l4x":18,"l4y":22,"l5x":22,"l5y":17},{"data":1000010,"id":500000211,"lvl":7,"x":25,"y":13,"l1x":36,"l1y":21,"l2x":23,"l2y":17,"l3x":17,"l3y":22,"l4x":17,"l4y":22,"l5x":14,"l5y":20},{"data":1000010,"id":500000212,"lvl":7,"x":25,"y":14,"l1x":30,"l1y":36,"l2x":25,"l2y":17,"l3x":17,"l3y":23,"l4x":17,"l4y":23,"l5x":25,"l5y":17},{"data":1000010,"id":500000213,"lvl":7,"x":25,"y":15,"l1x":29,"l1y":13,"l2x":24,"l2y":17,"l3x":17,"l3y":24,"l4x":17,"l4y":24,"l5x":23,"l5y":17},{"data":1000010,"id":500000214,"lvl":7,"x":25,"y":16,"l1x":15,"l1y":20,"l2x":25,"l2y":18,"l3x":14,"l3y":31,"l4x":14,"l4y":31,"l5x":25,"l5y":18},{"data":1000010,"id":500000215,"lvl":7,"x":25,"y":17,"l1x":22,"l1y":16,"l2x":25,"l2y":19,"l3x":17,"l3y":25,"l4x":17,"l4y":25,"l5x":25,"l5y":19},{"data":1000010,"id":500000216,"lvl":7,"x":26,"y":17,"l1x":14,"l1y":35,"l2x":25,"l2y":21,"l3x":17,"l3y":26,"l4x":17,"l4y":26,"l5x":25,"l5y":21},{"data":1000010,"id":500000217,"lvl":7,"x":27,"y":17,"l1x":33,"l1y":33,"l2x":25,"l2y":20,"l3x":17,"l3y":27,"l4x":17,"l4y":27,"l5x":25,"l5y":20},{"data":1000010,"id":500000218,"lvl":7,"x":28,"y":17,"l1x":18,"l1y":18,"l2x":16,"l2y":18,"l3x":17,"l3y":28,"l4x":17,"l4y":28,"l5x":16,"l5y":18},{"data":1000010,"id":500000219,"lvl":7,"x":29,"y":17,"l1x":28,"l1y":38,"l2x":16,"l2y":19,"l3x":17,"l3y":29,"l4x":17,"l4y":29,"l5x":16,"l5y":19},{"data":1000008,"id":500000220,"lvl":9,"x":18,"y":31,"l1x":19,"l1y":13,"l2x":36,"l2y":21,"l3x":31,"l3y":15,"l4x":31,"l4y":16,"l5x":36,"l5y":21},{"data":1000008,"id":500000221,"lvl":9,"x":31,"y":18,"l1x":33,"l1y":15,"l2x":19,"l2y":36,"l3x":10,"l3y":24,"l4x":10,"l4y":24,"l5x":19,"l5y":36},{"data":1000009,"id":500000222,"lvl":9,"x":24,"y":21,"l1x":26,"l1y":14,"l2x":25,"l2y":36,"l3x":12,"l3y":19,"l4x":12,"l4y":19,"l5x":25,"l5y":36},{"data":1000013,"id":500000225,"lvl":5,"x":33,"y":27,"l1x":15,"l1y":32,"l2x":15,"l2y":35,"l3x":19,"l3y":32,"l4x":19,"l4y":32,"l5x":15,"l5y":35},{"data":1000012,"id":500000226,"lvl":5,"x":30,"y":27,"l1x":26,"l1y":28,"l2x":19,"l2y":27,"l3x":18,"l3y":28,"l4x":18,"l4y":28,"l5x":19,"l5y":27},{"data":1000022,"id":500000227,"lvl":0,"x":34,"y":34,"l1x":30,"l1y":29,"l2x":26,"l2y":19,"l3x":21,"l3y":23,"l4x":21,"l4y":23,"l5x":26,"l5y":18},{"data":1000026,"id":500000228,"lvl":3,"x":13,"y":10,"unit_prod":{"unit_type":0},"l1x":34,"l1y":39,"l2x":28,"l2y":40,"l3x":11,"l3y":14,"l4x":11,"l4y":14,"l5x":28,"l5y":40},{"data":1000023,"id":500000229,"lvl":2,"x":22,"y":15,"res_time":0,"l1x":11,"l1y":34,"l2x":31,"l2y":41,"l3x":15,"l3y":37,"l4x":15,"l4y":37,"l5x":31,"l5y":41},{"data":1000028,"id":500000230,"lvl":3,"x":24,"y":24,"aim_angle":45,"aim_angle_draft":45,"aim_angle_war":45,"aim_angle_draft_war":45,"aim_angle2":135,"aim_angle_d2":135,"aim_angle3":45,"aim_angle_d3":45,"aim_angle4":225,"aim_angle_d4":225,"aim_angle5":45,"aim_angle_d5":45,"l1x":29,"l1y":23,"l2x":30,"l2y":17,"l3x":30,"l3y":24,"l4x":31,"l4y":24,"l5x":31,"l5y":16},{"data":1000003,"id":500000231,"lvl":10,"x":36,"y":27,"l1x":27,"l1y":33,"l2x":18,"l2y":32,"l3x":34,"l3y":29,"l4x":34,"l4y":29,"l5x":19,"l5y":32},{"data":1000005,"id":500000232,"lvl":10,"x":27,"y":36,"l1x":32,"l1y":18,"l2x":36,"l2y":27,"l3x":14,"l3y":26,"l4x":24,"l4y":29,"l5x":36,"l5y":27},{"data":1000009,"id":500000233,"lvl":9,"x":21,"y":24,"l1x":36,"l1y":29,"l2x":36,"l2y":31,"l3x":12,"l3y":32,"l4x":12,"l4y":32,"l5x":36,"l5y":31},{"data":1000011,"id":500000234,"lvl":5,"x":31,"y":36,"l1x":30,"l1y":13,"l2x":30,"l2y":23,"l3x":38,"l3y":33,"l4x":20,"l4y":19,"l5x":31,"l5y":22},{"data":1000013,"id":500000235,"lvl":5,"x":27,"y":33,"l1x":21,"l1y":28,"l2x":19,"l2y":9,"l3x":33,"l3y":35,"l4x":33,"l4y":35,"l5x":19,"l5y":9},{"data":1000012,"id":500000236,"lvl":5,"x":18,"y":18,"l1x":31,"l1y":22,"l2x":22,"l2y":18,"l3x":29,"l3y":27,"l4x":29,"l4y":27,"l5x":22,"l5y":18},{"data":1000010,"id":500000237,"lvl":7,"x":30,"y":17,"l1x":30,"l1y":37,"l2x":16,"l2y":20,"l3x":17,"l3y":30,"l4x":17,"l4y":30,"l5x":16,"l5y":20},{"data":1000010,"id":500000238,"lvl":7,"x":31,"y":17,"l1x":30,"l1y":33,"l2x":16,"l2y":22,"l3x":16,"l3y":31,"l4x":16,"l4y":31,"l5x":16,"l5y":22},{"data":1000010,"id":500000239,"lvl":7,"x":32,"y":17,"l1x":38,"l1y":33,"l2x":16,"l2y":21,"l3x":17,"l3y":31,"l4x":17,"l4y":31,"l5x":16,"l5y":21},{"data":1000010,"id":500000240,"lvl":7,"x":33,"y":17,"l1x":31,"l1y":32,"l2x":17,"l2y":22,"l3x":18,"l3y":31,"l4x":18,"l4y":31,"l5x":17,"l5y":22},{"data":1000010,"id":500000241,"lvl":7,"x":15,"y":25,"l1x":33,"l1y":26,"l2x":18,"l2y":22,"l3x":19,"l3y":31,"l4x":19,"l4y":31,"l5x":18,"l5y":22},{"data":1000010,"id":500000242,"lvl":7,"x":16,"y":25,"l1x":33,"l1y":27,"l2x":19,"l2y":22,"l3x":20,"l3y":31,"l4x":20,"l4y":31,"l5x":19,"l5y":22},{"data":1000010,"id":500000243,"lvl":7,"x":17,"y":25,"l1x":33,"l1y":28,"l2x":20,"l2y":22,"l3x":22,"l3y":31,"l4x":22,"l4y":31,"l5x":20,"l5y":22},{"data":1000010,"id":500000244,"lvl":7,"x":17,"y":26,"l1x":33,"l1y":29,"l2x":21,"l2y":22,"l3x":23,"l3y":28,"l4x":23,"l4y":28,"l5x":21,"l5y":22},{"data":1000010,"id":500000245,"lvl":7,"x":17,"y":27,"l1x":33,"l1y":30,"l2x":31,"l2y":22,"l3x":23,"l3y":29,"l4x":23,"l4y":29,"l5x":32,"l5y":21},{"data":1000010,"id":500000246,"lvl":7,"x":17,"y":28,"l1x":33,"l1y":31,"l2x":32,"l2y":22,"l3x":24,"l3y":28,"l4x":24,"l4y":28,"l5x":29,"l5y":21},{"data":1000010,"id":500000247,"lvl":7,"x":17,"y":29,"l1x":33,"l1y":32,"l2x":33,"l2y":22,"l3x":31,"l3y":26,"l4x":31,"l4y":26,"l5x":34,"l5y":21},{"data":1000010,"id":500000248,"lvl":7,"x":17,"y":30,"l1x":32,"l1y":32,"l2x":33,"l2y":23,"l3x":23,"l3y":31,"l4x":23,"l4y":31,"l5x":35,"l5y":22},{"data":1000010,"id":500000249,"lvl":7,"x":17,"y":31,"l1x":22,"l1y":36,"l2x":33,"l2y":24,"l3x":30,"l3y":26,"l4x":30,"l4y":26,"l5x":35,"l5y":23},{"data":1000010,"id":500000250,"lvl":7,"x":17,"y":32,"l1x":16,"l1y":27,"l2x":33,"l2y":25,"l3x":26,"l3y":32,"l4x":26,"l4y":32,"l5x":35,"l5y":24},{"data":1000010,"id":500000251,"lvl":7,"x":17,"y":33,"l1x":15,"l1y":27,"l2x":33,"l2y":26,"l3x":27,"l3y":32,"l4x":27,"l4y":32,"l5x":32,"l5y":26},{"data":1000010,"id":500000252,"lvl":7,"x":17,"y":34,"l1x":16,"l1y":19,"l2x":34,"l2y":27,"l3x":28,"l3y":32,"l4x":28,"l4y":32,"l5x":35,"l5y":25},{"data":1000010,"id":500000253,"lvl":7,"x":34,"y":17,"l1x":38,"l1y":32,"l2x":33,"l2y":27,"l3x":29,"l3y":32,"l4x":29,"l4y":32,"l5x":31,"l5y":26},{"data":1000010,"id":500000254,"lvl":7,"x":34,"y":18,"l1x":20,"l1y":28,"l2x":32,"l2y":27,"l3x":30,"l3y":32,"l4x":30,"l4y":32,"l5x":33,"l5y":26},{"data":1000010,"id":500000255,"lvl":7,"x":34,"y":20,"l1x":20,"l1y":29,"l2x":31,"l2y":27,"l3x":32,"l3y":32,"l4x":32,"l4y":32,"l5x":30,"l5y":26},{"data":1000010,"id":500000256,"lvl":7,"x":34,"y":19,"l1x":20,"l1y":30,"l2x":30,"l2y":27,"l3x":32,"l3y":31,"l4x":32,"l4y":31,"l5x":29,"l5y":26},{"data":1000010,"id":500000257,"lvl":7,"x":34,"y":21,"l1x":14,"l1y":34,"l2x":29,"l2y":27,"l3x":32,"l3y":30,"l4x":32,"l4y":30,"l5x":29,"l5y":27},{"data":1000010,"id":500000258,"lvl":7,"x":33,"y":21,"l1x":19,"l1y":27,"l2x":30,"l2y":22,"l3x":32,"l3y":29,"l4x":32,"l4y":29,"l5x":31,"l5y":21},{"data":1000010,"id":500000259,"lvl":7,"x":18,"y":34,"l1x":21,"l1y":36,"l2x":29,"l2y":22,"l3x":32,"l3y":28,"l4x":32,"l4y":28,"l5x":28,"l5y":21},{"data":1000010,"id":500000260,"lvl":7,"x":19,"y":34,"l1x":23,"l1y":39,"l2x":28,"l2y":22,"l3x":32,"l3y":27,"l4x":32,"l4y":27,"l5x":28,"l5y":22},{"data":1000010,"id":500000261,"lvl":7,"x":20,"y":34,"l1x":23,"l1y":38,"l2x":27,"l2y":22,"l3x":32,"l3y":26,"l4x":32,"l4y":26,"l5x":27,"l5y":22},{"data":1000010,"id":500000262,"lvl":7,"x":21,"y":34,"l1x":23,"l1y":37,"l2x":26,"l2y":22,"l3x":28,"l3y":25,"l4x":28,"l4y":25,"l5x":26,"l5y":22},{"data":1000010,"id":500000263,"lvl":7,"x":21,"y":33,"l1x":23,"l1y":36,"l2x":25,"l2y":22,"l3x":31,"l3y":32,"l4x":31,"l4y":32,"l5x":25,"l5y":22},{"data":1000010,"id":500000264,"lvl":7,"x":21,"y":32,"l1x":23,"l1y":34,"l2x":24,"l2y":22,"l3x":29,"l3y":26,"l4x":29,"l4y":26,"l5x":24,"l5y":22},{"data":1000010,"id":500000265,"lvl":7,"x":32,"y":21,"l1x":23,"l1y":35,"l2x":23,"l2y":22,"l3x":28,"l3y":26,"l4x":28,"l4y":26,"l5x":23,"l5y":22},{"data":1000010,"id":500000266,"lvl":7,"x":31,"y":21,"l1x":17,"l1y":35,"l2x":22,"l2y":22,"l3x":23,"l3y":30,"l4x":23,"l4y":30,"l5x":22,"l5y":22},{"data":1000010,"id":500000267,"lvl":7,"x":30,"y":21,"l1x":17,"l1y":36,"l2x":22,"l2y":23,"l3x":27,"l3y":24,"l4x":27,"l4y":24,"l5x":22,"l5y":23},{"data":1000010,"id":500000268,"lvl":7,"x":29,"y":21,"l1x":18,"l1y":36,"l2x":22,"l2y":24,"l3x":26,"l3y":24,"l4x":26,"l4y":24,"l5x":22,"l5y":24},{"data":1000010,"id":500000269,"lvl":7,"x":28,"y":21,"l1x":18,"l1y":13,"l2x":22,"l2y":25,"l3x":25,"l3y":24,"l4x":25,"l4y":24,"l5x":22,"l5y":25},{"data":1000010,"id":500000270,"lvl":7,"x":21,"y":31,"l1x":18,"l1y":14,"l2x":22,"l2y":26,"l3x":24,"l3y":24,"l4x":24,"l4y":24,"l5x":22,"l5y":26},{"data":1000010,"id":500000271,"lvl":7,"x":21,"y":30,"l1x":18,"l1y":15,"l2x":28,"l2y":27,"l3x":24,"l3y":25,"l4x":24,"l4y":25,"l5x":28,"l5y":27},{"data":1000010,"id":500000272,"lvl":7,"x":21,"y":28,"l1x":18,"l1y":16,"l2x":27,"l2y":27,"l3x":24,"l3y":26,"l4x":24,"l4y":26,"l5x":27,"l5y":27},{"data":1000010,"id":500000273,"lvl":7,"x":27,"y":21,"l1x":17,"l1y":19,"l2x":26,"l2y":27,"l3x":24,"l3y":27,"l4x":24,"l4y":27,"l5x":26,"l5y":27},{"data":1000010,"id":500000274,"lvl":7,"x":27,"y":22,"l1x":18,"l1y":17,"l2x":25,"l2y":27,"l3x":23,"l3y":32,"l4x":23,"l4y":32,"l5x":25,"l5y":27},{"data":1000010,"id":500000275,"lvl":7,"x":27,"y":23,"l1x":19,"l1y":36,"l2x":24,"l2y":27,"l3x":24,"l3y":32,"l4x":24,"l4y":32,"l5x":24,"l5y":27},{"data":1000010,"id":500000276,"lvl":7,"x":27,"y":24,"l1x":30,"l1y":38,"l2x":23,"l2y":27,"l3x":39,"l3y":28,"l4x":39,"l4y":28,"l5x":23,"l5y":27},{"data":1000010,"id":500000277,"lvl":7,"x":21,"y":29,"l1x":29,"l1y":32,"l2x":39,"l2y":26,"l3x":21,"l3y":31,"l4x":21,"l4y":31,"l5x":39,"l5y":26},{"data":1000010,"id":500000278,"lvl":7,"x":22,"y":27,"l1x":28,"l1y":32,"l2x":40,"l2y":23,"l3x":29,"l3y":37,"l4x":29,"l4y":37,"l5x":40,"l5y":23},{"data":1000010,"id":500000279,"lvl":7,"x":26,"y":24,"l1x":27,"l1y":32,"l2x":39,"l2y":32,"l3x":36,"l3y":37,"l4x":36,"l4y":37,"l5x":39,"l5y":32},{"data":1000010,"id":500000280,"lvl":7,"x":26,"y":25,"l1x":26,"l1y":32,"l2x":22,"l2y":27,"l3x":40,"l3y":24,"l4x":40,"l4y":24,"l5x":22,"l5y":27},{"data":1000010,"id":500000281,"lvl":7,"x":24,"y":27,"l1x":25,"l1y":32,"l2x":24,"l2y":39,"l3x":28,"l3y":24,"l4x":28,"l4y":24,"l5x":24,"l5y":39},{"data":1000010,"id":500000282,"lvl":7,"x":23,"y":27,"l1x":24,"l1y":32,"l2x":31,"l2y":11,"l3x":25,"l3y":11,"l4x":25,"l4y":11,"l5x":31,"l5y":11},{"data":1000010,"id":500000283,"lvl":7,"x":26,"y":26,"l1x":23,"l1y":32,"l2x":40,"l2y":26,"l3x":17,"l3y":17,"l4x":17,"l4y":17,"l5x":40,"l5y":26},{"data":1000010,"id":500000284,"lvl":7,"x":25,"y":26,"l1x":21,"l1y":33,"l2x":40,"l2y":24,"l3x":13,"l3y":22,"l4x":13,"l4y":22,"l5x":40,"l5y":24},{"data":1000010,"id":500000285,"lvl":7,"x":24,"y":26,"l1x":20,"l1y":33,"l2x":40,"l2y":25,"l3x":15,"l3y":31,"l4x":15,"l4y":31,"l5x":40,"l5y":25},{"data":1000010,"id":500000286,"lvl":7,"x":21,"y":27,"l1x":20,"l1y":32,"l2x":24,"l2y":38,"l3x":25,"l3y":32,"l4x":25,"l4y":32,"l5x":24,"l5y":38},{"data":1000026,"id":500000288,"lvl":3,"x":10,"y":13,"unit_prod":{"unit_type":0},"l1x":40,"l1y":30,"l2x":38,"l2y":39,"l3x":38,"l3y":36,"l4x":38,"l4y":36,"l5x":38,"l5y":39},{"data":1000023,"id":500000289,"lvl":2,"x":15,"y":22,"res_time":0,"l1x":11,"l1y":37,"l2x":23,"l2y":10,"l3x":12,"l3y":35,"l4x":12,"l4y":35,"l5x":22,"l5y":11},{"data":1000029,"id":500000290,"lvl":1,"x":10,"y":10,"units":[[26000010,1]],"unit_prod":{"unit_type":1},"l1x":29,"l1y":9,"l2x":6,"l2y":23,"l3x":19,"l3y":40,"l4x":19,"l4y":40,"l5x":6,"l5y":23}],"obstacles":[{"data":8000004,"id":503000000,"x":47,"y":10,"loot_multiply_ver":2},{"data":8000007,"id":503000001,"x":1,"y":13,"loot_multiply_ver":2},{"data":8000000,"id":503000002,"x":14,"y":0,"loot_multiply_ver":2},{"data":8000008,"id":503000003,"x":0,"y":34,"loot_multiply_ver":2},{"data":8000006,"id":503000004,"x":29,"y":0,"loot_multiply_ver":2},{"data":8000007,"id":503000005,"x":19,"y":0,"loot_multiply_ver":2},{"data":8000013,"id":503000006,"x":29,"y":47,"loot_multiply_ver":2},{"data":8000007,"id":503000007,"x":7,"y":8,"loot_multiply_ver":2},{"data":8000010,"id":503000008,"x":0,"y":30,"loot_multiply_ver":2},{"data":8000000,"id":503000009,"x":25,"y":0,"loot_multiply_ver":2},{"data":8000004,"id":503000010,"x":24,"y":48,"loot_multiply_ver":2},{"data":8000000,"id":503000011,"x":8,"y":48,"loot_multiply_ver":2},{"data":8000010,"id":503000012,"x":8,"y":42,"loot_multiply_ver":2},{"data":8000000,"id":503000013,"x":1,"y":9,"loot_multiply_ver":2},{"data":8000000,"id":503000014,"x":2,"y":40,"loot_multiply_ver":2},{"data":8000004,"id":503000015,"x":35,"y":0,"loot_multiply_ver":2},{"data":8000007,"id":503000016,"x":1,"y":37,"loot_multiply_ver":2},{"data":8000007,"id":503000017,"x":15,"y":47,"loot_multiply_ver":2},{"data":8000007,"id":503000018,"x":1,"y":19,"loot_multiply_ver":2},{"data":8000000,"id":503000019,"x":0,"y":26,"loot_multiply_ver":2},{"data":8000013,"id":503000020,"x":18,"y":47,"loot_multiply_ver":2},{"data":8000007,"id":503000021,"x":48,"y":17,"loot_multiply_ver":2},{"data":8000010,"id":503000022,"x":48,"y":31,"loot_multiply_ver":2},{"data":8000004,"id":503000023,"x":11,"y":48,"loot_multiply_ver":2},{"data":8000000,"id":503000024,"x":48,"y":25,"loot_multiply_ver":2},{"data":8000008,"id":503000025,"x":48,"y":7,"loot_multiply_ver":2},{"data":8000004,"id":503000026,"x":46,"y":0,"loot_multiply_ver":2},{"data":8000007,"id":503000027,"x":48,"y":34,"loot_multiply_ver":2},{"data":8000010,"id":503000028,"x":1,"y":2,"loot_multiply_ver":2},{"data":8000004,"id":503000029,"x":0,"y":47,"loot_multiply_ver":2},{"data":8000000,"id":503000030,"x":48,"y":38,"loot_multiply_ver":2},{"data":8000010,"id":503000031,"x":1,"y":16,"loot_multiply_ver":2},{"data":8000000,"id":503000032,"x":1,"y":23,"loot_multiply_ver":2},{"data":8000006,"id":503000033,"x":39,"y":48,"loot_multiply_ver":2},{"data":8000036,"id":503000034,"x":48,"y":20,"loot_multiply_ver":2},{"data":8000006,"id":503000035,"x":47,"y":41,"loot_multiply_ver":2},{"data":8000000,"id":503000036,"x":47,"y":28,"loot_multiply_ver":2},{"data":8000007,"id":503000037,"x":33,"y":48,"loot_multiply_ver":2},{"data":8000005,"id":503000038,"x":7,"y":1,"loot_multiply_ver":2},{"data":8000036,"id":503000039,"x":41,"y":10,"loot_multiply_ver":2},{"data":8000036,"id":503000040,"x":7,"y":39,"loot_multiply_ver":1},{"data":8000036,"id":503000041,"x":48,"y":13,"loot_multiply_ver":2},{"data":8000036,"id":503000042,"x":22,"y":2,"loot_multiply_ver":2},{"data":8000036,"id":503000043,"x":42,"y":6,"loot_multiply_ver":2},{"data":8000036,"id":503000044,"x":8,"y":33,"loot_multiply_ver":1},{"data":8000037,"id":503000078,"x":36,"y":48,"loot_multiply_ver":2},{"data":8000037,"id":503000119,"x":44,"y":14,"loot_multiply_ver":2},{"data":8000037,"id":503000160,"x":36,"y":3,"loot_multiply_ver":2},{"data":8000037,"id":503000201,"x":3,"y":5,"loot_multiply_ver":2},{"data":8000037,"id":503000242,"x":17,"y":41,"loot_multiply_ver":2},{"data":8000037,"id":503000243,"x":42,"y":21,"loot_multiply_ver":2},{"data":8000037,"id":503000284,"x":40,"y":1,"loot_multiply_ver":2},{"data":8000037,"id":503000305,"x":32,"y":4,"loot_multiply_ver":2},{"data":8000009,"id":503000306,"x":40,"y":22,"loot_multiply_ver":1},{"data":8000009,"id":503000307,"x":44,"y":41,"loot_multiply_ver":1},{"data":8000009,"id":503000308,"x":43,"y":28,"loot_multiply_ver":1},{"data":8000009,"id":503000309,"x":43,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000310,"x":44,"y":24,"loot_multiply_ver":1},{"data":8000023,"id":503000311,"x":43,"y":25,"loot_multiply_ver":1},{"data":8000023,"id":503000312,"x":44,"y":25,"loot_multiply_ver":1},{"data":8000023,"id":503000313,"x":43,"y":24,"loot_multiply_ver":1},{"data":8000023,"id":503000314,"x":42,"y":25,"loot_multiply_ver":1},{"data":8000023,"id":503000315,"x":44,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000316,"x":33,"y":45,"loot_multiply_ver":1},{"data":8000009,"id":503000317,"x":34,"y":44,"loot_multiply_ver":1},{"data":8000009,"id":503000318,"x":38,"y":33,"loot_multiply_ver":1},{"data":8000009,"id":503000319,"x":41,"y":25,"loot_multiply_ver":1},{"data":8000009,"id":503000320,"x":42,"y":24,"loot_multiply_ver":1},{"data":8000009,"id":503000321,"x":42,"y":29,"loot_multiply_ver":1},{"data":8000009,"id":503000322,"x":41,"y":29,"loot_multiply_ver":1},{"data":8000009,"id":503000323,"x":42,"y":28,"loot_multiply_ver":1},{"data":8000009,"id":503000324,"x":43,"y":29,"loot_multiply_ver":1},{"data":8000009,"id":503000325,"x":32,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000326,"x":31,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000327,"x":30,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000328,"x":33,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000329,"x":29,"y":42,"loot_multiply_ver":1},{"data":8000009,"id":503000330,"x":28,"y":42,"loot_multiply_ver":1},{"data":8000009,"id":503000331,"x":29,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000332,"x":26,"y":37,"loot_multiply_ver":1},{"data":8000009,"id":503000333,"x":20,"y":39,"loot_multiply_ver":1},{"data":8000009,"id":503000334,"x":20,"y":38,"loot_multiply_ver":1},{"data":8000009,"id":503000335,"x":27,"y":25,"loot_multiply_ver":1},{"data":8000009,"id":503000336,"x":26,"y":34,"loot_multiply_ver":1},{"data":8000009,"id":503000337,"x":32,"y":22,"loot_multiply_ver":1},{"data":8000009,"id":503000338,"x":35,"y":20,"loot_multiply_ver":1},{"data":8000009,"id":503000339,"x":26,"y":35,"loot_multiply_ver":1},{"data":8000009,"id":503000340,"x":36,"y":20,"loot_multiply_ver":1},{"data":8000009,"id":503000341,"x":28,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000342,"x":26,"y":36,"loot_multiply_ver":1},{"data":8000009,"id":503000343,"x":28,"y":25,"loot_multiply_ver":1},{"data":8000009,"id":503000344,"x":29,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000345,"x":20,"y":35,"loot_multiply_ver":1},{"data":8000038,"id":503000346,"x":45,"y":3,"defg":117171,"defe":104847,"defde":307,"loot_multiply_ver":1}],"decos":[],"respawnVars":{"secondsFromLastRespawn":231386,"respawnSeed":1914935487,"obstacleClearCounter":5,"time_to_gembox_drop":196966,"time_in_gembox_period":228665},"cooldowns":[],"newShopBuildings":[4,0,6,3,6,3,4,1,5,5,225,3,3,4,1,5,0,0,0,3,1,0,1,2,1,0,2,0,1,1,0,0],"newShopTraps":[6,6,3,0,0,4,2,0,2],"newShopDecos":[1,4,0,1,1,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"last_league_rank":9,"last_alliance_level":7,"last_league_shuffle":1,"last_season_seen":0,"last_news_seen":221,"troop_req_msg":"max hogs and max poison","war_req_msg":"2 witches, 1 barb","war_tutorials_seen":0,"war_base":true,"account_flags":14,"bool_layout_edit_shown_erase":true}
\ No newline at end of file
diff --git a/src/main/resources/examples/th9-sample-1.json b/src/main/resources/examples/th9-sample-1.json
new file mode 100644
index 0000000..785f9b1
--- /dev/null
+++ b/src/main/resources/examples/th9-sample-1.json
@@ -0,0 +1 @@
+{"wave_num":6,"exp_ver":1,"active_layout":0,"war_layout":1,"layout_state":[0,0,0,0,0,0],"buildings":[{"data":1000001,"id":500000000,"lvl":8,"x":30,"y":30,"l1x":25,"l1y":19,"l2x":13,"l2y":26,"l3x":29,"l3y":20,"l4x":29,"l4y":20,"l5x":13,"l5y":26},{"data":1000004,"id":500000001,"lvl":11,"x":22,"y":33,"res_time":87169,"l1x":11,"l1y":31,"l2x":24,"l2y":40,"l3x":41,"l3y":33,"l4x":41,"l4y":33,"l5x":24,"l5y":40},{"data":1000000,"id":500000002,"lvl":5,"x":35,"y":15,"units":[[4000008,2]],"l1x":29,"l1y":39,"l2x":38,"l2y":14,"l3x":40,"l3y":19,"l4x":40,"l4y":19,"l5x":38,"l5y":14},{"data":1000015,"id":500000003,"lvl":0,"x":41,"y":41,"l1x":14,"l1y":39,"l2x":9,"l2y":33,"l3x":36,"l3y":12,"l4x":36,"l4y":12,"l5x":41,"l5y":34},{"data":1000014,"id":500000004,"lvl":3,"x":27,"y":27,"l1x":25,"l1y":24,"l2x":23,"l2y":23,"l3x":25,"l3y":25,"l4x":25,"l4y":25,"l5x":23,"l5y":23},{"data":1000008,"id":500000005,"lvl":9,"x":37,"y":37,"l1x":36,"l1y":25,"l2x":33,"l2y":37,"l3x":34,"l3y":39,"l4x":34,"l4y":39,"l5x":33,"l5y":37},{"data":1000015,"id":500000006,"lvl":0,"x":38,"y":41,"l1x":16,"l1y":39,"l2x":37,"l2y":12,"l3x":32,"l3y":9,"l4x":32,"l4y":9,"l5x":37,"l5y":12},{"data":1000002,"id":500000007,"lvl":11,"x":37,"y":22,"res_time":87173,"l1x":21,"l1y":9,"l2x":41,"l2y":24,"l3x":8,"l3y":20,"l4x":8,"l4y":20,"l5x":41,"l5y":24},{"data":1000003,"id":500000008,"lvl":10,"x":39,"y":30,"l1x":16,"l1y":20,"l2x":26,"l2y":15,"l3x":29,"l3y":34,"l4x":29,"l4y":33,"l5x":26,"l5y":15},{"data":1000005,"id":500000009,"lvl":10,"x":30,"y":39,"l1x":24,"l1y":33,"l2x":27,"l2y":33,"l3x":16,"l3y":32,"l4x":16,"l4y":32,"l5x":27,"l5y":33},{"data":1000006,"id":500000010,"lvl":9,"x":8,"y":27,"unit_prod":{"unit_type":0},"l1x":40,"l1y":27,"l2x":21,"l2y":39,"l3x":30,"l3y":39,"l4x":30,"l4y":39,"l5x":21,"l5y":39},{"data":1000004,"id":500000011,"lvl":11,"x":33,"y":22,"res_time":65399,"l1x":18,"l1y":8,"l2x":36,"l2y":24,"l3x":42,"l3y":25,"l4x":42,"l4y":25,"l5x":36,"l5y":24},{"data":1000006,"id":500000012,"lvl":9,"x":21,"y":10,"unit_prod":{"unit_type":0},"l1x":39,"l1y":36,"l2x":18,"l2y":39,"l3x":7,"l3y":28,"l4x":7,"l4y":28,"l5x":18,"l5y":39},{"data":1000002,"id":500000013,"lvl":11,"x":31,"y":14,"res_time":61037,"l1x":34,"l1y":11,"l2x":41,"l2y":19,"l3x":8,"l3y":17,"l4x":8,"l4y":17,"l5x":41,"l5y":19},{"data":1000009,"id":500000014,"lvl":9,"x":24,"y":30,"l1x":15,"l1y":16,"l2x":12,"l2y":15,"l3x":24,"l3y":35,"l4x":24,"l4y":35,"l5x":12,"l5y":15},{"data":1000008,"id":500000015,"lvl":9,"x":10,"y":32,"l1x":30,"l1y":26,"l2x":9,"l2y":22,"l3x":38,"l3y":25,"l4x":38,"l4y":25,"l5x":9,"l5y":22},{"data":1000000,"id":500000016,"lvl":5,"x":15,"y":35,"units":[[4000008,2]],"l1x":24,"l1y":8,"l2x":32,"l2y":9,"l3x":20,"l3y":5,"l4x":20,"l4y":5,"l5x":32,"l5y":9},{"data":1000002,"id":500000017,"lvl":11,"x":10,"y":17,"res_time":87181,"l1x":37,"l1y":15,"l2x":40,"l2y":35,"l3x":14,"l3y":22,"l4x":14,"l4y":22,"l5x":9,"l5y":34},{"data":1000004,"id":500000018,"lvl":11,"x":14,"y":31,"res_time":61057,"l1x":40,"l1y":24,"l2x":15,"l2y":11,"l3x":40,"l3y":29,"l4x":40,"l4y":29,"l5x":15,"l5y":11},{"data":1000003,"id":500000019,"lvl":10,"x":39,"y":33,"l1x":35,"l1y":22,"l2x":19,"l2y":14,"l3x":22,"l3y":14,"l4x":22,"l4y":14,"l5x":20,"l5y":14},{"data":1000005,"id":500000020,"lvl":10,"x":33,"y":39,"l1x":19,"l1y":16,"l2x":30,"l2y":19,"l3x":18,"l3y":23,"l4x":18,"l4y":23,"l5x":30,"l5y":18},{"data":1000010,"id":500000021,"lvl":7,"x":40,"y":37,"l1x":24,"l1y":39,"l2x":23,"l2y":35,"l3x":36,"l3y":35,"l4x":36,"l4y":35,"l5x":24,"l5y":34},{"data":1000010,"id":500000022,"lvl":7,"x":42,"y":35,"l1x":25,"l1y":39,"l2x":23,"l2y":33,"l3x":15,"l3y":16,"l4x":15,"l4y":16,"l5x":24,"l5y":33},{"data":1000010,"id":500000023,"lvl":7,"x":42,"y":36,"l1x":26,"l1y":39,"l2x":24,"l2y":32,"l3x":15,"l3y":15,"l4x":15,"l4y":15,"l5x":24,"l5y":32},{"data":1000010,"id":500000024,"lvl":7,"x":41,"y":36,"l1x":27,"l1y":39,"l2x":15,"l2y":19,"l3x":15,"l3y":14,"l4x":15,"l4y":14,"l5x":13,"l5y":20},{"data":1000010,"id":500000025,"lvl":7,"x":34,"y":42,"l1x":28,"l1y":39,"l2x":14,"l2y":19,"l3x":22,"l3y":18,"l4x":19,"l4y":18,"l5x":15,"l5y":20},{"data":1000010,"id":500000026,"lvl":7,"x":35,"y":42,"l1x":26,"l1y":23,"l2x":13,"l2y":19,"l3x":41,"l3y":28,"l4x":41,"l4y":28,"l5x":34,"l5y":20},{"data":1000010,"id":500000027,"lvl":7,"x":36,"y":42,"l1x":29,"l1y":38,"l2x":17,"l2y":31,"l3x":15,"l3y":13,"l4x":15,"l4y":13,"l5x":17,"l5y":31},{"data":1000010,"id":500000028,"lvl":7,"x":37,"y":42,"l1x":30,"l1y":35,"l2x":17,"l2y":32,"l3x":16,"l3y":13,"l4x":16,"l4y":13,"l5x":17,"l5y":32},{"data":1000010,"id":500000029,"lvl":7,"x":37,"y":41,"l1x":31,"l1y":38,"l2x":17,"l2y":33,"l3x":17,"l3y":13,"l4x":17,"l4y":13,"l5x":17,"l5y":33},{"data":1000010,"id":500000030,"lvl":7,"x":40,"y":26,"l1x":32,"l1y":38,"l2x":16,"l2y":33,"l3x":18,"l3y":13,"l4x":18,"l4y":13,"l5x":16,"l5y":33},{"data":1000010,"id":500000031,"lvl":7,"x":37,"y":40,"l1x":34,"l1y":38,"l2x":15,"l2y":33,"l3x":19,"l3y":13,"l4x":19,"l4y":13,"l5x":15,"l5y":33},{"data":1000010,"id":500000032,"lvl":7,"x":38,"y":40,"l1x":35,"l1y":38,"l2x":14,"l2y":33,"l3x":36,"l3y":38,"l4x":36,"l4y":38,"l5x":14,"l5y":33},{"data":1000010,"id":500000033,"lvl":7,"x":39,"y":40,"l1x":36,"l1y":38,"l2x":13,"l2y":33,"l3x":41,"l3y":27,"l4x":41,"l4y":27,"l5x":13,"l5y":33},{"data":1000010,"id":500000034,"lvl":7,"x":40,"y":36,"l1x":37,"l1y":38,"l2x":12,"l2y":33,"l3x":35,"l3y":38,"l4x":35,"l4y":38,"l5x":12,"l5y":33},{"data":1000010,"id":500000035,"lvl":7,"x":42,"y":34,"l1x":38,"l1y":38,"l2x":11,"l2y":33,"l3x":34,"l3y":38,"l4x":34,"l4y":38,"l5x":11,"l5y":33},{"data":1000010,"id":500000036,"lvl":7,"x":40,"y":38,"l1x":38,"l1y":37,"l2x":11,"l2y":32,"l3x":33,"l3y":38,"l4x":33,"l4y":38,"l5x":11,"l5y":32},{"data":1000010,"id":500000037,"lvl":7,"x":40,"y":39,"l1x":38,"l1y":36,"l2x":11,"l2y":31,"l3x":32,"l3y":38,"l4x":32,"l4y":38,"l5x":11,"l5y":31},{"data":1000010,"id":500000038,"lvl":7,"x":39,"y":26,"l1x":32,"l1y":21,"l2x":11,"l2y":30,"l3x":31,"l3y":38,"l4x":31,"l4y":38,"l5x":11,"l5y":30},{"data":1000010,"id":500000039,"lvl":7,"x":38,"y":26,"l1x":29,"l1y":19,"l2x":11,"l2y":29,"l3x":31,"l3y":37,"l4x":31,"l4y":37,"l5x":11,"l5y":29},{"data":1000010,"id":500000040,"lvl":7,"x":38,"y":25,"l1x":38,"l1y":35,"l2x":12,"l2y":29,"l3x":36,"l3y":34,"l4x":36,"l4y":34,"l5x":12,"l5y":29},{"data":1000010,"id":500000041,"lvl":7,"x":37,"y":25,"l1x":38,"l1y":34,"l2x":12,"l2y":28,"l3x":36,"l3y":33,"l4x":36,"l4y":33,"l5x":12,"l5y":28},{"data":1000010,"id":500000042,"lvl":7,"x":36,"y":25,"l1x":27,"l1y":13,"l2x":12,"l2y":27,"l3x":36,"l3y":36,"l4x":36,"l4y":36,"l5x":12,"l5y":27},{"data":1000010,"id":500000043,"lvl":7,"x":36,"y":24,"l1x":23,"l1y":13,"l2x":12,"l2y":26,"l3x":23,"l3y":37,"l4x":22,"l4y":38,"l5x":12,"l5y":26},{"data":1000010,"id":500000044,"lvl":7,"x":36,"y":23,"l1x":24,"l1y":13,"l2x":12,"l2y":19,"l3x":30,"l3y":37,"l4x":30,"l4y":37,"l5x":34,"l5y":22},{"data":1000010,"id":500000045,"lvl":7,"x":36,"y":22,"l1x":25,"l1y":13,"l2x":12,"l2y":20,"l3x":27,"l3y":38,"l4x":27,"l4y":38,"l5x":12,"l5y":20},{"data":1000010,"id":500000046,"lvl":7,"x":36,"y":21,"l1x":26,"l1y":13,"l2x":12,"l2y":21,"l3x":33,"l3y":32,"l4x":33,"l4y":32,"l5x":12,"l5y":21},{"data":1000010,"id":500000047,"lvl":7,"x":35,"y":21,"l1x":28,"l1y":13,"l2x":12,"l2y":22,"l3x":34,"l3y":32,"l4x":34,"l4y":32,"l5x":12,"l5y":22},{"data":1000010,"id":500000048,"lvl":7,"x":40,"y":40,"l1x":32,"l1y":12,"l2x":12,"l2y":23,"l3x":35,"l3y":32,"l4x":35,"l4y":32,"l5x":12,"l5y":23},{"data":1000010,"id":500000049,"lvl":7,"x":12,"y":8,"l1x":29,"l1y":14,"l2x":12,"l2y":24,"l3x":36,"l3y":32,"l4x":36,"l4y":32,"l5x":12,"l5y":24},{"data":1000010,"id":500000050,"lvl":7,"x":26,"y":39,"l1x":29,"l1y":16,"l2x":12,"l2y":25,"l3x":37,"l3y":32,"l4x":37,"l4y":32,"l5x":12,"l5y":25},{"data":1000010,"id":500000051,"lvl":7,"x":26,"y":38,"l1x":29,"l1y":15,"l2x":18,"l2y":16,"l3x":37,"l3y":31,"l4x":37,"l4y":31,"l5x":18,"l5y":16},{"data":1000010,"id":500000052,"lvl":7,"x":25,"y":38,"l1x":29,"l1y":17,"l2x":18,"l2y":15,"l3x":37,"l3y":30,"l4x":37,"l4y":30,"l5x":18,"l5y":15},{"data":1000010,"id":500000053,"lvl":7,"x":25,"y":37,"l1x":31,"l1y":12,"l2x":18,"l2y":14,"l3x":37,"l3y":29,"l4x":37,"l4y":29,"l5x":18,"l5y":14},{"data":1000010,"id":500000054,"lvl":7,"x":25,"y":36,"l1x":36,"l1y":16,"l2x":18,"l2y":13,"l3x":37,"l3y":28,"l4x":37,"l4y":28,"l5x":18,"l5y":13},{"data":1000010,"id":500000055,"lvl":7,"x":24,"y":36,"l1x":29,"l1y":12,"l2x":18,"l2y":12,"l3x":38,"l3y":28,"l4x":38,"l4y":28,"l5x":18,"l5y":12},{"data":1000010,"id":500000056,"lvl":7,"x":23,"y":36,"l1x":35,"l1y":14,"l2x":18,"l2y":11,"l3x":40,"l3y":28,"l4x":40,"l4y":28,"l5x":18,"l5y":11},{"data":1000010,"id":500000057,"lvl":7,"x":22,"y":36,"l1x":36,"l1y":14,"l2x":18,"l2y":10,"l3x":41,"l3y":25,"l4x":41,"l4y":25,"l5x":18,"l5y":10},{"data":1000010,"id":500000058,"lvl":7,"x":21,"y":36,"l1x":29,"l1y":20,"l2x":18,"l2y":9,"l3x":41,"l3y":24,"l4x":41,"l4y":24,"l5x":18,"l5y":9},{"data":1000010,"id":500000059,"lvl":7,"x":8,"y":35,"l1x":30,"l1y":20,"l2x":18,"l2y":8,"l3x":34,"l3y":23,"l4x":34,"l4y":23,"l5x":18,"l5y":8},{"data":1000010,"id":500000060,"lvl":7,"x":21,"y":35,"l1x":31,"l1y":20,"l2x":30,"l2y":13,"l3x":35,"l3y":23,"l4x":35,"l4y":23,"l5x":30,"l5y":13},{"data":1000010,"id":500000061,"lvl":7,"x":7,"y":35,"l1x":31,"l1y":21,"l2x":19,"l2y":8,"l3x":36,"l3y":23,"l4x":36,"l4y":23,"l5x":19,"l5y":8},{"data":1000010,"id":500000062,"lvl":7,"x":7,"y":34,"l1x":34,"l1y":24,"l2x":20,"l2y":8,"l3x":37,"l3y":23,"l4x":37,"l4y":23,"l5x":20,"l5y":8},{"data":1000010,"id":500000063,"lvl":7,"x":7,"y":33,"l1x":34,"l1y":23,"l2x":21,"l2y":8,"l3x":38,"l3y":23,"l4x":38,"l4y":23,"l5x":21,"l5y":8},{"data":1000010,"id":500000064,"lvl":7,"x":7,"y":32,"l1x":34,"l1y":22,"l2x":22,"l2y":8,"l3x":39,"l3y":24,"l4x":39,"l4y":24,"l5x":22,"l5y":8},{"data":1000010,"id":500000065,"lvl":7,"x":7,"y":31,"l1x":33,"l1y":21,"l2x":23,"l2y":8,"l3x":39,"l3y":23,"l4x":39,"l4y":23,"l5x":23,"l5y":8},{"data":1000010,"id":500000066,"lvl":7,"x":7,"y":30,"l1x":34,"l1y":21,"l2x":24,"l2y":8,"l3x":39,"l3y":22,"l4x":39,"l4y":22,"l5x":24,"l5y":8},{"data":1000010,"id":500000067,"lvl":7,"x":7,"y":29,"l1x":30,"l1y":25,"l2x":25,"l2y":8,"l3x":39,"l3y":21,"l4x":39,"l4y":21,"l5x":25,"l5y":8},{"data":1000010,"id":500000068,"lvl":7,"x":7,"y":28,"l1x":31,"l1y":25,"l2x":26,"l2y":8,"l3x":39,"l3y":20,"l4x":39,"l4y":20,"l5x":26,"l5y":8},{"data":1000010,"id":500000069,"lvl":7,"x":7,"y":27,"l1x":32,"l1y":25,"l2x":27,"l2y":8,"l3x":39,"l3y":19,"l4x":39,"l4y":19,"l5x":27,"l5y":8},{"data":1000010,"id":500000070,"lvl":7,"x":7,"y":26,"l1x":19,"l1y":22,"l2x":28,"l2y":8,"l3x":39,"l3y":18,"l4x":39,"l4y":18,"l5x":28,"l5y":8},{"data":1000013,"id":500000071,"lvl":5,"x":18,"y":25,"l1x":35,"l1y":34,"l2x":15,"l2y":14,"l3x":36,"l3y":15,"l4x":36,"l4y":15,"l5x":15,"l5y":14},{"data":1000007,"id":500000072,"lvl":5,"x":21,"y":37,"l1x":10,"l1y":14,"l2x":8,"l2y":14,"l3x":26,"l3y":10,"l4x":26,"l4y":10,"l5x":8,"l5y":14},{"data":1000010,"id":500000073,"lvl":7,"x":9,"y":35,"l1x":19,"l1y":21,"l2x":29,"l2y":8,"l3x":39,"l3y":17,"l4x":39,"l4y":17,"l5x":29,"l5y":8},{"data":1000010,"id":500000074,"lvl":7,"x":35,"y":8,"l1x":19,"l1y":23,"l2x":30,"l2y":8,"l3x":39,"l3y":16,"l4x":39,"l4y":16,"l5x":30,"l5y":8},{"data":1000010,"id":500000075,"lvl":7,"x":35,"y":7,"l1x":20,"l1y":23,"l2x":31,"l2y":8,"l3x":39,"l3y":15,"l4x":39,"l4y":15,"l5x":31,"l5y":8},{"data":1000010,"id":500000076,"lvl":7,"x":34,"y":7,"l1x":33,"l1y":25,"l2x":31,"l2y":9,"l3x":39,"l3y":14,"l4x":39,"l4y":14,"l5x":31,"l5y":9},{"data":1000010,"id":500000077,"lvl":7,"x":33,"y":7,"l1x":28,"l1y":25,"l2x":31,"l2y":10,"l3x":38,"l3y":14,"l4x":38,"l4y":14,"l5x":31,"l5y":10},{"data":1000010,"id":500000078,"lvl":7,"x":32,"y":7,"l1x":29,"l1y":25,"l2x":23,"l2y":13,"l3x":37,"l3y":14,"l4x":37,"l4y":14,"l5x":35,"l5y":26},{"data":1000010,"id":500000079,"lvl":7,"x":31,"y":7,"l1x":28,"l1y":24,"l2x":23,"l2y":15,"l3x":36,"l3y":14,"l4x":36,"l4y":14,"l5x":25,"l5y":15},{"data":1000010,"id":500000080,"lvl":7,"x":30,"y":7,"l1x":19,"l1y":20,"l2x":24,"l2y":13,"l3x":35,"l3y":14,"l4x":35,"l4y":14,"l5x":34,"l5y":26},{"data":1000010,"id":500000081,"lvl":7,"x":29,"y":7,"l1x":34,"l1y":25,"l2x":23,"l2y":14,"l3x":34,"l3y":14,"l4x":34,"l4y":14,"l5x":25,"l5y":16},{"data":1000010,"id":500000082,"lvl":7,"x":28,"y":7,"l1x":19,"l1y":19,"l2x":25,"l2y":13,"l3x":33,"l3y":14,"l4x":33,"l4y":14,"l5x":25,"l5y":13},{"data":1000010,"id":500000083,"lvl":7,"x":27,"y":7,"l1x":22,"l1y":19,"l2x":26,"l2y":13,"l3x":32,"l3y":14,"l4x":32,"l4y":14,"l5x":26,"l5y":13},{"data":1000010,"id":500000084,"lvl":7,"x":26,"y":7,"l1x":21,"l1y":19,"l2x":27,"l2y":13,"l3x":31,"l3y":14,"l4x":31,"l4y":14,"l5x":27,"l5y":13},{"data":1000010,"id":500000085,"lvl":7,"x":25,"y":7,"l1x":20,"l1y":19,"l2x":29,"l2y":13,"l3x":30,"l3y":14,"l4x":30,"l4y":14,"l5x":29,"l5y":13},{"data":1000010,"id":500000086,"lvl":7,"x":35,"y":9,"l1x":25,"l1y":18,"l2x":28,"l2y":13,"l3x":27,"l3y":14,"l4x":27,"l4y":14,"l5x":28,"l5y":13},{"data":1000010,"id":500000087,"lvl":7,"x":35,"y":10,"l1x":24,"l1y":18,"l2x":31,"l2y":12,"l3x":25,"l3y":13,"l4x":25,"l4y":13,"l5x":31,"l5y":12},{"data":1000010,"id":500000088,"lvl":7,"x":35,"y":11,"l1x":24,"l1y":19,"l2x":31,"l2y":13,"l3x":25,"l3y":12,"l4x":25,"l4y":12,"l5x":31,"l5y":13},{"data":1000010,"id":500000089,"lvl":7,"x":35,"y":12,"l1x":23,"l1y":19,"l2x":31,"l2y":14,"l3x":25,"l3y":10,"l4x":25,"l4y":10,"l5x":31,"l5y":14},{"data":1000010,"id":500000090,"lvl":7,"x":35,"y":13,"l1x":29,"l1y":18,"l2x":32,"l2y":14,"l3x":24,"l3y":10,"l4x":24,"l4y":10,"l5x":32,"l5y":14},{"data":1000010,"id":500000091,"lvl":7,"x":7,"y":25,"l1x":28,"l1y":18,"l2x":33,"l2y":14,"l3x":23,"l3y":10,"l4x":23,"l4y":10,"l5x":33,"l5y":14},{"data":1000010,"id":500000092,"lvl":7,"x":10,"y":35,"l1x":27,"l1y":18,"l2x":34,"l2y":14,"l3x":22,"l3y":10,"l4x":22,"l4y":10,"l5x":34,"l5y":14},{"data":1000010,"id":500000093,"lvl":7,"x":11,"y":35,"l1x":26,"l1y":18,"l2x":35,"l2y":14,"l3x":21,"l3y":10,"l4x":21,"l4y":10,"l5x":35,"l5y":14},{"data":1000010,"id":500000094,"lvl":7,"x":12,"y":35,"l1x":27,"l1y":23,"l2x":36,"l2y":14,"l3x":20,"l3y":10,"l4x":20,"l4y":10,"l5x":36,"l5y":14},{"data":1000010,"id":500000095,"lvl":7,"x":13,"y":35,"l1x":28,"l1y":23,"l2x":37,"l2y":14,"l3x":20,"l3y":11,"l4x":20,"l4y":11,"l5x":37,"l5y":14},{"data":1000010,"id":500000096,"lvl":7,"x":26,"y":11,"l1x":25,"l1y":23,"l2x":37,"l2y":15,"l3x":20,"l3y":12,"l4x":20,"l4y":12,"l5x":37,"l5y":15},{"data":1000010,"id":500000097,"lvl":7,"x":27,"y":11,"l1x":24,"l1y":23,"l2x":37,"l2y":16,"l3x":20,"l3y":13,"l4x":20,"l4y":13,"l5x":37,"l5y":16},{"data":1000002,"id":500000098,"lvl":11,"x":17,"y":10,"res_time":87175,"l1x":23,"l1y":15,"l2x":35,"l2y":41,"l3x":16,"l3y":19,"l4x":16,"l4y":19,"l5x":35,"l5y":41},{"data":1000006,"id":500000099,"lvl":9,"x":27,"y":8,"unit_prod":{"unit_type":0,"t":0,"slots":[{"id":4000008,"cnt":1}]},"l1x":37,"l1y":18,"l2x":14,"l2y":38,"l3x":7,"l3y":23,"l4x":7,"l4y":23,"l5x":14,"l5y":38},{"data":1000004,"id":500000100,"lvl":11,"x":28,"y":18,"res_time":61043,"l1x":39,"l1y":33,"l2x":11,"l2y":11,"l3x":34,"l3y":42,"l4x":34,"l4y":42,"l5x":11,"l5y":11},{"data":1000009,"id":500000101,"lvl":9,"x":30,"y":24,"l1x":25,"l1y":36,"l2x":12,"l2y":30,"l3x":36,"l3y":20,"l4x":36,"l4y":20,"l5x":12,"l5y":30},{"data":1000015,"id":500000102,"lvl":0,"x":28,"y":40,"l1x":12,"l1y":18,"l2x":22,"l2y":37,"l3x":30,"l3y":9,"l4x":30,"l4y":9,"l5x":41,"l5y":36},{"data":1000012,"id":500000103,"lvl":5,"x":27,"y":30,"l1x":21,"l1y":20,"l2x":30,"l2y":29,"l3x":25,"l3y":20,"l4x":25,"l4y":20,"l5x":30,"l5y":29},{"data":1000015,"id":500000104,"lvl":0,"x":26,"y":40,"l1x":37,"l1y":13,"l2x":18,"l2y":23,"l3x":8,"l3y":26,"l4x":8,"l4y":26,"l5x":18,"l5y":23},{"data":1000000,"id":500000105,"lvl":5,"x":12,"y":26,"units":[[4000008,3]],"l1x":10,"l1y":20,"l2x":6,"l2y":28,"l3x":23,"l3y":39,"l4x":23,"l4y":39,"l5x":6,"l5y":28},{"data":1000020,"id":500000106,"lvl":2,"x":41,"y":37,"units":[[26000001,1],[26000000,2]],"unit_prod":{"unit_type":1,"t":0,"slots":[{"id":26000001,"cnt":1}]},"l1x":11,"l1y":27,"l2x":9,"l2y":25,"l3x":17,"l3y":10,"l4x":17,"l4y":10,"l5x":9,"l5y":25},{"data":1000011,"id":500000107,"lvl":5,"x":21,"y":21,"l1x":16,"l1y":24,"l2x":25,"l2y":29,"l3x":30,"l3y":11,"l4x":30,"l4y":11,"l5x":25,"l5y":28},{"data":1000008,"id":500000108,"lvl":9,"x":32,"y":10,"l1x":14,"l1y":36,"l2x":26,"l2y":10,"l3x":18,"l3y":37,"l4x":18,"l4y":37,"l5x":26,"l5y":10},{"data":1000010,"id":500000109,"lvl":7,"x":28,"y":11,"l1x":36,"l1y":15,"l2x":37,"l2y":18,"l3x":20,"l3y":14,"l4x":20,"l4y":14,"l5x":37,"l5y":18},{"data":1000010,"id":500000110,"lvl":7,"x":29,"y":11,"l1x":30,"l1y":12,"l2x":37,"l2y":17,"l3x":20,"l3y":15,"l4x":20,"l4y":15,"l5x":37,"l5y":17},{"data":1000010,"id":500000111,"lvl":7,"x":30,"y":11,"l1x":36,"l1y":17,"l2x":39,"l2y":34,"l3x":20,"l3y":16,"l4x":20,"l4y":16,"l5x":39,"l5y":34},{"data":1000010,"id":500000112,"lvl":7,"x":31,"y":11,"l1x":34,"l1y":14,"l2x":38,"l2y":34,"l3x":28,"l3y":14,"l4x":28,"l4y":14,"l5x":38,"l5y":34},{"data":1000010,"id":500000113,"lvl":7,"x":31,"y":12,"l1x":36,"l1y":18,"l2x":37,"l2y":34,"l3x":25,"l3y":14,"l4x":25,"l4y":14,"l5x":37,"l5y":34},{"data":1000010,"id":500000114,"lvl":7,"x":31,"y":13,"l1x":36,"l1y":19,"l2x":35,"l2y":34,"l3x":26,"l3y":14,"l4x":26,"l4y":14,"l5x":35,"l5y":34},{"data":1000010,"id":500000115,"lvl":7,"x":32,"y":13,"l1x":33,"l1y":13,"l2x":35,"l2y":33,"l3x":29,"l3y":14,"l4x":29,"l4y":14,"l5x":35,"l5y":33},{"data":1000010,"id":500000116,"lvl":7,"x":33,"y":13,"l1x":35,"l1y":21,"l2x":36,"l2y":34,"l3x":29,"l3y":15,"l4x":29,"l4y":15,"l5x":36,"l5y":34},{"data":1000010,"id":500000117,"lvl":7,"x":34,"y":13,"l1x":33,"l1y":38,"l2x":36,"l2y":35,"l3x":29,"l3y":16,"l4x":29,"l4y":16,"l5x":36,"l5y":35},{"data":1000010,"id":500000118,"lvl":7,"x":34,"y":14,"l1x":36,"l1y":20,"l2x":36,"l2y":36,"l3x":29,"l3y":17,"l4x":29,"l4y":17,"l5x":36,"l5y":36},{"data":1000010,"id":500000119,"lvl":7,"x":34,"y":15,"l1x":37,"l1y":21,"l2x":36,"l2y":37,"l3x":28,"l3y":37,"l4x":28,"l4y":37,"l5x":36,"l5y":37},{"data":1000010,"id":500000120,"lvl":7,"x":34,"y":16,"l1x":38,"l1y":21,"l2x":36,"l2y":38,"l3x":41,"l3y":26,"l4x":41,"l4y":26,"l5x":36,"l5y":38},{"data":1000010,"id":500000121,"lvl":7,"x":11,"y":26,"l1x":39,"l1y":21,"l2x":36,"l2y":39,"l3x":25,"l3y":17,"l4x":25,"l4y":17,"l5x":36,"l5y":39},{"data":1000010,"id":500000122,"lvl":7,"x":11,"y":27,"l1x":39,"l1y":22,"l2x":23,"l2y":16,"l3x":23,"l3y":17,"l4x":23,"l4y":17,"l5x":25,"l5y":14},{"data":1000010,"id":500000123,"lvl":7,"x":11,"y":28,"l1x":39,"l1y":23,"l2x":36,"l2y":40,"l3x":21,"l3y":17,"l4x":21,"l4y":17,"l5x":36,"l5y":40},{"data":1000010,"id":500000124,"lvl":7,"x":11,"y":29,"l1x":39,"l1y":24,"l2x":35,"l2y":40,"l3x":20,"l3y":17,"l4x":20,"l4y":17,"l5x":35,"l5y":40},{"data":1000010,"id":500000125,"lvl":7,"x":11,"y":30,"l1x":39,"l1y":25,"l2x":34,"l2y":40,"l3x":19,"l3y":17,"l4x":19,"l4y":17,"l5x":34,"l5y":40},{"data":1000010,"id":500000126,"lvl":7,"x":11,"y":31,"l1x":39,"l1y":26,"l2x":33,"l2y":40,"l3x":18,"l3y":17,"l4x":18,"l4y":17,"l5x":33,"l5y":40},{"data":1000010,"id":500000127,"lvl":7,"x":12,"y":31,"l1x":39,"l1y":27,"l2x":32,"l2y":40,"l3x":16,"l3y":17,"l4x":16,"l4y":17,"l5x":32,"l5y":40},{"data":1000010,"id":500000128,"lvl":7,"x":13,"y":31,"l1x":39,"l1y":28,"l2x":24,"l2y":35,"l3x":15,"l3y":17,"l4x":15,"l4y":17,"l5x":24,"l5y":35},{"data":1000010,"id":500000129,"lvl":7,"x":13,"y":32,"l1x":39,"l1y":29,"l2x":24,"l2y":36,"l3x":14,"l3y":17,"l4x":14,"l4y":17,"l5x":24,"l5y":36},{"data":1000010,"id":500000130,"lvl":7,"x":13,"y":33,"l1x":22,"l1y":23,"l2x":24,"l2y":37,"l3x":13,"l3y":17,"l4x":13,"l4y":17,"l5x":24,"l5y":37},{"data":1000010,"id":500000131,"lvl":7,"x":13,"y":34,"l1x":39,"l1y":30,"l2x":33,"l2y":21,"l3x":12,"l3y":17,"l4x":12,"l4y":17,"l5x":33,"l5y":21},{"data":1000010,"id":500000132,"lvl":7,"x":14,"y":34,"l1x":39,"l1y":31,"l2x":33,"l2y":20,"l3x":11,"l3y":17,"l4x":11,"l4y":17,"l5x":30,"l5y":21},{"data":1000010,"id":500000133,"lvl":7,"x":15,"y":34,"l1x":39,"l1y":32,"l2x":33,"l2y":19,"l3x":11,"l3y":18,"l4x":11,"l4y":18,"l5x":34,"l5y":19},{"data":1000002,"id":500000134,"lvl":11,"x":17,"y":14,"res_time":87179,"l1x":11,"l1y":11,"l2x":29,"l2y":36,"l3x":34,"l3y":25,"l4x":34,"l4y":25,"l5x":29,"l5y":36},{"data":1000004,"id":500000135,"lvl":11,"x":18,"y":28,"res_time":61053,"l1x":25,"l1y":40,"l2x":11,"l2y":34,"l3x":37,"l3y":39,"l4x":37,"l4y":39,"l5x":12,"l5y":34},{"data":1000009,"id":500000136,"lvl":9,"x":14,"y":14,"l1x":15,"l1y":28,"l2x":34,"l2y":15,"l3x":22,"l3y":11,"l4x":22,"l4y":11,"l5x":34,"l5y":15},{"data":1000002,"id":500000137,"lvl":11,"x":14,"y":17,"res_time":87173,"l1x":14,"l1y":8,"l2x":6,"l2y":20,"l3x":26,"l3y":15,"l4x":26,"l4y":15,"l5x":6,"l5y":20},{"data":1000004,"id":500000138,"lvl":11,"x":18,"y":22,"res_time":61055,"l1x":11,"l1y":8,"l2x":13,"l2y":21,"l3x":33,"l3y":11,"l4x":33,"l4y":11,"l5x":13,"l5y":21},{"data":1000013,"id":500000139,"lvl":5,"x":25,"y":18,"l1x":15,"l1y":12,"l2x":37,"l2y":35,"l3x":16,"l3y":14,"l4x":16,"l4y":14,"l5x":38,"l5y":35},{"data":1000010,"id":500000140,"lvl":7,"x":9,"y":25,"l1x":21,"l1y":23,"l2x":34,"l2y":19,"l3x":11,"l3y":19,"l4x":11,"l4y":19,"l5x":36,"l5y":18},{"data":1000010,"id":500000141,"lvl":7,"x":10,"y":25,"l1x":23,"l1y":23,"l2x":35,"l2y":19,"l3x":11,"l3y":20,"l4x":11,"l4y":20,"l5x":35,"l5y":18},{"data":1000010,"id":500000142,"lvl":7,"x":11,"y":25,"l1x":20,"l1y":26,"l2x":36,"l2y":19,"l3x":11,"l3y":21,"l4x":11,"l4y":21,"l5x":34,"l5y":18},{"data":1000010,"id":500000143,"lvl":7,"x":12,"y":25,"l1x":20,"l1y":36,"l2x":37,"l2y":19,"l3x":11,"l3y":22,"l4x":11,"l4y":22,"l5x":37,"l5y":19},{"data":1000010,"id":500000144,"lvl":7,"x":25,"y":9,"l1x":37,"l1y":33,"l2x":38,"l2y":19,"l3x":12,"l3y":22,"l4x":12,"l4y":22,"l5x":38,"l5y":19},{"data":1000010,"id":500000145,"lvl":7,"x":25,"y":10,"l1x":36,"l1y":33,"l2x":39,"l2y":19,"l3x":27,"l3y":33,"l4x":27,"l4y":33,"l5x":39,"l5y":19},{"data":1000010,"id":500000146,"lvl":7,"x":25,"y":11,"l1x":35,"l1y":33,"l2x":40,"l2y":19,"l3x":27,"l3y":34,"l4x":27,"l4y":34,"l5x":40,"l5y":19},{"data":1000010,"id":500000147,"lvl":7,"x":25,"y":12,"l1x":34,"l1y":33,"l2x":40,"l2y":20,"l3x":27,"l3y":35,"l4x":27,"l4y":35,"l5x":40,"l5y":20},{"data":1000010,"id":500000148,"lvl":7,"x":25,"y":8,"l1x":15,"l1y":26,"l2x":40,"l2y":21,"l3x":27,"l3y":36,"l4x":27,"l4y":36,"l5x":40,"l5y":21},{"data":1000010,"id":500000149,"lvl":7,"x":24,"y":8,"l1x":15,"l1y":25,"l2x":40,"l2y":22,"l3x":27,"l3y":37,"l4x":27,"l4y":37,"l5x":40,"l5y":22},{"data":1000010,"id":500000150,"lvl":7,"x":23,"y":8,"l1x":15,"l1y":24,"l2x":32,"l2y":39,"l3x":23,"l3y":38,"l4x":23,"l4y":38,"l5x":32,"l5y":39},{"data":1000010,"id":500000151,"lvl":7,"x":22,"y":8,"l1x":15,"l1y":23,"l2x":31,"l2y":39,"l3x":24,"l3y":38,"l4x":24,"l4y":38,"l5x":31,"l5y":39},{"data":1000010,"id":500000152,"lvl":7,"x":21,"y":8,"l1x":15,"l1y":21,"l2x":30,"l2y":39,"l3x":25,"l3y":38,"l4x":25,"l4y":38,"l5x":30,"l5y":39},{"data":1000010,"id":500000153,"lvl":7,"x":20,"y":8,"l1x":15,"l1y":22,"l2x":29,"l2y":39,"l3x":22,"l3y":37,"l4x":21,"l4y":38,"l5x":29,"l5y":39},{"data":1000010,"id":500000154,"lvl":7,"x":19,"y":8,"l1x":14,"l1y":19,"l2x":28,"l2y":39,"l3x":26,"l3y":38,"l4x":26,"l4y":38,"l5x":28,"l5y":39},{"data":1000010,"id":500000155,"lvl":7,"x":18,"y":8,"l1x":14,"l1y":18,"l2x":27,"l2y":39,"l3x":33,"l3y":18,"l4x":29,"l4y":19,"l5x":27,"l5y":39},{"data":1000010,"id":500000156,"lvl":7,"x":17,"y":8,"l1x":14,"l1y":17,"l2x":26,"l2y":39,"l3x":32,"l3y":18,"l4x":32,"l4y":19,"l5x":26,"l5y":39},{"data":1000010,"id":500000157,"lvl":7,"x":16,"y":8,"l1x":14,"l1y":16,"l2x":25,"l2y":39,"l3x":31,"l3y":18,"l4x":31,"l4y":19,"l5x":25,"l5y":39},{"data":1000010,"id":500000158,"lvl":7,"x":16,"y":34,"l1x":14,"l1y":15,"l2x":39,"l2y":33,"l3x":33,"l3y":19,"l4x":33,"l4y":19,"l5x":39,"l5y":33},{"data":1000010,"id":500000159,"lvl":7,"x":8,"y":25,"l1x":14,"l1y":14,"l2x":39,"l2y":27,"l3x":33,"l3y":20,"l4x":33,"l4y":20,"l5x":39,"l5y":27},{"data":1000010,"id":500000160,"lvl":7,"x":8,"y":24,"l1x":14,"l1y":13,"l2x":39,"l2y":28,"l3x":33,"l3y":21,"l4x":33,"l4y":21,"l5x":39,"l5y":28},{"data":1000010,"id":500000161,"lvl":7,"x":8,"y":23,"l1x":14,"l1y":12,"l2x":39,"l2y":29,"l3x":33,"l3y":22,"l4x":33,"l4y":22,"l5x":39,"l5y":29},{"data":1000010,"id":500000162,"lvl":7,"x":8,"y":22,"l1x":14,"l1y":11,"l2x":22,"l2y":32,"l3x":33,"l3y":23,"l4x":33,"l4y":23,"l5x":22,"l5y":32},{"data":1000010,"id":500000163,"lvl":7,"x":8,"y":21,"l1x":15,"l1y":11,"l2x":22,"l2y":31,"l3x":33,"l3y":24,"l4x":33,"l4y":24,"l5x":22,"l5y":31},{"data":1000010,"id":500000164,"lvl":7,"x":8,"y":20,"l1x":16,"l1y":11,"l2x":23,"l2y":32,"l3x":33,"l3y":25,"l4x":33,"l4y":25,"l5x":23,"l5y":32},{"data":1000011,"id":500000165,"lvl":5,"x":36,"y":30,"l1x":31,"l1y":35,"l2x":17,"l2y":19,"l3x":19,"l3y":19,"l4x":38,"l4y":33,"l5x":17,"l5y":19},{"data":1000015,"id":500000166,"lvl":0,"x":39,"y":27,"l1x":23,"l1y":40,"l2x":26,"l2y":25,"l3x":25,"l3y":8,"l4x":25,"l4y":8,"l5x":27,"l5y":25},{"data":1000024,"id":500000167,"lvl":3,"x":22,"y":18,"l1x":21,"l1y":24,"l2x":32,"l2y":34,"l3x":24,"l3y":29,"l4x":14,"l4y":26,"l5x":32,"l5y":33},{"data":1000006,"id":500000168,"lvl":9,"x":10,"y":21,"unit_prod":{"unit_type":0,"t":0,"slots":[{"id":4000008,"cnt":1}]},"l1x":37,"l1y":39,"l2x":9,"l2y":18,"l3x":10,"l3y":27,"l4x":10,"l4y":27,"l5x":9,"l5y":18},{"data":1000000,"id":500000169,"lvl":5,"x":26,"y":12,"units":[[4000008,3]],"l1x":18,"l1y":37,"l2x":40,"l2y":29,"l3x":7,"l3y":31,"l4x":7,"l4y":31,"l5x":40,"l5y":29},{"data":1000010,"id":500000170,"lvl":7,"x":8,"y":19,"l1x":17,"l1y":11,"l2x":23,"l2y":34,"l3x":33,"l3y":26,"l4x":33,"l4y":26,"l5x":24,"l5y":17},{"data":1000010,"id":500000171,"lvl":7,"x":8,"y":18,"l1x":18,"l1y":11,"l2x":25,"l2y":32,"l3x":30,"l3y":18,"l4x":30,"l4y":19,"l5x":25,"l5y":32},{"data":1000010,"id":500000172,"lvl":7,"x":8,"y":17,"l1x":33,"l1y":14,"l2x":26,"l2y":32,"l3x":29,"l3y":18,"l4x":29,"l4y":18,"l5x":26,"l5y":32},{"data":1000010,"id":500000173,"lvl":7,"x":8,"y":16,"l1x":19,"l1y":12,"l2x":27,"l2y":32,"l3x":28,"l3y":18,"l4x":28,"l4y":18,"l5x":27,"l5y":32},{"data":1000010,"id":500000174,"lvl":7,"x":9,"y":16,"l1x":20,"l1y":12,"l2x":28,"l2y":32,"l3x":27,"l3y":18,"l4x":27,"l4y":18,"l5x":28,"l5y":32},{"data":1000010,"id":500000175,"lvl":7,"x":10,"y":16,"l1x":21,"l1y":12,"l2x":29,"l2y":32,"l3x":26,"l3y":18,"l4x":26,"l4y":18,"l5x":29,"l5y":32},{"data":1000010,"id":500000176,"lvl":7,"x":11,"y":16,"l1x":22,"l1y":12,"l2x":30,"l2y":32,"l3x":25,"l3y":18,"l4x":25,"l4y":18,"l5x":30,"l5y":32},{"data":1000010,"id":500000177,"lvl":7,"x":12,"y":16,"l1x":22,"l1y":13,"l2x":31,"l2y":32,"l3x":22,"l3y":17,"l4x":22,"l4y":17,"l5x":31,"l5y":32},{"data":1000010,"id":500000178,"lvl":7,"x":16,"y":9,"l1x":22,"l1y":14,"l2x":32,"l2y":32,"l3x":24,"l3y":17,"l4x":24,"l4y":17,"l5x":32,"l5y":32},{"data":1000010,"id":500000179,"lvl":7,"x":16,"y":10,"l1x":22,"l1y":15,"l2x":33,"l2y":32,"l3x":22,"l3y":20,"l4x":19,"l4y":21,"l5x":33,"l5y":32},{"data":1000010,"id":500000180,"lvl":7,"x":16,"y":11,"l1x":22,"l1y":18,"l2x":34,"l2y":32,"l3x":22,"l3y":19,"l4x":19,"l4y":20,"l5x":34,"l5y":32},{"data":1000010,"id":500000181,"lvl":7,"x":14,"y":9,"l1x":22,"l1y":17,"l2x":35,"l2y":32,"l3x":22,"l3y":21,"l4x":19,"l4y":19,"l5x":35,"l5y":32},{"data":1000010,"id":500000182,"lvl":7,"x":9,"y":14,"l1x":15,"l1y":19,"l2x":35,"l2y":31,"l3x":21,"l3y":37,"l4x":21,"l4y":37,"l5x":35,"l5y":31},{"data":1000010,"id":500000183,"lvl":7,"x":16,"y":12,"l1x":20,"l1y":25,"l2x":35,"l2y":30,"l3x":21,"l3y":36,"l4x":21,"l4y":36,"l5x":35,"l5y":30},{"data":1000010,"id":500000184,"lvl":7,"x":8,"y":12,"l1x":29,"l1y":30,"l2x":35,"l2y":29,"l3x":20,"l3y":36,"l4x":20,"l4y":36,"l5x":35,"l5y":29},{"data":1000010,"id":500000185,"lvl":7,"x":9,"y":9,"l1x":18,"l1y":19,"l2x":35,"l2y":28,"l3x":19,"l3y":36,"l4x":19,"l4y":36,"l5x":35,"l5y":28},{"data":1000010,"id":500000186,"lvl":7,"x":13,"y":14,"l1x":30,"l1y":32,"l2x":39,"l2y":31,"l3x":18,"l3y":36,"l4x":18,"l4y":36,"l5x":39,"l5y":31},{"data":1000010,"id":500000187,"lvl":7,"x":13,"y":13,"l1x":29,"l1y":27,"l2x":35,"l2y":27,"l3x":17,"l3y":36,"l4x":17,"l4y":36,"l5x":35,"l5y":27},{"data":1000010,"id":500000188,"lvl":7,"x":14,"y":13,"l1x":29,"l1y":31,"l2x":17,"l2y":23,"l3x":16,"l3y":36,"l4x":16,"l4y":36,"l5x":17,"l5y":23},{"data":1000010,"id":500000189,"lvl":7,"x":15,"y":13,"l1x":29,"l1y":26,"l2x":17,"l2y":24,"l3x":15,"l3y":36,"l4x":15,"l4y":36,"l5x":17,"l5y":24},{"data":1000010,"id":500000190,"lvl":7,"x":16,"y":13,"l1x":18,"l1y":12,"l2x":17,"l2y":25,"l3x":15,"l3y":35,"l4x":15,"l4y":35,"l5x":17,"l5y":25},{"data":1000010,"id":500000191,"lvl":7,"x":17,"y":13,"l1x":29,"l1y":28,"l2x":17,"l2y":26,"l3x":15,"l3y":34,"l4x":15,"l4y":34,"l5x":17,"l5y":26},{"data":1000010,"id":500000192,"lvl":7,"x":18,"y":13,"l1x":29,"l1y":29,"l2x":17,"l2y":27,"l3x":15,"l3y":33,"l4x":15,"l4y":33,"l5x":17,"l5y":27},{"data":1000010,"id":500000193,"lvl":7,"x":19,"y":13,"l1x":20,"l1y":24,"l2x":17,"l2y":28,"l3x":15,"l3y":32,"l4x":15,"l4y":32,"l5x":17,"l5y":28},{"data":1000010,"id":500000194,"lvl":7,"x":13,"y":15,"l1x":22,"l1y":33,"l2x":17,"l2y":29,"l3x":13,"l3y":23,"l4x":13,"l4y":23,"l5x":17,"l5y":29},{"data":1000010,"id":500000195,"lvl":7,"x":13,"y":16,"l1x":20,"l1y":31,"l2x":39,"l2y":30,"l3x":13,"l3y":24,"l4x":13,"l4y":24,"l5x":39,"l5y":30},{"data":1000010,"id":500000196,"lvl":7,"x":13,"y":17,"l1x":14,"l1y":30,"l2x":17,"l2y":30,"l3x":13,"l3y":25,"l4x":13,"l4y":25,"l5x":17,"l5y":30},{"data":1000010,"id":500000197,"lvl":7,"x":13,"y":18,"l1x":14,"l1y":29,"l2x":18,"l2y":30,"l3x":13,"l3y":26,"l4x":13,"l4y":26,"l5x":18,"l5y":30},{"data":1000010,"id":500000198,"lvl":7,"x":13,"y":19,"l1x":14,"l1y":28,"l2x":19,"l2y":30,"l3x":13,"l3y":27,"l4x":13,"l4y":27,"l5x":19,"l5y":30},{"data":1000010,"id":500000199,"lvl":7,"x":20,"y":13,"l1x":14,"l1y":27,"l2x":20,"l2y":30,"l3x":13,"l3y":28,"l4x":13,"l4y":28,"l5x":20,"l5y":30},{"data":1000010,"id":500000200,"lvl":7,"x":21,"y":13,"l1x":14,"l1y":33,"l2x":21,"l2y":30,"l3x":13,"l3y":29,"l4x":13,"l4y":29,"l5x":21,"l5y":30},{"data":1000010,"id":500000201,"lvl":7,"x":22,"y":13,"l1x":23,"l1y":33,"l2x":22,"l2y":30,"l3x":13,"l3y":30,"l4x":13,"l4y":30,"l5x":22,"l5y":30},{"data":1000010,"id":500000202,"lvl":7,"x":23,"y":13,"l1x":14,"l1y":31,"l2x":22,"l2y":29,"l3x":13,"l3y":31,"l4x":13,"l4y":31,"l5x":22,"l5y":29},{"data":1000010,"id":500000203,"lvl":7,"x":24,"y":13,"l1x":16,"l1y":35,"l2x":22,"l2y":28,"l3x":24,"l3y":23,"l4x":24,"l4y":23,"l5x":22,"l5y":28},{"data":1000010,"id":500000204,"lvl":7,"x":13,"y":20,"l1x":15,"l1y":35,"l2x":16,"l2y":17,"l3x":24,"l3y":22,"l4x":24,"l4y":22,"l5x":16,"l5y":17},{"data":1000010,"id":500000205,"lvl":7,"x":13,"y":21,"l1x":14,"l1y":32,"l2x":17,"l2y":17,"l3x":23,"l3y":22,"l4x":23,"l4y":22,"l5x":17,"l5y":17},{"data":1000010,"id":500000206,"lvl":7,"x":13,"y":22,"l1x":20,"l1y":27,"l2x":18,"l2y":17,"l3x":22,"l3y":22,"l4x":22,"l4y":22,"l5x":18,"l5y":17},{"data":1000010,"id":500000207,"lvl":7,"x":13,"y":23,"l1x":17,"l1y":27,"l2x":19,"l2y":17,"l3x":21,"l3y":22,"l4x":21,"l4y":22,"l5x":19,"l5y":17},{"data":1000010,"id":500000208,"lvl":7,"x":13,"y":24,"l1x":18,"l1y":27,"l2x":20,"l2y":17,"l3x":20,"l3y":22,"l4x":20,"l4y":22,"l5x":20,"l5y":17},{"data":1000010,"id":500000209,"lvl":7,"x":13,"y":25,"l1x":30,"l1y":34,"l2x":21,"l2y":17,"l3x":19,"l3y":22,"l4x":19,"l4y":22,"l5x":21,"l5y":17},{"data":1000010,"id":500000210,"lvl":7,"x":14,"y":25,"l1x":33,"l1y":12,"l2x":22,"l2y":17,"l3x":18,"l3y":22,"l4x":18,"l4y":22,"l5x":22,"l5y":17},{"data":1000010,"id":500000211,"lvl":7,"x":25,"y":13,"l1x":36,"l1y":21,"l2x":23,"l2y":17,"l3x":17,"l3y":22,"l4x":17,"l4y":22,"l5x":14,"l5y":20},{"data":1000010,"id":500000212,"lvl":7,"x":25,"y":14,"l1x":30,"l1y":36,"l2x":25,"l2y":17,"l3x":17,"l3y":23,"l4x":17,"l4y":23,"l5x":25,"l5y":17},{"data":1000010,"id":500000213,"lvl":7,"x":25,"y":15,"l1x":29,"l1y":13,"l2x":24,"l2y":17,"l3x":17,"l3y":24,"l4x":17,"l4y":24,"l5x":23,"l5y":17},{"data":1000010,"id":500000214,"lvl":7,"x":25,"y":16,"l1x":15,"l1y":20,"l2x":25,"l2y":18,"l3x":14,"l3y":31,"l4x":14,"l4y":31,"l5x":25,"l5y":18},{"data":1000010,"id":500000215,"lvl":7,"x":25,"y":17,"l1x":22,"l1y":16,"l2x":25,"l2y":19,"l3x":17,"l3y":25,"l4x":17,"l4y":25,"l5x":25,"l5y":19},{"data":1000010,"id":500000216,"lvl":7,"x":26,"y":17,"l1x":14,"l1y":35,"l2x":25,"l2y":21,"l3x":17,"l3y":26,"l4x":17,"l4y":26,"l5x":25,"l5y":21},{"data":1000010,"id":500000217,"lvl":7,"x":27,"y":17,"l1x":33,"l1y":33,"l2x":25,"l2y":20,"l3x":17,"l3y":27,"l4x":17,"l4y":27,"l5x":25,"l5y":20},{"data":1000010,"id":500000218,"lvl":7,"x":28,"y":17,"l1x":18,"l1y":18,"l2x":16,"l2y":18,"l3x":17,"l3y":28,"l4x":17,"l4y":28,"l5x":16,"l5y":18},{"data":1000010,"id":500000219,"lvl":7,"x":29,"y":17,"l1x":28,"l1y":38,"l2x":16,"l2y":19,"l3x":17,"l3y":29,"l4x":17,"l4y":29,"l5x":16,"l5y":19},{"data":1000008,"id":500000220,"lvl":9,"x":18,"y":31,"l1x":19,"l1y":13,"l2x":36,"l2y":21,"l3x":31,"l3y":15,"l4x":31,"l4y":16,"l5x":36,"l5y":21},{"data":1000008,"id":500000221,"lvl":9,"x":31,"y":18,"l1x":33,"l1y":15,"l2x":19,"l2y":36,"l3x":10,"l3y":24,"l4x":10,"l4y":24,"l5x":19,"l5y":36},{"data":1000009,"id":500000222,"lvl":9,"x":24,"y":21,"l1x":26,"l1y":14,"l2x":25,"l2y":36,"l3x":12,"l3y":19,"l4x":12,"l4y":19,"l5x":25,"l5y":36},{"data":1000013,"id":500000225,"lvl":5,"x":33,"y":27,"l1x":15,"l1y":32,"l2x":15,"l2y":35,"l3x":19,"l3y":32,"l4x":19,"l4y":32,"l5x":15,"l5y":35},{"data":1000012,"id":500000226,"lvl":5,"x":30,"y":27,"l1x":26,"l1y":28,"l2x":19,"l2y":27,"l3x":18,"l3y":28,"l4x":18,"l4y":28,"l5x":19,"l5y":27},{"data":1000022,"id":500000227,"lvl":0,"x":34,"y":34,"l1x":30,"l1y":29,"l2x":26,"l2y":19,"l3x":21,"l3y":23,"l4x":21,"l4y":23,"l5x":26,"l5y":18},{"data":1000026,"id":500000228,"lvl":3,"x":13,"y":10,"unit_prod":{"unit_type":0},"l1x":34,"l1y":39,"l2x":28,"l2y":40,"l3x":11,"l3y":14,"l4x":11,"l4y":14,"l5x":28,"l5y":40},{"data":1000023,"id":500000229,"lvl":2,"x":22,"y":15,"res_time":0,"l1x":11,"l1y":34,"l2x":31,"l2y":41,"l3x":15,"l3y":37,"l4x":15,"l4y":37,"l5x":31,"l5y":41},{"data":1000028,"id":500000230,"lvl":3,"x":24,"y":24,"aim_angle":45,"aim_angle_draft":45,"aim_angle_war":45,"aim_angle_draft_war":45,"aim_angle2":135,"aim_angle_d2":135,"aim_angle3":45,"aim_angle_d3":45,"aim_angle4":225,"aim_angle_d4":225,"aim_angle5":45,"aim_angle_d5":45,"l1x":29,"l1y":23,"l2x":30,"l2y":17,"l3x":30,"l3y":24,"l4x":31,"l4y":24,"l5x":31,"l5y":16},{"data":1000003,"id":500000231,"lvl":10,"x":36,"y":27,"l1x":27,"l1y":33,"l2x":18,"l2y":32,"l3x":34,"l3y":29,"l4x":34,"l4y":29,"l5x":19,"l5y":32},{"data":1000005,"id":500000232,"lvl":10,"x":27,"y":36,"l1x":32,"l1y":18,"l2x":36,"l2y":27,"l3x":14,"l3y":26,"l4x":24,"l4y":29,"l5x":36,"l5y":27},{"data":1000009,"id":500000233,"lvl":9,"x":21,"y":24,"l1x":36,"l1y":29,"l2x":36,"l2y":31,"l3x":12,"l3y":32,"l4x":12,"l4y":32,"l5x":36,"l5y":31},{"data":1000011,"id":500000234,"lvl":5,"x":31,"y":36,"l1x":30,"l1y":13,"l2x":30,"l2y":23,"l3x":38,"l3y":33,"l4x":20,"l4y":19,"l5x":31,"l5y":22},{"data":1000013,"id":500000235,"lvl":5,"x":27,"y":33,"l1x":21,"l1y":28,"l2x":19,"l2y":9,"l3x":33,"l3y":35,"l4x":33,"l4y":35,"l5x":19,"l5y":9},{"data":1000012,"id":500000236,"lvl":5,"x":18,"y":18,"l1x":31,"l1y":22,"l2x":22,"l2y":18,"l3x":29,"l3y":27,"l4x":29,"l4y":27,"l5x":22,"l5y":18},{"data":1000010,"id":500000237,"lvl":7,"x":30,"y":17,"l1x":30,"l1y":37,"l2x":16,"l2y":20,"l3x":17,"l3y":30,"l4x":17,"l4y":30,"l5x":16,"l5y":20},{"data":1000010,"id":500000238,"lvl":7,"x":31,"y":17,"l1x":30,"l1y":33,"l2x":16,"l2y":22,"l3x":16,"l3y":31,"l4x":16,"l4y":31,"l5x":16,"l5y":22},{"data":1000010,"id":500000239,"lvl":7,"x":32,"y":17,"l1x":38,"l1y":33,"l2x":16,"l2y":21,"l3x":17,"l3y":31,"l4x":17,"l4y":31,"l5x":16,"l5y":21},{"data":1000010,"id":500000240,"lvl":7,"x":33,"y":17,"l1x":31,"l1y":32,"l2x":17,"l2y":22,"l3x":18,"l3y":31,"l4x":18,"l4y":31,"l5x":17,"l5y":22},{"data":1000010,"id":500000241,"lvl":7,"x":15,"y":25,"l1x":33,"l1y":26,"l2x":18,"l2y":22,"l3x":19,"l3y":31,"l4x":19,"l4y":31,"l5x":18,"l5y":22},{"data":1000010,"id":500000242,"lvl":7,"x":16,"y":25,"l1x":33,"l1y":27,"l2x":19,"l2y":22,"l3x":20,"l3y":31,"l4x":20,"l4y":31,"l5x":19,"l5y":22},{"data":1000010,"id":500000243,"lvl":7,"x":17,"y":25,"l1x":33,"l1y":28,"l2x":20,"l2y":22,"l3x":22,"l3y":31,"l4x":22,"l4y":31,"l5x":20,"l5y":22},{"data":1000010,"id":500000244,"lvl":7,"x":17,"y":26,"l1x":33,"l1y":29,"l2x":21,"l2y":22,"l3x":23,"l3y":28,"l4x":23,"l4y":28,"l5x":21,"l5y":22},{"data":1000010,"id":500000245,"lvl":7,"x":17,"y":27,"l1x":33,"l1y":30,"l2x":31,"l2y":22,"l3x":23,"l3y":29,"l4x":23,"l4y":29,"l5x":32,"l5y":21},{"data":1000010,"id":500000246,"lvl":7,"x":17,"y":28,"l1x":33,"l1y":31,"l2x":32,"l2y":22,"l3x":24,"l3y":28,"l4x":24,"l4y":28,"l5x":29,"l5y":21},{"data":1000010,"id":500000247,"lvl":7,"x":17,"y":29,"l1x":33,"l1y":32,"l2x":33,"l2y":22,"l3x":31,"l3y":26,"l4x":31,"l4y":26,"l5x":34,"l5y":21},{"data":1000010,"id":500000248,"lvl":7,"x":17,"y":30,"l1x":32,"l1y":32,"l2x":33,"l2y":23,"l3x":23,"l3y":31,"l4x":23,"l4y":31,"l5x":35,"l5y":22},{"data":1000010,"id":500000249,"lvl":7,"x":17,"y":31,"l1x":22,"l1y":36,"l2x":33,"l2y":24,"l3x":30,"l3y":26,"l4x":30,"l4y":26,"l5x":35,"l5y":23},{"data":1000010,"id":500000250,"lvl":7,"x":17,"y":32,"l1x":16,"l1y":27,"l2x":33,"l2y":25,"l3x":26,"l3y":32,"l4x":26,"l4y":32,"l5x":35,"l5y":24},{"data":1000010,"id":500000251,"lvl":7,"x":17,"y":33,"l1x":15,"l1y":27,"l2x":33,"l2y":26,"l3x":27,"l3y":32,"l4x":27,"l4y":32,"l5x":32,"l5y":26},{"data":1000010,"id":500000252,"lvl":7,"x":17,"y":34,"l1x":16,"l1y":19,"l2x":34,"l2y":27,"l3x":28,"l3y":32,"l4x":28,"l4y":32,"l5x":35,"l5y":25},{"data":1000010,"id":500000253,"lvl":7,"x":34,"y":17,"l1x":38,"l1y":32,"l2x":33,"l2y":27,"l3x":29,"l3y":32,"l4x":29,"l4y":32,"l5x":31,"l5y":26},{"data":1000010,"id":500000254,"lvl":7,"x":34,"y":18,"l1x":20,"l1y":28,"l2x":32,"l2y":27,"l3x":30,"l3y":32,"l4x":30,"l4y":32,"l5x":33,"l5y":26},{"data":1000010,"id":500000255,"lvl":7,"x":34,"y":20,"l1x":20,"l1y":29,"l2x":31,"l2y":27,"l3x":32,"l3y":32,"l4x":32,"l4y":32,"l5x":30,"l5y":26},{"data":1000010,"id":500000256,"lvl":7,"x":34,"y":19,"l1x":20,"l1y":30,"l2x":30,"l2y":27,"l3x":32,"l3y":31,"l4x":32,"l4y":31,"l5x":29,"l5y":26},{"data":1000010,"id":500000257,"lvl":7,"x":34,"y":21,"l1x":14,"l1y":34,"l2x":29,"l2y":27,"l3x":32,"l3y":30,"l4x":32,"l4y":30,"l5x":29,"l5y":27},{"data":1000010,"id":500000258,"lvl":7,"x":33,"y":21,"l1x":19,"l1y":27,"l2x":30,"l2y":22,"l3x":32,"l3y":29,"l4x":32,"l4y":29,"l5x":31,"l5y":21},{"data":1000010,"id":500000259,"lvl":7,"x":18,"y":34,"l1x":21,"l1y":36,"l2x":29,"l2y":22,"l3x":32,"l3y":28,"l4x":32,"l4y":28,"l5x":28,"l5y":21},{"data":1000010,"id":500000260,"lvl":7,"x":19,"y":34,"l1x":23,"l1y":39,"l2x":28,"l2y":22,"l3x":32,"l3y":27,"l4x":32,"l4y":27,"l5x":28,"l5y":22},{"data":1000010,"id":500000261,"lvl":7,"x":20,"y":34,"l1x":23,"l1y":38,"l2x":27,"l2y":22,"l3x":32,"l3y":26,"l4x":32,"l4y":26,"l5x":27,"l5y":22},{"data":1000010,"id":500000262,"lvl":7,"x":21,"y":34,"l1x":23,"l1y":37,"l2x":26,"l2y":22,"l3x":28,"l3y":25,"l4x":28,"l4y":25,"l5x":26,"l5y":22},{"data":1000010,"id":500000263,"lvl":7,"x":21,"y":33,"l1x":23,"l1y":36,"l2x":25,"l2y":22,"l3x":31,"l3y":32,"l4x":31,"l4y":32,"l5x":25,"l5y":22},{"data":1000010,"id":500000264,"lvl":7,"x":21,"y":32,"l1x":23,"l1y":34,"l2x":24,"l2y":22,"l3x":29,"l3y":26,"l4x":29,"l4y":26,"l5x":24,"l5y":22},{"data":1000010,"id":500000265,"lvl":7,"x":32,"y":21,"l1x":23,"l1y":35,"l2x":23,"l2y":22,"l3x":28,"l3y":26,"l4x":28,"l4y":26,"l5x":23,"l5y":22},{"data":1000010,"id":500000266,"lvl":7,"x":31,"y":21,"l1x":17,"l1y":35,"l2x":22,"l2y":22,"l3x":23,"l3y":30,"l4x":23,"l4y":30,"l5x":22,"l5y":22},{"data":1000010,"id":500000267,"lvl":7,"x":30,"y":21,"l1x":17,"l1y":36,"l2x":22,"l2y":23,"l3x":27,"l3y":24,"l4x":27,"l4y":24,"l5x":22,"l5y":23},{"data":1000010,"id":500000268,"lvl":7,"x":29,"y":21,"l1x":18,"l1y":36,"l2x":22,"l2y":24,"l3x":26,"l3y":24,"l4x":26,"l4y":24,"l5x":22,"l5y":24},{"data":1000010,"id":500000269,"lvl":7,"x":28,"y":21,"l1x":18,"l1y":13,"l2x":22,"l2y":25,"l3x":25,"l3y":24,"l4x":25,"l4y":24,"l5x":22,"l5y":25},{"data":1000010,"id":500000270,"lvl":7,"x":21,"y":31,"l1x":18,"l1y":14,"l2x":22,"l2y":26,"l3x":24,"l3y":24,"l4x":24,"l4y":24,"l5x":22,"l5y":26},{"data":1000010,"id":500000271,"lvl":7,"x":21,"y":30,"l1x":18,"l1y":15,"l2x":28,"l2y":27,"l3x":24,"l3y":25,"l4x":24,"l4y":25,"l5x":28,"l5y":27},{"data":1000010,"id":500000272,"lvl":7,"x":21,"y":28,"l1x":18,"l1y":16,"l2x":27,"l2y":27,"l3x":24,"l3y":26,"l4x":24,"l4y":26,"l5x":27,"l5y":27},{"data":1000010,"id":500000273,"lvl":7,"x":27,"y":21,"l1x":17,"l1y":19,"l2x":26,"l2y":27,"l3x":24,"l3y":27,"l4x":24,"l4y":27,"l5x":26,"l5y":27},{"data":1000010,"id":500000274,"lvl":7,"x":27,"y":22,"l1x":18,"l1y":17,"l2x":25,"l2y":27,"l3x":23,"l3y":32,"l4x":23,"l4y":32,"l5x":25,"l5y":27},{"data":1000010,"id":500000275,"lvl":7,"x":27,"y":23,"l1x":19,"l1y":36,"l2x":24,"l2y":27,"l3x":24,"l3y":32,"l4x":24,"l4y":32,"l5x":24,"l5y":27},{"data":1000010,"id":500000276,"lvl":7,"x":27,"y":24,"l1x":30,"l1y":38,"l2x":23,"l2y":27,"l3x":39,"l3y":28,"l4x":39,"l4y":28,"l5x":23,"l5y":27},{"data":1000010,"id":500000277,"lvl":7,"x":21,"y":29,"l1x":29,"l1y":32,"l2x":39,"l2y":26,"l3x":21,"l3y":31,"l4x":21,"l4y":31,"l5x":39,"l5y":26},{"data":1000010,"id":500000278,"lvl":7,"x":22,"y":27,"l1x":28,"l1y":32,"l2x":40,"l2y":23,"l3x":29,"l3y":37,"l4x":29,"l4y":37,"l5x":40,"l5y":23},{"data":1000010,"id":500000279,"lvl":7,"x":26,"y":24,"l1x":27,"l1y":32,"l2x":39,"l2y":32,"l3x":36,"l3y":37,"l4x":36,"l4y":37,"l5x":39,"l5y":32},{"data":1000010,"id":500000280,"lvl":7,"x":26,"y":25,"l1x":26,"l1y":32,"l2x":22,"l2y":27,"l3x":40,"l3y":24,"l4x":40,"l4y":24,"l5x":22,"l5y":27},{"data":1000010,"id":500000281,"lvl":7,"x":24,"y":27,"l1x":25,"l1y":32,"l2x":24,"l2y":39,"l3x":28,"l3y":24,"l4x":28,"l4y":24,"l5x":24,"l5y":39},{"data":1000010,"id":500000282,"lvl":7,"x":23,"y":27,"l1x":24,"l1y":32,"l2x":31,"l2y":11,"l3x":25,"l3y":11,"l4x":25,"l4y":11,"l5x":31,"l5y":11},{"data":1000010,"id":500000283,"lvl":7,"x":26,"y":26,"l1x":23,"l1y":32,"l2x":40,"l2y":26,"l3x":17,"l3y":17,"l4x":17,"l4y":17,"l5x":40,"l5y":26},{"data":1000010,"id":500000284,"lvl":7,"x":25,"y":26,"l1x":21,"l1y":33,"l2x":40,"l2y":24,"l3x":13,"l3y":22,"l4x":13,"l4y":22,"l5x":40,"l5y":24},{"data":1000010,"id":500000285,"lvl":7,"x":24,"y":26,"l1x":20,"l1y":33,"l2x":40,"l2y":25,"l3x":15,"l3y":31,"l4x":15,"l4y":31,"l5x":40,"l5y":25},{"data":1000010,"id":500000286,"lvl":7,"x":21,"y":27,"l1x":20,"l1y":32,"l2x":24,"l2y":38,"l3x":25,"l3y":32,"l4x":25,"l4y":32,"l5x":24,"l5y":38},{"data":1000026,"id":500000288,"lvl":3,"x":10,"y":13,"unit_prod":{"unit_type":0},"l1x":40,"l1y":30,"l2x":38,"l2y":39,"l3x":38,"l3y":36,"l4x":38,"l4y":36,"l5x":38,"l5y":39},{"data":1000023,"id":500000289,"lvl":2,"x":15,"y":22,"res_time":0,"l1x":11,"l1y":37,"l2x":23,"l2y":10,"l3x":12,"l3y":35,"l4x":12,"l4y":35,"l5x":22,"l5y":11},{"data":1000029,"id":500000290,"lvl":1,"x":10,"y":10,"units":[[26000010,1]],"unit_prod":{"unit_type":1},"l1x":29,"l1y":9,"l2x":6,"l2y":23,"l3x":19,"l3y":40,"l4x":19,"l4y":40,"l5x":6,"l5y":23}],"obstacles":[{"data":8000004,"id":503000000,"x":47,"y":10,"loot_multiply_ver":2},{"data":8000007,"id":503000001,"x":1,"y":13,"loot_multiply_ver":2},{"data":8000000,"id":503000002,"x":14,"y":0,"loot_multiply_ver":2},{"data":8000008,"id":503000003,"x":0,"y":34,"loot_multiply_ver":2},{"data":8000006,"id":503000004,"x":29,"y":0,"loot_multiply_ver":2},{"data":8000007,"id":503000005,"x":19,"y":0,"loot_multiply_ver":2},{"data":8000013,"id":503000006,"x":29,"y":47,"loot_multiply_ver":2},{"data":8000007,"id":503000007,"x":7,"y":8,"loot_multiply_ver":2},{"data":8000010,"id":503000008,"x":0,"y":30,"loot_multiply_ver":2},{"data":8000000,"id":503000009,"x":25,"y":0,"loot_multiply_ver":2},{"data":8000004,"id":503000010,"x":24,"y":48,"loot_multiply_ver":2},{"data":8000000,"id":503000011,"x":8,"y":48,"loot_multiply_ver":2},{"data":8000010,"id":503000012,"x":8,"y":42,"loot_multiply_ver":2},{"data":8000000,"id":503000013,"x":1,"y":9,"loot_multiply_ver":2},{"data":8000000,"id":503000014,"x":2,"y":40,"loot_multiply_ver":2},{"data":8000004,"id":503000015,"x":35,"y":0,"loot_multiply_ver":2},{"data":8000007,"id":503000016,"x":1,"y":37,"loot_multiply_ver":2},{"data":8000007,"id":503000017,"x":15,"y":47,"loot_multiply_ver":2},{"data":8000007,"id":503000018,"x":1,"y":19,"loot_multiply_ver":2},{"data":8000000,"id":503000019,"x":0,"y":26,"loot_multiply_ver":2},{"data":8000013,"id":503000020,"x":18,"y":47,"loot_multiply_ver":2},{"data":8000007,"id":503000021,"x":48,"y":17,"loot_multiply_ver":2},{"data":8000010,"id":503000022,"x":48,"y":31,"loot_multiply_ver":2},{"data":8000004,"id":503000023,"x":11,"y":48,"loot_multiply_ver":2},{"data":8000000,"id":503000024,"x":48,"y":25,"loot_multiply_ver":2},{"data":8000008,"id":503000025,"x":48,"y":7,"loot_multiply_ver":2},{"data":8000004,"id":503000026,"x":46,"y":0,"loot_multiply_ver":2},{"data":8000007,"id":503000027,"x":48,"y":34,"loot_multiply_ver":2},{"data":8000010,"id":503000028,"x":1,"y":2,"loot_multiply_ver":2},{"data":8000004,"id":503000029,"x":0,"y":47,"loot_multiply_ver":2},{"data":8000000,"id":503000030,"x":48,"y":38,"loot_multiply_ver":2},{"data":8000010,"id":503000031,"x":1,"y":16,"loot_multiply_ver":2},{"data":8000000,"id":503000032,"x":1,"y":23,"loot_multiply_ver":2},{"data":8000006,"id":503000033,"x":39,"y":48,"loot_multiply_ver":2},{"data":8000036,"id":503000034,"x":48,"y":20,"loot_multiply_ver":2},{"data":8000006,"id":503000035,"x":47,"y":41,"loot_multiply_ver":2},{"data":8000000,"id":503000036,"x":47,"y":28,"loot_multiply_ver":2},{"data":8000007,"id":503000037,"x":33,"y":48,"loot_multiply_ver":2},{"data":8000005,"id":503000038,"x":7,"y":1,"loot_multiply_ver":2},{"data":8000036,"id":503000039,"x":41,"y":10,"loot_multiply_ver":2},{"data":8000036,"id":503000040,"x":7,"y":39,"loot_multiply_ver":1},{"data":8000036,"id":503000041,"x":48,"y":13,"loot_multiply_ver":2},{"data":8000036,"id":503000042,"x":22,"y":2,"loot_multiply_ver":2},{"data":8000036,"id":503000043,"x":42,"y":6,"loot_multiply_ver":2},{"data":8000036,"id":503000044,"x":8,"y":33,"loot_multiply_ver":1},{"data":8000037,"id":503000078,"x":36,"y":48,"loot_multiply_ver":2},{"data":8000037,"id":503000119,"x":44,"y":14,"loot_multiply_ver":2},{"data":8000037,"id":503000160,"x":36,"y":3,"loot_multiply_ver":2},{"data":8000037,"id":503000201,"x":3,"y":5,"loot_multiply_ver":2},{"data":8000037,"id":503000242,"x":17,"y":41,"loot_multiply_ver":2},{"data":8000037,"id":503000243,"x":42,"y":21,"loot_multiply_ver":2},{"data":8000037,"id":503000284,"x":40,"y":1,"loot_multiply_ver":2},{"data":8000037,"id":503000305,"x":32,"y":4,"loot_multiply_ver":2},{"data":8000009,"id":503000306,"x":40,"y":22,"loot_multiply_ver":1},{"data":8000009,"id":503000307,"x":44,"y":41,"loot_multiply_ver":1},{"data":8000009,"id":503000308,"x":43,"y":28,"loot_multiply_ver":1},{"data":8000009,"id":503000309,"x":43,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000310,"x":44,"y":24,"loot_multiply_ver":1},{"data":8000023,"id":503000311,"x":43,"y":25,"loot_multiply_ver":1},{"data":8000023,"id":503000312,"x":44,"y":25,"loot_multiply_ver":1},{"data":8000023,"id":503000313,"x":43,"y":24,"loot_multiply_ver":1},{"data":8000023,"id":503000314,"x":42,"y":25,"loot_multiply_ver":1},{"data":8000023,"id":503000315,"x":44,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000316,"x":33,"y":45,"loot_multiply_ver":1},{"data":8000009,"id":503000317,"x":34,"y":44,"loot_multiply_ver":1},{"data":8000009,"id":503000318,"x":38,"y":33,"loot_multiply_ver":1},{"data":8000009,"id":503000319,"x":41,"y":25,"loot_multiply_ver":1},{"data":8000009,"id":503000320,"x":42,"y":24,"loot_multiply_ver":1},{"data":8000009,"id":503000321,"x":42,"y":29,"loot_multiply_ver":1},{"data":8000009,"id":503000322,"x":41,"y":29,"loot_multiply_ver":1},{"data":8000009,"id":503000323,"x":42,"y":28,"loot_multiply_ver":1},{"data":8000009,"id":503000324,"x":43,"y":29,"loot_multiply_ver":1},{"data":8000009,"id":503000325,"x":32,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000326,"x":31,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000327,"x":30,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000328,"x":33,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000329,"x":29,"y":42,"loot_multiply_ver":1},{"data":8000009,"id":503000330,"x":28,"y":42,"loot_multiply_ver":1},{"data":8000009,"id":503000331,"x":29,"y":43,"loot_multiply_ver":1},{"data":8000009,"id":503000332,"x":26,"y":37,"loot_multiply_ver":1},{"data":8000009,"id":503000333,"x":20,"y":39,"loot_multiply_ver":1},{"data":8000009,"id":503000334,"x":20,"y":38,"loot_multiply_ver":1},{"data":8000009,"id":503000335,"x":27,"y":25,"loot_multiply_ver":1},{"data":8000009,"id":503000336,"x":26,"y":34,"loot_multiply_ver":1},{"data":8000009,"id":503000337,"x":32,"y":22,"loot_multiply_ver":1},{"data":8000009,"id":503000338,"x":35,"y":20,"loot_multiply_ver":1},{"data":8000009,"id":503000339,"x":26,"y":35,"loot_multiply_ver":1},{"data":8000009,"id":503000340,"x":36,"y":20,"loot_multiply_ver":1},{"data":8000009,"id":503000341,"x":28,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000342,"x":26,"y":36,"loot_multiply_ver":1},{"data":8000009,"id":503000343,"x":28,"y":25,"loot_multiply_ver":1},{"data":8000009,"id":503000344,"x":29,"y":26,"loot_multiply_ver":1},{"data":8000009,"id":503000345,"x":20,"y":35,"loot_multiply_ver":1},{"data":8000038,"id":503000346,"x":45,"y":3,"defg":117171,"defe":104847,"defde":307,"loot_multiply_ver":1}],"decos":[],"respawnVars":{"secondsFromLastRespawn":231386,"respawnSeed":1914935487,"obstacleClearCounter":5,"time_to_gembox_drop":196966,"time_in_gembox_period":228665},"cooldowns":[],"newShopBuildings":[4,0,6,3,6,3,4,1,5,5,225,3,3,4,1,5,0,0,0,3,1,0,1,2,1,0,2,0,1,1,0,0],"newShopTraps":[6,6,3,0,0,4,2,0,2],"newShopDecos":[1,4,0,1,1,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"last_league_rank":9,"last_alliance_level":7,"last_league_shuffle":1,"last_season_seen":0,"last_news_seen":221,"troop_req_msg":"max hogs and max poison","war_req_msg":"2 witches, 1 barb","war_tutorials_seen":0,"war_base":true,"account_flags":14,"bool_layout_edit_shown_erase":true}
\ No newline at end of file
diff --git a/src/main/resources/tile.png b/src/main/resources/tile.png
new file mode 100644
index 0000000..4f667c8
Binary files /dev/null and b/src/main/resources/tile.png differ
diff --git a/src/main/resources/tile_center.jpg b/src/main/resources/tile_center.jpg
new file mode 100644
index 0000000..e099807
Binary files /dev/null and b/src/main/resources/tile_center.jpg differ
diff --git a/src/main/resources/tiles.jpg b/src/main/resources/tiles.jpg
new file mode 100644
index 0000000..37dab96
Binary files /dev/null and b/src/main/resources/tiles.jpg differ
diff --git a/src/main/resources/web/base-analysis.mustache b/src/main/resources/web/base-analysis.mustache
new file mode 100644
index 0000000..1d9797d
--- /dev/null
+++ b/src/main/resources/web/base-analysis.mustache
@@ -0,0 +1,115 @@
+{{
+ {{/styles}}
+
+ {{$navbar}}
+ {{#times}}
+
+ {{/content}}
+{{/base}}
\ No newline at end of file
diff --git a/src/main/resources/web/error.mustache b/src/main/resources/web/error.mustache
new file mode 100644
index 0000000..6b9bb0a
--- /dev/null
+++ b/src/main/resources/web/error.mustache
@@ -0,0 +1,9 @@
+{{{{message}}
+ {{/content}}
+{{/base}}
\ No newline at end of file
diff --git a/src/main/resources/web/index.html b/src/main/resources/web/index.html
new file mode 100644
index 0000000..5d8289b
--- /dev/null
+++ b/src/main/resources/web/index.html
@@ -0,0 +1,16 @@
+
+
+
+ CoC War Base Analyser
+
+
+
+
+
+
CoC War Base Analyser
+
Nothing to see here... Ask for the link to your clan page from the developer
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/web/war-bases.mustache b/src/main/resources/web/war-bases.mustache
new file mode 100644
index 0000000..b43b78f
--- /dev/null
+++ b/src/main/resources/web/war-bases.mustache
@@ -0,0 +1,89 @@
+{{
+ .glyphicon-remove-sign {
+ color: #ff5555;
+ }
+ button.collapsed .hide-instruction {
+ display:none;
+ }
+
+ {{/styles}}
+
+ {{$heading}}{{name}} Bulk War Bases Analysis{{/heading}}
+
+ {{$content}}
+
").append(" " + result.report.connectionTime + " | " + result.report.analysisTime));
+ };
+
+ var render = function() {
+ // Loading
+ if (loading.length == 0) {
+ loadingContainer.hide();
+ } else {
+ loadingContainer.show();
+ loadingContainer.find(".loading-names").html(_.pluck(loading, 'ign').join(", "));
+ if (toLoad.length > 0) {
+ loadingContainer.find(".queued-button").show();
+ loadingContainer.find(".queued-count").html(toLoad.length);
+ loadingContainer.find(".queued-names").html(_.pluck(toLoad, 'ign').join(', '));
+ } else {
+ loadingContainer.find(".queued-button").hide();
+ }
+ }
+
+ // Table Results
+ _.each(
+ results,
+ function(result) {
+ var rowId = "player-row-" + result.player.id;
+ if ($("#" + rowId).size() > 0) {
+ return;
+ }
+
+ if (result.report == null) {
+ renderProblem(result);
+ return;
+ }
+
+ var townHallContainer = ensureTownHallContainerRendered(result);
+
+ var anyError = _.some(result.report.resultSummaries, function(summary) { return !summary.success; });
+ createResultRow(rowId, result, anyError)
+ .appendTo(townHallContainer.find("tbody"))
+ .slideDown();
+
+ if (anyError) {
+ townHallContainer.find(".plain-text-summary")
+ .find("textarea")
+ .append(result.player.ign + ": " + _.pluck(result.report.resultSummaries, 'shortName').join(', ') + "\n");
+ }
+ }
+ );
+ };
+
+ loadNext();
+});
\ No newline at end of file
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/AirSnipedDefenseRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/AirSnipedDefenseRuleSpec.scala
new file mode 100644
index 0000000..faa52b5
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/AirSnipedDefenseRuleSpec.scala
@@ -0,0 +1,39 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.{AirDefense, AirSweeper, Cannon}
+import org.danielholmes.coc.baseanalyser.model.trash.BuilderHut
+import org.danielholmes.coc.baseanalyser.model.troops.{Minion, MinionAttackPosition}
+import org.scalatest._
+
+class AirSnipedDefenseRuleSpec extends FlatSpec with Matchers {
+ val rule = new AirSnipedDefenseRule
+
+ "AirSnipedDefenseRule" should "return no violation for base without defenses" in {
+ rule.analyse(Village.empty).success should be (true)
+ }
+
+ it should "return sniped ground when no air def" in {
+ val cannon = Cannon(1, Tile.MapOrigin)
+ val result = rule.analyse(Village(Set(cannon))).asInstanceOf[AirSnipedDefenseRuleResult]
+ result.success should be (false)
+ result.snipedDefenses.size should be (1)
+ result.snipedDefenses.map(_.targeting) should be (Set(cannon))
+ }
+
+ it should "return no sniped ground when air def" in {
+ val airDef = AirDefense(1, Tile(6, 6))
+ rule.analyse(Village(Set(Cannon(1, Tile.MapOrigin), airDef))) should be (AirSnipedDefenseRuleResult(
+ Set.empty,
+ Set(airDef)
+ ))
+ }
+
+ it should "return no sniped ground when air sweeper" in {
+ val airSweeper = AirSweeper(1, Tile(10, 10), Angle.Quarter * 7)
+ rule.analyse(Village(Set(BuilderHut(Tile(4, 4)), airSweeper))) should be (AirSnipedDefenseRuleResult(
+ Set.empty,
+ Set(airSweeper)
+ ))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/ArcherAnchorRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/ArcherAnchorRuleSpec.scala
new file mode 100644
index 0000000..2caae44
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/ArcherAnchorRuleSpec.scala
@@ -0,0 +1,55 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.{AirDefense, ArcherTower, Cannon}
+import org.danielholmes.coc.baseanalyser.model.trash.BuilderHut
+import org.scalatest._
+
+class ArcherAnchorRuleSpec extends FlatSpec with Matchers {
+ val rule = new ArcherAnchorRule
+
+ "ArcherAnchorRule" should "return no violation for base without element" in {
+ rule.analyse(Village.empty).success should be (true)
+ }
+
+ it should "return violation for base with element and no defenses" in {
+ rule.analyse(Village(Set(BuilderHut(Tile.MapOrigin)))).success should be (false)
+ }
+
+ it should "return success for base with element covered by ground shooting" in {
+ val village = Village(
+ Set(
+ BuilderHut(Tile(5, 5)),
+ ArcherTower(1, Tile(7, 7))
+ )
+ )
+ rule.analyse(village).success should be (true)
+ }
+
+ it should "return fail for base with element covered by air shooting" in {
+ val village = Village(
+ Set(
+ BuilderHut(Tile(5, 5)),
+ AirDefense(1, Tile(7, 7))
+ )
+ )
+ rule.analyse(village).success should be (false)
+ }
+
+ it should "return success for base with wall not covered by ground shooting" in {
+ val village = Village(
+ Set(
+ Wall(1, Tile(5, 5)),
+ ArcherTower(1, Tile(30, 30))
+ )
+ )
+ rule.analyse(village).success should be (true)
+ }
+
+ it should "return defenses that can hit archer" in {
+ val at = ArcherTower(1, Tile(30, 30))
+ val cannon = Cannon(1, Tile(36, 36))
+ val village = Village(Set(at, AirDefense(1, Tile(33, 33)), cannon))
+ rule.analyse(village).asInstanceOf[ArcherAnchorRuleResult].aimingDefenses should be (Set(at, cannon))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/BKSwappableRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/BKSwappableRuleSpec.scala
new file mode 100644
index 0000000..207834c
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/BKSwappableRuleSpec.scala
@@ -0,0 +1,97 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.heroes.BarbarianKingAltar
+import org.danielholmes.coc.baseanalyser.model.trash.{ArmyCamp, Barrack}
+import org.danielholmes.coc.baseanalyser.stringdisplay.StringDisplayer
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalatest._
+
+class BKSwappableRuleSpec extends FlatSpec with Matchers {
+ val rule = new BKSwappableRule
+
+ "BKSwappableRule" should "return success for empty village" in {
+ rule.analyse(Village.empty) should be (BKSwappableRuleResult(Set.empty))
+ }
+
+ it should "return fail for BK on his own" in {
+ rule.analyse(Village(Set(BarbarianKingAltar(1, Tile(1, 1))))).success should be (false)
+ }
+
+ it should "return success for deep walled BK" in {
+ val elements =
+ ElementsBuilder.elementFence(Tile(9, 9), 27, 27) ++
+ ElementsBuilder.repeatX(Tile(10, 10), 5, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(10, 15), 5, 5, ArmyCamp(1, _)) ++
+ Set[Element](
+ ArmyCamp(1, Tile(10, 20)), ArmyCamp(1, Tile(15, 20)), BarbarianKingAltar(1, Tile(21, 21)), ArmyCamp(1, Tile(25, 20)), ArmyCamp(1, Tile(30, 20))
+ ) ++
+ ElementsBuilder.repeatX(Tile(10, 25), 5, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(10, 30), 5, 5, ArmyCamp(1, _))
+ rule.analyse(Village(elements)).success should be (true)
+ }
+
+ it should "return fail for exposed BK" in {
+ val elements =
+ ElementsBuilder.elementFence(Tile(9, 9), 27, 27) ++
+ ElementsBuilder.repeatX(Tile(10, 10), 5, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(10, 15), 5, 5, ArmyCamp(1, _)) ++
+ Set[Element](
+ ArmyCamp(1, Tile(10, 20)), ArmyCamp(1, Tile(15, 20)), ArmyCamp(1, Tile(20, 20)), Barrack(1, Tile(26, 21)),
+ BarbarianKingAltar(1, Tile(29, 21)), Barrack(1, Tile(32, 21))
+ ) ++
+ ElementsBuilder.repeatX(Tile(10, 25), 5, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(10, 30), 5, 5, ArmyCamp(1, _))
+
+ val result = rule.analyse(Village(elements))
+ result.success should be (false)
+ }
+
+ it should "return success for very deep non-walled BK" in {
+ val elements =
+ ElementsBuilder.repeatX(Tile(5, 5), 7, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(5, 10), 7, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(5, 15), 7, 5, ArmyCamp(1, _)) ++
+ Set[Element](
+ ArmyCamp(1, Tile(5, 20)), ArmyCamp(1, Tile(10, 20)), ArmyCamp(1, Tile(15, 20)),
+ BarbarianKingAltar(1, Tile(21, 21)),
+ ArmyCamp(1, Tile(25, 20)), ArmyCamp(1, Tile(30, 20)), ArmyCamp(1, Tile(35, 20))
+ ) ++
+ ElementsBuilder.repeatX(Tile(5, 25), 7, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(5, 30), 7, 5, ArmyCamp(1, _)) ++
+ ElementsBuilder.repeatX(Tile(5, 35), 7, 5, ArmyCamp(1, _))
+ val result = rule.analyse(Village(elements))
+ result.success should be (true)
+ }
+
+ it should "return success for only slightly exposed BK" in {
+ val elements =
+ ElementsBuilder.elementFence(Tile(4, 4), 15, 15) ++
+ ElementsBuilder.repeatX(Tile(6, 6), 3, 4, Barrack(1, _)) ++
+ Set[Element](
+ Barrack(1, Tile(6, 10)),
+ BarbarianKingAltar(1, Tile(10, 10)),
+ Barrack(1, Tile(14, 10))
+ ) ++
+ ElementsBuilder.repeatX(Tile(6, 14), 3, 4, Barrack(1, _))
+ val result = rule.analyse(Village(elements))
+ result.success should be (true)
+ }
+
+ it should "return correct exposed tiles for offset BK" in {
+ val elements =
+ ElementsBuilder.elementFence(Tile(4, 4), 14, 15) ++
+ ElementsBuilder.repeatX(Tile(6, 6), 3, 4, Barrack(1, _)) ++
+ Set[Element](
+ Barrack(1, Tile(6, 10)),
+ BarbarianKingAltar(1, Tile(10, 10)),
+ Barrack(1, Tile(14, 10))
+ ) ++
+ ElementsBuilder.repeatX(Tile(6, 14), 3, 4, Barrack(1, _))
+
+ val result = rule.analyse(Village(elements))
+ result.success should be (false)
+ result.asInstanceOf[BKSwappableRuleResult].exposedTiles should contain (Tile(18, 11))
+ result.asInstanceOf[BKSwappableRuleResult].exposedTiles should not contain (Tile(3, 11))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/EnoughPossibleTrapLocationsRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/EnoughPossibleTrapLocationsRuleSpec.scala
new file mode 100644
index 0000000..042d211
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/EnoughPossibleTrapLocationsRuleSpec.scala
@@ -0,0 +1,95 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.HiddenTesla
+import org.danielholmes.coc.baseanalyser.model.traps.GiantBomb
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalactic.anyvals.{PosZDouble, PosZInt}
+import org.scalatest._
+
+class EnoughPossibleTrapLocationsRuleSpec extends FlatSpec with Matchers {
+ val rule = new EnoughPossibleTrapLocationsRule
+
+ "EnoughPossibleTrapLocationsRule" should "return violation for base without wall compartments" in {
+ rule.analyse(Village.empty).success should be (false)
+ }
+
+ it should "return no violation for base with large wall compartments" in {
+ val walls = ElementsBuilder.elementFence(Tile.MapOrigin, 40, 40)
+ rule.analyse(Village(walls)).success should be (true)
+ }
+
+ it should "return violation if only 20 possibilities" in {
+ val walls = Range.inclusive(1, 4)
+ .flatMap(row =>
+ Range.inclusive(1, 5)
+ .map(col => Tile(PosZInt.from(col * 4).get, PosZInt.from(row * 4).get))
+ .flatMap(ElementsBuilder.elementFence(_, 4, 4))
+ )
+ .toSet
+ val village = Village(walls)
+ assert(village.possibleInternalLargeTraps.size == 20)
+
+ rule.analyse(village).success should be (false)
+ }
+
+ it should "return no violation if 24 possibilities" in {
+ val elements = create24CompartmentsHolding(t => None)
+ val village = Village(elements)
+ assert(village.possibleInternalLargeTraps.size == 24)
+
+ rule.analyse(village).success should be (true)
+ }
+
+ it should "return no violation if 24 possibilities with real Teslas" in {
+ val elements = create24CompartmentsHolding(t => Some(HiddenTesla(1, t)))
+ val village = Village(elements)
+ assert(village.possibleInternalLargeTraps.size == 24)
+
+ rule.analyse(village).success should be (true)
+ }
+
+ it should "return no violation if 24 possibilities with real Giant Bomb" in {
+ val elements = create24CompartmentsHolding(t => Some(GiantBomb(1, t)))
+ val village = Village(elements)
+ assert(village.possibleInternalLargeTraps.size == 24)
+
+ rule.analyse(village).success should be (true)
+ }
+
+ it should "return violation if 24 possibilities with Decoration" in {
+ val elements = create24CompartmentsHolding(t => Some(Decoration(t)))
+ val village = Village(elements)
+ assert(village.possibleInternalLargeTraps.isEmpty)
+
+ rule.analyse(village).success should be (false)
+ }
+
+ it should "allocate an equal score for 2 separate 2x2s as 1 3x3" in {
+ val villageSeparate2x2 = Village(ElementsBuilder.elementFence(Tile(10, 10), 4, 4) ++ ElementsBuilder.wallFence(Tile(15, 15), 4, 4))
+ val village3x3 = Village(ElementsBuilder.elementFence(Tile(10, 10), 5, 5))
+
+ rule.calculateScore(village3x3) should be (rule.calculateScore(villageSeparate2x2))
+ }
+
+ it should "give a higher score for 2 separate 2x2s then 1 3x2" in {
+ val villageSeparate2x2 = Village(ElementsBuilder.elementFence(Tile(10, 10), 4, 4) ++ ElementsBuilder.wallFence(Tile(15, 15), 4, 4))
+ val village3x2 = Village(ElementsBuilder.elementFence(Tile(10, 10), 5, 4))
+
+ rule.calculateScore(village3x2) should be < rule.calculateScore(villageSeparate2x2)
+ }
+
+ it should "single trap should score 1.0" in {
+ rule.calculateScore(Village(ElementsBuilder.elementFence(Tile(10, 10), 4, 4))) should be (PosZDouble(1.0))
+ }
+
+ private def create24CompartmentsHolding(factory: Tile => Option[Element]): Set[Element] = {
+ Range.inclusive(1, 4)
+ .flatMap(row =>
+ Range.inclusive(1, 6)
+ .map(col => Tile(PosZInt.from(col * 4).get, PosZInt.from(row * 4).get))
+ .flatMap(t => ElementsBuilder.elementFence(t, 4, 4) ++ factory.apply(t.offset(1, 1)))
+ )
+ .toSet
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/HighHPUnderAirDefRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/HighHPUnderAirDefRuleSpec.scala
new file mode 100644
index 0000000..03372f8
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/HighHPUnderAirDefRuleSpec.scala
@@ -0,0 +1,43 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.AirDefense
+import org.danielholmes.coc.baseanalyser.model.trash.GoldStorage
+import org.scalatest._
+
+class HighHPUnderAirDefRuleSpec extends FlatSpec with Matchers {
+ val rule = new HighHPUnderAirDefRule
+
+ "HighHPUnderAirDefRule" should "return no violation for base without air def" in {
+ rule.analyse(Village.empty).success should be (true)
+ }
+
+ it should "return pass for base with air def and no storages" in {
+ rule.analyse(Village(Set(AirDefense(1, Tile.MapOrigin)))).success should be (true)
+ }
+
+ it should "return fail for base with air def and storage outside" in {
+ val storage = GoldStorage(1, Tile(30, 30))
+ rule.analyse(Village(Set(AirDefense(1, Tile.MapOrigin), storage))) should be (HighHPUnderAirDefRuleResult(Set(storage), Set.empty))
+ }
+
+ it should "return fail for base with air def cutting storage" in {
+ val storageOutside = GoldStorage(1, Tile(7, 11))
+ val storageInside = GoldStorage(1, Tile(7, 5))
+ rule.analyse(Village(Set(AirDefense(1, Tile.MapOrigin), storageOutside, storageInside))) should be
+ HighHPUnderAirDefRuleResult(Set(storageOutside), Set(storageInside))
+ }
+
+ it should "return true for base with air def just covering storage" in {
+ rule.analyse(Village(Set(AirDefense(1, Tile.MapOrigin), GoldStorage(1, Tile(5, 7))))).success should be (true)
+ }
+
+ it should "return success for base with storage requiring 2 air defs for full coverage" in {
+ val village = Village(Set(
+ AirDefense(1, Tile(5, 5)),
+ GoldStorage(1, Tile(14, 5)),
+ AirDefense(1, Tile(23, 5))
+ ))
+ rule.analyse(village).success should be (true)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/HogCCLureRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/HogCCLureRuleSpec.scala
new file mode 100644
index 0000000..fb9c5dc
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/HogCCLureRuleSpec.scala
@@ -0,0 +1,31 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.special.ClanCastle
+import org.danielholmes.coc.baseanalyser.model.trash.Barrack
+import org.danielholmes.coc.baseanalyser.model.troops.HogRider
+import org.scalatest._
+
+class HogCCLureRuleSpec extends FlatSpec with Matchers {
+ val rule = new HogCCLureRule
+
+ "HogCCLureRule" should "return no violation for base without CC" in {
+ rule.analyse(Village.empty).success should be (true)
+ }
+
+ it should "return violation for base with CC and no blocking" in {
+ rule.analyse(Village(Set(ClanCastle(1, Tile.MapOrigin)))).success should be (false)
+ }
+
+ it should "return success for base with paths blocked off" in {
+ val ccPosition = Tile(20, 20)
+ val cc = ClanCastle(1, ccPosition)
+ val village = Village(
+ Set(cc) ++
+ Tile(2, 2).matrixOfTilesTo(Tile(41, 41), 3)
+ .filter(_ != ccPosition)
+ .map(Barrack(1, _))
+ )
+ rule.analyse(village).success should be (true)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/MinimumCompartmentsRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/MinimumCompartmentsRuleSpec.scala
new file mode 100644
index 0000000..235caa3
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/MinimumCompartmentsRuleSpec.scala
@@ -0,0 +1,37 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.trash.BuilderHut
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalactic.anyvals.{PosInt, PosZInt}
+import org.scalatest._
+
+class MinimumCompartmentsRuleSpec extends FlatSpec with Matchers {
+ val rule = new MinimumCompartmentsRule
+
+ "MinimumCompartmentsRule" should "return violation for empty village" in {
+ rule.analyse(Village.empty) should be (MinimumCompartmentsRuleResult(8, Set.empty))
+ }
+
+ it should "return violation for 1 compartment" in {
+ rule.analyse(Village(ElementsBuilder.elementFence(Tile(1, 1), 3, 3))) should be
+ (MinimumCompartmentsRuleResult(8, Set.empty))
+ }
+
+ it should "return no violation for 8 compartments" in {
+ val elements = Range(0, 8)
+ .map(x => PosZInt.from(x * 4).get)
+ .map(Tile.MapOrigin.offset(_, 1))
+ .flatMap(t => ElementsBuilder.elementFence(t, 4, 4) + BuilderHut(t.offset(1, 1)))
+ .toSet
+ rule.analyse(Village(elements)).success should be (true)
+ }
+
+ it should "return violation for 8 empty compartments" in {
+ val elements = Range.inclusive(1, 22, 3)
+ .map(PosInt.from(_).get)
+ .flatMap(x => ElementsBuilder.elementFence(Tile(x, 1), 3, 3))
+ .toSet
+ rule.analyse(Village(elements)).success should be (false)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/QueenWalkedAirDefenseRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/QueenWalkedAirDefenseRuleSpec.scala
new file mode 100644
index 0000000..19cbeb2
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/QueenWalkedAirDefenseRuleSpec.scala
@@ -0,0 +1,45 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.AirDefense
+import org.danielholmes.coc.baseanalyser.model.trash.Barrack
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalatest._
+
+class QueenWalkedAirDefenseRuleSpec extends FlatSpec with Matchers {
+ val rule = new QueenWalkedAirDefenseRule
+
+ "QueenWalkedAirDefenseRuleSpec" should "return no violation for empty village" in {
+ rule.analyse(Village.empty).success should be (true)
+ }
+
+ it should "return violation for non-walled air defense" in {
+ val ad = AirDefense(1, Tile(1, 1))
+ val result = rule.analyse(Village(Set(ad))).asInstanceOf[QueenWalkedAirDefenseRuleResult]
+ result.success should be (false)
+ result.attackings.head.targeting should be (ad)
+ result.nonReachableAirDefs should be (empty)
+ }
+
+ it should "return success for deep-walled air defense" in {
+ val ad = AirDefense(1, Tile(15, 15))
+ val elements: Set[Element] = ElementsBuilder.elementFence(Tile(10, 10), 13, 13) ++
+ ElementsBuilder.rectangle(Tile(12, 12), 3, 3, 3, Barrack(1, _)) ++
+ Set(ad)
+ val result = rule.analyse(Village(elements)).asInstanceOf[QueenWalkedAirDefenseRuleResult]
+ result.success should be (true)
+ result.attackings should be (empty)
+ result.nonReachableAirDefs should contain (ad)
+ }
+
+ it should "return fail for shallow-walled air defense" in {
+ val ad = AirDefense(1, Tile(15, 15))
+ val elements: Set[Element] = ElementsBuilder.elementFence(Tile(11, 11), 11, 11) ++
+ ElementsBuilder.rectangle(Tile(12, 12), 3, 3, 3, Barrack(1, _)) ++
+ Set(ad)
+ val result = rule.analyse(Village(elements)).asInstanceOf[QueenWalkedAirDefenseRuleResult]
+ result.success should be (false)
+ result.attackings.head.targeting should be (ad)
+ result.nonReachableAirDefs should be (empty)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/QueenWontLeaveCompartmentRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/QueenWontLeaveCompartmentRuleSpec.scala
new file mode 100644
index 0000000..0db2bdc
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/QueenWontLeaveCompartmentRuleSpec.scala
@@ -0,0 +1,36 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.heroes.ArcherQueenAltar
+import org.danielholmes.coc.baseanalyser.stringdisplay.StringDisplayer
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalatest._
+
+class QueenWontLeaveCompartmentRuleSpec extends FlatSpec with Matchers {
+ val rule = new QueenWontLeaveCompartmentRule
+
+ "QueenWontLeaveCompartmentRule" should "return no violation for base without element" in {
+ rule.analyse(Village.empty).success should be (true)
+ }
+
+ it should "return no violation for base with walls but without queen" in {
+ val walls = ElementsBuilder.elementFence(Tile(10, 10), 5, 5)
+ rule.analyse(Village(walls)).success should be (true)
+ }
+
+ it should "return violation for base with queen but no compartments" in {
+ rule.analyse(Village(Set(ArcherQueenAltar(1, Tile(1, 1))))).success should be (false)
+ }
+
+ it should "return no violation for base with queen inside 9x9" in {
+ val aq = ArcherQueenAltar(1, Tile(13, 13))
+ val walls = ElementsBuilder.elementFence(Tile(9, 9), 11, 11)
+ rule.analyse(Village(walls + aq)).success should be (true)
+ }
+
+ it should "return violation for base with queen inside 7x7" in {
+ val aq = ArcherQueenAltar(1, Tile(13, 13))
+ val walls = ElementsBuilder.elementFence(Tile(10, 10), 9, 9)
+ rule.analyse(Village(walls + aq)).success should be (false)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/WizardTowersOutOfHoundPositionsRuleSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/WizardTowersOutOfHoundPositionsRuleSpec.scala
new file mode 100644
index 0000000..57ea8fe
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/analysis/WizardTowersOutOfHoundPositionsRuleSpec.scala
@@ -0,0 +1,64 @@
+package org.danielholmes.coc.baseanalyser.analysis
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.{AirDefense, WizardTower}
+import org.danielholmes.coc.baseanalyser.model.troops.WizardTowerHoundTargeting
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalatest._
+
+class WizardTowersOutOfHoundPositionsRuleSpec extends FlatSpec with Matchers {
+ val rule = new WizardTowersOutOfHoundPositionsRule
+
+ "WizardTowersOutOfHoundPositionsRule" should "return success for no air defs" in {
+ val wt = WizardTower(1, Tile(1, 1))
+ val result = rule.analyse(Village(Set(wt)))
+ result.success should be (true)
+ result should be (WizardTowersOutOfHoundPositionsRuleResult(Set(wt), Set.empty))
+ }
+
+ it should "return success for no wiz towers" in {
+ val ad = AirDefense(1, Tile(1, 1))
+ val result = rule.analyse(Village(Set(ad)))
+ result.success should be (true)
+ result should be (WizardTowersOutOfHoundPositionsRuleResult(Set.empty, Set.empty))
+ }
+
+ it should "return fail for wt in range of air def" in {
+ val wt = WizardTower(1, Tile(4, 4))
+ val ad = AirDefense(1, Tile(1, 1))
+ val result = rule.analyse(Village(Set(ad, wt)))
+ result.success should be (false)
+ result should be (WizardTowersOutOfHoundPositionsRuleResult(Set.empty, Set(WizardTowerHoundTargeting(wt, ad, ad.block.contractBy(1)))))
+ }
+
+ it should "return succeed for wt out of range of air def" in {
+ val wt = WizardTower(1, Tile(20, 4))
+ val ad = AirDefense(1, Tile(1, 1))
+ val result = rule.analyse(Village(Set(ad, wt)))
+ result.success should be (true)
+ result should be (WizardTowersOutOfHoundPositionsRuleResult(Set(wt), Set.empty))
+ }
+
+ it should "return success for half wts in range of air def" in {
+ val wtInRange = WizardTower(1, Tile(4, 4))
+ val wtOutRange = WizardTower(1, Tile(35, 35))
+ val ad = AirDefense(1, Tile(1, 1))
+ val result = rule.analyse(Village(Set(ad, wtInRange, wtOutRange)))
+ result.success should be (true)
+ result should be (WizardTowersOutOfHoundPositionsRuleResult(Set(wtOutRange), Set(WizardTowerHoundTargeting(wtInRange, ad, ad.block.contractBy(1)))))
+ }
+
+ it should "count a WT in range of 2 air defs only once" in {
+ val wtInRange = WizardTower(1, Tile(4, 4))
+ val wtOutRange = WizardTower(1, Tile(35, 35))
+ val ad1 = AirDefense(1, Tile(1, 1))
+ val ad2 = AirDefense(1, Tile(1, 4))
+ val result = rule.analyse(Village(Set(ad1, ad2, wtInRange, wtOutRange)))
+ result.success should be (true)
+ result should be (WizardTowersOutOfHoundPositionsRuleResult(
+ Set(wtOutRange),
+ Set(WizardTowerHoundTargeting(wtInRange, ad1, ad1.block.contractBy(1)),
+ WizardTowerHoundTargeting(wtInRange, ad2, ad2.block.contractBy(1))))
+ )
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/baseparser/HardCodedElementFactorySpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/baseparser/HardCodedElementFactorySpec.scala
new file mode 100644
index 0000000..c654937
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/baseparser/HardCodedElementFactorySpec.scala
@@ -0,0 +1,36 @@
+package org.danielholmes.coc.baseanalyser.baseparser
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.{AirSweeper, Cannon}
+import org.danielholmes.coc.baseanalyser.model.special.TownHall
+import org.scalatest._
+
+class HardCodedElementFactorySpec extends FlatSpec with Matchers {
+ val factory = new HardCodedElementFactory
+
+ "Hardcoded Building Factory" should "reject unknown code" in {
+ a[RuntimeException] should be thrownBy {
+ factory.build(RawElement(-1, 9, 1, 2))
+ }
+ }
+
+ it should "create town hall" in {
+ factory.build(RawElement(1000001, 9, 3, 3)) should contain (TownHall(10, Tile(3, 3)))
+ }
+
+ it should "create construction buildings as level 1" in {
+ factory.build(RawElement(1000008, -1, 3, 4)) should contain (Cannon(1, Tile(3, 4)))
+ }
+
+ it should "ignore obstacles" in {
+ factory.build(RawElement(8000000, 9, 5, 6)) should be (None)
+ }
+
+ it should "ignore decorations" in {
+ factory.build(RawElement(18000000, 9, 5, 6)) should be (None)
+ }
+
+ it should "parse correct air sweeper" in {
+ factory.build(RawElement(1000028, 4, 5, 6, Some(45))) should contain (AirSweeper(5, Tile(5, 6), Angle.degrees(45)))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/baseparser/VillageJsonParserSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/baseparser/VillageJsonParserSpec.scala
new file mode 100644
index 0000000..2358ca2
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/baseparser/VillageJsonParserSpec.scala
@@ -0,0 +1,82 @@
+package org.danielholmes.coc.baseanalyser.baseparser
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.scalatest._
+import org.scalactic.anyvals.{PosInt, PosZInt}
+
+class VillageJsonParserSpec extends FlatSpec with Matchers {
+ val buildingFactory = StubElementFactory
+
+ val parser = new VillageJsonParser(buildingFactory)
+
+ "A Village JSON Parser" should "throw an exception if invalid json provided" in {
+ a[InvalidJsonException] should be thrownBy {
+ parser.parse("something random[ {")
+ }
+ }
+
+ it should "return empty village is empty input" in {
+ val villages = parser.parse("""{"war_base": false, "buildings":[]}""")
+
+ villages should be (Villages(Village.empty, None))
+ }
+
+ it should "return simple village" in {
+ val result = parser.parse("""{"exp_ver":1, "war_base": false, "buildings":[{ "data": 1000001, "lvl": 1, "x": 21, "y": 20 }]}""")
+
+ result should be (Villages(Village(Set(StubBaseElement(1, Tile(21, 20), None))), None))
+ }
+
+ it should "return village without ignored elements" in {
+ val result = parser.parse("""{"exp_ver":1, "war_base": false, "buildings":[{ "data": 999999, "lvl": 1, "x": 21, "y": 20 }]}""")
+
+ result should be (Villages(Village.empty, None))
+ }
+
+ it should "return war village" in {
+ val result = parser.parse(
+ """{"exp_ver":1, "war_layout": 4,"war_base": true,
+ |"buildings":[{ "data": 1000001, "lvl": 1, "x": 20, "y": 20, "l4x": 30, "l4y": 30 }]}""".stripMargin)
+
+ result should be (Villages(
+ Village(Set(StubBaseElement(1, Tile(20, 20), None))),
+ Some(Village(Set(StubBaseElement(1, Tile(30, 30), None))))
+ ))
+ }
+
+ it should "return war element aim angle" in {
+ val result = parser.parse(
+ """{"exp_ver":1, "war_layout": 4,"war_base": true,
+ |"buildings":[{ "data": 1000029, "lvl": 1, "x": 20, "y": 20, "l4x": 30, "l4y": 30, "aim_angle": 90, "aim_angle_war": 45 }]}""".stripMargin)
+
+ result should be (Villages(
+ Village(Set(StubBaseElement(1, Tile(20, 20), Some(90)))),
+ Some(Village(Set(StubBaseElement(1, Tile(30, 30), Some(45)))))
+ ))
+ }
+
+ it should "return war village without not yet placed buildings" in {
+ val result = parser.parse(
+ """{"exp_ver":1, "war_layout": 4,"war_base": true,
+ |"buildings":[
+ |{ "data": 1000001, "lvl": 1, "x": 20, "y": 20, "l4x": 30, "l4y": 30 },
+ |{ "data": 1000001, "lvl": 2, "x": 40, "y": 40 }]}""".stripMargin)
+
+ result should be (Villages(
+ Village(Set(StubBaseElement(1, Tile(20, 20), None), StubBaseElement(2, Tile(40, 40), None))),
+ Some(Village(Set(StubBaseElement(1, Tile(30, 30), None))))
+ ))
+ }
+}
+
+object StubElementFactory extends ElementFactory {
+ def build(raw: RawElement): Option[Element] = {
+ Some(raw)
+ .filter(_.data != 999999)
+ .map(r => StubBaseElement(PosInt.from(r.lvl).get, Tile(PosZInt.from(r.x).get, PosZInt.from(r.y).get), raw.aimAngle))
+ }
+}
+
+case class StubBaseElement(level: PosInt, tile: Tile, aimAngle: Option[Int]) extends Element {
+ val size = PosInt(3)
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/gameconnection/ClanSeekerAkkaServiceAgentSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/gameconnection/ClanSeekerAkkaServiceAgentSpec.scala
new file mode 100644
index 0000000..f39e3c8
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/gameconnection/ClanSeekerAkkaServiceAgentSpec.scala
@@ -0,0 +1,20 @@
+package org.danielholmes.coc.baseanalyser.gameconnection
+
+import org.scalatest._
+
+// Specs ignored at the moment because clan seeker not running
+class ClanSeekerAkkaServiceAgentSpec extends FlatSpec with Matchers {
+ val client = new ClanSeekerGameConnection
+
+ /* "Clan Seeker Service Client" */ ignore should "get clan details for OH alpha" in {
+ client.getClanDetails(154621406673L).get.name should be ("OneHive Alpha")
+ }
+
+ ignore should "get player village for Dakota" in {
+ client.getPlayerVillage(223343461050L).get.village.raw.isEmpty should be (false)
+ }
+
+ ignore should "get empty for non-existent" in {
+ client.getPlayerVillage(999999999999999L) should be (empty)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/BlockSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/BlockSpec.scala
new file mode 100644
index 0000000..85ebb22
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/BlockSpec.scala
@@ -0,0 +1,79 @@
+package org.danielholmes.coc.baseanalyser.model
+
+import org.scalatest._
+
+class BlockSpec extends FlatSpec with Matchers {
+ val block = Block(Tile(5, 5), 3)
+
+ "Block" should "find closest coordinate in x axis left" in {
+ block.findClosestCoordinate(TileCoordinate(0, 5)) should be (TileCoordinate(5, 5))
+ }
+
+ it should "find closest coordinate in x axis right" in {
+ block.findClosestCoordinate(TileCoordinate(10, 5)) should be (TileCoordinate(8, 5))
+ }
+
+ it should "find closest coordinate in y axis up" in {
+ block.findClosestCoordinate(TileCoordinate(5, 0)) should be (TileCoordinate(5, 5))
+ }
+
+ it should "find closest coordinate in y axis down" in {
+ block.findClosestCoordinate(TileCoordinate(5, 10)) should be (TileCoordinate(5, 8))
+ }
+
+ it should "find closest coordinate diagonally" in {
+ block.findClosestCoordinate(TileCoordinate(10, 10)) should be (TileCoordinate(8, 8))
+ }
+
+ it should "return empty internal coords when 1x1" in {
+ Block(Tile(0, 0), 1).internalCoordinates should be (Set.empty)
+ }
+
+ it should "return empty internal coords when 2x2" in {
+ Block(Tile(0, 0), 2).internalCoordinates should contain theSameElementsAs Set(TileCoordinate(1, 1))
+ }
+
+ it should "return correct internal coords when 3x3 or more" in {
+ Block(Tile(0, 0), 3).internalCoordinates should contain theSameElementsAs
+ Set(TileCoordinate(1, 1), TileCoordinate(2, 1), TileCoordinate(1, 2), TileCoordinate(2, 2))
+ }
+
+ it should "return true intersect for overlapping items" in {
+ Block(Tile.Origin, 2).intersects(Block(Tile(1, 1), 2)) should be (true)
+ }
+
+ it should "return false any intersect for no items" in {
+ Block.firstIntersecting(Set.empty) should be (empty)
+ }
+
+ it should "return false any intersect for one item" in {
+ Block.firstIntersecting(Set(Block(Tile.Origin, 2))) should be (empty)
+ }
+
+ it should "return false any intersect for edge touching items" in {
+ Block.firstIntersecting(Set(Block(Tile.Origin, 2), Block(Tile(0, 2), 2))) should be (empty)
+ }
+
+ it should "return true any intersect for overlapping items" in {
+ val b1 = Block(Tile.Origin, 2)
+ val b2 = Block(Tile(1, 1), 2)
+ Block.firstIntersecting(Set(b1, b2)).get should be ((b2, b1))
+ }
+
+ it should "return correct all tiles" in {
+ Block(Tile.Origin, 2).tiles should contain theSameElementsAs Set(Tile.Origin, Tile(0, 1), Tile(1, 0), Tile(1, 1))
+ }
+
+ it should "return correct all coordinates" in {
+ Block(Tile.Origin, 2).allCoordinates should contain theSameElementsAs
+ Set(
+ TileCoordinate(0, 0), TileCoordinate(1, 0), TileCoordinate(2, 0),
+ TileCoordinate(0, 1), TileCoordinate(1, 1), TileCoordinate(2, 1),
+ TileCoordinate(0, 2), TileCoordinate(1, 2), TileCoordinate(2, 2)
+ )
+ }
+
+ it should "expand to size correctly" in {
+ Block(Tile(1, 1), 2).expandToSize(4) should be (Block(Tile(0, 0), 4))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/HogTargetingSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/HogTargetingSpec.scala
new file mode 100644
index 0000000..460b5c4
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/HogTargetingSpec.scala
@@ -0,0 +1,28 @@
+package org.danielholmes.coc.baseanalyser.model
+
+import org.danielholmes.coc.baseanalyser.model.range.CircularElementRange
+import org.danielholmes.coc.baseanalyser.model.trash.BuilderHut
+import org.danielholmes.coc.baseanalyser.model.troops.HogTargeting
+import org.scalatest._
+
+class HogTargetingSpec extends FlatSpec with Matchers {
+ "HogTargeting" should "return correct cutting result for non-cutting" in {
+ HogTargeting(TileCoordinate(0, 0), BuilderHut(Tile(10, 0)))
+ .cutsRadius(CircularElementRange(FloatMapCoordinate(5, 5), 1)) shouldBe false
+ }
+
+ it should "return correct cutting result for just cutting" in {
+ HogTargeting(TileCoordinate(0, 0), BuilderHut(Tile(10, 0)))
+ .cutsRadius(CircularElementRange(FloatMapCoordinate(5, 2), 2)) shouldBe false
+ }
+
+ it should "return correct cutting result for middle cutting" in {
+ HogTargeting(TileCoordinate(0, 0), BuilderHut(Tile(10, 0)))
+ .cutsRadius(CircularElementRange(FloatMapCoordinate(5, 1), 2)) shouldBe true
+ }
+
+ it should "return correct cutting result for random" in {
+ HogTargeting(TileCoordinate(0, 0), BuilderHut(Tile(0, 5)))
+ .cutsRadius(CircularElementRange(FloatMapCoordinate(0, 10), 1)) shouldBe false
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/TileCoordinateSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/TileCoordinateSpec.scala
new file mode 100644
index 0000000..045de4c
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/TileCoordinateSpec.scala
@@ -0,0 +1,16 @@
+package org.danielholmes.coc.baseanalyser.model
+
+import org.scalatest._
+import org.scalactic.anyvals.PosZDouble
+
+class TileCoordinateSpec extends FlatSpec with Matchers {
+ val coord = TileCoordinate(5, 5)
+
+ "Tile Coordinate" should "find correct x axis right distance" in {
+ coord.distanceTo(TileCoordinate(10, 5)) should be (PosZDouble(5))
+ }
+
+ it should "find correct diagonal distance" in {
+ coord.distanceTo(TileCoordinate(8, 9)) should be (PosZDouble(5))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/TileSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/TileSpec.scala
new file mode 100644
index 0000000..d888bbc
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/TileSpec.scala
@@ -0,0 +1,55 @@
+package org.danielholmes.coc.baseanalyser.model
+
+import org.scalatest._
+import org.scalactic.anyvals.{PosZInt, PosZDouble}
+
+class TileSpec extends FlatSpec with Matchers {
+ "Tile" should "return correct matrix of tiles" in {
+ Tile(2, 2).matrixOfTilesInDirection(2, 2) should contain theSameElementsAs
+ Set(Tile(2, 2), Tile(2, 3), Tile(3, 2), Tile(3, 3))
+ }
+
+ it should "return correct rectangle of tiles" in {
+ Tile(2, 2).rectangleTo(Tile(4, 4)) should contain theSameElementsAs
+ Set(
+ Tile(2, 2), Tile(3, 2), Tile(4, 2),
+ Tile(2, 3), /*Tile(3, 2),*/ Tile(4, 3),
+ Tile(2, 4), Tile(3, 4), Tile(4, 4)
+ )
+ }
+
+ it should "return correct neighbours" in {
+ Tile(1, 1).neighbours should be (Set(
+ Tile(0, 0), Tile(1, 0), Tile(2, 0),
+ Tile(0, 1), /*Tile(1, 1),*/ Tile(2, 1),
+ Tile(0, 2), Tile(1, 2), Tile(2, 2)
+ ))
+ }
+
+ it should "return correct neighbours at origin" in {
+ Tile(0, 0).neighbours should be (Set(
+ /*Tile(0, 0), */ Tile(1, 0),
+ Tile(0, 1), Tile(1, 1)
+ ))
+ }
+
+ it should "return correct neighbours at end" in {
+ Tile(Tile.MaxCoordinate, Tile.MaxCoordinate).neighbours should be (Set(
+ Tile(PosZInt.from(Tile.MaxCoordinate - 1).get, PosZInt.from(Tile.MaxCoordinate - 1).get),
+ Tile(PosZInt.from(Tile.MaxCoordinate - 1).get, Tile.MaxCoordinate),
+ Tile(Tile.MaxCoordinate, PosZInt.from(Tile.MaxCoordinate - 1).get)
+ ))
+ }
+
+ it should "return correct distance to touching" in {
+ Tile(1, 0).distanceTo(Tile(2, 0)) should be (PosZDouble(0))
+ }
+
+ it should "return correct distance to x difference" in {
+ Tile(0, 0).distanceTo(Tile(11, 0)) should be (PosZDouble(10))
+ }
+
+ it should "return correct distance to diagonal" in {
+ Tile(10, 10).distanceTo(Tile(6, 5)) should be (PosZDouble(5))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/VillageSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/VillageSpec.scala
new file mode 100644
index 0000000..5113b8a
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/VillageSpec.scala
@@ -0,0 +1,85 @@
+package org.danielholmes.coc.baseanalyser.model
+
+import org.danielholmes.coc.baseanalyser.model.trash.{Barrack, BuilderHut}
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalatest._
+
+class VillageSpec extends FlatSpec with Matchers {
+ "Village" should "return every coordinate when empty" in {
+ Village.empty.coordinatesAllowedToDropTroop should contain theSameElementsAs TileCoordinate.All
+ }
+
+ it should "return all with hit areas excluded" in {
+ val builderPlacement = TileCoordinate(2, 2).matrixOfCoordinatesTo(TileCoordinate(4, 4))
+ val expected = TileCoordinate.All.toSet -- builderPlacement
+
+ val village = Village(Set(BuilderHut(Tile(2, 2))))
+ val result = village.coordinatesAllowedToDropTroop
+ result should have size expected.size
+ result should contain theSameElementsAs expected
+ }
+
+ it should "return all with hit areas excluded when on edge of map" in {
+ val builderPlacement = TileCoordinate.MapOrigin.matrixOfCoordinatesTo(TileCoordinate.MapOrigin.offset(2, 2))
+ val expected = TileCoordinate.All.toSet -- builderPlacement
+
+ val village = Village(Set(BuilderHut(Tile.MapOrigin)))
+ val result = village.coordinatesAllowedToDropTroop
+ result should have size expected.size
+ result should contain theSameElementsAs expected
+ }
+
+ it should "disallow overlapping elements" in {
+ a[IllegalArgumentException] should be thrownBy {
+ Village(
+ Set(
+ Barrack(1, Tile.MapOrigin),
+ Barrack(1, Tile.MapOrigin.offset(1, 1))
+ )
+ )
+ }
+ }
+
+ it should "not include intersections of attack placements in coordinates allowed to drop troop" in {
+ val village = Village(Set(BuilderHut(Tile(1, 1)), BuilderHut(Tile(5, 1))))
+
+ village.coordinatesAllowedToDropTroop should not contain TileCoordinate(4, 1)
+ village.coordinatesAllowedToDropTroop should not contain TileCoordinate(4, 2)
+ village.coordinatesAllowedToDropTroop should not contain TileCoordinate(4, 3)
+ }
+
+ it should "return no compartments for empty village" in {
+ Village.empty.wallCompartments should be (empty)
+ }
+
+ it should "return no compartments for village with walls but no compartments" in {
+ ElementsBuilder.villageFromString("WWW\nW W\n WW", Tile(1, 1), Wall(1, _)).wallCompartments should be (empty)
+ }
+
+ it should "return single simple compartment" in {
+ val walls = ElementsBuilder.wallFence(Tile(5, 5), 3, 3)
+ Village(walls.map(_.asInstanceOf[Element])).wallCompartments should be (Set(WallCompartment(
+ walls, Set(Tile(6, 6)), Set.empty
+ )))
+ }
+
+ it should "return single compartment with a building inside" in {
+ val walls = ElementsBuilder.elementFence(Tile(6, 6), 5, 5)
+ val barrack = Barrack(1, Tile(7, 7))
+
+ Village(walls + barrack).wallCompartments should be (Set(WallCompartment(
+ walls.map(_.asInstanceOf[Wall]),
+ Tile(7, 7).matrixOfTilesTo(Tile(9, 9)),
+ Set(barrack)
+ )))
+ }
+
+ it should "return multiple compartments" in {
+ val walls1 = ElementsBuilder.fromString("WWW\nW W\nWWW", Tile(6, 6), Wall(1, _))
+ val walls2 = ElementsBuilder.fromString("WWW\nW W\nWWW", Tile(16, 6), Wall(1, _))
+ Village((walls1 ++ walls2).map(_.asInstanceOf[Element])).wallCompartments should be (Set(
+ WallCompartment(walls1, Set(Tile(7, 7)), Set.empty),
+ WallCompartment(walls2, Set(Tile(17, 7)), Set.empty)
+ ))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/WallCompartmentSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/WallCompartmentSpec.scala
new file mode 100644
index 0000000..c2be656
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/WallCompartmentSpec.scala
@@ -0,0 +1,32 @@
+package org.danielholmes.coc.baseanalyser.model
+
+import org.danielholmes.coc.baseanalyser.model.trash.Barrack
+import org.danielholmes.coc.baseanalyser.util.ElementsBuilder
+import org.scalatest._
+
+class WallCompartmentSpec extends FlatSpec with Matchers {
+ "WallCompartment" should "return empty tiles for no buildings" in {
+ val walls = ElementsBuilder.wallFence(Tile.MapOrigin, 3, 3)
+ val inner = Set(Tile.MapOrigin.offset(1, 1))
+ WallCompartment(walls, inner, Set.empty).emptyTiles should contain theSameElementsAs inner
+ }
+
+ it should "return empty tiles for building inside" in {
+ val walls = ElementsBuilder.wallFence(Tile.MapOrigin, 6, 5)
+ val inner = Tile.MapOrigin.offset(1, 1).matrixOfTilesTo(Tile.MapOrigin.offset(4, 3))
+ val buildings = Set[Element](Barrack(1, Tile.MapOrigin.offset(2, 1)))
+ WallCompartment(walls, inner, buildings).emptyTiles should contain theSameElementsAs
+ Tile.MapOrigin.offset(1, 1).matrixOfTilesTo(Tile.MapOrigin.offset(1, 3))
+ }
+
+ it should "return empty possible large traps for 1x1 only" in {
+ WallCompartment(ElementsBuilder.wallFence(Tile.MapOrigin, 3, 3), Set(Tile.MapOrigin.offset(1, 1)), Set.empty).possibleLargeTraps should be (empty)
+ }
+
+ it should "return 2 possible large traps for 3x2 empty space" in {
+ val walls = ElementsBuilder.wallFence(Tile.MapOrigin, 5, 4)
+ val inner = Tile.MapOrigin.offset(1, 1).matrixOfTilesTo(Tile.MapOrigin.offset(3, 2))
+ WallCompartment(walls, inner, Set.empty).possibleLargeTraps should contain theSameElementsAs
+ Set(PossibleLargeTrap(Tile.MapOrigin.offset(1, 1)), PossibleLargeTrap(Tile.MapOrigin.offset(2, 1)))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/range/BlindSpotSectorElementRangeSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/range/BlindSpotSectorElementRangeSpec.scala
new file mode 100644
index 0000000..6d3a554
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/range/BlindSpotSectorElementRangeSpec.scala
@@ -0,0 +1,18 @@
+package org.danielholmes.coc.baseanalyser.model.range
+
+import org.danielholmes.coc.baseanalyser.model.{Angle, FloatMapCoordinate, TileCoordinate}
+import org.scalatest._
+
+class BlindSpotSectorElementRangeSpec extends FlatSpec with Matchers {
+ "BlindSpotSectorElementRange" should "contains inside" in {
+ BlindSpotSectorElementRange(TileCoordinate(10, 10), 1.0, 10.0, Angle.Quarter, Angle.Half).contains(FloatMapCoordinate(15.0, 10.0)) should be (true)
+ }
+
+ it should "not contain outside" in {
+ BlindSpotSectorElementRange(TileCoordinate(10, 10), 1.0, 10.0, Angle.Quarter, Angle.Half).contains(FloatMapCoordinate(25.0, 10.0)) should be (false)
+ }
+
+ it should "not contain outside opposite direction" in {
+ BlindSpotSectorElementRange(TileCoordinate(10, 10), 1.0, 10.0, Angle.Quarter, Angle.Half).contains(FloatMapCoordinate(5.0, 10.0)) should be (false)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/troops/HogRiderSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/troops/HogRiderSpec.scala
new file mode 100644
index 0000000..18d32c6
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/troops/HogRiderSpec.scala
@@ -0,0 +1,54 @@
+package org.danielholmes.coc.baseanalyser.model.troops
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.{AirSweeper, ArcherTower, Cannon}
+import org.danielholmes.coc.baseanalyser.model.heroes.BarbarianKingAltar
+import org.danielholmes.coc.baseanalyser.model.trash.Barrack
+import org.scalatest._
+
+class HogRiderSpec extends FlatSpec with Matchers {
+ val origin = TileCoordinate(0, 0)
+
+ "HogRider" should "return defense target" in {
+ val at = ArcherTower(1, Tile(1, 1))
+ HogRider.findTargets(origin, Village(Set(at))) should contain (at)
+ }
+
+ it should "return non-defense target if none available" in {
+ val barrack = Barrack(1, Tile(1, 1))
+ HogRider.findTargets(origin, Village(Set(barrack))) should contain (barrack)
+ }
+
+ it should "return empty if only wall available" in {
+ HogRider.findTargets(origin, Village(Set(Wall(1, Tile.MapOrigin)))) should be (empty)
+ }
+
+ it should "return closest defense target if available" in {
+ val at = ArcherTower(1, Tile(10, 10))
+ val closeBarrack = Barrack(1, Tile(2, 2))
+ HogRider.findTargets(origin, Village(Set(at, closeBarrack))) should contain (at)
+ }
+
+ it should "not include heroes as target" in {
+ val bk = BarbarianKingAltar(1, Tile(10, 10))
+ val closeBarrack = Barrack(1, Tile(2, 2))
+ HogRider.findTargets(origin, Village(Set(bk, closeBarrack))) should contain (closeBarrack)
+ }
+
+ it should "include air sweeper as target" in {
+ val as = AirSweeper(1, Tile(10, 10), Angle.Half)
+ val closeBarrack = Barrack(1, Tile(2, 2))
+ HogRider.findTargets(origin, Village(Set(as, closeBarrack))) should contain (as)
+ }
+
+ it should "return none for empty village" in {
+ HogRider.findTargets(origin, Village.empty) shouldBe empty
+ }
+
+ it should "return all equidistant defense targets" in {
+ val at = ArcherTower(1, Tile(0, 10))
+ val cannon = Cannon(1, Tile(10, 0))
+ val closeBarrack = Barrack(1, Tile(2, 2))
+ HogRider.findTargets(origin, Village(Set(at, cannon, closeBarrack))) should be (Set(at, cannon))
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/model/troops/TroopSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/model/troops/TroopSpec.scala
new file mode 100644
index 0000000..6031a63
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/model/troops/TroopSpec.scala
@@ -0,0 +1,36 @@
+package org.danielholmes.coc.baseanalyser.model.troops
+
+import org.danielholmes.coc.baseanalyser.model.trash.{ArmyCamp, BuilderHut}
+import org.danielholmes.coc.baseanalyser.model._
+import org.scalactic.anyvals.{PosDouble, PosZDouble}
+import org.scalatest._
+
+class TroopSpec extends FlatSpec with Matchers {
+ "Troop" should "return correct points can attack building from" in {
+ val LowCornerCoord = PosDouble.from(2.0 - Math.sqrt(0.75 / 2)).get
+ val HighCornerCoord = PosDouble.from(6.0 - LowCornerCoord).get
+ ExampleTroop.getAttackFloatCoordinates(BuilderHut(Tile(2, 2))) should contain theSameElementsAs
+ (Set(
+ FloatMapCoordinate(LowCornerCoord, LowCornerCoord), FloatMapCoordinate(2, 1.25), FloatMapCoordinate(3, 1.25),
+ FloatMapCoordinate(4, 1.25), FloatMapCoordinate(HighCornerCoord, LowCornerCoord),
+ FloatMapCoordinate(1.25, 2), FloatMapCoordinate(2, 2), FloatMapCoordinate(3, 2), FloatMapCoordinate(4, 2), FloatMapCoordinate(4.75, 2),
+ FloatMapCoordinate(1.25, 3), FloatMapCoordinate(2, 3), FloatMapCoordinate(3, 3), FloatMapCoordinate(4, 3), FloatMapCoordinate(4.75, 3),
+ FloatMapCoordinate(1.25, 4), FloatMapCoordinate(2, 4), FloatMapCoordinate(3, 4), FloatMapCoordinate(4, 4), FloatMapCoordinate(4.75, 4),
+ FloatMapCoordinate(LowCornerCoord, HighCornerCoord), FloatMapCoordinate(2, 4.75), FloatMapCoordinate(3, 4.75),
+ FloatMapCoordinate(4, 4.75), FloatMapCoordinate(HighCornerCoord, HighCornerCoord)
+ ))
+ }
+
+ it should "find targets with correct hit area" in {
+ val armyCamp = ArmyCamp(1, Tile(10, 10))
+ ExampleTroop.findTargets(TileCoordinate(10, 10), Village(Set(armyCamp))) should contain theSameElementsAs Set(armyCamp)
+ }
+}
+
+object ExampleTroop extends Troop {
+ val Range = PosZDouble(0.75)
+
+ override protected def getPrioritisedTargets(village: Village): List[Set[Structure]] = {
+ getAnyBuildingsTargets(village)
+ }
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/stringdisplay/StringDisplayerSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/stringdisplay/StringDisplayerSpec.scala
new file mode 100644
index 0000000..b32c80e
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/stringdisplay/StringDisplayerSpec.scala
@@ -0,0 +1,93 @@
+package org.danielholmes.coc.baseanalyser.stringdisplay
+
+import org.danielholmes.coc.baseanalyser.model._
+import org.danielholmes.coc.baseanalyser.model.defense.ArcherTower
+import org.danielholmes.coc.baseanalyser.model.special.{ClanCastle, TownHall}
+import org.scalatest._
+
+class StringDisplayerSpec extends FlatSpec with Matchers {
+ val DrawnRowSize = Tile.MaxCoordinate + 1 + 2 + 1 // All tiles, borders and a new line char
+ val displayer = new StringDisplayer
+
+ "String Displayer" should "display empty base" in {
+ displayer.build(Village.empty) should be (EMPTY)
+ }
+
+ it should "display origin town hall" in {
+ val result = displayer.build(Village(Set(TownHall(1, Tile(1, 1)))))
+ for (row <- 2 to 5) {
+ for (col <- 2 to 5) {
+ result.charAt(row * DrawnRowSize + col) should be('#')
+ }
+ }
+ }
+
+ it should "display end of earth archer tower" in {
+ val result = displayer.build(Village(Set(ArcherTower(1, Tile(41, 41)))))
+ for (row <- 42 to 44) {
+ for (col <- 42 to 44) {
+ result.charAt(row * DrawnRowSize + col) should be('A')
+ }
+ }
+ }
+
+ it should "display end of map cc radius without exception" in {
+ displayer.build(Village(Set(ClanCastle(1, Tile(41, 41)))))
+ //result.charAt(1600) should be('^')
+ }
+
+ val EMPTY =
+"""+--------------------------------------------------+
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
++--------------------------------------------------+
+"""
+}
diff --git a/src/test/scala/org/danielholmes/coc/baseanalyser/util/ElementsBuilderSpec.scala b/src/test/scala/org/danielholmes/coc/baseanalyser/util/ElementsBuilderSpec.scala
new file mode 100644
index 0000000..17707b6
--- /dev/null
+++ b/src/test/scala/org/danielholmes/coc/baseanalyser/util/ElementsBuilderSpec.scala
@@ -0,0 +1,14 @@
+package org.danielholmes.coc.baseanalyser.util
+
+import org.danielholmes.coc.baseanalyser.model.{Tile, Wall}
+import org.scalatest._
+
+class ElementsBuilderSpec extends FlatSpec with Matchers {
+ "ElementsBuilder" should "return correctly built elements" in {
+ ElementsBuilder.fromString("WWW\nW W\nWWW", Tile(10, 10), Wall(1, _)) should be (Set(
+ Wall(1, Tile(10, 10)), Wall(1, Tile(11, 10)), Wall(1, Tile(12, 10)),
+ Wall(1, Tile(10, 11)), /*Wall(1, Tile(11, 11)),*/ Wall(1, Tile(12, 11)),
+ Wall(1, Tile(10, 12)), Wall(1, Tile(11, 12)), Wall(1, Tile(12, 12))
+ ))
+ }
+}