diff --git a/.classpath b/.classpath
index b16f6f148..08f3d781d 100644
--- a/.classpath
+++ b/.classpath
@@ -1,11 +1,11 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
index 5fc51296e..f6a001b2f 100644
--- a/.project
+++ b/.project
@@ -1,23 +1,23 @@
-
-
- JCloisterZone
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.eclipse.m2e.core.maven2Nature
- org.eclipse.jdt.core.javanature
-
-
+
+
+ JCloisterZone
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/pom.xml b/pom.xml
index 800981358..92f8a0519 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,170 +1,170 @@
-
- 4.0.0
- jcloisterzone
- jcloisterzone
- JCloisterZone
- 2-SNAPSHOT
- Carcassonne board game Java implementation
- http://www.jcloisterzone.org/
-
-
-
- gettext-commons-site
- http://gettext-commons.googlecode.com/svn/maven-repository/
-
-
-
-
-
- gettext-commons-site
- http://gettext-commons.googlecode.com/svn/maven-repository/
-
- daily
-
-
-
-
-
-
-
- com.google.guava
- guava
- 15.0
-
-
- org.slf4j
- slf4j-simple
- 1.7.5
-
-
- org.apache.mina
- mina-core
- 2.0.7
-
-
- org.ini4j
- ini4j
- 0.5.2
-
-
- com.miglayout
- miglayout
- 3.7.4
-
-
- org.xnap.commons
- gettext-commons
- 0.9.6
-
-
- com.apple
- AppleJavaExtensions
- 1.4
-
-
-
-
- junit
- junit
- 4.10
- test
-
-
- org.mockito
- mockito-all
- 1.9.0
- test
-
-
-
-
-
-
-
- src/main/resources
-
- **/*
-
-
-
- src/main/config
-
- **/*
-
-
- config.ini
- debug.ini
-
-
-
-
-
- maven-compiler-plugin
-
- 1.7
-
- 1.7
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 2.1
-
-
- package
-
- shade
-
-
- JCloisterZone
- build
-
-
-
- com.jcloisterzone.ui.Bootstrap
- sysimages/jcloisterzone.png
- .
-
-
-
- true
-
-
- jcloisterzone:jcloisterzone
-
- config.ini
- debug.ini
- **/.gitignore
- plugins/**
-
-
-
-
-
-
-
-
-
-
- prepare-package
-
- dist
-
-
-
- org.xnap.commons
- maven-gettext-plugin
- 1.2.3
-
- ${basedir}/po
- ${project.build.sourceDirectory}
- ${project.build.outputDirectory}
- Messages
-
-
-
-
+
+ 4.0.0
+ jcloisterzone
+ jcloisterzone
+ JCloisterZone
+ 2-SNAPSHOT
+ Carcassonne board game Java implementation
+ http://www.jcloisterzone.org/
+
+
+
+ gettext-commons-site
+ http://gettext-commons.googlecode.com/svn/maven-repository/
+
+
+
+
+
+ gettext-commons-site
+ http://gettext-commons.googlecode.com/svn/maven-repository/
+
+ daily
+
+
+
+
+
+
+
+ com.google.guava
+ guava
+ 15.0
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.5
+
+
+ org.apache.mina
+ mina-core
+ 2.0.7
+
+
+ org.ini4j
+ ini4j
+ 0.5.2
+
+
+ com.miglayout
+ miglayout
+ 3.7.4
+
+
+ org.xnap.commons
+ gettext-commons
+ 0.9.6
+
+
+ com.apple
+ AppleJavaExtensions
+ 1.4
+
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+ org.mockito
+ mockito-all
+ 1.9.0
+ test
+
+
+
+
+
+
+
+ src/main/resources
+
+ **/*
+
+
+
+ src/main/config
+
+ **/*
+
+
+ config.ini
+ debug.ini
+
+
+
+
+
+ maven-compiler-plugin
+
+ 1.7
+
+ 1.7
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.1
+
+
+ package
+
+ shade
+
+
+ JCloisterZone
+ build
+
+
+
+ com.jcloisterzone.ui.Bootstrap
+ sysimages/jcloisterzone.png
+ .
+
+
+
+ true
+
+
+ jcloisterzone:jcloisterzone
+
+ config.ini
+ debug.ini
+ **/.gitignore
+ plugins/**
+
+
+
+
+
+
+
+
+
+
+ prepare-package
+
+ dist
+
+
+
+ org.xnap.commons
+ maven-gettext-plugin
+ 1.2.3
+
+ ${basedir}/po
+ ${project.build.sourceDirectory}
+ ${project.build.outputDirectory}
+ Messages
+
+
+
+
\ No newline at end of file
diff --git a/src/main/config/simplelogger.properties b/src/main/config/simplelogger.properties
index 639281a48..04f3dff8a 100644
--- a/src/main/config/simplelogger.properties
+++ b/src/main/config/simplelogger.properties
@@ -1,2 +1,2 @@
-org.slf4j.simpleLogger.logFile=System.out
+org.slf4j.simpleLogger.logFile=System.out
org.slf4j.simpleLogger.defaultLogLevel=WARN
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/AppUpdate.java b/src/main/java/com/jcloisterzone/AppUpdate.java
index 682bb0cb3..6cadee9d0 100644
--- a/src/main/java/com/jcloisterzone/AppUpdate.java
+++ b/src/main/java/com/jcloisterzone/AppUpdate.java
@@ -1,52 +1,52 @@
-package com.jcloisterzone;
-
-import java.net.URL;
-
-import org.w3c.dom.Element;
-
-public class AppUpdate {
-
- private String version;
- private String description;
- private String downloadUrl;
-
- public static AppUpdate fetch(URL url) {
- Element el = XmlUtils.parseDocument(url).getDocumentElement();
- String version = XmlUtils.childValue(el, "number");
- String description = XmlUtils.childValue(el, "description");
- String downloadUrl = XmlUtils.childValue(el, "url");
- return new AppUpdate(version, description, downloadUrl);
- }
-
- public AppUpdate(String version, String description, String downloadUrl) {
- this.version = version;
- this.description = description;
- this.downloadUrl = downloadUrl;
- }
-
- @Override
- public String toString() {
- return this.version + " " + this.description;
- }
-
- public String getVersion() {
- return version;
- }
- public void setVersion(String version) {
- this.version = version;
- }
- public String getDescription() {
- return description;
- }
- public void setDescription(String description) {
- this.description = description;
- }
- public String getDownloadUrl() {
- return downloadUrl;
- }
- public void setDownloadUrl(String downloadUrl) {
- this.downloadUrl = downloadUrl;
- }
-
-
-}
+package com.jcloisterzone;
+
+import java.net.URL;
+
+import org.w3c.dom.Element;
+
+public class AppUpdate {
+
+ private String version;
+ private String description;
+ private String downloadUrl;
+
+ public static AppUpdate fetch(URL url) {
+ Element el = XmlUtils.parseDocument(url).getDocumentElement();
+ String version = XmlUtils.childValue(el, "number");
+ String description = XmlUtils.childValue(el, "description");
+ String downloadUrl = XmlUtils.childValue(el, "url");
+ return new AppUpdate(version, description, downloadUrl);
+ }
+
+ public AppUpdate(String version, String description, String downloadUrl) {
+ this.version = version;
+ this.description = description;
+ this.downloadUrl = downloadUrl;
+ }
+
+ @Override
+ public String toString() {
+ return this.version + " " + this.description;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+ public void setVersion(String version) {
+ this.version = version;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public String getDownloadUrl() {
+ return downloadUrl;
+ }
+ public void setDownloadUrl(String downloadUrl) {
+ this.downloadUrl = downloadUrl;
+ }
+
+
+}
diff --git a/src/main/java/com/jcloisterzone/Application.java b/src/main/java/com/jcloisterzone/Application.java
index a95c1e4c6..fc0c341f5 100644
--- a/src/main/java/com/jcloisterzone/Application.java
+++ b/src/main/java/com/jcloisterzone/Application.java
@@ -1,18 +1,18 @@
-package com.jcloisterzone;
-
-/**
- * Various application constants.
- * @author Roman Krejcik
- */
-public interface Application {
-
-// public String VERSION = "2.3";
-// public String BUILD_DATE = "YYYY-MM-DD";
-
- public String VERSION = "dev-snapshot";
- public String BUILD_DATE = "";
-
- public int PROTCOL_VERSION = 15;
-
- public static final String ILLEGAL_STATE_MSG = "Method '{}' called in invalid state";
-}
+package com.jcloisterzone;
+
+/**
+ * Various application constants.
+ * @author Roman Krejcik
+ */
+public interface Application {
+
+// public String VERSION = "2.3";
+// public String BUILD_DATE = "YYYY-MM-DD";
+
+ public String VERSION = "dev-snapshot";
+ public String BUILD_DATE = "";
+
+ public int PROTCOL_VERSION = 15;
+
+ public static final String ILLEGAL_STATE_MSG = "Method '{}' called in invalid state";
+}
diff --git a/src/main/java/com/jcloisterzone/Expansion.java b/src/main/java/com/jcloisterzone/Expansion.java
index ec069594e..f77a7d2e9 100644
--- a/src/main/java/com/jcloisterzone/Expansion.java
+++ b/src/main/java/com/jcloisterzone/Expansion.java
@@ -1,131 +1,131 @@
-package com.jcloisterzone;
-
-import static com.jcloisterzone.ui.I18nUtils._;
-
-import com.jcloisterzone.game.Capability;
-import com.jcloisterzone.game.capability.AbbeyCapability;
-import com.jcloisterzone.game.capability.BarnCapability;
-import com.jcloisterzone.game.capability.BazaarCapability;
-import com.jcloisterzone.game.capability.BigFollowerCapability;
-import com.jcloisterzone.game.capability.BridgeCapability;
-import com.jcloisterzone.game.capability.BuilderCapability;
-import com.jcloisterzone.game.capability.CastleCapability;
-import com.jcloisterzone.game.capability.CathedralCapability;
-import com.jcloisterzone.game.capability.ClothWineGrainCapability;
-import com.jcloisterzone.game.capability.CornCircleCapability;
-import com.jcloisterzone.game.capability.CountCapability;
-import com.jcloisterzone.game.capability.DragonCapability;
-import com.jcloisterzone.game.capability.FairyCapability;
-import com.jcloisterzone.game.capability.FestivalCapability;
-import com.jcloisterzone.game.capability.FlierCapability;
-import com.jcloisterzone.game.capability.InnCapability;
-import com.jcloisterzone.game.capability.KingScoutCapability;
-import com.jcloisterzone.game.capability.MayorCapability;
-import com.jcloisterzone.game.capability.PhantomCapability;
-import com.jcloisterzone.game.capability.PigCapability;
-import com.jcloisterzone.game.capability.PlagueCapability;
-import com.jcloisterzone.game.capability.PortalCapability;
-import com.jcloisterzone.game.capability.PrincessCapability;
-import com.jcloisterzone.game.capability.RiverCapability;
-import com.jcloisterzone.game.capability.ShrineCapability;
-import com.jcloisterzone.game.capability.SiegeCapability;
-import com.jcloisterzone.game.capability.TowerCapability;
-import com.jcloisterzone.game.capability.TunnelCapability;
-import com.jcloisterzone.game.capability.WagonCapability;
-import com.jcloisterzone.game.capability.WindRoseCapability;
-
-@SuppressWarnings("unchecked")
-public enum Expansion {
- //Basic sets
- BASIC("BA", _("Basic game")),
- WINTER("WI", _("Winter Edition")),
- WHEEL_OF_FORTUNE("WF", _("Wheel of Fortune"), false),
-
- //Big expansions
- INNS_AND_CATHEDRALS("IC", _("Inns & Cathedrals"),
- new Class[] { BigFollowerCapability.class ,InnCapability.class, CathedralCapability.class}),
- TRADERS_AND_BUILDERS("TB", _("Traders & Builders"),
- new Class[] { PigCapability.class, BuilderCapability.class, ClothWineGrainCapability.class }),
- PRINCESS_AND_DRAGON("DG", _("The Princess & the Dragon"),
- new Class[] { FairyCapability.class, DragonCapability.class, PortalCapability.class, PrincessCapability.class }),
- TOWER("TO", _("The Tower"),
- new Class[] { TowerCapability.class }),
- ABBEY_AND_MAYOR("AM", _("Abbey & Mayor"),
- new Class[] { AbbeyCapability.class, WagonCapability.class, MayorCapability.class, BarnCapability.class }),
- CATAPULT("CA", _("The Catapult") + " (" + _("tiles only") + ")"),
- BRIDGES_CASTLES_AND_BAZAARS("BB", _("Bridges, Castles and Bazaars"),
- new Class[] { BridgeCapability.class, CastleCapability.class, BazaarCapability.class }),
-
- //Small expansion
- KING_AND_SCOUT("KS", _("King and Scout"), new Class[] { KingScoutCapability.class }),
- RIVER("R1", _("The River"), new Class[] { RiverCapability.class }),
- RIVER_II("R2", _("The River II"), new Class[] { RiverCapability.class }),
- CATHARS("SI", _("The Cathars / Siege"), new Class[] { SiegeCapability.class }),
- COUNT("CO", _("The Count of Carcassonne") + " (" + _("tiles only") + ")", new Class[] { CountCapability.class }),
- GQ11("GQ", _("The Mini Expansion (GQ11)")),
- CULT("CU", _("The Cult"), new Class[] { ShrineCapability.class }),
- TUNNEL("TU", _("The Tunnel"), new Class[] { TunnelCapability.class }),
- CORN_CIRCLES("CC", _("The Corn Circles"), new Class[] { CornCircleCapability.class }),
- PLAGUE("PL", _("The Plague") + " (" + _("tiles only") + ")", new Class[] { /*PlagueCapability.class*/ }),
- PHANTOM("PH", _("The Phantom"), new Class[] { PhantomCapability.class }),
- FESTIVAL("FE", _("The Festival (10th an.)"), new Class[] { FestivalCapability.class }),
- HOUSES("LB", _("Little Buildings"), false),
- WIND_ROSE("WR", _("The Wind Rose"), new Class[] { WindRoseCapability.class }),
-
- //minis expansion line
- FLIER("FL", "#1 - " + _("The Flier"), new Class[] { FlierCapability.class }),
- MESSAGES("ME", "#2 - " + _("The Messages"), false),
- FERRIES("FR", "#3 - " + _("The Ferries"), false),
- GOLDMINES("GO", "#4 - " + _("The Goldmines"), false),
- MAGE_WITCH("MW", "#5 - " + _("Mage & Witch"), false),
- ROBBER("RO", "#6 - " + _("The Robber"), false),
- CORN_CIRCLES_II("C2", "#7 - " + _("The Corn circles II"), new Class[] { CornCircleCapability.class });
-
- //promo/one tile expansions
- //LA_PORXADA("PX", _("La porxada"), false),
- //SCHOOL("SC", _("The school"), false);
-
- String code;
- String label;
- boolean enabled = true;
- Class extends Capability>[] capabilities;
-
- Expansion(String code, String label) {
- this(code, label, null);
- }
-
- Expansion(String code, String label, boolean enabled) {
- this(code, label, null);
- this.enabled = enabled;
- }
-
- Expansion(String code, String label, Class extends Capability>[] capabilities) {
- this.code = code;
- this.label = label;
- this.capabilities = capabilities == null ? new Class[0] : capabilities;
- }
-
- public String getCode() {
- return code;
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public Class extends Capability>[] getCapabilities() {
- return capabilities;
- }
-
- @Override
- public String toString() {
- return label;
- }
-
- public static Expansion valueOfCode(String code) {
- for (Expansion exp : values()) {
- if (exp.code.equals(code)) return exp;
- }
- return null;
- }
+package com.jcloisterzone;
+
+import static com.jcloisterzone.ui.I18nUtils._;
+
+import com.jcloisterzone.game.Capability;
+import com.jcloisterzone.game.capability.AbbeyCapability;
+import com.jcloisterzone.game.capability.BarnCapability;
+import com.jcloisterzone.game.capability.BazaarCapability;
+import com.jcloisterzone.game.capability.BigFollowerCapability;
+import com.jcloisterzone.game.capability.BridgeCapability;
+import com.jcloisterzone.game.capability.BuilderCapability;
+import com.jcloisterzone.game.capability.CastleCapability;
+import com.jcloisterzone.game.capability.CathedralCapability;
+import com.jcloisterzone.game.capability.ClothWineGrainCapability;
+import com.jcloisterzone.game.capability.CornCircleCapability;
+import com.jcloisterzone.game.capability.CountCapability;
+import com.jcloisterzone.game.capability.DragonCapability;
+import com.jcloisterzone.game.capability.FairyCapability;
+import com.jcloisterzone.game.capability.FestivalCapability;
+import com.jcloisterzone.game.capability.FlierCapability;
+import com.jcloisterzone.game.capability.InnCapability;
+import com.jcloisterzone.game.capability.KingScoutCapability;
+import com.jcloisterzone.game.capability.MayorCapability;
+import com.jcloisterzone.game.capability.PhantomCapability;
+import com.jcloisterzone.game.capability.PigCapability;
+import com.jcloisterzone.game.capability.PlagueCapability;
+import com.jcloisterzone.game.capability.PortalCapability;
+import com.jcloisterzone.game.capability.PrincessCapability;
+import com.jcloisterzone.game.capability.RiverCapability;
+import com.jcloisterzone.game.capability.ShrineCapability;
+import com.jcloisterzone.game.capability.SiegeCapability;
+import com.jcloisterzone.game.capability.TowerCapability;
+import com.jcloisterzone.game.capability.TunnelCapability;
+import com.jcloisterzone.game.capability.WagonCapability;
+import com.jcloisterzone.game.capability.WindRoseCapability;
+
+@SuppressWarnings("unchecked")
+public enum Expansion {
+ //Basic sets
+ BASIC("BA", _("Basic game")),
+ WINTER("WI", _("Winter Edition")),
+ WHEEL_OF_FORTUNE("WF", _("Wheel of Fortune"), false),
+
+ //Big expansions
+ INNS_AND_CATHEDRALS("IC", _("Inns & Cathedrals"),
+ new Class[] { BigFollowerCapability.class ,InnCapability.class, CathedralCapability.class}),
+ TRADERS_AND_BUILDERS("TB", _("Traders & Builders"),
+ new Class[] { PigCapability.class, BuilderCapability.class, ClothWineGrainCapability.class }),
+ PRINCESS_AND_DRAGON("DG", _("The Princess & the Dragon"),
+ new Class[] { FairyCapability.class, DragonCapability.class, PortalCapability.class, PrincessCapability.class }),
+ TOWER("TO", _("The Tower"),
+ new Class[] { TowerCapability.class }),
+ ABBEY_AND_MAYOR("AM", _("Abbey & Mayor"),
+ new Class[] { AbbeyCapability.class, WagonCapability.class, MayorCapability.class, BarnCapability.class }),
+ CATAPULT("CA", _("The Catapult") + " (" + _("tiles only") + ")"),
+ BRIDGES_CASTLES_AND_BAZAARS("BB", _("Bridges, Castles and Bazaars"),
+ new Class[] { BridgeCapability.class, CastleCapability.class, BazaarCapability.class }),
+
+ //Small expansion
+ KING_AND_SCOUT("KS", _("King and Scout"), new Class[] { KingScoutCapability.class }),
+ RIVER("R1", _("The River"), new Class[] { RiverCapability.class }),
+ RIVER_II("R2", _("The River II"), new Class[] { RiverCapability.class }),
+ CATHARS("SI", _("The Cathars / Siege"), new Class[] { SiegeCapability.class }),
+ COUNT("CO", _("The Count of Carcassonne") + " (" + _("tiles only") + ")", new Class[] { CountCapability.class }),
+ GQ11("GQ", _("The Mini Expansion (GQ11)")),
+ CULT("CU", _("The Cult"), new Class[] { ShrineCapability.class }),
+ TUNNEL("TU", _("The Tunnel"), new Class[] { TunnelCapability.class }),
+ CORN_CIRCLES("CC", _("The Corn Circles"), new Class[] { CornCircleCapability.class }),
+ PLAGUE("PL", _("The Plague") + " (" + _("tiles only") + ")", new Class[] { /*PlagueCapability.class*/ }),
+ PHANTOM("PH", _("The Phantom"), new Class[] { PhantomCapability.class }),
+ FESTIVAL("FE", _("The Festival (10th an.)"), new Class[] { FestivalCapability.class }),
+ HOUSES("LB", _("Little Buildings"), false),
+ WIND_ROSE("WR", _("The Wind Rose"), new Class[] { WindRoseCapability.class }),
+
+ //minis expansion line
+ FLIER("FL", "#1 - " + _("The Flier"), new Class[] { FlierCapability.class }),
+ MESSAGES("ME", "#2 - " + _("The Messages"), false),
+ FERRIES("FR", "#3 - " + _("The Ferries"), false),
+ GOLDMINES("GO", "#4 - " + _("The Goldmines"), false),
+ MAGE_WITCH("MW", "#5 - " + _("Mage & Witch"), false),
+ ROBBER("RO", "#6 - " + _("The Robber"), false),
+ CORN_CIRCLES_II("C2", "#7 - " + _("The Corn circles II"), new Class[] { CornCircleCapability.class });
+
+ //promo/one tile expansions
+ //LA_PORXADA("PX", _("La porxada"), false),
+ //SCHOOL("SC", _("The school"), false);
+
+ String code;
+ String label;
+ boolean enabled = true;
+ Class extends Capability>[] capabilities;
+
+ Expansion(String code, String label) {
+ this(code, label, null);
+ }
+
+ Expansion(String code, String label, boolean enabled) {
+ this(code, label, null);
+ this.enabled = enabled;
+ }
+
+ Expansion(String code, String label, Class extends Capability>[] capabilities) {
+ this.code = code;
+ this.label = label;
+ this.capabilities = capabilities == null ? new Class[0] : capabilities;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public Class extends Capability>[] getCapabilities() {
+ return capabilities;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ public static Expansion valueOfCode(String code) {
+ for (Expansion exp : values()) {
+ if (exp.code.equals(code)) return exp;
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/FileTeeStream.java b/src/main/java/com/jcloisterzone/FileTeeStream.java
index a3bc46c61..d133dc34a 100644
--- a/src/main/java/com/jcloisterzone/FileTeeStream.java
+++ b/src/main/java/com/jcloisterzone/FileTeeStream.java
@@ -1,53 +1,53 @@
-package com.jcloisterzone;
-
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.PrintStream;
-
-public class FileTeeStream extends PrintStream {
-
- PrintStream fileOut;
- String fileName;
-
- public FileTeeStream(PrintStream err, String fileName) {
- super(err);
- this.fileName = fileName;
- }
-
- @Override
- public void write(byte buf[], int off, int len) {
- if (fileOut == null && fileName != null) {
- try {
- fileOut = new PrintStream(new FileOutputStream(fileName), true);
- } catch (FileNotFoundException e) {
- fileName = null;
- e.printStackTrace();
- }
- }
- super.write(buf, off, len);
- if (fileOut != null) {
- fileOut.write(buf, off, len);
- if (fileOut.checkError()) {
- System.err.println("File stream write error.");
- fileOut = null;
- fileName = null;
- }
- }
- }
-
- @Override
- public void flush() {
- super.flush();
- if (fileOut != null) {
- fileOut.flush();
- }
- }
-
- @Override
- public void close() {
- super.close();
- if (fileOut != null) {
- fileOut.close();
- }
- }
-}
+package com.jcloisterzone;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+
+public class FileTeeStream extends PrintStream {
+
+ PrintStream fileOut;
+ String fileName;
+
+ public FileTeeStream(PrintStream err, String fileName) {
+ super(err);
+ this.fileName = fileName;
+ }
+
+ @Override
+ public void write(byte buf[], int off, int len) {
+ if (fileOut == null && fileName != null) {
+ try {
+ fileOut = new PrintStream(new FileOutputStream(fileName), true);
+ } catch (FileNotFoundException e) {
+ fileName = null;
+ e.printStackTrace();
+ }
+ }
+ super.write(buf, off, len);
+ if (fileOut != null) {
+ fileOut.write(buf, off, len);
+ if (fileOut.checkError()) {
+ System.err.println("File stream write error.");
+ fileOut = null;
+ fileName = null;
+ }
+ }
+ }
+
+ @Override
+ public void flush() {
+ super.flush();
+ if (fileOut != null) {
+ fileOut.flush();
+ }
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ if (fileOut != null) {
+ fileOut.close();
+ }
+ }
+}
diff --git a/src/main/java/com/jcloisterzone/Player.java b/src/main/java/com/jcloisterzone/Player.java
index e4286ac50..62a9a1398 100644
--- a/src/main/java/com/jcloisterzone/Player.java
+++ b/src/main/java/com/jcloisterzone/Player.java
@@ -1,154 +1,154 @@
-package com.jcloisterzone;
-
-import java.io.Serializable;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-import com.jcloisterzone.figure.Follower;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.figure.SmallFollower;
-import com.jcloisterzone.figure.Special;
-import com.jcloisterzone.figure.predicate.MeeplePredicates;
-import com.jcloisterzone.game.PlayerSlot;
-
-
-/**
- * Represents one player in game. Contains information about figures, points and
- * control informations.
- *
- * @author Roman Krejcik
- */
-public class Player implements Serializable {
-
- private static final long serialVersionUID = -7276471952562769832L;
-
- private int points;
- private final Map pointStats = new HashMap<>();
-
- private final List followers = new ArrayList(SmallFollower.QUANTITY + 3);
- private final List specialMeeples = new ArrayList(3);
- private final Iterable meeples = Iterables.concat(followers, specialMeeples);
-
-
- final private String nick;
- final private int index;
- private PlayerSlot slot;
-
- public Player(String nick, int index, PlayerSlot slot) {
- this.nick = nick;
- this.index = index;
- this.slot = slot;
- }
-
- public void addMeeple(Meeple meeple) {
- if (meeple instanceof Follower) {
- followers.add((Follower) meeple);
- } else {
- specialMeeples.add((Special) meeple);
- }
- }
-
- public List getFollowers() {
- return followers;
- }
-
- public List getSpecialMeeples() {
- return specialMeeples;
- }
-
- public Iterable getMeeples() {
- return meeples;
- }
-
- public boolean hasSpecialMeeple(Class extends Special> clazz) {
- assert !Modifier.isAbstract(clazz.getModifiers());
- return Iterables.any(specialMeeples, Predicates.and(MeeplePredicates.inSupply(), MeeplePredicates.type(clazz)));
- }
-
- public boolean hasFollower() {
- return Iterables.any(followers, MeeplePredicates.inSupply());
- }
-
- public boolean hasFollower(Class extends Follower> clazz) {
- assert !Modifier.isAbstract(clazz.getModifiers());
- //chcek equality not instanceOf - phantom is subclass of small follower
- return Iterables.any(followers, Predicates.and(MeeplePredicates.inSupply(), MeeplePredicates.type(clazz)));
- }
-
-
- public Meeple getMeepleFromSupply(Class extends Meeple> clazz) {
- assert !Modifier.isAbstract(clazz.getModifiers());
- Iterable extends Meeple> collection = (Follower.class.isAssignableFrom(clazz) ? followers : specialMeeples);
- return Iterables.find(collection, Predicates.and(MeeplePredicates.inSupply(), MeeplePredicates.type(clazz)));
- }
-
- public void addPoints(int points, PointCategory category) {
- this.points += points;
- if (pointStats.containsKey(category)) {
- pointStats.put(category, pointStats.get(category) + points);
- } else {
- pointStats.put(category, points);
- }
- }
-
- public int getPoints() {
- return points;
- }
-
- public String getNick() {
- return nick;
- }
-
- @Override
- public String toString() {
- return nick + " " + points;
- }
-
- public int getIndex() {
- return index;
- }
-
- public Long getOwnerId() {
- return slot.getOwner();
- }
-
- public PlayerSlot getSlot() {
- return slot;
- }
- public void setSlot(PlayerSlot slot) {
- this.slot = slot;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o instanceof Player) {
- if (((Player) o).index == index && index != -1)
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(index, nick);
- }
-
- public void setPoints(int points) {
- this.points = points;
- }
-
-
- public int getPointsInCategory(PointCategory cat) {
- Integer points = pointStats.get(cat);
- return points == null ? 0 : points;
- }
-
-}
+package com.jcloisterzone;
+
+import java.io.Serializable;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.jcloisterzone.figure.Follower;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.figure.SmallFollower;
+import com.jcloisterzone.figure.Special;
+import com.jcloisterzone.figure.predicate.MeeplePredicates;
+import com.jcloisterzone.game.PlayerSlot;
+
+
+/**
+ * Represents one player in game. Contains information about figures, points and
+ * control informations.
+ *
+ * @author Roman Krejcik
+ */
+public class Player implements Serializable {
+
+ private static final long serialVersionUID = -7276471952562769832L;
+
+ private int points;
+ private final Map pointStats = new HashMap<>();
+
+ private final List followers = new ArrayList(SmallFollower.QUANTITY + 3);
+ private final List specialMeeples = new ArrayList(3);
+ private final Iterable meeples = Iterables.concat(followers, specialMeeples);
+
+
+ final private String nick;
+ final private int index;
+ private PlayerSlot slot;
+
+ public Player(String nick, int index, PlayerSlot slot) {
+ this.nick = nick;
+ this.index = index;
+ this.slot = slot;
+ }
+
+ public void addMeeple(Meeple meeple) {
+ if (meeple instanceof Follower) {
+ followers.add((Follower) meeple);
+ } else {
+ specialMeeples.add((Special) meeple);
+ }
+ }
+
+ public List getFollowers() {
+ return followers;
+ }
+
+ public List getSpecialMeeples() {
+ return specialMeeples;
+ }
+
+ public Iterable getMeeples() {
+ return meeples;
+ }
+
+ public boolean hasSpecialMeeple(Class extends Special> clazz) {
+ assert !Modifier.isAbstract(clazz.getModifiers());
+ return Iterables.any(specialMeeples, Predicates.and(MeeplePredicates.inSupply(), MeeplePredicates.type(clazz)));
+ }
+
+ public boolean hasFollower() {
+ return Iterables.any(followers, MeeplePredicates.inSupply());
+ }
+
+ public boolean hasFollower(Class extends Follower> clazz) {
+ assert !Modifier.isAbstract(clazz.getModifiers());
+ //chcek equality not instanceOf - phantom is subclass of small follower
+ return Iterables.any(followers, Predicates.and(MeeplePredicates.inSupply(), MeeplePredicates.type(clazz)));
+ }
+
+
+ public Meeple getMeepleFromSupply(Class extends Meeple> clazz) {
+ assert !Modifier.isAbstract(clazz.getModifiers());
+ Iterable extends Meeple> collection = (Follower.class.isAssignableFrom(clazz) ? followers : specialMeeples);
+ return Iterables.find(collection, Predicates.and(MeeplePredicates.inSupply(), MeeplePredicates.type(clazz)));
+ }
+
+ public void addPoints(int points, PointCategory category) {
+ this.points += points;
+ if (pointStats.containsKey(category)) {
+ pointStats.put(category, pointStats.get(category) + points);
+ } else {
+ pointStats.put(category, points);
+ }
+ }
+
+ public int getPoints() {
+ return points;
+ }
+
+ public String getNick() {
+ return nick;
+ }
+
+ @Override
+ public String toString() {
+ return nick + " " + points;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Long getOwnerId() {
+ return slot.getOwner();
+ }
+
+ public PlayerSlot getSlot() {
+ return slot;
+ }
+ public void setSlot(PlayerSlot slot) {
+ this.slot = slot;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o instanceof Player) {
+ if (((Player) o).index == index && index != -1)
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(index, nick);
+ }
+
+ public void setPoints(int points) {
+ this.points = points;
+ }
+
+
+ public int getPointsInCategory(PointCategory cat) {
+ Integer points = pointStats.get(cat);
+ return points == null ? 0 : points;
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/PlayerRestriction.java b/src/main/java/com/jcloisterzone/PlayerRestriction.java
index c2a27f1b9..32cda8648 100644
--- a/src/main/java/com/jcloisterzone/PlayerRestriction.java
+++ b/src/main/java/com/jcloisterzone/PlayerRestriction.java
@@ -1,32 +1,32 @@
-package com.jcloisterzone;
-
-/** represents set of players with rights to something */
-public class PlayerRestriction {
-
- final Player include;
- final Player exclude;
-
- private PlayerRestriction(Player include, Player exclude) {
- this.include = include;
- this.exclude = exclude;
- }
-
- public static PlayerRestriction any() {
- return new PlayerRestriction(null, null);
- }
-
- public static PlayerRestriction only(Player p) {
- return new PlayerRestriction(p, null);
- }
-
- public static PlayerRestriction except(Player p) {
- return new PlayerRestriction(null, p);
- }
-
- public boolean isAllowed(Player p) {
- if (exclude != null && p.equals(exclude)) return false;
- if (include != null && !p.equals(include)) return false;
- return true;
- }
-
-}
+package com.jcloisterzone;
+
+/** represents set of players with rights to something */
+public class PlayerRestriction {
+
+ final Player include;
+ final Player exclude;
+
+ private PlayerRestriction(Player include, Player exclude) {
+ this.include = include;
+ this.exclude = exclude;
+ }
+
+ public static PlayerRestriction any() {
+ return new PlayerRestriction(null, null);
+ }
+
+ public static PlayerRestriction only(Player p) {
+ return new PlayerRestriction(p, null);
+ }
+
+ public static PlayerRestriction except(Player p) {
+ return new PlayerRestriction(null, p);
+ }
+
+ public boolean isAllowed(Player p) {
+ if (exclude != null && p.equals(exclude)) return false;
+ if (include != null && !p.equals(include)) return false;
+ return true;
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/PointCategory.java b/src/main/java/com/jcloisterzone/PointCategory.java
index 43a70aa84..e52abc2eb 100644
--- a/src/main/java/com/jcloisterzone/PointCategory.java
+++ b/src/main/java/com/jcloisterzone/PointCategory.java
@@ -1,19 +1,19 @@
-package com.jcloisterzone;
-
-public enum PointCategory {
-
- ROAD,
- CITY,
- FARM,
- CLOISTER,
- CASTLE,
-
- TRADE_GOODS,
- FAIRY,
- TOWER_RANSOM,
- BIGGEST_CITY,
- LONGEST_ROAD,
- BAZAAR_AUCTION,
- WIND_ROSE
-
-}
+package com.jcloisterzone;
+
+public enum PointCategory {
+
+ ROAD,
+ CITY,
+ FARM,
+ CLOISTER,
+ CASTLE,
+
+ TRADE_GOODS,
+ FAIRY,
+ TOWER_RANSOM,
+ BIGGEST_CITY,
+ LONGEST_ROAD,
+ BAZAAR_AUCTION,
+ WIND_ROSE
+
+}
diff --git a/src/main/java/com/jcloisterzone/TradeResource.java b/src/main/java/com/jcloisterzone/TradeResource.java
index 92940ac15..1112829e5 100644
--- a/src/main/java/com/jcloisterzone/TradeResource.java
+++ b/src/main/java/com/jcloisterzone/TradeResource.java
@@ -1,9 +1,9 @@
-package com.jcloisterzone;
-
-public enum TradeResource {
-
- WINE,
- CLOTH,
- GRAIN
-
-}
+package com.jcloisterzone;
+
+public enum TradeResource {
+
+ WINE,
+ CLOTH,
+ GRAIN
+
+}
diff --git a/src/main/java/com/jcloisterzone/UserInterface.java b/src/main/java/com/jcloisterzone/UserInterface.java
index fafe9fa35..b98197adc 100644
--- a/src/main/java/com/jcloisterzone/UserInterface.java
+++ b/src/main/java/com/jcloisterzone/UserInterface.java
@@ -1,26 +1,26 @@
-package com.jcloisterzone;
-
-import java.util.EventListener;
-import java.util.List;
-import java.util.Set;
-
-import com.jcloisterzone.action.PlayerAction;
-import com.jcloisterzone.board.Position;
-
-
-public interface UserInterface extends EventListener {
-
- void selectAction(List actions, boolean canPass);
- void selectBazaarTile();
- void makeBazaarBid(int supplyIndex);
- void selectBuyOrSellBazaarOffer(int supplyIndex);
- void selectCornCircleOption();
-
- void showWarning(String title, String message);
-
- //TODO deprecated - use unified interface
- void selectDragonMove(Set positions, int movesLeft);
-
-
-
-}
+package com.jcloisterzone;
+
+import java.util.EventListener;
+import java.util.List;
+import java.util.Set;
+
+import com.jcloisterzone.action.PlayerAction;
+import com.jcloisterzone.board.Position;
+
+
+public interface UserInterface extends EventListener {
+
+ void selectAction(List actions, boolean canPass);
+ void selectBazaarTile();
+ void makeBazaarBid(int supplyIndex);
+ void selectBuyOrSellBazaarOffer(int supplyIndex);
+ void selectCornCircleOption();
+
+ void showWarning(String title, String message);
+
+ //TODO deprecated - use unified interface
+ void selectDragonMove(Set positions, int movesLeft);
+
+
+
+}
diff --git a/src/main/java/com/jcloisterzone/VersionComparator.java b/src/main/java/com/jcloisterzone/VersionComparator.java
index 9a0536262..3ad78e8cc 100644
--- a/src/main/java/com/jcloisterzone/VersionComparator.java
+++ b/src/main/java/com/jcloisterzone/VersionComparator.java
@@ -1,129 +1,129 @@
-package com.jcloisterzone;
-
-import java.util.Comparator;
-
-public class VersionComparator implements Comparator {
-
- public int compare(String version1, String version2) {
- VersionTokenizer tokenizer1 = new VersionTokenizer(version1);
- VersionTokenizer tokenizer2 = new VersionTokenizer(version2);
-
- int number1 = 0, number2 = 0;
- String suffix1 = "", suffix2 = "";
-
- while (tokenizer1.MoveNext()) {
- if (!tokenizer2.MoveNext()) {
- do {
- number1 = tokenizer1.getNumber();
- suffix1 = tokenizer1.getSuffix();
- if (number1 != 0 || suffix1.length() != 0) {
- // Version one is longer than number two, and non-zero
- return 1;
- }
- }
- while (tokenizer1.MoveNext());
-
- // Version one is longer than version two, but zero
- return 0;
- }
-
- number1 = tokenizer1.getNumber();
- suffix1 = tokenizer1.getSuffix();
- number2 = tokenizer2.getNumber();
- suffix2 = tokenizer2.getSuffix();
-
- if (number1 < number2) {
- // Number one is less than number two
- return -1;
- }
- if (number1 > number2) {
- // Number one is greater than number two
- return 1;
- }
-
- boolean empty1 = suffix1.length() == 0;
- boolean empty2 = suffix2.length() == 0;
-
- if (empty1 && empty2) continue; // No suffixes
- if (empty1) return 1; // First suffix is empty (1.2 > 1.2b)
- if (empty2) return -1; // Second suffix is empty (1.2a < 1.2)
-
- // Lexical comparison of suffixes
- int result = suffix1.compareTo(suffix2);
- if (result != 0) return result;
-
- }
- if (tokenizer2.MoveNext()) {
- do {
- number2 = tokenizer2.getNumber();
- suffix2 = tokenizer2.getSuffix();
- if (number2 != 0 || suffix2.length() != 0) {
- // Version one is longer than version two, and non-zero
- return -1;
- }
- }
- while (tokenizer2.MoveNext());
-
- // Version two is longer than version one, but zero
- return 0;
- }
- return 0;
- }
-
- private static class VersionTokenizer {
- private final String _versionString;
- private final int _length;
-
- private int _position;
- private int _number;
- private String _suffix;
-
- public int getNumber() {
- return _number;
- }
-
- public String getSuffix() {
- return _suffix;
- }
-
- public VersionTokenizer(String versionString) {
- if (versionString == null)
- throw new IllegalArgumentException("versionString is null");
-
- _versionString = versionString;
- _length = versionString.length();
- }
-
- public boolean MoveNext() {
- _number = 0;
- _suffix = "";
-
- // No more characters
- if (_position >= _length)
- return false;
-
-
-
- while (_position < _length) {
- char c = _versionString.charAt(_position);
- if (c < '0' || c > '9') break;
- _number = _number * 10 + (c - '0');
- _position++;
- }
-
- int suffixStart = _position;
-
- while (_position < _length) {
- char c = _versionString.charAt(_position);
- if (c == '.') break;
- _position++;
- }
-
- _suffix = _versionString.substring(suffixStart, _position);
-
- if (_position < _length) _position++;
-
- return true;
- }
- }
+package com.jcloisterzone;
+
+import java.util.Comparator;
+
+public class VersionComparator implements Comparator {
+
+ public int compare(String version1, String version2) {
+ VersionTokenizer tokenizer1 = new VersionTokenizer(version1);
+ VersionTokenizer tokenizer2 = new VersionTokenizer(version2);
+
+ int number1 = 0, number2 = 0;
+ String suffix1 = "", suffix2 = "";
+
+ while (tokenizer1.MoveNext()) {
+ if (!tokenizer2.MoveNext()) {
+ do {
+ number1 = tokenizer1.getNumber();
+ suffix1 = tokenizer1.getSuffix();
+ if (number1 != 0 || suffix1.length() != 0) {
+ // Version one is longer than number two, and non-zero
+ return 1;
+ }
+ }
+ while (tokenizer1.MoveNext());
+
+ // Version one is longer than version two, but zero
+ return 0;
+ }
+
+ number1 = tokenizer1.getNumber();
+ suffix1 = tokenizer1.getSuffix();
+ number2 = tokenizer2.getNumber();
+ suffix2 = tokenizer2.getSuffix();
+
+ if (number1 < number2) {
+ // Number one is less than number two
+ return -1;
+ }
+ if (number1 > number2) {
+ // Number one is greater than number two
+ return 1;
+ }
+
+ boolean empty1 = suffix1.length() == 0;
+ boolean empty2 = suffix2.length() == 0;
+
+ if (empty1 && empty2) continue; // No suffixes
+ if (empty1) return 1; // First suffix is empty (1.2 > 1.2b)
+ if (empty2) return -1; // Second suffix is empty (1.2a < 1.2)
+
+ // Lexical comparison of suffixes
+ int result = suffix1.compareTo(suffix2);
+ if (result != 0) return result;
+
+ }
+ if (tokenizer2.MoveNext()) {
+ do {
+ number2 = tokenizer2.getNumber();
+ suffix2 = tokenizer2.getSuffix();
+ if (number2 != 0 || suffix2.length() != 0) {
+ // Version one is longer than version two, and non-zero
+ return -1;
+ }
+ }
+ while (tokenizer2.MoveNext());
+
+ // Version two is longer than version one, but zero
+ return 0;
+ }
+ return 0;
+ }
+
+ private static class VersionTokenizer {
+ private final String _versionString;
+ private final int _length;
+
+ private int _position;
+ private int _number;
+ private String _suffix;
+
+ public int getNumber() {
+ return _number;
+ }
+
+ public String getSuffix() {
+ return _suffix;
+ }
+
+ public VersionTokenizer(String versionString) {
+ if (versionString == null)
+ throw new IllegalArgumentException("versionString is null");
+
+ _versionString = versionString;
+ _length = versionString.length();
+ }
+
+ public boolean MoveNext() {
+ _number = 0;
+ _suffix = "";
+
+ // No more characters
+ if (_position >= _length)
+ return false;
+
+
+
+ while (_position < _length) {
+ char c = _versionString.charAt(_position);
+ if (c < '0' || c > '9') break;
+ _number = _number * 10 + (c - '0');
+ _position++;
+ }
+
+ int suffixStart = _position;
+
+ while (_position < _length) {
+ char c = _versionString.charAt(_position);
+ if (c == '.') break;
+ _position++;
+ }
+
+ _suffix = _versionString.substring(suffixStart, _position);
+
+ if (_position < _length) _position++;
+
+ return true;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/XmlUtils.java b/src/main/java/com/jcloisterzone/XmlUtils.java
index 20504eeb4..a2ae4bb30 100644
--- a/src/main/java/com/jcloisterzone/XmlUtils.java
+++ b/src/main/java/com/jcloisterzone/XmlUtils.java
@@ -1,151 +1,151 @@
-package com.jcloisterzone;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.net.URL;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.game.SnapshotCorruptedException;
-
-
-public class XmlUtils {
-
-// public static Document parseDocument(String fileName) {
-// URL url = XmlUtils.class.getClassLoader().getResource(fileName);
-// return XmlUtils.parseDocument(url);
-// }
-
- public static Document parseDocument(URL url) {
- try (InputStream is = url.openStream()){
- return XmlUtils.parseDocument(is);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static Document parseDocument(InputStream is) {
- try {
- DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
- docBuilderFactory.setNamespaceAware(true);
- DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
- return docBuilder.parse(is);
- } catch (ParserConfigurationException | SAXException | IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static Document newDocument() {
- try {
- DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
- docBuilderFactory.setNamespaceAware(true);
- DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
- return builder.newDocument();
- } catch (ParserConfigurationException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static String childValue(Element parent, String childName) {
- NodeList nl = parent.getElementsByTagName(childName);
- if (nl.getLength() == 0) return null;
- return nl.item(0).getTextContent();
- }
-
- public static String nodeToString(Node node) {
- StringWriter sw = new StringWriter();
- try {
- Transformer t = TransformerFactory.newInstance().newTransformer();
- t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- t.setOutputProperty(OutputKeys.INDENT, "yes");
- t.transform(new DOMSource(node), new StreamResult(sw));
- } catch (TransformerException te) {
- throw new RuntimeException(te);
- }
- return sw.toString();
- }
-
- public static String[] asLocation(Element e) {
- return e.getFirstChild().getNodeValue().trim().split("\\s+");
- }
-
- public static String[] asLocations(Element e, String attr) {
- return e.getAttribute(attr).trim().split("\\s+");
- }
-
- public static String getTileId(Expansion expansion, Element xml) {
- return expansion.getCode() + "." + xml.getAttribute("id");
- }
-
- public static Location union(String[] locations) {
- Location u = null;
- for (String locStr : locations) {
- Location loc = Location.valueOf(locStr);
- u = loc.union(u);
- }
- return u;
- }
-
- public static boolean attributeBoolValue(Element e, String attr) {
- return e.getAttribute(attr).equals("yes") || e.getAttribute(attr).equals("true") || e.getAttribute(attr).equals("1");
- }
-
- public static int attributeIntValue(Element e, String attr) {
- return attributeIntValue(e, attr, null);
- }
-
- public static int attributeIntValue(Element e, String attr, Integer defaultValue) {
- if (!e.hasAttribute(attr)) {
- return defaultValue;
- }
- if (e.getAttribute(attr).equals("yes") || e.getAttribute(attr).equals("true")) {
- return 1;
- }
- return Integer.parseInt(e.getAttribute(attr));
- }
-
- public static String attributeStringValue(Element e, String attr, String defaultValue) {
- if (!e.hasAttribute(attr)) {
- return defaultValue;
- }
- return e.getAttribute(attr);
- }
-
- // Snapshot xml utils
-
- public static Class> classForName(String className) throws SnapshotCorruptedException {
- try {
- return Class.forName(className);
- } catch (ClassNotFoundException e) {
- throw new SnapshotCorruptedException(e);
- }
- }
-
- public static void injectPosition(Element el, Position p) {
- el.setAttribute("x", "" + p.x);
- el.setAttribute("y", "" + p.y);
- }
-
- public static Position extractPosition(Element el) {
- int x = Integer.parseInt(el.getAttribute("x"));
- int y = Integer.parseInt(el.getAttribute("y"));
- return new Position(x, y);
- }
-}
+package com.jcloisterzone;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URL;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.game.SnapshotCorruptedException;
+
+
+public class XmlUtils {
+
+// public static Document parseDocument(String fileName) {
+// URL url = XmlUtils.class.getClassLoader().getResource(fileName);
+// return XmlUtils.parseDocument(url);
+// }
+
+ public static Document parseDocument(URL url) {
+ try (InputStream is = url.openStream()){
+ return XmlUtils.parseDocument(is);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Document parseDocument(InputStream is) {
+ try {
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ return docBuilder.parse(is);
+ } catch (ParserConfigurationException | SAXException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Document newDocument() {
+ try {
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
+ return builder.newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String childValue(Element parent, String childName) {
+ NodeList nl = parent.getElementsByTagName(childName);
+ if (nl.getLength() == 0) return null;
+ return nl.item(0).getTextContent();
+ }
+
+ public static String nodeToString(Node node) {
+ StringWriter sw = new StringWriter();
+ try {
+ Transformer t = TransformerFactory.newInstance().newTransformer();
+ t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ t.setOutputProperty(OutputKeys.INDENT, "yes");
+ t.transform(new DOMSource(node), new StreamResult(sw));
+ } catch (TransformerException te) {
+ throw new RuntimeException(te);
+ }
+ return sw.toString();
+ }
+
+ public static String[] asLocation(Element e) {
+ return e.getFirstChild().getNodeValue().trim().split("\\s+");
+ }
+
+ public static String[] asLocations(Element e, String attr) {
+ return e.getAttribute(attr).trim().split("\\s+");
+ }
+
+ public static String getTileId(Expansion expansion, Element xml) {
+ return expansion.getCode() + "." + xml.getAttribute("id");
+ }
+
+ public static Location union(String[] locations) {
+ Location u = null;
+ for (String locStr : locations) {
+ Location loc = Location.valueOf(locStr);
+ u = loc.union(u);
+ }
+ return u;
+ }
+
+ public static boolean attributeBoolValue(Element e, String attr) {
+ return e.getAttribute(attr).equals("yes") || e.getAttribute(attr).equals("true") || e.getAttribute(attr).equals("1");
+ }
+
+ public static int attributeIntValue(Element e, String attr) {
+ return attributeIntValue(e, attr, null);
+ }
+
+ public static int attributeIntValue(Element e, String attr, Integer defaultValue) {
+ if (!e.hasAttribute(attr)) {
+ return defaultValue;
+ }
+ if (e.getAttribute(attr).equals("yes") || e.getAttribute(attr).equals("true")) {
+ return 1;
+ }
+ return Integer.parseInt(e.getAttribute(attr));
+ }
+
+ public static String attributeStringValue(Element e, String attr, String defaultValue) {
+ if (!e.hasAttribute(attr)) {
+ return defaultValue;
+ }
+ return e.getAttribute(attr);
+ }
+
+ // Snapshot xml utils
+
+ public static Class> classForName(String className) throws SnapshotCorruptedException {
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new SnapshotCorruptedException(e);
+ }
+ }
+
+ public static void injectPosition(Element el, Position p) {
+ el.setAttribute("x", "" + p.x);
+ el.setAttribute("y", "" + p.y);
+ }
+
+ public static Position extractPosition(Element el) {
+ int x = Integer.parseInt(el.getAttribute("x"));
+ int y = Integer.parseInt(el.getAttribute("y"));
+ return new Position(x, y);
+ }
+}
diff --git a/src/main/java/com/jcloisterzone/action/AbbeyPlacementAction.java b/src/main/java/com/jcloisterzone/action/AbbeyPlacementAction.java
index d038502ed..121bc4d64 100644
--- a/src/main/java/com/jcloisterzone/action/AbbeyPlacementAction.java
+++ b/src/main/java/com/jcloisterzone/action/AbbeyPlacementAction.java
@@ -1,34 +1,34 @@
-package com.jcloisterzone.action;
-
-import java.awt.Image;
-import java.util.Set;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.Rotation;
-import com.jcloisterzone.rmi.Client2ClientIF;
-import com.jcloisterzone.ui.grid.GridLayer;
-import com.jcloisterzone.ui.grid.layer.AbbeyPlacementLayer;
-
-public class AbbeyPlacementAction extends SelectTileAction {
-
- public AbbeyPlacementAction(Set sites) {
- super("abbeyplacement", sites);
- }
-
- @Override
- public Image getImage(Player player, boolean active) {
- return client.getResourceManager().getAbbeyImage();
- }
-
- @Override
- public void perform(Client2ClientIF server, Position p) {
- server.placeTile(Rotation.R0, p);
- }
-
- @Override
- protected GridLayer createGridLayer() {
- return new AbbeyPlacementLayer(client.getGridPanel(), this);
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.awt.Image;
+import java.util.Set;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.Rotation;
+import com.jcloisterzone.rmi.Client2ClientIF;
+import com.jcloisterzone.ui.grid.GridLayer;
+import com.jcloisterzone.ui.grid.layer.AbbeyPlacementLayer;
+
+public class AbbeyPlacementAction extends SelectTileAction {
+
+ public AbbeyPlacementAction(Set sites) {
+ super("abbeyplacement", sites);
+ }
+
+ @Override
+ public Image getImage(Player player, boolean active) {
+ return client.getResourceManager().getAbbeyImage();
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position p) {
+ server.placeTile(Rotation.R0, p);
+ }
+
+ @Override
+ protected GridLayer createGridLayer() {
+ return new AbbeyPlacementLayer(client.getGridPanel(), this);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/BarnAction.java b/src/main/java/com/jcloisterzone/action/BarnAction.java
index d015d7f91..99e6dd134 100644
--- a/src/main/java/com/jcloisterzone/action/BarnAction.java
+++ b/src/main/java/com/jcloisterzone/action/BarnAction.java
@@ -1,30 +1,30 @@
-package com.jcloisterzone.action;
-
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.figure.Barn;
-import com.jcloisterzone.rmi.Client2ClientIF;
-import com.jcloisterzone.ui.grid.GridLayer;
-import com.jcloisterzone.ui.grid.layer.BarnAreaLayer;
-
-
-public class BarnAction extends SelectFeatureAction {
-
- public BarnAction() {
- super("barn");
- }
-
- public void perform(Client2ClientIF server, Position p, Location d) {
- server.deployMeeple(p, d, Barn.class);
- }
-
- @Override
- protected GridLayer createGridLayer() {
- return new BarnAreaLayer(client.getGridPanel(), this);
- }
-
- @Override
- protected int getSortOrder() {
- return 9;
- }
-}
+package com.jcloisterzone.action;
+
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.figure.Barn;
+import com.jcloisterzone.rmi.Client2ClientIF;
+import com.jcloisterzone.ui.grid.GridLayer;
+import com.jcloisterzone.ui.grid.layer.BarnAreaLayer;
+
+
+public class BarnAction extends SelectFeatureAction {
+
+ public BarnAction() {
+ super("barn");
+ }
+
+ public void perform(Client2ClientIF server, Position p, Location d) {
+ server.deployMeeple(p, d, Barn.class);
+ }
+
+ @Override
+ protected GridLayer createGridLayer() {
+ return new BarnAreaLayer(client.getGridPanel(), this);
+ }
+
+ @Override
+ protected int getSortOrder() {
+ return 9;
+ }
+}
diff --git a/src/main/java/com/jcloisterzone/action/BridgeAction.java b/src/main/java/com/jcloisterzone/action/BridgeAction.java
index 4f5d479bf..a2b77cc53 100644
--- a/src/main/java/com/jcloisterzone/action/BridgeAction.java
+++ b/src/main/java/com/jcloisterzone/action/BridgeAction.java
@@ -1,18 +1,18 @@
-package com.jcloisterzone.action;
-
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public class BridgeAction extends SelectFeatureAction {
-
- public BridgeAction() {
- super("bridge");
- }
-
- @Override
- public void perform(Client2ClientIF server, Position p, Location loc) {
- server.deployBridge(p, loc);
- }
-
-}
+package com.jcloisterzone.action;
+
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public class BridgeAction extends SelectFeatureAction {
+
+ public BridgeAction() {
+ super("bridge");
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position p, Location loc) {
+ server.deployBridge(p, loc);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/CastleAction.java b/src/main/java/com/jcloisterzone/action/CastleAction.java
index 45a8343d1..1871d26a7 100644
--- a/src/main/java/com/jcloisterzone/action/CastleAction.java
+++ b/src/main/java/com/jcloisterzone/action/CastleAction.java
@@ -1,19 +1,19 @@
-package com.jcloisterzone.action;
-
-import java.util.Set;
-
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public class CastleAction extends SelectFeatureAction {
-
- public CastleAction( Position position, Set sites) {
- super("castle", position, sites);
- }
-
- public void perform(Client2ClientIF server, Position pos, Location loc) {
- server.deployCastle(pos, loc);
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.util.Set;
+
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public class CastleAction extends SelectFeatureAction {
+
+ public CastleAction( Position position, Set sites) {
+ super("castle", position, sites);
+ }
+
+ public void perform(Client2ClientIF server, Position pos, Location loc) {
+ server.deployCastle(pos, loc);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/FairyAction.java b/src/main/java/com/jcloisterzone/action/FairyAction.java
index 78783bb5c..5fbf0e5f3 100644
--- a/src/main/java/com/jcloisterzone/action/FairyAction.java
+++ b/src/main/java/com/jcloisterzone/action/FairyAction.java
@@ -1,32 +1,32 @@
-package com.jcloisterzone.action;
-
-import java.awt.Image;
-
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.rmi.Client2ClientIF;
-import com.jcloisterzone.ui.grid.GridLayer;
-import com.jcloisterzone.ui.grid.layer.TileActionLayer;
-
-public class FairyAction extends SelectTileAction {
-
- public FairyAction() {
- super("fairy");
- }
-
- @Override
- public void perform(Client2ClientIF server, Position p) {
- server.moveFairy(p);
- }
-
- @Override
- protected int getSortOrder() {
- return 30;
- }
-
- @Override
- protected GridLayer createGridLayer() {
- Image gd = client.getControlsTheme().getActionDecoration("fairy");
- return new TileActionLayer(client.getGridPanel(), this, gd);
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.awt.Image;
+
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.rmi.Client2ClientIF;
+import com.jcloisterzone.ui.grid.GridLayer;
+import com.jcloisterzone.ui.grid.layer.TileActionLayer;
+
+public class FairyAction extends SelectTileAction {
+
+ public FairyAction() {
+ super("fairy");
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position p) {
+ server.moveFairy(p);
+ }
+
+ @Override
+ protected int getSortOrder() {
+ return 30;
+ }
+
+ @Override
+ protected GridLayer createGridLayer() {
+ Image gd = client.getControlsTheme().getActionDecoration("fairy");
+ return new TileActionLayer(client.getGridPanel(), this, gd);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/MeepleAction.java b/src/main/java/com/jcloisterzone/action/MeepleAction.java
index 69d7b8d26..ad1c7221e 100644
--- a/src/main/java/com/jcloisterzone/action/MeepleAction.java
+++ b/src/main/java/com/jcloisterzone/action/MeepleAction.java
@@ -1,58 +1,58 @@
-package com.jcloisterzone.action;
-
-import java.util.Set;
-
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.collection.LocationsMap;
-import com.jcloisterzone.figure.BigFollower;
-import com.jcloisterzone.figure.Builder;
-import com.jcloisterzone.figure.Mayor;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.figure.Phantom;
-import com.jcloisterzone.figure.Pig;
-import com.jcloisterzone.figure.SmallFollower;
-import com.jcloisterzone.figure.Wagon;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public class MeepleAction extends SelectFeatureAction {
-
- private final Class extends Meeple> meepleType;
-
- public MeepleAction(Class extends Meeple> meepleType) {
- super(meepleType.getSimpleName().toLowerCase());
- this.meepleType = meepleType;
- }
-
- public MeepleAction(Class extends Meeple> meepleType, LocationsMap sites) {
- super(meepleType.getSimpleName().toLowerCase(), sites);
- this.meepleType = meepleType;
- }
-
- public MeepleAction(Class extends Meeple> meepleType, Position p, Set locations) {
- super(meepleType.getSimpleName().toLowerCase(), p, locations);
- this.meepleType = meepleType;
- }
-
- public Class extends Meeple> getMeepleType() {
- return meepleType;
- }
-
- @Override
- public void perform(Client2ClientIF server, Position p, Location d) {
- server.deployMeeple(p, d, meepleType);
- }
-
- @Override
- protected int getSortOrder() {
- if (meepleType.equals(SmallFollower.class)) return 10;
- if (meepleType.equals(BigFollower.class)) return 11;
- if (meepleType.equals(Wagon.class)) return 12;
- if (meepleType.equals(Mayor.class)) return 13;
- if (meepleType.equals(Builder.class)) return 14;
- if (meepleType.equals(Pig.class)) return 15;
- if (meepleType.equals(Phantom.class)) return 16;
- return 19;
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.util.Set;
+
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.collection.LocationsMap;
+import com.jcloisterzone.figure.BigFollower;
+import com.jcloisterzone.figure.Builder;
+import com.jcloisterzone.figure.Mayor;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.figure.Phantom;
+import com.jcloisterzone.figure.Pig;
+import com.jcloisterzone.figure.SmallFollower;
+import com.jcloisterzone.figure.Wagon;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public class MeepleAction extends SelectFeatureAction {
+
+ private final Class extends Meeple> meepleType;
+
+ public MeepleAction(Class extends Meeple> meepleType) {
+ super(meepleType.getSimpleName().toLowerCase());
+ this.meepleType = meepleType;
+ }
+
+ public MeepleAction(Class extends Meeple> meepleType, LocationsMap sites) {
+ super(meepleType.getSimpleName().toLowerCase(), sites);
+ this.meepleType = meepleType;
+ }
+
+ public MeepleAction(Class extends Meeple> meepleType, Position p, Set locations) {
+ super(meepleType.getSimpleName().toLowerCase(), p, locations);
+ this.meepleType = meepleType;
+ }
+
+ public Class extends Meeple> getMeepleType() {
+ return meepleType;
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position p, Location d) {
+ server.deployMeeple(p, d, meepleType);
+ }
+
+ @Override
+ protected int getSortOrder() {
+ if (meepleType.equals(SmallFollower.class)) return 10;
+ if (meepleType.equals(BigFollower.class)) return 11;
+ if (meepleType.equals(Wagon.class)) return 12;
+ if (meepleType.equals(Mayor.class)) return 13;
+ if (meepleType.equals(Builder.class)) return 14;
+ if (meepleType.equals(Pig.class)) return 15;
+ if (meepleType.equals(Phantom.class)) return 16;
+ return 19;
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/PlayerAction.java b/src/main/java/com/jcloisterzone/action/PlayerAction.java
index 64e87235b..6ccaf9014 100644
--- a/src/main/java/com/jcloisterzone/action/PlayerAction.java
+++ b/src/main/java/com/jcloisterzone/action/PlayerAction.java
@@ -1,77 +1,77 @@
-package com.jcloisterzone.action;
-
-import java.awt.Color;
-import java.awt.Image;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.ui.Client;
-import com.jcloisterzone.ui.grid.ForwardBackwardListener;
-import com.jcloisterzone.ui.grid.GridLayer;
-
-public abstract class PlayerAction implements Comparable, ForwardBackwardListener {
-
- private final String name;
-
- protected Client client;
- private GridLayer gridLayer;
-
- public PlayerAction(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public Image getImage(Player player, boolean active) {
- return getImage(active ? client.getPlayerColor(player) : Color.GRAY);
- }
-
- protected GridLayer createGridLayer() {
- return null;
- }
-
- /** Called when user select action in action panel */
- public void select() {
- gridLayer = createGridLayer();
- if (gridLayer != null) {
- client.getGridPanel().addLayer(gridLayer);
- }
- }
-
- /** Called when user deselect action in action panel */
- public void deselect() {
- if (gridLayer != null) {
- client.getGridPanel().clearActionDecorations();
- gridLayer = null;
- }
- }
-
- /** Called after right mouse click */
- public void forward() {
- client.getControlPanel().getActionPanel().rollAction(1);
- }
-
- public void backward() {
- client.getControlPanel().getActionPanel().rollAction(-1);
- }
-
- protected Image getImage(Color color) {
- return client.getFigureTheme().getActionImage(this, color);
- }
-
-
- protected int getSortOrder() {
- return 1024;
- }
-
- @Override
- public int compareTo(PlayerAction o) {
- return getSortOrder() - o.getSortOrder();
- }
-
- public void setClient(Client client) {
- this.client = client;
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.awt.Color;
+import java.awt.Image;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.ui.Client;
+import com.jcloisterzone.ui.grid.ForwardBackwardListener;
+import com.jcloisterzone.ui.grid.GridLayer;
+
+public abstract class PlayerAction implements Comparable, ForwardBackwardListener {
+
+ private final String name;
+
+ protected Client client;
+ private GridLayer gridLayer;
+
+ public PlayerAction(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Image getImage(Player player, boolean active) {
+ return getImage(active ? client.getPlayerColor(player) : Color.GRAY);
+ }
+
+ protected GridLayer createGridLayer() {
+ return null;
+ }
+
+ /** Called when user select action in action panel */
+ public void select() {
+ gridLayer = createGridLayer();
+ if (gridLayer != null) {
+ client.getGridPanel().addLayer(gridLayer);
+ }
+ }
+
+ /** Called when user deselect action in action panel */
+ public void deselect() {
+ if (gridLayer != null) {
+ client.getGridPanel().clearActionDecorations();
+ gridLayer = null;
+ }
+ }
+
+ /** Called after right mouse click */
+ public void forward() {
+ client.getControlPanel().getActionPanel().rollAction(1);
+ }
+
+ public void backward() {
+ client.getControlPanel().getActionPanel().rollAction(-1);
+ }
+
+ protected Image getImage(Color color) {
+ return client.getFigureTheme().getActionImage(this, color);
+ }
+
+
+ protected int getSortOrder() {
+ return 1024;
+ }
+
+ @Override
+ public int compareTo(PlayerAction o) {
+ return getSortOrder() - o.getSortOrder();
+ }
+
+ public void setClient(Client client) {
+ this.client = client;
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/PrincessAction.java b/src/main/java/com/jcloisterzone/action/PrincessAction.java
index a358c052f..ac7abfea7 100644
--- a/src/main/java/com/jcloisterzone/action/PrincessAction.java
+++ b/src/main/java/com/jcloisterzone/action/PrincessAction.java
@@ -1,16 +1,16 @@
-package com.jcloisterzone.action;
-
-import com.jcloisterzone.PlayerRestriction;
-
-
-public class PrincessAction extends UndeployAction {
-
- public PrincessAction() {
- super("princess", PlayerRestriction.any());
- }
-
- @Override
- protected int getSortOrder() {
- return 1;
- }
-}
+package com.jcloisterzone.action;
+
+import com.jcloisterzone.PlayerRestriction;
+
+
+public class PrincessAction extends UndeployAction {
+
+ public PrincessAction() {
+ super("princess", PlayerRestriction.any());
+ }
+
+ @Override
+ protected int getSortOrder() {
+ return 1;
+ }
+}
diff --git a/src/main/java/com/jcloisterzone/action/SelectFeatureAction.java b/src/main/java/com/jcloisterzone/action/SelectFeatureAction.java
index 8feddcbe3..e4c4aea77 100644
--- a/src/main/java/com/jcloisterzone/action/SelectFeatureAction.java
+++ b/src/main/java/com/jcloisterzone/action/SelectFeatureAction.java
@@ -1,56 +1,56 @@
-package com.jcloisterzone.action;
-
-import java.util.Collections;
-import java.util.Set;
-
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.collection.LocationsMap;
-import com.jcloisterzone.rmi.Client2ClientIF;
-import com.jcloisterzone.ui.grid.GridLayer;
-import com.jcloisterzone.ui.grid.layer.FeatureAreaLayer;
-
-public abstract class SelectFeatureAction extends PlayerAction {
-
- private final LocationsMap locMap;
-
- public SelectFeatureAction(String name) {
- this(name, new LocationsMap());
- }
-
- public SelectFeatureAction(String name, Position p, Set locations) {
- this(name);
- locMap.put(p, locations);
- }
-
- public SelectFeatureAction(String name, LocationsMap sites) {
- super(name);
- this.locMap = sites;
- }
-
- public LocationsMap getLocationsMap() {
- return locMap;
- }
-
- public Set get(Position p) {
- Set locs = locMap.get(p);
- return locs != null ? locs : Collections.emptySet();
- }
-
- public Set getOrCreate(Position p) {
- return locMap.getOrCreate(p);
- }
-
- @Override
- protected GridLayer createGridLayer() {
- return new FeatureAreaLayer(client.getGridPanel(), this);
- }
-
- public abstract void perform(Client2ClientIF server, Position p, Location loc);
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + '=' + locMap.toString();
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.util.Collections;
+import java.util.Set;
+
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.collection.LocationsMap;
+import com.jcloisterzone.rmi.Client2ClientIF;
+import com.jcloisterzone.ui.grid.GridLayer;
+import com.jcloisterzone.ui.grid.layer.FeatureAreaLayer;
+
+public abstract class SelectFeatureAction extends PlayerAction {
+
+ private final LocationsMap locMap;
+
+ public SelectFeatureAction(String name) {
+ this(name, new LocationsMap());
+ }
+
+ public SelectFeatureAction(String name, Position p, Set locations) {
+ this(name);
+ locMap.put(p, locations);
+ }
+
+ public SelectFeatureAction(String name, LocationsMap sites) {
+ super(name);
+ this.locMap = sites;
+ }
+
+ public LocationsMap getLocationsMap() {
+ return locMap;
+ }
+
+ public Set get(Position p) {
+ Set locs = locMap.get(p);
+ return locs != null ? locs : Collections.emptySet();
+ }
+
+ public Set getOrCreate(Position p) {
+ return locMap.getOrCreate(p);
+ }
+
+ @Override
+ protected GridLayer createGridLayer() {
+ return new FeatureAreaLayer(client.getGridPanel(), this);
+ }
+
+ public abstract void perform(Client2ClientIF server, Position p, Location loc);
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + '=' + locMap.toString();
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/SelectFollowerAction.java b/src/main/java/com/jcloisterzone/action/SelectFollowerAction.java
index 605558ee4..ea53748a9 100644
--- a/src/main/java/com/jcloisterzone/action/SelectFollowerAction.java
+++ b/src/main/java/com/jcloisterzone/action/SelectFollowerAction.java
@@ -1,40 +1,40 @@
-package com.jcloisterzone.action;
-
-import java.util.List;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.PlayerRestriction;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public abstract class SelectFollowerAction extends SelectFeatureAction {
-
- /** set null if anybody is allowed */
- private final PlayerRestriction players;
-
- public SelectFollowerAction(String name, PlayerRestriction players) {
- super(name);
- this.players = players;
- }
-
- public PlayerRestriction getPlayers() {
- return players;
- }
-
- @Override
- public final void perform(Client2ClientIF server, Position pos, Location loc) {
- List meeples = client.getGame().getBoard().get(pos).getFeature(loc).getMeeples();
- for (Meeple m : meeples) {
- if (players.isAllowed(m.getPlayer())) {
- perform(server, pos, loc, m.getClass(), m.getPlayer());
- return;
- }
- }
- throw new IllegalStateException("No legal meeple is placed on feature.");
- }
-
- public abstract void perform(Client2ClientIF server, Position pos, Location loc, Class extends Meeple> meepleType, Player owner);
-
-}
+package com.jcloisterzone.action;
+
+import java.util.List;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.PlayerRestriction;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public abstract class SelectFollowerAction extends SelectFeatureAction {
+
+ /** set null if anybody is allowed */
+ private final PlayerRestriction players;
+
+ public SelectFollowerAction(String name, PlayerRestriction players) {
+ super(name);
+ this.players = players;
+ }
+
+ public PlayerRestriction getPlayers() {
+ return players;
+ }
+
+ @Override
+ public final void perform(Client2ClientIF server, Position pos, Location loc) {
+ List meeples = client.getGame().getBoard().get(pos).getFeature(loc).getMeeples();
+ for (Meeple m : meeples) {
+ if (players.isAllowed(m.getPlayer())) {
+ perform(server, pos, loc, m.getClass(), m.getPlayer());
+ return;
+ }
+ }
+ throw new IllegalStateException("No legal meeple is placed on feature.");
+ }
+
+ public abstract void perform(Client2ClientIF server, Position pos, Location loc, Class extends Meeple> meepleType, Player owner);
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/SelectTileAction.java b/src/main/java/com/jcloisterzone/action/SelectTileAction.java
index b4f0da694..7834333de 100644
--- a/src/main/java/com/jcloisterzone/action/SelectTileAction.java
+++ b/src/main/java/com/jcloisterzone/action/SelectTileAction.java
@@ -1,28 +1,28 @@
-package com.jcloisterzone.action;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public abstract class SelectTileAction extends PlayerAction {
-
- private final Set sites;
-
- public SelectTileAction(String name) {
- super(name);
- this.sites = new HashSet<>();
- }
-
- public SelectTileAction(String name, Set sites) {
- super(name);
- this.sites = sites;
- }
-
- public Set getSites() {
- return sites;
- }
-
- public abstract void perform(Client2ClientIF server, Position p);
-}
+package com.jcloisterzone.action;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public abstract class SelectTileAction extends PlayerAction {
+
+ private final Set sites;
+
+ public SelectTileAction(String name) {
+ super(name);
+ this.sites = new HashSet<>();
+ }
+
+ public SelectTileAction(String name, Set sites) {
+ super(name);
+ this.sites = sites;
+ }
+
+ public Set getSites() {
+ return sites;
+ }
+
+ public abstract void perform(Client2ClientIF server, Position p);
+}
diff --git a/src/main/java/com/jcloisterzone/action/TakePrisonerAction.java b/src/main/java/com/jcloisterzone/action/TakePrisonerAction.java
index b7c19eef8..64e66954f 100644
--- a/src/main/java/com/jcloisterzone/action/TakePrisonerAction.java
+++ b/src/main/java/com/jcloisterzone/action/TakePrisonerAction.java
@@ -1,21 +1,21 @@
-package com.jcloisterzone.action;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.PlayerRestriction;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public class TakePrisonerAction extends SelectFollowerAction {
-
- public TakePrisonerAction(PlayerRestriction players) {
- super("takeprisoner", players);
- }
-
- @Override
- public void perform(Client2ClientIF server, Position pos, Location loc, Class extends Meeple> meepleType, Player owner) {
- server.takePrisoner(pos, loc, meepleType, owner.getIndex());
- }
-
-}
+package com.jcloisterzone.action;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.PlayerRestriction;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public class TakePrisonerAction extends SelectFollowerAction {
+
+ public TakePrisonerAction(PlayerRestriction players) {
+ super("takeprisoner", players);
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position pos, Location loc, Class extends Meeple> meepleType, Player owner) {
+ server.takePrisoner(pos, loc, meepleType, owner.getIndex());
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/TilePlacementAction.java b/src/main/java/com/jcloisterzone/action/TilePlacementAction.java
index dd809f79d..e77471365 100644
--- a/src/main/java/com/jcloisterzone/action/TilePlacementAction.java
+++ b/src/main/java/com/jcloisterzone/action/TilePlacementAction.java
@@ -1,82 +1,82 @@
-package com.jcloisterzone.action;
-
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.util.Map;
-import java.util.Set;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.Rotation;
-import com.jcloisterzone.board.Tile;
-import com.jcloisterzone.rmi.Client2ClientIF;
-import com.jcloisterzone.ui.UiUtils;
-import com.jcloisterzone.ui.controls.ActionPanel;
-import com.jcloisterzone.ui.grid.GridLayer;
-import com.jcloisterzone.ui.grid.layer.TilePlacementLayer;
-
-public class TilePlacementAction extends PlayerAction {
-
- private final Tile tile;
- private final Map> placements;
-
- private Rotation tileRotation = Rotation.R0;
-
- public TilePlacementAction(Tile tile, Map> placements) {
- super("tileplacement");
- this.tile = tile;
- this.placements = placements;
- }
-
- public Tile getTile() {
- return tile;
- }
-
- public Rotation getTileRotation() {
- return tileRotation;
- }
-
- public Map> getAvailablePlacements() {
- return placements;
- }
-
- @Override
- public Image getImage(Player player, boolean active) {
- Image img = client.getResourceManager().getTileImage(tile);
- int w = img.getWidth(null), h = img.getHeight(null);
- BufferedImage bi = UiUtils.newTransparentImage(w, h);
- AffineTransform at = tileRotation.getAffineTransform(w);
- Graphics2D ig = bi.createGraphics();
- ig.drawImage(img, at, null);
- ig.setColor(Color.BLACK);
- ig.drawRect(0, 0, w-1, h-1);
- return bi;
- }
-
- public void perform(Client2ClientIF server, Rotation rotation, Position p) {
- server.placeTile(rotation, p);
- }
-
- @Override
- protected GridLayer createGridLayer() {
- return new TilePlacementLayer(client.getGridPanel(), this);
- }
-
- @Override
- public void forward() {
- tileRotation = tileRotation.next();
- ActionPanel panel = client.getControlPanel().getActionPanel();
- panel.refreshImageCache();
- }
-
- @Override
- public void backward() {
- tileRotation = tileRotation.prev();
- ActionPanel panel = client.getControlPanel().getActionPanel();
- panel.refreshImageCache();
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.util.Map;
+import java.util.Set;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.Rotation;
+import com.jcloisterzone.board.Tile;
+import com.jcloisterzone.rmi.Client2ClientIF;
+import com.jcloisterzone.ui.UiUtils;
+import com.jcloisterzone.ui.controls.ActionPanel;
+import com.jcloisterzone.ui.grid.GridLayer;
+import com.jcloisterzone.ui.grid.layer.TilePlacementLayer;
+
+public class TilePlacementAction extends PlayerAction {
+
+ private final Tile tile;
+ private final Map> placements;
+
+ private Rotation tileRotation = Rotation.R0;
+
+ public TilePlacementAction(Tile tile, Map> placements) {
+ super("tileplacement");
+ this.tile = tile;
+ this.placements = placements;
+ }
+
+ public Tile getTile() {
+ return tile;
+ }
+
+ public Rotation getTileRotation() {
+ return tileRotation;
+ }
+
+ public Map> getAvailablePlacements() {
+ return placements;
+ }
+
+ @Override
+ public Image getImage(Player player, boolean active) {
+ Image img = client.getResourceManager().getTileImage(tile);
+ int w = img.getWidth(null), h = img.getHeight(null);
+ BufferedImage bi = UiUtils.newTransparentImage(w, h);
+ AffineTransform at = tileRotation.getAffineTransform(w);
+ Graphics2D ig = bi.createGraphics();
+ ig.drawImage(img, at, null);
+ ig.setColor(Color.BLACK);
+ ig.drawRect(0, 0, w-1, h-1);
+ return bi;
+ }
+
+ public void perform(Client2ClientIF server, Rotation rotation, Position p) {
+ server.placeTile(rotation, p);
+ }
+
+ @Override
+ protected GridLayer createGridLayer() {
+ return new TilePlacementLayer(client.getGridPanel(), this);
+ }
+
+ @Override
+ public void forward() {
+ tileRotation = tileRotation.next();
+ ActionPanel panel = client.getControlPanel().getActionPanel();
+ panel.refreshImageCache();
+ }
+
+ @Override
+ public void backward() {
+ tileRotation = tileRotation.prev();
+ ActionPanel panel = client.getControlPanel().getActionPanel();
+ panel.refreshImageCache();
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/TowerPieceAction.java b/src/main/java/com/jcloisterzone/action/TowerPieceAction.java
index 15f7ea49b..9aa4816ce 100644
--- a/src/main/java/com/jcloisterzone/action/TowerPieceAction.java
+++ b/src/main/java/com/jcloisterzone/action/TowerPieceAction.java
@@ -1,39 +1,39 @@
-package com.jcloisterzone.action;
-
-import java.awt.Image;
-import java.util.Set;
-
-import com.google.common.collect.Sets;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.rmi.Client2ClientIF;
-import com.jcloisterzone.ui.grid.GridLayer;
-import com.jcloisterzone.ui.grid.layer.TileActionLayer;
-
-
-public class TowerPieceAction extends SelectTileAction {
-
- public TowerPieceAction() {
- this(Sets.newHashSet());
- }
-
- public TowerPieceAction(Set sites) {
- super("towerpiece", sites);
- }
-
- @Override
- public void perform(Client2ClientIF server, Position p) {
- server.placeTowerPiece(p);
- }
-
- @Override
- protected int getSortOrder() {
- return 20;
- }
-
- @Override
- protected GridLayer createGridLayer() {
- Image gd = client.getControlsTheme().getActionDecoration("tower");
- return new TileActionLayer(client.getGridPanel(), this, gd);
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.awt.Image;
+import java.util.Set;
+
+import com.google.common.collect.Sets;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.rmi.Client2ClientIF;
+import com.jcloisterzone.ui.grid.GridLayer;
+import com.jcloisterzone.ui.grid.layer.TileActionLayer;
+
+
+public class TowerPieceAction extends SelectTileAction {
+
+ public TowerPieceAction() {
+ this(Sets.newHashSet());
+ }
+
+ public TowerPieceAction(Set sites) {
+ super("towerpiece", sites);
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position p) {
+ server.placeTowerPiece(p);
+ }
+
+ @Override
+ protected int getSortOrder() {
+ return 20;
+ }
+
+ @Override
+ protected GridLayer createGridLayer() {
+ Image gd = client.getControlsTheme().getActionDecoration("tower");
+ return new TileActionLayer(client.getGridPanel(), this, gd);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/TunnelAction.java b/src/main/java/com/jcloisterzone/action/TunnelAction.java
index 182306bcc..3e0aa2e1a 100644
--- a/src/main/java/com/jcloisterzone/action/TunnelAction.java
+++ b/src/main/java/com/jcloisterzone/action/TunnelAction.java
@@ -1,44 +1,44 @@
-package com.jcloisterzone.action;
-
-import java.awt.Image;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.collection.LocationsMap;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public class TunnelAction extends SelectFeatureAction {
-
- private final boolean secondTunnelPiece;
-
- public TunnelAction(boolean secondTunnelPiece, LocationsMap sites) {
- super("tunnel", sites);
- this.secondTunnelPiece = secondTunnelPiece;
- }
-
- @Override
- public Image getImage(Player player, boolean active) {
- if (active && isSecondTunnelPiece()) {
- return getImage(client.getPlayerSecondTunelColor(player));
- } else {
- return super.getImage(player, active);
- }
- }
-
- public boolean isSecondTunnelPiece() {
- return secondTunnelPiece;
- }
-
- @Override
- public void perform(Client2ClientIF server, Position p, Location d) {
- server.placeTunnelPiece(p, d, secondTunnelPiece);
-
- }
-
- @Override
- protected int getSortOrder() {
- return secondTunnelPiece ? 41 : 40;
- }
-
-}
+package com.jcloisterzone.action;
+
+import java.awt.Image;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.collection.LocationsMap;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public class TunnelAction extends SelectFeatureAction {
+
+ private final boolean secondTunnelPiece;
+
+ public TunnelAction(boolean secondTunnelPiece, LocationsMap sites) {
+ super("tunnel", sites);
+ this.secondTunnelPiece = secondTunnelPiece;
+ }
+
+ @Override
+ public Image getImage(Player player, boolean active) {
+ if (active && isSecondTunnelPiece()) {
+ return getImage(client.getPlayerSecondTunelColor(player));
+ } else {
+ return super.getImage(player, active);
+ }
+ }
+
+ public boolean isSecondTunnelPiece() {
+ return secondTunnelPiece;
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position p, Location d) {
+ server.placeTunnelPiece(p, d, secondTunnelPiece);
+
+ }
+
+ @Override
+ protected int getSortOrder() {
+ return secondTunnelPiece ? 41 : 40;
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/action/UndeployAction.java b/src/main/java/com/jcloisterzone/action/UndeployAction.java
index fa86dfb51..198c8ff1a 100644
--- a/src/main/java/com/jcloisterzone/action/UndeployAction.java
+++ b/src/main/java/com/jcloisterzone/action/UndeployAction.java
@@ -1,21 +1,21 @@
-package com.jcloisterzone.action;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.PlayerRestriction;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.rmi.Client2ClientIF;
-
-public class UndeployAction extends SelectFollowerAction {
-
- public UndeployAction(String name, PlayerRestriction players) {
- super(name, players);
- }
-
- @Override
- public void perform(Client2ClientIF server, Position pos, Location loc, Class extends Meeple> meepleType, Player owner) {
- server.undeployMeeple(pos, loc, meepleType, owner.getIndex());
- }
-
-}
+package com.jcloisterzone.action;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.PlayerRestriction;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.rmi.Client2ClientIF;
+
+public class UndeployAction extends SelectFollowerAction {
+
+ public UndeployAction(String name, PlayerRestriction players) {
+ super(name, players);
+ }
+
+ @Override
+ public void perform(Client2ClientIF server, Position pos, Location loc, Class extends Meeple> meepleType, Player owner) {
+ server.undeployMeeple(pos, loc, meepleType, owner.getIndex());
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/AiPlayer.java b/src/main/java/com/jcloisterzone/ai/AiPlayer.java
index d98cca442..a7257eeeb 100644
--- a/src/main/java/com/jcloisterzone/ai/AiPlayer.java
+++ b/src/main/java/com/jcloisterzone/ai/AiPlayer.java
@@ -1,164 +1,164 @@
-package com.jcloisterzone.ai;
-
-import java.lang.reflect.Proxy;
-import java.util.List;
-import java.util.Set;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.UserInterface;
-import com.jcloisterzone.action.AbbeyPlacementAction;
-import com.jcloisterzone.action.MeepleAction;
-import com.jcloisterzone.action.PlayerAction;
-import com.jcloisterzone.action.TakePrisonerAction;
-import com.jcloisterzone.action.TilePlacementAction;
-import com.jcloisterzone.board.Board;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.TilePack;
-import com.jcloisterzone.feature.City;
-import com.jcloisterzone.feature.Cloister;
-import com.jcloisterzone.feature.Feature;
-import com.jcloisterzone.feature.Road;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.game.Game;
-import com.jcloisterzone.rmi.ServerIF;
-import com.jcloisterzone.rmi.mina.ClientStub;
-
-public abstract class AiPlayer implements UserInterface {
-
- protected final transient Logger logger = LoggerFactory.getLogger(getClass());
-
- protected Game game;
-
- private ServerIF server;
- private ClientStub clientStub;
- private Player player;
-
- public void setGame(Game game) {
- this.game = game;
- }
-
- public Game getGame() {
- return game;
- }
-
- public ServerIF getServer() {
- return server;
- }
-
- public void setServer(ServerIF server) {
- Integer placeTileDelay = game.getConfig().get("players", "ai_place_tile_delay", Integer.class);
- this.server = new DelayedServer(server, placeTileDelay == null ? 0 : placeTileDelay);
- this.clientStub = (ClientStub) Proxy.getInvocationHandler(server);
- }
-
- public Player getPlayer() {
- return player;
- }
-
- public void setPlayer(Player player) {
- this.player = player;
- }
-
- protected Board getBoard() {
- return game.getBoard();
- }
-
- protected TilePack getTilePack() {
- return game.getTilePack();
- }
-
- protected ClientStub getClientStub() {
- return clientStub;
- }
-
- protected boolean isMe(Player p) {
- return player.equals(p);
- }
-
- public boolean isAiPlayerActive() {
- if (server == null) return false;
- return player.equals(game.getActivePlayer());
- }
-
- @Override
- public void showWarning(String title, String message) {
- //do nothing
- }
-
- protected void handleRuntimeError(Exception e) {
- logger.error("AI player exception", e);
- }
-
- // dummy implementations
-
- protected final void selectDummyAction(List actions, boolean canPass) {
- for (PlayerAction action : actions) {
- if (action instanceof TilePlacementAction) {
- if (selectDummyTilePlacement((TilePlacementAction) action)) return;
- }
- if (action instanceof AbbeyPlacementAction) {
- if (selectDummyAbbeyPlacement((AbbeyPlacementAction) action)) return;
- }
- if (action instanceof MeepleAction) {
- if (selectDummyMeepleAction((MeepleAction) action)) return;
- }
- if (action instanceof TakePrisonerAction) {
- if (selectDummyTowerCapture((TakePrisonerAction) action)) return;
- }
- }
- getServer().pass();
- }
-
- protected boolean selectDummyAbbeyPlacement(AbbeyPlacementAction action) {
- getServer().pass();
- return true;
- }
-
- protected boolean selectDummyTilePlacement(TilePlacementAction action) {
- Position nearest = null, p0 = new Position(0, 0);
- int min = Integer.MAX_VALUE;
- for (Position pos : action.getAvailablePlacements().keySet()) {
- int dist = pos.squareDistance(p0);
- if (dist < min) {
- min = dist;
- nearest = pos;
- }
- }
- getServer().placeTile(action.getAvailablePlacements().get(nearest).iterator().next(), nearest);
- return true;
- }
-
- protected boolean selectDummyMeepleAction(MeepleAction ma) {
- Position p = ma.getLocationsMap().keySet().iterator().next();
- for (Location loc : ma.getLocationsMap().get(p)) {
- Feature f = getBoard().get(p).getFeature(loc);
- if (f instanceof City || f instanceof Road || f instanceof Cloister) {
- getServer().deployMeeple(p, loc, ma.getMeepleType());
- return true;
- }
- }
- return false;
- }
-
- protected boolean selectDummyTowerCapture(TakePrisonerAction action) {
- Position p = action.getLocationsMap().keySet().iterator().next();
- Location loc = action.getLocationsMap().get(p).iterator().next();
- Meeple m = getBoard().get(p).getFeature(loc).getMeeples().get(0);
- getServer().takePrisoner(p, loc, m.getClass(), m.getPlayer().getIndex());
- return true;
- }
-
- protected final void selectDummyDragonMove(Set positions, int movesLeft) {
- getServer().moveDragon(positions.iterator().next());
- }
-
- @Override
- public String toString() {
- return String.valueOf(player);
- }
-
-}
+package com.jcloisterzone.ai;
+
+import java.lang.reflect.Proxy;
+import java.util.List;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.UserInterface;
+import com.jcloisterzone.action.AbbeyPlacementAction;
+import com.jcloisterzone.action.MeepleAction;
+import com.jcloisterzone.action.PlayerAction;
+import com.jcloisterzone.action.TakePrisonerAction;
+import com.jcloisterzone.action.TilePlacementAction;
+import com.jcloisterzone.board.Board;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.TilePack;
+import com.jcloisterzone.feature.City;
+import com.jcloisterzone.feature.Cloister;
+import com.jcloisterzone.feature.Feature;
+import com.jcloisterzone.feature.Road;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.game.Game;
+import com.jcloisterzone.rmi.ServerIF;
+import com.jcloisterzone.rmi.mina.ClientStub;
+
+public abstract class AiPlayer implements UserInterface {
+
+ protected final transient Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected Game game;
+
+ private ServerIF server;
+ private ClientStub clientStub;
+ private Player player;
+
+ public void setGame(Game game) {
+ this.game = game;
+ }
+
+ public Game getGame() {
+ return game;
+ }
+
+ public ServerIF getServer() {
+ return server;
+ }
+
+ public void setServer(ServerIF server) {
+ Integer placeTileDelay = game.getConfig().get("players", "ai_place_tile_delay", Integer.class);
+ this.server = new DelayedServer(server, placeTileDelay == null ? 0 : placeTileDelay);
+ this.clientStub = (ClientStub) Proxy.getInvocationHandler(server);
+ }
+
+ public Player getPlayer() {
+ return player;
+ }
+
+ public void setPlayer(Player player) {
+ this.player = player;
+ }
+
+ protected Board getBoard() {
+ return game.getBoard();
+ }
+
+ protected TilePack getTilePack() {
+ return game.getTilePack();
+ }
+
+ protected ClientStub getClientStub() {
+ return clientStub;
+ }
+
+ protected boolean isMe(Player p) {
+ return player.equals(p);
+ }
+
+ public boolean isAiPlayerActive() {
+ if (server == null) return false;
+ return player.equals(game.getActivePlayer());
+ }
+
+ @Override
+ public void showWarning(String title, String message) {
+ //do nothing
+ }
+
+ protected void handleRuntimeError(Exception e) {
+ logger.error("AI player exception", e);
+ }
+
+ // dummy implementations
+
+ protected final void selectDummyAction(List actions, boolean canPass) {
+ for (PlayerAction action : actions) {
+ if (action instanceof TilePlacementAction) {
+ if (selectDummyTilePlacement((TilePlacementAction) action)) return;
+ }
+ if (action instanceof AbbeyPlacementAction) {
+ if (selectDummyAbbeyPlacement((AbbeyPlacementAction) action)) return;
+ }
+ if (action instanceof MeepleAction) {
+ if (selectDummyMeepleAction((MeepleAction) action)) return;
+ }
+ if (action instanceof TakePrisonerAction) {
+ if (selectDummyTowerCapture((TakePrisonerAction) action)) return;
+ }
+ }
+ getServer().pass();
+ }
+
+ protected boolean selectDummyAbbeyPlacement(AbbeyPlacementAction action) {
+ getServer().pass();
+ return true;
+ }
+
+ protected boolean selectDummyTilePlacement(TilePlacementAction action) {
+ Position nearest = null, p0 = new Position(0, 0);
+ int min = Integer.MAX_VALUE;
+ for (Position pos : action.getAvailablePlacements().keySet()) {
+ int dist = pos.squareDistance(p0);
+ if (dist < min) {
+ min = dist;
+ nearest = pos;
+ }
+ }
+ getServer().placeTile(action.getAvailablePlacements().get(nearest).iterator().next(), nearest);
+ return true;
+ }
+
+ protected boolean selectDummyMeepleAction(MeepleAction ma) {
+ Position p = ma.getLocationsMap().keySet().iterator().next();
+ for (Location loc : ma.getLocationsMap().get(p)) {
+ Feature f = getBoard().get(p).getFeature(loc);
+ if (f instanceof City || f instanceof Road || f instanceof Cloister) {
+ getServer().deployMeeple(p, loc, ma.getMeepleType());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean selectDummyTowerCapture(TakePrisonerAction action) {
+ Position p = action.getLocationsMap().keySet().iterator().next();
+ Location loc = action.getLocationsMap().get(p).iterator().next();
+ Meeple m = getBoard().get(p).getFeature(loc).getMeeples().get(0);
+ getServer().takePrisoner(p, loc, m.getClass(), m.getPlayer().getIndex());
+ return true;
+ }
+
+ protected final void selectDummyDragonMove(Set positions, int movesLeft) {
+ getServer().moveDragon(positions.iterator().next());
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(player);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/AiScoreContext.java b/src/main/java/com/jcloisterzone/ai/AiScoreContext.java
index 46e7b1433..68b155d49 100644
--- a/src/main/java/com/jcloisterzone/ai/AiScoreContext.java
+++ b/src/main/java/com/jcloisterzone/ai/AiScoreContext.java
@@ -1,10 +1,10 @@
-package com.jcloisterzone.ai;
-
-import com.jcloisterzone.feature.visitor.score.ScoreContext;
-
-public interface AiScoreContext extends ScoreContext {
-
- boolean isValid();
- void setValid(boolean valid);
-
-}
+package com.jcloisterzone.ai;
+
+import com.jcloisterzone.feature.visitor.score.ScoreContext;
+
+public interface AiScoreContext extends ScoreContext {
+
+ boolean isValid();
+ void setValid(boolean valid);
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/AiUserInterfaceAdapter.java b/src/main/java/com/jcloisterzone/ai/AiUserInterfaceAdapter.java
index 18346c5b4..8eceb8e97 100644
--- a/src/main/java/com/jcloisterzone/ai/AiUserInterfaceAdapter.java
+++ b/src/main/java/com/jcloisterzone/ai/AiUserInterfaceAdapter.java
@@ -1,79 +1,79 @@
-package com.jcloisterzone.ai;
-
-import java.util.List;
-import java.util.Set;
-
-import com.jcloisterzone.UserInterface;
-import com.jcloisterzone.action.PlayerAction;
-import com.jcloisterzone.board.Position;
-
-/**
- * Dummy fallback prevent application freezing for some AiPlayers implementation errors
- */
-public class AiUserInterfaceAdapter implements UserInterface {
-
- private AiPlayer aiPlayer;
-
- public AiUserInterfaceAdapter(AiPlayer aiPlayer) {
- this.aiPlayer = aiPlayer;
- }
-
- public AiPlayer getAiPlayer() {
- return aiPlayer;
- }
-
- public void setAiPlayer(AiPlayer aiPlayer) {
- this.aiPlayer = aiPlayer;
- }
-
- @Override
- public void selectAction(List actions, boolean canPass) {
- if (aiPlayer.isAiPlayerActive()) {
- try {
- aiPlayer.selectAction(actions, canPass);
- } catch (Exception e) {
- aiPlayer.handleRuntimeError(e);
- aiPlayer.selectDummyAction(actions, canPass);
- }
- }
- }
-
- @Override
- public void selectBazaarTile() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void makeBazaarBid(int supplyIndex) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectBuyOrSellBazaarOffer(int supplyIndex) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectCornCircleOption() {
- throw new UnsupportedOperationException();
- }
-
-
- @Override
- public void selectDragonMove(Set positions, int movesLeft) {
- if (aiPlayer.isAiPlayerActive()) {
- try {
- aiPlayer.selectDragonMove(positions, movesLeft);
- } catch (Exception e) {
- aiPlayer.handleRuntimeError(e);
- aiPlayer.selectDummyDragonMove(positions, movesLeft);
- }
- }
- }
-
- @Override
- public void showWarning(String title, String message) {
- aiPlayer.showWarning(title, message);
- }
-
-}
+package com.jcloisterzone.ai;
+
+import java.util.List;
+import java.util.Set;
+
+import com.jcloisterzone.UserInterface;
+import com.jcloisterzone.action.PlayerAction;
+import com.jcloisterzone.board.Position;
+
+/**
+ * Dummy fallback prevent application freezing for some AiPlayers implementation errors
+ */
+public class AiUserInterfaceAdapter implements UserInterface {
+
+ private AiPlayer aiPlayer;
+
+ public AiUserInterfaceAdapter(AiPlayer aiPlayer) {
+ this.aiPlayer = aiPlayer;
+ }
+
+ public AiPlayer getAiPlayer() {
+ return aiPlayer;
+ }
+
+ public void setAiPlayer(AiPlayer aiPlayer) {
+ this.aiPlayer = aiPlayer;
+ }
+
+ @Override
+ public void selectAction(List actions, boolean canPass) {
+ if (aiPlayer.isAiPlayerActive()) {
+ try {
+ aiPlayer.selectAction(actions, canPass);
+ } catch (Exception e) {
+ aiPlayer.handleRuntimeError(e);
+ aiPlayer.selectDummyAction(actions, canPass);
+ }
+ }
+ }
+
+ @Override
+ public void selectBazaarTile() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void makeBazaarBid(int supplyIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectBuyOrSellBazaarOffer(int supplyIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectCornCircleOption() {
+ throw new UnsupportedOperationException();
+ }
+
+
+ @Override
+ public void selectDragonMove(Set positions, int movesLeft) {
+ if (aiPlayer.isAiPlayerActive()) {
+ try {
+ aiPlayer.selectDragonMove(positions, movesLeft);
+ } catch (Exception e) {
+ aiPlayer.handleRuntimeError(e);
+ aiPlayer.selectDummyDragonMove(positions, movesLeft);
+ }
+ }
+ }
+
+ @Override
+ public void showWarning(String title, String message) {
+ aiPlayer.showWarning(title, message);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/DelayedServer.java b/src/main/java/com/jcloisterzone/ai/DelayedServer.java
index c15c5ad6c..7d395ae2b 100644
--- a/src/main/java/com/jcloisterzone/ai/DelayedServer.java
+++ b/src/main/java/com/jcloisterzone/ai/DelayedServer.java
@@ -1,140 +1,140 @@
-package com.jcloisterzone.ai;
-
-import java.util.EnumSet;
-
-import com.jcloisterzone.Expansion;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.Rotation;
-import com.jcloisterzone.figure.Follower;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.game.CustomRule;
-import com.jcloisterzone.game.PlayerSlot;
-import com.jcloisterzone.rmi.ServerIF;
-
-public class DelayedServer implements ServerIF {
-
- private final ServerIF server;
- private final int placeTileDelay;
-
- public DelayedServer(ServerIF server, int placeTileDelay) {
- this.server = server;
- this.placeTileDelay = placeTileDelay;
- }
-
- @Override
- public void updateSlot(PlayerSlot slot,
- EnumSet supportedExpansions) {
- server.updateSlot(slot, supportedExpansions);
- }
-
- @Override
- public void selectTiles(int tilesCount, int drawCount) {
- server.selectTiles(tilesCount, drawCount);
- }
-
- @Override
- public void rollFlierDice() {
- server.rollFlierDice();
- }
-
- @Override
- public void updateExpansion(Expansion expansion, Boolean enabled) {
- server.updateExpansion(expansion, enabled);
- }
-
- @Override
- public void updateCustomRule(CustomRule rule, Boolean enabled) {
- server.updateCustomRule(rule, enabled);
- }
-
- @Override
- public void startGame() {
- server.startGame();
- }
-
- @Override
- public void pass() {
- server.pass();
- }
-
- @Override
- public void placeTile(Rotation rotation, Position position) {
- try {
- Thread.sleep(placeTileDelay);
- } catch (InterruptedException e) {}
- server.placeTile(rotation, position);
- }
-
- @Override
- public void deployMeeple(Position pos, Location loc,
- Class extends Meeple> meepleType) {
- server.deployMeeple(pos, loc, meepleType);
- }
-
- @Override
- public void undeployMeeple(Position pos, Location loc, Class extends Meeple> meepleType, Integer meepleOwner) {
- server.undeployMeeple(pos, loc, meepleType, meepleOwner);
- }
-
- @Override
- public void placeTowerPiece(Position pos) {
- server.placeTowerPiece(pos);
- }
-
- @Override
- public void takePrisoner(Position pos, Location loc, Class extends Meeple> meepleType, Integer meepleOwner) {
- server.takePrisoner(pos, loc, meepleType, meepleOwner);
- }
-
- @Override
- public void placeTunnelPiece(Position pos, Location loc,
- boolean isSecondPiece) {
- server.placeTunnelPiece(pos, loc, isSecondPiece);
- }
-
- @Override
- public void moveFairy(Position pos) {
- server.moveFairy(pos);
- }
-
- @Override
- public void moveDragon(Position pos) {
- try {
- Thread.sleep(placeTileDelay / 2);
- } catch (InterruptedException e) {}
- server.moveDragon(pos);
- }
-
- @Override
- public void payRansom(Integer playerIndexToPay,
- Class extends Follower> meepleType) {
- server.payRansom(playerIndexToPay, meepleType);
- }
-
- @Override
- public void deployBridge(Position pos, Location loc) {
- server.deployBridge(pos, loc);
- }
-
- @Override
- public void deployCastle(Position pos, Location loc) {
- server.deployCastle(pos, loc);
- }
-
- @Override
- public void bazaarBid(Integer supplyIndex, Integer price) {
- server.bazaarBid(supplyIndex, price);
- }
-
- @Override
- public void bazaarBuyOrSell(boolean buy) {
- server.bazaarBuyOrSell(buy);
- }
-
- @Override
- public void cornCiclesRemoveOrDeploy(boolean remove) {
- server.cornCiclesRemoveOrDeploy(remove);
- }
-
+package com.jcloisterzone.ai;
+
+import java.util.EnumSet;
+
+import com.jcloisterzone.Expansion;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.Rotation;
+import com.jcloisterzone.figure.Follower;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.game.CustomRule;
+import com.jcloisterzone.game.PlayerSlot;
+import com.jcloisterzone.rmi.ServerIF;
+
+public class DelayedServer implements ServerIF {
+
+ private final ServerIF server;
+ private final int placeTileDelay;
+
+ public DelayedServer(ServerIF server, int placeTileDelay) {
+ this.server = server;
+ this.placeTileDelay = placeTileDelay;
+ }
+
+ @Override
+ public void updateSlot(PlayerSlot slot,
+ EnumSet supportedExpansions) {
+ server.updateSlot(slot, supportedExpansions);
+ }
+
+ @Override
+ public void selectTiles(int tilesCount, int drawCount) {
+ server.selectTiles(tilesCount, drawCount);
+ }
+
+ @Override
+ public void rollFlierDice() {
+ server.rollFlierDice();
+ }
+
+ @Override
+ public void updateExpansion(Expansion expansion, Boolean enabled) {
+ server.updateExpansion(expansion, enabled);
+ }
+
+ @Override
+ public void updateCustomRule(CustomRule rule, Boolean enabled) {
+ server.updateCustomRule(rule, enabled);
+ }
+
+ @Override
+ public void startGame() {
+ server.startGame();
+ }
+
+ @Override
+ public void pass() {
+ server.pass();
+ }
+
+ @Override
+ public void placeTile(Rotation rotation, Position position) {
+ try {
+ Thread.sleep(placeTileDelay);
+ } catch (InterruptedException e) {}
+ server.placeTile(rotation, position);
+ }
+
+ @Override
+ public void deployMeeple(Position pos, Location loc,
+ Class extends Meeple> meepleType) {
+ server.deployMeeple(pos, loc, meepleType);
+ }
+
+ @Override
+ public void undeployMeeple(Position pos, Location loc, Class extends Meeple> meepleType, Integer meepleOwner) {
+ server.undeployMeeple(pos, loc, meepleType, meepleOwner);
+ }
+
+ @Override
+ public void placeTowerPiece(Position pos) {
+ server.placeTowerPiece(pos);
+ }
+
+ @Override
+ public void takePrisoner(Position pos, Location loc, Class extends Meeple> meepleType, Integer meepleOwner) {
+ server.takePrisoner(pos, loc, meepleType, meepleOwner);
+ }
+
+ @Override
+ public void placeTunnelPiece(Position pos, Location loc,
+ boolean isSecondPiece) {
+ server.placeTunnelPiece(pos, loc, isSecondPiece);
+ }
+
+ @Override
+ public void moveFairy(Position pos) {
+ server.moveFairy(pos);
+ }
+
+ @Override
+ public void moveDragon(Position pos) {
+ try {
+ Thread.sleep(placeTileDelay / 2);
+ } catch (InterruptedException e) {}
+ server.moveDragon(pos);
+ }
+
+ @Override
+ public void payRansom(Integer playerIndexToPay,
+ Class extends Follower> meepleType) {
+ server.payRansom(playerIndexToPay, meepleType);
+ }
+
+ @Override
+ public void deployBridge(Position pos, Location loc) {
+ server.deployBridge(pos, loc);
+ }
+
+ @Override
+ public void deployCastle(Position pos, Location loc) {
+ server.deployCastle(pos, loc);
+ }
+
+ @Override
+ public void bazaarBid(Integer supplyIndex, Integer price) {
+ server.bazaarBid(supplyIndex, price);
+ }
+
+ @Override
+ public void bazaarBuyOrSell(boolean buy) {
+ server.bazaarBuyOrSell(buy);
+ }
+
+ @Override
+ public void cornCiclesRemoveOrDeploy(boolean remove) {
+ server.cornCiclesRemoveOrDeploy(remove);
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/ai/DummyAiPlayer.java b/src/main/java/com/jcloisterzone/ai/DummyAiPlayer.java
index 00617a63f..7567bba45 100644
--- a/src/main/java/com/jcloisterzone/ai/DummyAiPlayer.java
+++ b/src/main/java/com/jcloisterzone/ai/DummyAiPlayer.java
@@ -1,41 +1,41 @@
-package com.jcloisterzone.ai;
-
-import java.util.List;
-import java.util.Set;
-
-import com.jcloisterzone.action.PlayerAction;
-import com.jcloisterzone.board.Position;
-
-public class DummyAiPlayer extends AiPlayer {
-
- @Override
- public void selectAction(List actions, boolean canPass) {
- selectDummyAction(actions, canPass);
- }
-
- @Override
- public void selectDragonMove(Set positions, int movesLeft) {
- selectDummyDragonMove(positions, movesLeft);
- }
-
- @Override
- public void selectBazaarTile() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void makeBazaarBid(int supplyIndex) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectBuyOrSellBazaarOffer(int supplyIndex) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectCornCircleOption() {
- throw new UnsupportedOperationException();
- }
-
-}
+package com.jcloisterzone.ai;
+
+import java.util.List;
+import java.util.Set;
+
+import com.jcloisterzone.action.PlayerAction;
+import com.jcloisterzone.board.Position;
+
+public class DummyAiPlayer extends AiPlayer {
+
+ @Override
+ public void selectAction(List actions, boolean canPass) {
+ selectDummyAction(actions, canPass);
+ }
+
+ @Override
+ public void selectDragonMove(Set positions, int movesLeft) {
+ selectDummyDragonMove(positions, movesLeft);
+ }
+
+ @Override
+ public void selectBazaarTile() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void makeBazaarBid(int supplyIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectBuyOrSellBazaarOffer(int supplyIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectCornCircleOption() {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/NotSupportedInteraction.java b/src/main/java/com/jcloisterzone/ai/NotSupportedInteraction.java
index cfb59f4f3..b73b6a470 100644
--- a/src/main/java/com/jcloisterzone/ai/NotSupportedInteraction.java
+++ b/src/main/java/com/jcloisterzone/ai/NotSupportedInteraction.java
@@ -1,47 +1,47 @@
-package com.jcloisterzone.ai;
-
-import java.util.List;
-import java.util.Set;
-
-import com.jcloisterzone.UserInterface;
-import com.jcloisterzone.action.PlayerAction;
-import com.jcloisterzone.board.Position;
-
-public class NotSupportedInteraction implements UserInterface {
-
- @Override
- public void selectAction(List actions, boolean canPass) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectBazaarTile() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void makeBazaarBid(int supplyIndex) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectBuyOrSellBazaarOffer(int supplyIndex) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectCornCircleOption() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void showWarning(String title, String message) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void selectDragonMove(Set positions, int movesLeft) {
- throw new UnsupportedOperationException();
- }
-
+package com.jcloisterzone.ai;
+
+import java.util.List;
+import java.util.Set;
+
+import com.jcloisterzone.UserInterface;
+import com.jcloisterzone.action.PlayerAction;
+import com.jcloisterzone.board.Position;
+
+public class NotSupportedInteraction implements UserInterface {
+
+ @Override
+ public void selectAction(List actions, boolean canPass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectBazaarTile() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void makeBazaarBid(int supplyIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectBuyOrSellBazaarOffer(int supplyIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectCornCircleOption() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void showWarning(String title, String message) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void selectDragonMove(Set positions, int movesLeft) {
+ throw new UnsupportedOperationException();
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/ai/PositionRanking.java b/src/main/java/com/jcloisterzone/ai/PositionRanking.java
index 14e3b0d83..2abb86bff 100644
--- a/src/main/java/com/jcloisterzone/ai/PositionRanking.java
+++ b/src/main/java/com/jcloisterzone/ai/PositionRanking.java
@@ -1,110 +1,110 @@
-package com.jcloisterzone.ai;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.List;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.action.MeepleAction;
-import com.jcloisterzone.action.PlayerAction;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.Rotation;
-import com.jcloisterzone.figure.Meeple;
-
-public class PositionRanking {
-
- public static class SelectedAction {
-
- public SelectedAction(PlayerAction action) {
- this(action, null, null, null, null);
- }
-
- public SelectedAction(PlayerAction action, Position position, Location location) {
- this(action, position, location, null, null);
- }
-
- public SelectedAction(PlayerAction action, Position actionPosition, Location actionLocation, Class extends Meeple> meepleType, Player meepleOwner) {
- this.action = action;
- this.position = actionPosition;
- this.location = actionLocation;
- this.meepleType = meepleType;
- this.meepleOwner = meepleOwner;
- }
-
- public PlayerAction action;
- public Position position;
- public Location location;
- public Class extends Meeple> meepleType;
- public Player meepleOwner;
- }
-
- private double rank;
-
- private Position position;
- private Rotation rotation;
-
- private Deque selectedActions = new LinkedList<>();
-
-
- public PositionRanking(double rank) {
- this.rank = rank;
- }
-
- public PositionRanking(double rank, Position position, Rotation rotation) {
- this.rank = rank;
- this.position = position;
- this.rotation = rotation;
- }
-
- public double getRank() {
- return rank;
- }
-
- public void setRank(double rank) {
- this.rank = rank;
- }
-
- public Position getPosition() {
- return position;
- }
-
- public void setPosition(Position position) {
- this.position = position;
- }
-
- public Rotation getRotation() {
- return rotation;
- }
-
- public void setRotation(Rotation rotation) {
- this.rotation = rotation;
- }
-
- public Deque getSelectedActions() {
- return selectedActions;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(rank);
- sb.append(" Pos: ").append(position);
- sb.append(" Rot: ").append(rotation);
- for (SelectedAction sa : selectedActions) {
- if (sa.action instanceof MeepleAction) {
- sb.append(" Meeple: ").append(((MeepleAction)sa.action).getMeepleType().getSimpleName());
- sb.append(" APos: ").append(sa.position);
- sb.append(" ALoc: ").append(sa.location);
- } else {
- sb.append(" Action: ").append(sa.action.getName());
- }
- }
- return sb.toString();
- }
-
-
-
-}
-
+package com.jcloisterzone.ai;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.action.MeepleAction;
+import com.jcloisterzone.action.PlayerAction;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.Rotation;
+import com.jcloisterzone.figure.Meeple;
+
+public class PositionRanking {
+
+ public static class SelectedAction {
+
+ public SelectedAction(PlayerAction action) {
+ this(action, null, null, null, null);
+ }
+
+ public SelectedAction(PlayerAction action, Position position, Location location) {
+ this(action, position, location, null, null);
+ }
+
+ public SelectedAction(PlayerAction action, Position actionPosition, Location actionLocation, Class extends Meeple> meepleType, Player meepleOwner) {
+ this.action = action;
+ this.position = actionPosition;
+ this.location = actionLocation;
+ this.meepleType = meepleType;
+ this.meepleOwner = meepleOwner;
+ }
+
+ public PlayerAction action;
+ public Position position;
+ public Location location;
+ public Class extends Meeple> meepleType;
+ public Player meepleOwner;
+ }
+
+ private double rank;
+
+ private Position position;
+ private Rotation rotation;
+
+ private Deque selectedActions = new LinkedList<>();
+
+
+ public PositionRanking(double rank) {
+ this.rank = rank;
+ }
+
+ public PositionRanking(double rank, Position position, Rotation rotation) {
+ this.rank = rank;
+ this.position = position;
+ this.rotation = rotation;
+ }
+
+ public double getRank() {
+ return rank;
+ }
+
+ public void setRank(double rank) {
+ this.rank = rank;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ public Rotation getRotation() {
+ return rotation;
+ }
+
+ public void setRotation(Rotation rotation) {
+ this.rotation = rotation;
+ }
+
+ public Deque getSelectedActions() {
+ return selectedActions;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(rank);
+ sb.append(" Pos: ").append(position);
+ sb.append(" Rot: ").append(rotation);
+ for (SelectedAction sa : selectedActions) {
+ if (sa.action instanceof MeepleAction) {
+ sb.append(" Meeple: ").append(((MeepleAction)sa.action).getMeepleType().getSimpleName());
+ sb.append(" APos: ").append(sa.position);
+ sb.append(" ALoc: ").append(sa.location);
+ } else {
+ sb.append(" Action: ").append(sa.action.getName());
+ }
+ }
+ return sb.toString();
+ }
+
+
+
+}
+
diff --git a/src/main/java/com/jcloisterzone/ai/RankingAiPlayer.java b/src/main/java/com/jcloisterzone/ai/RankingAiPlayer.java
index b35dc7f18..0b1f616ad 100644
--- a/src/main/java/com/jcloisterzone/ai/RankingAiPlayer.java
+++ b/src/main/java/com/jcloisterzone/ai/RankingAiPlayer.java
@@ -1,419 +1,419 @@
-package com.jcloisterzone.ai;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.jcloisterzone.UserInterface;
-import com.jcloisterzone.action.AbbeyPlacementAction;
-import com.jcloisterzone.action.BarnAction;
-import com.jcloisterzone.action.FairyAction;
-import com.jcloisterzone.action.MeepleAction;
-import com.jcloisterzone.action.PlayerAction;
-import com.jcloisterzone.action.SelectFollowerAction;
-import com.jcloisterzone.action.SelectTileAction;
-import com.jcloisterzone.action.TakePrisonerAction;
-import com.jcloisterzone.action.TilePlacementAction;
-import com.jcloisterzone.action.TowerPieceAction;
-import com.jcloisterzone.action.UndeployAction;
-import com.jcloisterzone.ai.PositionRanking.SelectedAction;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.Rotation;
-import com.jcloisterzone.board.Tile;
-import com.jcloisterzone.figure.Barn;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.game.Game;
-import com.jcloisterzone.game.PlayerSlot;
-import com.jcloisterzone.game.Snapshot;
-import com.jcloisterzone.game.phase.ActionPhase;
-import com.jcloisterzone.game.phase.EscapePhase;
-import com.jcloisterzone.game.phase.LoadGamePhase;
-import com.jcloisterzone.game.phase.Phase;
-import com.jcloisterzone.game.phase.TowerCapturePhase;
-
-public abstract class RankingAiPlayer extends AiPlayer {
-
- class PositionLocation {
- Position position;
- Location location;
- }
-
- private Game original;
- private SavePointManager spm;
-
- private UserInterface defaultInteractionHandler;
- private UserInterface interactionHandler;
-
- //private Map scoreCache = new HashMap<>();
- private PositionRanking bestSoFar;
- //private List hopefulGatePlacements = new ArrayList();
-
-// public Map getScoreCache() {
-// return scoreCache;
-// }
-
- public RankingAiPlayer() {
- defaultInteractionHandler = createDefaultInteractionHandler();
- interactionHandler = defaultInteractionHandler;
- }
-
- protected UserInterface createDefaultInteractionHandler() {
- return new DefaultInteraction();
- }
-
- /* TODP COPIED FROM CLIENT STUB */
- private void phaseLoop() {
- Phase phase = getGame().getPhase();
- List> allowed = Lists.newArrayList(ActionPhase.class, EscapePhase.class, TowerCapturePhase.class);
- while (!phase.isEntered()) {
- if (!Iterables.contains(allowed, phase.getClass())) {
- break;
- }
- //logger.info(" * not entered {} -> {}", phase, phase.getDefaultNext());
- phase.setEntered(true);
- phase.enter();
- phase = getGame().getPhase();
- }
- }
-
- //TODO is there faster game copying without snapshot? or without re-creating board and tile instances
- private Game deepGameCopy(Game game) {
- Snapshot snapshot = new Snapshot(game, 0);
- Game copy = snapshot.asGame();
- copy.setConfig(game.getConfig());
- copy.addUserInterface(this);
-
- LoadGamePhase phase = new LoadGamePhase(copy, snapshot, null);
- phase.setSlots(new PlayerSlot[0]);
- copy.getPhases().put(phase.getClass(), phase);
- copy.setPhase(phase);
- phase.startGame();
- return copy;
- }
-
-
- protected void backupGame() {
- assert original == null;
- original = getGame();
- Game copy = deepGameCopy(original);
- setGame(copy);
- interactionHandler = new RankingInteractionHanlder();
-
- spm = new SavePointManager(copy);
- bestSoFar = new PositionRanking(Double.NEGATIVE_INFINITY);
- spm.startRecording();
- }
-
- protected void restoreGame() {
- assert original != null;
- spm.stopRecording();
- spm = null;
- setGame(original);
- original = null;
- interactionHandler = defaultInteractionHandler;
- }
-
-// private boolean isRankingInProcess() {
-// return original != null;
-// }
-
-// public PositionRanking getBestSoFar() {
-// return bestSoFar;
-// }
-
-
- protected void selectAbbeyPlacement(AbbeyPlacementAction action) {
- Map> placements = new HashMap<>();
- for (Position pos : action.getSites()) {
- placements.put(pos, Collections.singleton(Rotation.R0));
- }
- rankTilePlacement(placements);
- if (bestSoFar.getRank() > 2.0) {
- getServer().placeTile(bestSoFar.getRotation(), bestSoFar.getPosition());
- } else {
- getServer().pass();
- }
- }
-
- protected void selectTilePlacement(TilePlacementAction action) {
- String autosave = game.getConfig().get("debug", "save_before_ranking");
- if (autosave != null && autosave.length() > 0) {
- Snapshot snapshot = new Snapshot(game, 0);
- if ("plain".equals(game.getConfig().get("debug", "save_format"))) {
- snapshot.setGzipOutput(false);
- }
- try {
- snapshot.save(new File(autosave));
- } catch (Exception e) {
- logger.error("Auto save before ranking failed.", e);
- }
- }
-
- Map> placements = action.getAvailablePlacements();
- rankTilePlacement(placements);
- getServer().placeTile(bestSoFar.getRotation(), bestSoFar.getPosition());
- }
-
- protected void rankTilePlacement(Map> placements) {
- //logger.info("---------- Ranking start ---------------");
- //logger.info("Positions: {} ", placements.keySet());
-
- backupGame();
- SavePoint sp = spm.save();
- for (Entry> entry : placements.entrySet()) {
- Position pos = entry.getKey();
- for (Rotation rot : entry.getValue()) {
- //logger.info(" * phase {} -> {}", getGame().getPhase(), getGame().getPhase().getDefaultNext());
- //logger.info(" * placing {} {}", pos, rot);
- getGame().getPhase().placeTile(rot, pos);
- //logger.info(" * phase {} -> {}", getGame().getPhase(), getGame().getPhase().getDefaultNext());
- phaseLoop();
- double currRank = rank();
- if (currRank > bestSoFar.getRank()) {
- bestSoFar = new PositionRanking(currRank, pos, rot);
- }
- spm.restore(sp);
- //TODO fix hopefulGatePlacement
- //now rank meeple placements - must restore because rank change game
- //getGame().getPhase().placeTile(rot, pos);
- //hopefulGatePlacements.clear();
- //spm.restore(sp);
- //TODO add best placements for MAGIC GATE
- //getGame().getPhase().enter();
- }
- }
- restoreGame();
- logger.info("Rank {} > {}", getGame().getCurrentTile() == null ? "Abbey" : getGame().getCurrentTile().getId(), bestSoFar);
- }
-
- protected void rankAction(List actions) {
- Tile currTile = getGame().getCurrentTile();
- Position pos = currTile.getPosition();
- for (PlayerAction action : actions) {
- if (action instanceof MeepleAction) {
- MeepleAction ma = (MeepleAction) action;
- rankMeeplePlacement(currTile, ma, ma.getMeepleType(), pos, ma.getLocationsMap().get(pos));
-// for (PositionLocation posloc : hopefulGatePlacements) {
-// rankMeepleAction(currTile, ma, posloc.position, Collections.singleton(posloc.location));
-// }
- }
- if (action instanceof BarnAction) {
- BarnAction ba = (BarnAction) action;
- rankMeeplePlacement(currTile, ba, Barn.class, pos, ba.get(pos));
- }
- if (action instanceof FairyAction) {
- rankFairyPlacement(currTile, (FairyAction) action);
- }
- if (action instanceof TowerPieceAction) {
- rankTowerPiecePlacement(currTile, (TowerPieceAction) action);
- }
- }
- }
-
- protected void rankFairyPlacement(Tile currTile, FairyAction action) {
- SavePoint sp = spm.save();
- for (Position pos: action.getSites()) {
- getGame().getPhase().moveFairy(pos);
- double currRank = rank();
- if (currRank > bestSoFar.getRank()) {
- bestSoFar = new PositionRanking(currRank, currTile.getPosition(), currTile.getRotation());
- bestSoFar.getSelectedActions().add(new SelectedAction(action, pos, null));
- }
- spm.restore(sp);
- }
- }
-
- protected void rankTowerPiecePlacementOnTile(final Tile currTile, final TowerPieceAction towerPieceAction, final Position towerPiecePos) {
- this.interactionHandler = new NotSupportedInteraction() {
- public void selectAction(List actions, boolean canPass) {
- phaseLoop();
- SavePoint sp = spm.save();
- TakePrisonerAction prisonerAction = (TakePrisonerAction) actions.get(0);
- for (Entry> entry : prisonerAction.getLocationsMap().entrySet()) {
- Position pos = entry.getKey();
- for (Location loc : entry.getValue()) {
- for (Meeple m : getBoard().get(pos).getFeature(loc).getMeeples()) {
- getGame().getPhase().takePrisoner(pos, loc, m.getClass(), m.getPlayer().getIndex());
- double currRank = rank();
- if (currRank > bestSoFar.getRank()) {
- bestSoFar = new PositionRanking(currRank, currTile.getPosition(), currTile.getRotation());
- Deque sa = bestSoFar.getSelectedActions();
- sa.add(new SelectedAction(towerPieceAction, towerPiecePos, null));
- sa.add(new SelectedAction(prisonerAction, pos, loc, m.getClass(), m.getPlayer()));
- }
- spm.restore(sp);
- }
- }
- }
- };
- };
- getGame().getPhase().placeTowerPiece(towerPiecePos);
- }
-
- protected void rankTowerPiecePlacement(Tile currTile,TowerPieceAction action) {
- UserInterface interactionHandlerBackup = this.interactionHandler;
- SavePoint sp = spm.save();
- for (Position pos: action.getSites()) {
- rankTowerPiecePlacementOnTile(currTile, action, pos);
- spm.restore(sp);
- }
-
- this.interactionHandler = interactionHandlerBackup;
- }
-
- protected void rankMeeplePlacement(Tile currTile, PlayerAction action, Class extends Meeple> meepleType, Position pos, Set locations) {
- if (locations == null) {
- return;
- }
- SavePoint sp = spm.save();
- for (Location loc : locations) {
- //logger.info(" . deploying {}", meepleType);
- getGame().getPhase().deployMeeple(pos, loc, meepleType);
- double currRank = rank();
- if (currRank > bestSoFar.getRank()) {
- bestSoFar = new PositionRanking(currRank, currTile.getPosition(), currTile.getRotation());
- bestSoFar.getSelectedActions().add(new SelectedAction(action, pos, loc));
- }
- spm.restore(sp);
- }
- }
-
- public void rankPass() {
- SavePoint sp = spm.save();
- getGame().getPhase().pass();
- double currRank = rank();
- if (currRank > bestSoFar.getRank()) {
- bestSoFar = new PositionRanking(currRank);
- }
- spm.restore(sp);
- }
-
- public void cleanRanking() {
- bestSoFar = null;
- //scoreCache.clear();
- }
-
- @Override
- public void selectAction(List actions, boolean canPass) {
- interactionHandler.selectAction(actions, canPass);
- }
-
- @Override
- public void selectDragonMove(Set positions, int movesLeft) {
- interactionHandler.selectDragonMove(positions, movesLeft);
- }
-
- @Override
- public void selectBazaarTile() {
- interactionHandler.selectBazaarTile();
- }
-
- @Override
- public void makeBazaarBid(int supplyIndex) {
- interactionHandler.makeBazaarBid(supplyIndex);
- }
-
- @Override
- public void selectBuyOrSellBazaarOffer(int supplyIndex) {
- interactionHandler.selectBuyOrSellBazaarOffer(supplyIndex);
- }
-
- @Override
- public void selectCornCircleOption() {
- interactionHandler.selectCornCircleOption();
- }
-
- abstract protected double rank();
-
- @Override
- protected void handleRuntimeError(Exception e) {
- super.handleRuntimeError(e);
- cleanRanking();
- if (original != null) {
- restoreGame();
- }
- }
-
- class RankingInteractionHanlder extends NotSupportedInteraction {
- @Override
- public void selectAction(List actions, boolean canPass) {
- rankAction(actions);
- }
- }
-
- class DefaultInteraction extends NotSupportedInteraction {
-
- @Override
- public void selectAction(List actions, boolean canPass) {
- if (actions.size() > 0) {
- PlayerAction firstAction = actions.get(0);
-
- if (firstAction instanceof TilePlacementAction) {
- selectTilePlacement((TilePlacementAction) firstAction);
- return;
- }
- if (firstAction instanceof AbbeyPlacementAction) {
- selectAbbeyPlacement((AbbeyPlacementAction) firstAction);
- return;
- }
- if (firstAction instanceof UndeployAction ) {
- //hack, ai never use escape, TODO
- if (firstAction.getName().equals("escape")) {
- getServer().pass();
- }
- }
- }
-
- if (bestSoFar == null) { //loaded game or wagon phase
- backupGame();
- if (canPass) rankPass();
- rankAction(actions);
- restoreGame();
- }
-
- Deque selected = bestSoFar.getSelectedActions();
- SelectedAction sa = selected.pollFirst();
- if (sa != null) {
- //logger.info("Polling " + sa.action);
- if (sa.action instanceof MeepleAction) {
- MeepleAction action = (MeepleAction) sa.action;
- //debug, should never happen, but it happens sometimes when Tower game is enabled
- try {
- getPlayer().getMeepleFromSupply(action.getMeepleType());
- } catch (NoSuchElementException e) {
- logger.error(e.getMessage(), e);
- throw e;
- }
- action.perform(getServer(), sa.position, sa.location);
- } else if (sa.action instanceof BarnAction) {
- BarnAction action = (BarnAction) sa.action;
- action.perform(getServer(), sa.position, sa.location);
- } else if (sa.action instanceof SelectTileAction) {
- SelectTileAction action = (SelectTileAction) sa.action;
- action.perform(getServer(), sa.position);
- } else if (sa.action instanceof SelectFollowerAction) {
- SelectFollowerAction action = (SelectFollowerAction) sa.action;
- action.perform(getServer(), sa.position, sa.location, sa.meepleType, sa.meepleOwner);
- } else {
- throw new UnsupportedOperationException("Unhandled action type " + sa.action.getName()); //should never happen
- }
- } else {
- getServer().pass();
- }
-
- if (selected.isEmpty()) {
- cleanRanking();
- }
- }
- }
-
-}
+package com.jcloisterzone.ai;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.jcloisterzone.UserInterface;
+import com.jcloisterzone.action.AbbeyPlacementAction;
+import com.jcloisterzone.action.BarnAction;
+import com.jcloisterzone.action.FairyAction;
+import com.jcloisterzone.action.MeepleAction;
+import com.jcloisterzone.action.PlayerAction;
+import com.jcloisterzone.action.SelectFollowerAction;
+import com.jcloisterzone.action.SelectTileAction;
+import com.jcloisterzone.action.TakePrisonerAction;
+import com.jcloisterzone.action.TilePlacementAction;
+import com.jcloisterzone.action.TowerPieceAction;
+import com.jcloisterzone.action.UndeployAction;
+import com.jcloisterzone.ai.PositionRanking.SelectedAction;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.Rotation;
+import com.jcloisterzone.board.Tile;
+import com.jcloisterzone.figure.Barn;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.game.Game;
+import com.jcloisterzone.game.PlayerSlot;
+import com.jcloisterzone.game.Snapshot;
+import com.jcloisterzone.game.phase.ActionPhase;
+import com.jcloisterzone.game.phase.EscapePhase;
+import com.jcloisterzone.game.phase.LoadGamePhase;
+import com.jcloisterzone.game.phase.Phase;
+import com.jcloisterzone.game.phase.TowerCapturePhase;
+
+public abstract class RankingAiPlayer extends AiPlayer {
+
+ class PositionLocation {
+ Position position;
+ Location location;
+ }
+
+ private Game original;
+ private SavePointManager spm;
+
+ private UserInterface defaultInteractionHandler;
+ private UserInterface interactionHandler;
+
+ //private Map scoreCache = new HashMap<>();
+ private PositionRanking bestSoFar;
+ //private List hopefulGatePlacements = new ArrayList();
+
+// public Map getScoreCache() {
+// return scoreCache;
+// }
+
+ public RankingAiPlayer() {
+ defaultInteractionHandler = createDefaultInteractionHandler();
+ interactionHandler = defaultInteractionHandler;
+ }
+
+ protected UserInterface createDefaultInteractionHandler() {
+ return new DefaultInteraction();
+ }
+
+ /* TODP COPIED FROM CLIENT STUB */
+ private void phaseLoop() {
+ Phase phase = getGame().getPhase();
+ List> allowed = Lists.newArrayList(ActionPhase.class, EscapePhase.class, TowerCapturePhase.class);
+ while (!phase.isEntered()) {
+ if (!Iterables.contains(allowed, phase.getClass())) {
+ break;
+ }
+ //logger.info(" * not entered {} -> {}", phase, phase.getDefaultNext());
+ phase.setEntered(true);
+ phase.enter();
+ phase = getGame().getPhase();
+ }
+ }
+
+ //TODO is there faster game copying without snapshot? or without re-creating board and tile instances
+ private Game deepGameCopy(Game game) {
+ Snapshot snapshot = new Snapshot(game, 0);
+ Game copy = snapshot.asGame();
+ copy.setConfig(game.getConfig());
+ copy.addUserInterface(this);
+
+ LoadGamePhase phase = new LoadGamePhase(copy, snapshot, null);
+ phase.setSlots(new PlayerSlot[0]);
+ copy.getPhases().put(phase.getClass(), phase);
+ copy.setPhase(phase);
+ phase.startGame();
+ return copy;
+ }
+
+
+ protected void backupGame() {
+ assert original == null;
+ original = getGame();
+ Game copy = deepGameCopy(original);
+ setGame(copy);
+ interactionHandler = new RankingInteractionHanlder();
+
+ spm = new SavePointManager(copy);
+ bestSoFar = new PositionRanking(Double.NEGATIVE_INFINITY);
+ spm.startRecording();
+ }
+
+ protected void restoreGame() {
+ assert original != null;
+ spm.stopRecording();
+ spm = null;
+ setGame(original);
+ original = null;
+ interactionHandler = defaultInteractionHandler;
+ }
+
+// private boolean isRankingInProcess() {
+// return original != null;
+// }
+
+// public PositionRanking getBestSoFar() {
+// return bestSoFar;
+// }
+
+
+ protected void selectAbbeyPlacement(AbbeyPlacementAction action) {
+ Map> placements = new HashMap<>();
+ for (Position pos : action.getSites()) {
+ placements.put(pos, Collections.singleton(Rotation.R0));
+ }
+ rankTilePlacement(placements);
+ if (bestSoFar.getRank() > 2.0) {
+ getServer().placeTile(bestSoFar.getRotation(), bestSoFar.getPosition());
+ } else {
+ getServer().pass();
+ }
+ }
+
+ protected void selectTilePlacement(TilePlacementAction action) {
+ String autosave = game.getConfig().get("debug", "save_before_ranking");
+ if (autosave != null && autosave.length() > 0) {
+ Snapshot snapshot = new Snapshot(game, 0);
+ if ("plain".equals(game.getConfig().get("debug", "save_format"))) {
+ snapshot.setGzipOutput(false);
+ }
+ try {
+ snapshot.save(new File(autosave));
+ } catch (Exception e) {
+ logger.error("Auto save before ranking failed.", e);
+ }
+ }
+
+ Map> placements = action.getAvailablePlacements();
+ rankTilePlacement(placements);
+ getServer().placeTile(bestSoFar.getRotation(), bestSoFar.getPosition());
+ }
+
+ protected void rankTilePlacement(Map> placements) {
+ //logger.info("---------- Ranking start ---------------");
+ //logger.info("Positions: {} ", placements.keySet());
+
+ backupGame();
+ SavePoint sp = spm.save();
+ for (Entry> entry : placements.entrySet()) {
+ Position pos = entry.getKey();
+ for (Rotation rot : entry.getValue()) {
+ //logger.info(" * phase {} -> {}", getGame().getPhase(), getGame().getPhase().getDefaultNext());
+ //logger.info(" * placing {} {}", pos, rot);
+ getGame().getPhase().placeTile(rot, pos);
+ //logger.info(" * phase {} -> {}", getGame().getPhase(), getGame().getPhase().getDefaultNext());
+ phaseLoop();
+ double currRank = rank();
+ if (currRank > bestSoFar.getRank()) {
+ bestSoFar = new PositionRanking(currRank, pos, rot);
+ }
+ spm.restore(sp);
+ //TODO fix hopefulGatePlacement
+ //now rank meeple placements - must restore because rank change game
+ //getGame().getPhase().placeTile(rot, pos);
+ //hopefulGatePlacements.clear();
+ //spm.restore(sp);
+ //TODO add best placements for MAGIC GATE
+ //getGame().getPhase().enter();
+ }
+ }
+ restoreGame();
+ logger.info("Rank {} > {}", getGame().getCurrentTile() == null ? "Abbey" : getGame().getCurrentTile().getId(), bestSoFar);
+ }
+
+ protected void rankAction(List actions) {
+ Tile currTile = getGame().getCurrentTile();
+ Position pos = currTile.getPosition();
+ for (PlayerAction action : actions) {
+ if (action instanceof MeepleAction) {
+ MeepleAction ma = (MeepleAction) action;
+ rankMeeplePlacement(currTile, ma, ma.getMeepleType(), pos, ma.getLocationsMap().get(pos));
+// for (PositionLocation posloc : hopefulGatePlacements) {
+// rankMeepleAction(currTile, ma, posloc.position, Collections.singleton(posloc.location));
+// }
+ }
+ if (action instanceof BarnAction) {
+ BarnAction ba = (BarnAction) action;
+ rankMeeplePlacement(currTile, ba, Barn.class, pos, ba.get(pos));
+ }
+ if (action instanceof FairyAction) {
+ rankFairyPlacement(currTile, (FairyAction) action);
+ }
+ if (action instanceof TowerPieceAction) {
+ rankTowerPiecePlacement(currTile, (TowerPieceAction) action);
+ }
+ }
+ }
+
+ protected void rankFairyPlacement(Tile currTile, FairyAction action) {
+ SavePoint sp = spm.save();
+ for (Position pos: action.getSites()) {
+ getGame().getPhase().moveFairy(pos);
+ double currRank = rank();
+ if (currRank > bestSoFar.getRank()) {
+ bestSoFar = new PositionRanking(currRank, currTile.getPosition(), currTile.getRotation());
+ bestSoFar.getSelectedActions().add(new SelectedAction(action, pos, null));
+ }
+ spm.restore(sp);
+ }
+ }
+
+ protected void rankTowerPiecePlacementOnTile(final Tile currTile, final TowerPieceAction towerPieceAction, final Position towerPiecePos) {
+ this.interactionHandler = new NotSupportedInteraction() {
+ public void selectAction(List actions, boolean canPass) {
+ phaseLoop();
+ SavePoint sp = spm.save();
+ TakePrisonerAction prisonerAction = (TakePrisonerAction) actions.get(0);
+ for (Entry> entry : prisonerAction.getLocationsMap().entrySet()) {
+ Position pos = entry.getKey();
+ for (Location loc : entry.getValue()) {
+ for (Meeple m : getBoard().get(pos).getFeature(loc).getMeeples()) {
+ getGame().getPhase().takePrisoner(pos, loc, m.getClass(), m.getPlayer().getIndex());
+ double currRank = rank();
+ if (currRank > bestSoFar.getRank()) {
+ bestSoFar = new PositionRanking(currRank, currTile.getPosition(), currTile.getRotation());
+ Deque sa = bestSoFar.getSelectedActions();
+ sa.add(new SelectedAction(towerPieceAction, towerPiecePos, null));
+ sa.add(new SelectedAction(prisonerAction, pos, loc, m.getClass(), m.getPlayer()));
+ }
+ spm.restore(sp);
+ }
+ }
+ }
+ };
+ };
+ getGame().getPhase().placeTowerPiece(towerPiecePos);
+ }
+
+ protected void rankTowerPiecePlacement(Tile currTile,TowerPieceAction action) {
+ UserInterface interactionHandlerBackup = this.interactionHandler;
+ SavePoint sp = spm.save();
+ for (Position pos: action.getSites()) {
+ rankTowerPiecePlacementOnTile(currTile, action, pos);
+ spm.restore(sp);
+ }
+
+ this.interactionHandler = interactionHandlerBackup;
+ }
+
+ protected void rankMeeplePlacement(Tile currTile, PlayerAction action, Class extends Meeple> meepleType, Position pos, Set locations) {
+ if (locations == null) {
+ return;
+ }
+ SavePoint sp = spm.save();
+ for (Location loc : locations) {
+ //logger.info(" . deploying {}", meepleType);
+ getGame().getPhase().deployMeeple(pos, loc, meepleType);
+ double currRank = rank();
+ if (currRank > bestSoFar.getRank()) {
+ bestSoFar = new PositionRanking(currRank, currTile.getPosition(), currTile.getRotation());
+ bestSoFar.getSelectedActions().add(new SelectedAction(action, pos, loc));
+ }
+ spm.restore(sp);
+ }
+ }
+
+ public void rankPass() {
+ SavePoint sp = spm.save();
+ getGame().getPhase().pass();
+ double currRank = rank();
+ if (currRank > bestSoFar.getRank()) {
+ bestSoFar = new PositionRanking(currRank);
+ }
+ spm.restore(sp);
+ }
+
+ public void cleanRanking() {
+ bestSoFar = null;
+ //scoreCache.clear();
+ }
+
+ @Override
+ public void selectAction(List actions, boolean canPass) {
+ interactionHandler.selectAction(actions, canPass);
+ }
+
+ @Override
+ public void selectDragonMove(Set positions, int movesLeft) {
+ interactionHandler.selectDragonMove(positions, movesLeft);
+ }
+
+ @Override
+ public void selectBazaarTile() {
+ interactionHandler.selectBazaarTile();
+ }
+
+ @Override
+ public void makeBazaarBid(int supplyIndex) {
+ interactionHandler.makeBazaarBid(supplyIndex);
+ }
+
+ @Override
+ public void selectBuyOrSellBazaarOffer(int supplyIndex) {
+ interactionHandler.selectBuyOrSellBazaarOffer(supplyIndex);
+ }
+
+ @Override
+ public void selectCornCircleOption() {
+ interactionHandler.selectCornCircleOption();
+ }
+
+ abstract protected double rank();
+
+ @Override
+ protected void handleRuntimeError(Exception e) {
+ super.handleRuntimeError(e);
+ cleanRanking();
+ if (original != null) {
+ restoreGame();
+ }
+ }
+
+ class RankingInteractionHanlder extends NotSupportedInteraction {
+ @Override
+ public void selectAction(List actions, boolean canPass) {
+ rankAction(actions);
+ }
+ }
+
+ class DefaultInteraction extends NotSupportedInteraction {
+
+ @Override
+ public void selectAction(List actions, boolean canPass) {
+ if (actions.size() > 0) {
+ PlayerAction firstAction = actions.get(0);
+
+ if (firstAction instanceof TilePlacementAction) {
+ selectTilePlacement((TilePlacementAction) firstAction);
+ return;
+ }
+ if (firstAction instanceof AbbeyPlacementAction) {
+ selectAbbeyPlacement((AbbeyPlacementAction) firstAction);
+ return;
+ }
+ if (firstAction instanceof UndeployAction ) {
+ //hack, ai never use escape, TODO
+ if (firstAction.getName().equals("escape")) {
+ getServer().pass();
+ }
+ }
+ }
+
+ if (bestSoFar == null) { //loaded game or wagon phase
+ backupGame();
+ if (canPass) rankPass();
+ rankAction(actions);
+ restoreGame();
+ }
+
+ Deque selected = bestSoFar.getSelectedActions();
+ SelectedAction sa = selected.pollFirst();
+ if (sa != null) {
+ //logger.info("Polling " + sa.action);
+ if (sa.action instanceof MeepleAction) {
+ MeepleAction action = (MeepleAction) sa.action;
+ //debug, should never happen, but it happens sometimes when Tower game is enabled
+ try {
+ getPlayer().getMeepleFromSupply(action.getMeepleType());
+ } catch (NoSuchElementException e) {
+ logger.error(e.getMessage(), e);
+ throw e;
+ }
+ action.perform(getServer(), sa.position, sa.location);
+ } else if (sa.action instanceof BarnAction) {
+ BarnAction action = (BarnAction) sa.action;
+ action.perform(getServer(), sa.position, sa.location);
+ } else if (sa.action instanceof SelectTileAction) {
+ SelectTileAction action = (SelectTileAction) sa.action;
+ action.perform(getServer(), sa.position);
+ } else if (sa.action instanceof SelectFollowerAction) {
+ SelectFollowerAction action = (SelectFollowerAction) sa.action;
+ action.perform(getServer(), sa.position, sa.location, sa.meepleType, sa.meepleOwner);
+ } else {
+ throw new UnsupportedOperationException("Unhandled action type " + sa.action.getName()); //should never happen
+ }
+ } else {
+ getServer().pass();
+ }
+
+ if (selected.isEmpty()) {
+ cleanRanking();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/SavePoint.java b/src/main/java/com/jcloisterzone/ai/SavePoint.java
index 7bbe58ae9..45e2915b8 100644
--- a/src/main/java/com/jcloisterzone/ai/SavePoint.java
+++ b/src/main/java/com/jcloisterzone/ai/SavePoint.java
@@ -1,28 +1,28 @@
-package com.jcloisterzone.ai;
-
-import com.jcloisterzone.ai.operation.Operation;
-import com.jcloisterzone.game.phase.Phase;
-
-public class SavePoint {
- private final Operation operation;
- private final Phase phase;
- private final Object[] capabilitiesBackups;
-
- public SavePoint(Operation operation, Phase phase, Object[] capabilitiesBackups) {
- this.operation = operation;
- this.phase = phase;
- this.capabilitiesBackups = capabilitiesBackups;
- }
-
- public Operation getOperation() {
- return operation;
- }
-
- public Phase getPhase() {
- return phase;
- }
-
- public Object[] getCapabilitiesBackups() {
- return capabilitiesBackups;
- }
+package com.jcloisterzone.ai;
+
+import com.jcloisterzone.ai.operation.Operation;
+import com.jcloisterzone.game.phase.Phase;
+
+public class SavePoint {
+ private final Operation operation;
+ private final Phase phase;
+ private final Object[] capabilitiesBackups;
+
+ public SavePoint(Operation operation, Phase phase, Object[] capabilitiesBackups) {
+ this.operation = operation;
+ this.phase = phase;
+ this.capabilitiesBackups = capabilitiesBackups;
+ }
+
+ public Operation getOperation() {
+ return operation;
+ }
+
+ public Phase getPhase() {
+ return phase;
+ }
+
+ public Object[] getCapabilitiesBackups() {
+ return capabilitiesBackups;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/ai/SavePointManager.java b/src/main/java/com/jcloisterzone/ai/SavePointManager.java
index a983ff543..82ab61f9c 100644
--- a/src/main/java/com/jcloisterzone/ai/SavePointManager.java
+++ b/src/main/java/com/jcloisterzone/ai/SavePointManager.java
@@ -1,106 +1,106 @@
-package com.jcloisterzone.ai;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.ai.operation.MeepleDeployedOperation;
-import com.jcloisterzone.ai.operation.MeepleUndeployedOperation;
-import com.jcloisterzone.ai.operation.Operation;
-import com.jcloisterzone.ai.operation.ScoreOperation;
-import com.jcloisterzone.ai.operation.TilePlacedOperation;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.Tile;
-import com.jcloisterzone.event.GameEventAdapter;
-import com.jcloisterzone.event.GameEventListener;
-import com.jcloisterzone.feature.Feature;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.game.Capability;
-import com.jcloisterzone.game.Game;
-import com.jcloisterzone.game.phase.Phase;
-
-public class SavePointManager {
-
- private final Game game;
- protected Deque operations = new ArrayDeque();
- private GameEventListener operationRecorder = new OperationRecorder();
-
- protected final transient Logger logger = LoggerFactory.getLogger(getClass());
-
- public SavePointManager(Game game) {
- this.game = game;
- }
-
- public Game getGame() {
- return game;
- }
-
- public void startRecording() {
- game.addGameListener(operationRecorder);
- }
-
- public void stopRecording() {
- game.removeGameListener(operationRecorder);
- operations.clear();
- }
-
- public SavePoint save() {
- Object[] backups = new Object[game.getCapabilities().size()];
- int i = 0;
- for (Capability cap : game.getCapabilities()) {
- backups[i++] = cap.backup();
- }
- return new SavePoint(operations.peekLast(), game.getPhase(), backups);
-
- }
-
- public void restore(SavePoint sp) {
- game.removeGameListener(operationRecorder);
- Operation target = sp.getOperation();
- while (operations.peekLast() != target) {
- //logger.info(" < undo {}", item);
- operations.pollLast().undo(game);
- }
- int i = 0;
- for (Capability cap : game.getCapabilities()) {
- cap.restore(sp.getCapabilitiesBackups()[i++]);
- }
-
- Phase phase = sp.getPhase();
- game.setPhase(phase);
- phase.setEntered(true);
- game.addGameListener(operationRecorder);
- }
-
- class OperationRecorder extends GameEventAdapter {
- @Override
- public void tilePlaced(Tile tile) {
- operations.addLast(new TilePlacedOperation(tile));
- }
- @Override
- public void deployed(Meeple meeple) {
- operations.addLast(new MeepleDeployedOperation(meeple));
- }
- @Override
- public void undeployed(Meeple meeple) {
- operations.addLast(new MeepleUndeployedOperation(meeple));
- }
-// @Override
-// public void playerActivated(Player turnPlayer, Player activePlayer) {
-// // TODO Auto-generated method stub
-// super.playerActivated(turnPlayer, activePlayer);
-// }
- @Override
- public void scored(Feature feature, int points, String label, Meeple meeple, boolean isFinal) {
- operations.addLast(new ScoreOperation(meeple.getPlayer(), points));
- }
- @Override
- public void scored(Position position, Player player, int points, String label, boolean isFinal) {
- operations.addLast(new ScoreOperation(player, points));
- }
- }
+package com.jcloisterzone.ai;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.ai.operation.MeepleDeployedOperation;
+import com.jcloisterzone.ai.operation.MeepleUndeployedOperation;
+import com.jcloisterzone.ai.operation.Operation;
+import com.jcloisterzone.ai.operation.ScoreOperation;
+import com.jcloisterzone.ai.operation.TilePlacedOperation;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.Tile;
+import com.jcloisterzone.event.GameEventAdapter;
+import com.jcloisterzone.event.GameEventListener;
+import com.jcloisterzone.feature.Feature;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.game.Capability;
+import com.jcloisterzone.game.Game;
+import com.jcloisterzone.game.phase.Phase;
+
+public class SavePointManager {
+
+ private final Game game;
+ protected Deque operations = new ArrayDeque();
+ private GameEventListener operationRecorder = new OperationRecorder();
+
+ protected final transient Logger logger = LoggerFactory.getLogger(getClass());
+
+ public SavePointManager(Game game) {
+ this.game = game;
+ }
+
+ public Game getGame() {
+ return game;
+ }
+
+ public void startRecording() {
+ game.addGameListener(operationRecorder);
+ }
+
+ public void stopRecording() {
+ game.removeGameListener(operationRecorder);
+ operations.clear();
+ }
+
+ public SavePoint save() {
+ Object[] backups = new Object[game.getCapabilities().size()];
+ int i = 0;
+ for (Capability cap : game.getCapabilities()) {
+ backups[i++] = cap.backup();
+ }
+ return new SavePoint(operations.peekLast(), game.getPhase(), backups);
+
+ }
+
+ public void restore(SavePoint sp) {
+ game.removeGameListener(operationRecorder);
+ Operation target = sp.getOperation();
+ while (operations.peekLast() != target) {
+ //logger.info(" < undo {}", item);
+ operations.pollLast().undo(game);
+ }
+ int i = 0;
+ for (Capability cap : game.getCapabilities()) {
+ cap.restore(sp.getCapabilitiesBackups()[i++]);
+ }
+
+ Phase phase = sp.getPhase();
+ game.setPhase(phase);
+ phase.setEntered(true);
+ game.addGameListener(operationRecorder);
+ }
+
+ class OperationRecorder extends GameEventAdapter {
+ @Override
+ public void tilePlaced(Tile tile) {
+ operations.addLast(new TilePlacedOperation(tile));
+ }
+ @Override
+ public void deployed(Meeple meeple) {
+ operations.addLast(new MeepleDeployedOperation(meeple));
+ }
+ @Override
+ public void undeployed(Meeple meeple) {
+ operations.addLast(new MeepleUndeployedOperation(meeple));
+ }
+// @Override
+// public void playerActivated(Player turnPlayer, Player activePlayer) {
+// // TODO Auto-generated method stub
+// super.playerActivated(turnPlayer, activePlayer);
+// }
+ @Override
+ public void scored(Feature feature, int points, String label, Meeple meeple, boolean isFinal) {
+ operations.addLast(new ScoreOperation(meeple.getPlayer(), points));
+ }
+ @Override
+ public void scored(Position position, Player player, int points, String label, boolean isFinal) {
+ operations.addLast(new ScoreOperation(player, points));
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiFarmScoreContext.java b/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiFarmScoreContext.java
index 2048e2867..2c9b65d88 100644
--- a/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiFarmScoreContext.java
+++ b/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiFarmScoreContext.java
@@ -1,34 +1,34 @@
-package com.jcloisterzone.ai.legacyplayer;
-
-import com.jcloisterzone.ai.AiScoreContext;
-import com.jcloisterzone.feature.Feature;
-import com.jcloisterzone.feature.visitor.score.FarmScoreContext;
-import com.jcloisterzone.game.Game;
-
-public class LegacyAiFarmScoreContext extends FarmScoreContext implements AiScoreContext {
-
- //private final Map scoreCache;
- private boolean valid;
-
- public LegacyAiFarmScoreContext(Game game/*, Map scoreCache*/) {
- super(game);
- //this.scoreCache = scoreCache;
- }
-
- @Override
- public boolean isValid() {
- return valid;
- }
-
- @Override
- public void setValid(boolean valid) {
- this.valid = valid;
- }
-
- @Override
- public boolean visit(Feature feature) {
- //scoreCache.put(feature, this);
- return super.visit(feature);
- }
-
-}
+package com.jcloisterzone.ai.legacyplayer;
+
+import com.jcloisterzone.ai.AiScoreContext;
+import com.jcloisterzone.feature.Feature;
+import com.jcloisterzone.feature.visitor.score.FarmScoreContext;
+import com.jcloisterzone.game.Game;
+
+public class LegacyAiFarmScoreContext extends FarmScoreContext implements AiScoreContext {
+
+ //private final Map scoreCache;
+ private boolean valid;
+
+ public LegacyAiFarmScoreContext(Game game/*, Map scoreCache*/) {
+ super(game);
+ //this.scoreCache = scoreCache;
+ }
+
+ @Override
+ public boolean isValid() {
+ return valid;
+ }
+
+ @Override
+ public void setValid(boolean valid) {
+ this.valid = valid;
+ }
+
+ @Override
+ public boolean visit(Feature feature) {
+ //scoreCache.put(feature, this);
+ return super.visit(feature);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiPlayer.java b/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiPlayer.java
index 8a289ceac..7ede7b2f9 100644
--- a/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiPlayer.java
+++ b/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiPlayer.java
@@ -1,691 +1,691 @@
-package com.jcloisterzone.ai.legacyplayer;
-
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.jcloisterzone.Expansion;
-import com.jcloisterzone.Player;
-import com.jcloisterzone.ai.RankingAiPlayer;
-import com.jcloisterzone.board.EdgePattern;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.board.Rotation;
-import com.jcloisterzone.board.Tile;
-import com.jcloisterzone.feature.Castle;
-import com.jcloisterzone.feature.City;
-import com.jcloisterzone.feature.Cloister;
-import com.jcloisterzone.feature.Completable;
-import com.jcloisterzone.feature.Farm;
-import com.jcloisterzone.feature.Feature;
-import com.jcloisterzone.feature.Road;
-import com.jcloisterzone.feature.score.ScoreAllCallback;
-import com.jcloisterzone.feature.score.ScoreAllFeatureFinder;
-import com.jcloisterzone.feature.visitor.score.AbstractScoreContext;
-import com.jcloisterzone.feature.visitor.score.CityScoreContext;
-import com.jcloisterzone.feature.visitor.score.CompletableScoreContext;
-import com.jcloisterzone.feature.visitor.score.FarmScoreContext;
-import com.jcloisterzone.feature.visitor.score.RoadScoreContext;
-import com.jcloisterzone.feature.visitor.score.ScoreContext;
-import com.jcloisterzone.figure.Barn;
-import com.jcloisterzone.figure.BigFollower;
-import com.jcloisterzone.figure.Builder;
-import com.jcloisterzone.figure.Follower;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.figure.SmallFollower;
-import com.jcloisterzone.figure.predicate.MeeplePredicates;
-import com.jcloisterzone.game.capability.BuilderCapability;
-import com.jcloisterzone.game.capability.BuilderCapability.BuilderState;
-import com.jcloisterzone.game.capability.DragonCapability;
-import com.jcloisterzone.game.capability.FairyCapability;
-import com.jcloisterzone.game.capability.TowerCapability;
-import com.jcloisterzone.game.phase.ScorePhase;
-
-public class LegacyAiPlayer extends RankingAiPlayer {
-
- private static final double TRAPPED_MY_FIGURE_POINTS = -12.0;
- private static final double TRAPPED_ENEMY_FIGURE_POINTS = 3.0;
- private static final double SELF_MERGE_PENALTY = 6.0;
-
- private static final double MIN_CHANCE = 0.4;
-
- protected int packSize, myTurnsLeft;
- protected int enemyPlayers;
-
- private static final int OPEN_COUNT_ROAD = 0;
- private static final int OPEN_COUNT_CITY = 1;
- private static final int OPEN_COUNT_FARM = 2;
- private static final int OPEN_COUNT_CLOITSTER = 3;
-
-
- private int[] openCount = new int[4]; //number of my open objects
-
- public static EnumSet supportedExpansions() {
- return EnumSet.of(
- Expansion.BASIC,
- Expansion.WINTER,
- Expansion.ABBEY_AND_MAYOR,
- Expansion.INNS_AND_CATHEDRALS,
- Expansion.TRADERS_AND_BUILDERS,
- Expansion.PRINCESS_AND_DRAGON,
- //Expansion.TOWER, //causing error sometimes
- Expansion.KING_AND_SCOUT,
- Expansion.RIVER,
- Expansion.RIVER_II,
- Expansion.GQ11,
- Expansion.CATAPULT,
- Expansion.WIND_ROSE,
- Expansion.CATHARS,
- //Expansion.PHANTOM,
- //only cards
- Expansion.COUNT,
- Expansion.PLAGUE
- );
- }
-
-
-
- protected void initVars() {
- packSize = getTilePack().totalSize();
- enemyPlayers = game.getAllPlayers().length - 1;
- myTurnsLeft = ((packSize-1) / (enemyPlayers+1)) + 1;
- }
-
- @Override
- protected double rank() {
- double ranking = 0;
- initVars();
-
- //trigger score
- game.getPhase().next(ScorePhase.class);
- game.getPhase().enter();
-
- Arrays.fill(openCount, 0);
-
- ranking += meepleRating();
- ranking += pointRating();
- ranking += openObjectRating();
-
- ranking += rankPossibleFeatureConnections();
- ranking += rankConvexity();
- ranking += rankFairy();
-
-// // --- dbg print --
-// Tile tile = getGame().getCurrentTile();
-// Feature meeplePlacement = Iterables.find(tile.getFeatures(), new Predicate() {
-// @Override
-// public boolean apply(Feature f) {
-// return !f.getMeeples().isEmpty();
-// }
-// }, null);
-// Arrays.fill(openCount, 0);
-// System.err.println(
-// String.format("%8s (%4s) %10s %4s/%5s %8.3f = Mepl %.3f Poit %.3f Open %.3f Conn %.3f Covx %.3f Fair %.3f",
-// tile.getId(), tile.getRotation(), tile.getPosition(),
-// meeplePlacement == null ? "" : meeplePlacement.getClass().getSimpleName(),
-// meeplePlacement == null ? "" : meeplePlacement.getLocation(),
-// ranking, meepleRating(), pointRating(), openObjectRating(),
-// rankPossibleFeatureConnections(), rankConvexity(), rankFairy()));
-// // --- end of debug print
-
- //objectRatings.clear();
-
- return ranking;
- }
-
- protected double reducePoints(double points, Player p) {
- if (isMe(p)) return points;
- return -points/enemyPlayers;
- }
-
- protected double chanceToPlaceTile(Position pos) {
- EdgePattern pattern = game.getBoard().getAvailMoveEdgePattern(pos);
- if (pattern != null && pattern.wildcardSize() < 2) {
- int remains = game.getTilePack().getSizeForEdgePattern(pattern);
- if (remains == 0) return 0.0;
- if (remains < game.getAllPlayers().length) {
- if (remains == 0) return 0.0;
- return 1.0 - Math.pow(1.0 - 1.0 / (game.getAllPlayers().length), remains);
- }
- }
- return 1.0;
- }
-
- protected double meepleRating() {
- double rating = 0;
-
- for (Player p : game.getAllPlayers()) {
- double meeplePoints = 0;
- int limit = 0;
- for (Follower f : Iterables.filter(p.getFollowers(), MeeplePredicates.deployed())) {
- if (f instanceof SmallFollower) {
- meeplePoints += 0.15;
- } else if (f instanceof BigFollower) {
- meeplePoints += 0.25;
- }
- if (++limit == myTurnsLeft) break;
- }
- rating += reducePoints(meeplePoints, p);
- }
- return rating;
- }
-
- class LegacyAiScoreAllCallback implements ScoreAllCallback {
-
- private double rank = 0;
-
- private Set towerDanger = new HashSet<>();
-
- public LegacyAiScoreAllCallback() {
- TowerCapability towerCap = game.getCapability(TowerCapability.class);
- if (towerCap != null) {
- for (Position towerPos : towerCap.getTowers()) {
- int dangerDistance = 1 + game.getBoard().get(towerPos).getTower().getHeight();
- towerDanger.add(towerPos);
- for (int i = 1; i < dangerDistance; i++) {
- towerDanger.add(towerPos.add(new Position(i, 0)));
- towerDanger.add(towerPos.add(new Position(-i, 0)));
- towerDanger.add(towerPos.add(new Position(0, i)));
- towerDanger.add(towerPos.add(new Position(0, -i)));
- }
- }
- }
- }
-
- private boolean isInTowerDanger(ScoreContext ctx) {
- //not exact but it is easy heuristic at now
- if (!towerDanger.isEmpty()) {
- for (Follower f : ctx.getFollowers()) {
- if (towerDanger.contains(f.getPosition())) {
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public void scoreCastle(Meeple meeple, Castle castle) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public LegacyAiScoreContext getCompletableScoreContext(Completable completable) {
- //TODO uncomment after invalidate implemeted
-// AiScoreContext ctx = getScoreCache().get(completable);
-// if (ctx != null && ctx.isValid()) {
-// return (CompletableScoreContext) ctx;
-// }
- return new LegacyAiScoreContext(LegacyAiPlayer.this, completable.getScoreContext()/*, getScoreCache()*/);
- }
-
- @Override
- public LegacyAiFarmScoreContext getFarmScoreContext(Farm farm) {
- //TODO uncomment after invalidate implemeted
-// AiScoreContext ctx = getScoreCache().get(farm);
-// if (ctx != null && ctx.isValid()) {
-// return (FarmScoreContext) ctx;
-// }
- return new LegacyAiFarmScoreContext(game/*, getScoreCache()*/);
- }
-
- @Override
- public void scoreFarm(FarmScoreContext ctx, Player player) {
- if (isInTowerDanger(ctx)) return;
- double points = getFarmPoints((Farm) ctx.getMasterFeature(), player, ctx);
- rank += reducePoints(points, player);
- }
-
- @Override
- public void scoreBarn(FarmScoreContext ctx, Barn meeple) {
- //prefer barn placement - magic constant
- rank += reducePoints(1.2 * ctx.getBarnPoints(), meeple.getPlayer());
- }
-
- @Override
- public void scoreCompletableFeature(CompletableScoreContext ctx) {
- if (isInTowerDanger(ctx)) return;
- rank += rankUnfishedCompletable(ctx.getMasterFeature(), (LegacyAiScoreContext) ctx);
- rank += rankTrappedMeeples((LegacyAiScoreContext) ctx);
- rank += rankSpecialFigures((LegacyAiScoreContext) ctx);
- }
-
- public double getRanking() {
- return rank;
- }
-
- }
-
- protected double pointRating() {
- double rating = 0;
-
- for (Player p : game.getAllPlayers()) {
- rating += reducePoints(p.getPoints(), p);
- }
-
- ScoreAllFeatureFinder scoreAll = new ScoreAllFeatureFinder();
- LegacyAiScoreAllCallback callback = new LegacyAiScoreAllCallback();
- scoreAll.scoreAll(game, callback);
- rating += callback.getRanking();
-
- return rating;
- }
-
-
- public static final double[][] OPEN_PENALTY = {
- { 0.0, 1.0, 2.5, 4.5, 7.5, 10.5, 14.5, 19.0, 29.0 }, //road
- { 0.0, 0.5, 1.5, 3.0, 5.0, 8.0, 12.0, 17.0, 27.0 }, //city
- { 0.0, 5.0, 10.0, 19.0, 28.0, 37.0, 47.0, 57.0, 67.0 }, //farm
- { 0.0, 0.0, 0.4, 0.8, 1.2, 2.0, 4.0, 7.0, 11.0 } //cloister
- };
-
- protected double openObjectRating() {
- double rating = 0;
-
- for (int i = 0; i < OPEN_PENALTY.length; i++ ){
- double penalty;
- //fast fix for strange bug causes ArrayIndexOutOfBoundsException: 9
- if (openCount[i] >= OPEN_PENALTY[i].length) {
- penalty = OPEN_PENALTY[i][OPEN_PENALTY[i].length - 1];
- } else {
- penalty = OPEN_PENALTY[i][openCount[i]];
- }
- if (i == 2) {
- //Farm
- double modifier = (packSize - ((1+enemyPlayers) * 3)) / 20.0;
- if (modifier < 1.0) modifier = 1.0;
- rating -= modifier * penalty;
- } else {
- rating -= penalty;
- }
- }
- return rating;
- }
-
- private ScoreContext futureConnectionCreateScoreContext(Feature feature) {
- LegacyAiScoreAllCallback ctxHelper = new LegacyAiScoreAllCallback();
- ScoreContext ctx;
- if (feature instanceof Completable) {
- ctx = ctxHelper.getCompletableScoreContext((Completable) feature);
- } else {
- ctx = ctxHelper.getFarmScoreContext((Farm) feature);
- ((LegacyAiFarmScoreContext) ctx).setCityCache(new HashMap());
- }
- feature.walk(ctx);
- return ctx;
- }
-
- private double futureConnectionGetFeaturePoints(Feature feature, ScoreContext featureCtx) {
- if (featureCtx instanceof CompletableScoreContext) {
- return getUnfinishedCompletablePoints((Completable) feature, (LegacyAiScoreContext) featureCtx);
- } else {
- return ((FarmScoreContext) featureCtx).getPoints(getPlayer());
- }
- }
-
- private double futureConnectionRateConnection(Location toEmpty, Location toFeature, Position f2Pos, double chance) {
- Tile tile1 = getGame().getCurrentTile();
- Tile tile2 = getBoard().get(f2Pos);
-
- double rating = 0;
-
- Completable f1 = (Completable) tile1.getFeaturePartOf(toEmpty);
- Completable f2 = (Completable) tile2.getFeaturePartOf(toFeature.rev());
-
- if (f1 != null && f2 != null) {
- if (f1.getClass().equals(f2.getClass())) {
- // System.err.println(" " + tile1.getPosition() + " <-->" + f2Pos + " / " + f1 + " " + f2);
- rating += futureConnectionRateFeatures(toEmpty, toFeature, chance, f1, f2);
- } else {
- rating += futureConnectionRateCrossing(toEmpty, toFeature, chance, f1, f2);
- }
- }
-
- if (toEmpty != toFeature) {
- boolean left = toEmpty.rotateCCW(Rotation.R90) == toFeature;
- Farm farm1 = (Farm) tile2.getFeaturePartOf(left ? toEmpty.getLeftFarm() : toEmpty.getRightFarm());
- Farm farm2 = (Farm) tile2.getFeaturePartOf(left ? toFeature.rev().getRightFarm() : toFeature.rev().getLeftFarm());
-
- if (farm1 != null && farm2 != null) {
-// System.err.println(" " + tile1.getPosition() + " <-->" + f2Pos + " / " + farm1 + " " + farm2);
- rating += futureConnectionRateFeatures(toEmpty, toFeature, chance, farm1, farm2);
- }
- }
- return rating;
- }
-
- private double futureConnectionRateCrossing(Location toEmpty, Location toFeature, double chance, Feature f1, Feature f2) {
- ScoreContext f1Ctx = futureConnectionCreateScoreContext(f1);
- ScoreContext f2Ctx = futureConnectionCreateScoreContext(f2);
- Map f1Powers = f1Ctx.getPowers();
- Map f2Powers = f2Ctx.getPowers();
-
- int[] powers = funtureConnectionSumPower(f2Powers, null);
- int myPower = powers[0];
- int bestEnemy = powers[1];
-
- if (f1Powers.size() == 0) {
- if (bestEnemy > myPower) {
- return 0.2;
- } else if (myPower > 0) {
- return -2.5;
- }
- } else {
- if (bestEnemy > myPower) {
- return -0.1;
- } else if (myPower > 0) {
- return -0.5;
- } else {
- return -0.5;
- }
- }
- return 0;
- }
-
-
- private double futureConnectionRateFeatures(Location toEmpty, Location toFeature, double chance, Feature f1, Feature f2) {
- ScoreContext f1Ctx = futureConnectionCreateScoreContext(f1);
- ScoreContext f2Ctx = futureConnectionCreateScoreContext(f2);
- Map f1Powers = f1Ctx.getPowers();
- Map f2Powers = f2Ctx.getPowers();
-
- if (f1Powers.size() == 0) {
- return 0;
- }
-
- if (f1Powers.size() == 1 && f2Powers.size() == 1 && f1Powers.containsKey(getPlayer()) && f2Powers.containsKey(getPlayer())) {
-// System.err.println(" !self merge");
-// System.err.println(f1Powers + " // " + f2Powers);
- return -SELF_MERGE_PENALTY;
- }
-
- double myPoints = futureConnectionGetFeaturePoints(f1, f1Ctx);
- double enemyPoints = futureConnectionGetFeaturePoints(f2, f2Ctx);
-
- if (enemyPoints < (toEmpty == toFeature ? 7.0 : 5.0)) {
-// System.err.println("too small penalty: " + enemyPoints);
- return -0.05; //small penalty
- }
-
- int[] powers = funtureConnectionSumPower(f1Powers, f2Powers);
- int myPower = powers[0];
- int bestEnemy = powers[1];
-
- double coef = toEmpty != toFeature ? 0.7 : 0.4; //corner / straight connection
-
-// System.err.println("@@@ @@@ " + myPower + "/" + myPoints + " vs " + bestEnemy + "/" + enemyPoints);
-
- if (myPower == bestEnemy) {
- return coef * (enemyPoints - myPoints) * chance;
- }
- if (myPower > bestEnemy) {
- return coef * enemyPoints * chance;
-
- }
- return -myPoints * chance; //no coef here
-
- }
-
- private int[] funtureConnectionSumPower(Map f1Powers, Map f2Powers) {
- Map sum = new HashMap(f1Powers);
- if (f2Powers != null) {
- for (Entry epower : f2Powers.entrySet()) {
- Integer val = sum.get(epower.getKey());
- sum.put(epower.getKey(), val == null ? epower.getValue() : val + epower.getValue());
- }
- }
- int myPower = 0;
- int bestEnemy = 0;
- for (Entry esum : sum.entrySet()) {
- int value = esum.getValue();
- if (esum.getKey().equals(getPlayer())) {
- myPower = value;
- } else {
- if (value > bestEnemy) bestEnemy = value;
- }
- }
- return new int[] { myPower, bestEnemy };
- }
-
- private double rankPossibleFeatureConnections() {
- double rank = 0;
-
- Tile tile = getGame().getCurrentTile();
- Position placement = tile.getPosition();
- assert placement != null;
-
- for (Entry eplace : Position.ADJACENT.entrySet()) {
- Position pos = placement.add(eplace.getValue());
- if (getBoard().get(pos) != null) continue;
-
- double chance = chanceToPlaceTile(pos);
- if (chance < MIN_CHANCE) continue;
-
- for (Entry econn : Position.ADJACENT.entrySet()) {
- Position conn = pos.add(econn.getValue());
- if (conn.equals(placement)) continue;
- Tile connTile = getBoard().get(conn);
- if (connTile == null) continue;
-
- rank += futureConnectionRateConnection(eplace.getKey(), econn.getKey(), conn, chance);
- }
- }
- return rank;
- }
-
- protected double rankFairy() {
- if (!game.hasCapability(FairyCapability.class)) return 0;
- FairyCapability fc = game.getCapability(FairyCapability.class);
- Position fairyPos = fc.getFairyPosition();
- if (fairyPos == null) return 0;
-
- double rating = 0;
-
-// TODO more sophisticated rating
- for (Meeple meeple : game.getDeployedMeeples()) {
- if (!meeple.at(fairyPos)) continue;
- if (!(meeple instanceof Follower)) continue;
- if (meeple.getFeature() instanceof Castle) continue;
-
- rating += reducePoints(1.0, meeple.getPlayer());
- }
-
- return rating;
-
-// //OLD legacy impl
-// Set onTile = gc.getPlacedFiguresForTile(board.get(fairyPos.x,fairyPos.y));
-// Set onePointPlayers = new HashSet<>();
-// for (PlacedFigure pfi : onTile) {
-// if (pfi != null && gi.get().equals(pfi.player)) {
-// onePointPlayers.add(pfi.player);
-// }
-// if (pfi.et == FeatureType.TOWER) continue;
-// LastPlaceInfo last = gc.getBoard().getLastPlacementInfo();
-// if (gc.getBoard().get(last.pos).getTrigger() == TileTrigger.DRAGON) {
-// int dist = last.pos.squareDistance(pfi.position);
-// if (dist == 1) {
-// //figurka hned vedle, heuristika
-// /*Tile lastTile = gc.getBoard().get(pfi.position);
-// if (pfi.et == ElementType.CLOISTER) {
-// //lastTile.getCloister().get ...
-// }*/
-// rating += reducePoints(4, pfi.player); //zatim proste odhadnem cenu figurky na 4 body
-// }
-// }
-// }
-// //kvuli brane a vice figurkam na jednom poli, aby kazdy hrac max +1 za kolo
-//
-// for (Player player : onePointPlayers) {
-// rating += reducePoints(0.8, player);
-// }
- }
-
- protected double rankConvexity() {
- Position pos = game.getCurrentTile().getPosition();
- return 0.001 * getBoard().getAdjacentAndDiagonalTiles(pos).size();
- }
-
- protected double rankUnfishedCompletable(Completable completable, LegacyAiScoreContext ctx) {
- double rating = 0.0;
- double points = getUnfinishedCompletablePoints(completable, ctx);
- for (Player p : ctx.getMajorOwners()) {
- rating += reducePoints(points, p);
- }
- return rating;
- }
-
- protected double getUnfinishedCompletablePoints(Completable complatable, LegacyAiScoreContext ctx) {
- if (complatable instanceof City) {
- return getUnfinishedCityPoints((City) complatable, ctx);
- }
- if (complatable instanceof Road) {
- return getUnfinishedRoadPoints((Road) complatable, ctx);
- }
- if (complatable instanceof Cloister) {
- return getUnfinishedCloisterPoints((Cloister) complatable, ctx);
- }
- throw new IllegalArgumentException();
- }
-
- protected double getUnfinishedCityPoints(City city, LegacyAiScoreContext ctx) {
- double chanceToClose = ctx.getChanceToClose();
-
- if (chanceToClose > MIN_CHANCE && ctx.getMajorOwners().contains(getPlayer())) {
- openCount[OPEN_COUNT_CITY]++;
- }
-
- //legacy heuristic
- CityScoreContext cityCtx = (CityScoreContext) ctx.getCompletableScoreContext();
- if (chanceToClose < MIN_CHANCE) {
- return cityCtx.getPoints(false) + 3.0*chanceToClose;
- } else {
- return cityCtx.getPoints(true) - 3.0*(1.0-chanceToClose);
- }
- }
-
- protected double getUnfinishedRoadPoints(Road road, LegacyAiScoreContext ctx) {
- double chanceToClose = ctx.getChanceToClose();;
-
- if (chanceToClose > MIN_CHANCE && ctx.getMajorOwners().contains(getPlayer())) {
- openCount[OPEN_COUNT_ROAD]++;
- }
-
- //legacy heuristic
- RoadScoreContext roadCtx = (RoadScoreContext) ctx.getCompletableScoreContext();
- if (chanceToClose < MIN_CHANCE) {
- return roadCtx.getPoints(false) + 3.0*chanceToClose;
- } else {
- return roadCtx.getPoints(true) - 3.0*(1.0-chanceToClose);
- }
-
- }
-
- protected double getUnfinishedCloisterPoints(Cloister cloister, LegacyAiScoreContext ctx) {
- List followers = cloister.getMeeples();
- if (!followers.isEmpty() && isMe(followers.get(0).getPlayer())) {
- openCount[OPEN_COUNT_CLOITSTER]++;
- }
- double chanceToClose = ctx.getChanceToClose();
- int points = ctx.getPoints();
- return points + (9-points)*chanceToClose;
- }
-
- protected double getFarmPoints(Farm farm, Player p, FarmScoreContext ctx) {
- if (isMe(p)) {
- openCount[OPEN_COUNT_FARM]++;
- }
- return ctx.getPoints(p);
- }
-
- protected double rankSpecialFigures(LegacyAiScoreContext ctx) {
- double rating = 0.0;
- for (Meeple m : ctx.getSpecialMeeples()) {
- if (m instanceof Builder && isMe(m.getPlayer())) {
- rating += rankBuilder((Builder) m, ctx);
- }
- }
- return rating;
- }
-
- protected double rankBuilder(Builder builder, LegacyAiScoreContext ctx) {
- if (!ctx.getMajorOwners().contains(getPlayer())) {
- return -3.0; //builder in enemy object penalty
- }
- if (ctx.getChanceToClose() < 0.55) return 0.0;
- double rating = 0.0;
- //builder placed in object
- if (builder.getFeature() instanceof City) {
- rating += 1.5;
- } else {
- rating += 0.5;
- }
-
- BuilderCapability bc = game.getCapability(BuilderCapability.class);
- //builder used on object
- if (bc.getBuilderState() == BuilderState.ACTIVATED) {
- rating += 3.5;
- }
- return rating;
- }
-
- private double rankTrappedMeeples(LegacyAiScoreContext ctx) {
- //musi tu byt dolni mez - btw nestaci toto misto hodnoceni figurek, spis asi :)
-
- //TODO lepe
- if (myTurnsLeft < 8) return 0.0;
-
- if (ctx.getChanceToClose() > 0.4) return 0.0;
-
- double rating = 0.0;
- for (Meeple m : ctx.getMeeples()) {
- if (isMe(m.getPlayer())) {
- rating += TRAPPED_MY_FIGURE_POINTS;
- } else {
- rating += TRAPPED_ENEMY_FIGURE_POINTS;
- }
- }
- return (1.0 - ctx.getChanceToClose()) * rating; //no reduce
- }
-
-
- @Override
- public void selectDragonMove(Set positions, int movesLeft) {
- initVars();
- Position dragonPosition = game.getCapability(DragonCapability.class).getDragonPosition();
- double tensionX = 0, tensionY = 0;
-
- for (Meeple m : game.getDeployedMeeples()) {
- int distance = dragonPosition.squareDistance(m.getPosition());
- if (distance == 0 || distance > movesLeft) continue;
- if (m.getFeature() instanceof Castle) continue;
-
- double weight = 1.0 / (distance * distance);
- if (isMe(m.getPlayer())) {
- weight *= -0.8; //co takhle 0.8
- }
- tensionX += weight * (m.getPosition().x - dragonPosition.x);
- tensionY += weight * (m.getPosition().y - dragonPosition.y);
- }
-
- double minDiff = Double.MAX_VALUE;
- Position result = null;
- for (Position p : positions) {
- double diff =
- Math.abs(p.x - dragonPosition.x - tensionX) + Math.abs(p.y - dragonPosition.y - tensionY);
- if (diff < minDiff) {
- minDiff = diff;
- result = p;
- }
- }
- logger.info("Selected dragon move: {}", result);
- getServer().moveDragon(result);
- }
-
-}
+package com.jcloisterzone.ai.legacyplayer;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.jcloisterzone.Expansion;
+import com.jcloisterzone.Player;
+import com.jcloisterzone.ai.RankingAiPlayer;
+import com.jcloisterzone.board.EdgePattern;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.board.Rotation;
+import com.jcloisterzone.board.Tile;
+import com.jcloisterzone.feature.Castle;
+import com.jcloisterzone.feature.City;
+import com.jcloisterzone.feature.Cloister;
+import com.jcloisterzone.feature.Completable;
+import com.jcloisterzone.feature.Farm;
+import com.jcloisterzone.feature.Feature;
+import com.jcloisterzone.feature.Road;
+import com.jcloisterzone.feature.score.ScoreAllCallback;
+import com.jcloisterzone.feature.score.ScoreAllFeatureFinder;
+import com.jcloisterzone.feature.visitor.score.AbstractScoreContext;
+import com.jcloisterzone.feature.visitor.score.CityScoreContext;
+import com.jcloisterzone.feature.visitor.score.CompletableScoreContext;
+import com.jcloisterzone.feature.visitor.score.FarmScoreContext;
+import com.jcloisterzone.feature.visitor.score.RoadScoreContext;
+import com.jcloisterzone.feature.visitor.score.ScoreContext;
+import com.jcloisterzone.figure.Barn;
+import com.jcloisterzone.figure.BigFollower;
+import com.jcloisterzone.figure.Builder;
+import com.jcloisterzone.figure.Follower;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.figure.SmallFollower;
+import com.jcloisterzone.figure.predicate.MeeplePredicates;
+import com.jcloisterzone.game.capability.BuilderCapability;
+import com.jcloisterzone.game.capability.BuilderCapability.BuilderState;
+import com.jcloisterzone.game.capability.DragonCapability;
+import com.jcloisterzone.game.capability.FairyCapability;
+import com.jcloisterzone.game.capability.TowerCapability;
+import com.jcloisterzone.game.phase.ScorePhase;
+
+public class LegacyAiPlayer extends RankingAiPlayer {
+
+ private static final double TRAPPED_MY_FIGURE_POINTS = -12.0;
+ private static final double TRAPPED_ENEMY_FIGURE_POINTS = 3.0;
+ private static final double SELF_MERGE_PENALTY = 6.0;
+
+ private static final double MIN_CHANCE = 0.4;
+
+ protected int packSize, myTurnsLeft;
+ protected int enemyPlayers;
+
+ private static final int OPEN_COUNT_ROAD = 0;
+ private static final int OPEN_COUNT_CITY = 1;
+ private static final int OPEN_COUNT_FARM = 2;
+ private static final int OPEN_COUNT_CLOITSTER = 3;
+
+
+ private int[] openCount = new int[4]; //number of my open objects
+
+ public static EnumSet supportedExpansions() {
+ return EnumSet.of(
+ Expansion.BASIC,
+ Expansion.WINTER,
+ Expansion.ABBEY_AND_MAYOR,
+ Expansion.INNS_AND_CATHEDRALS,
+ Expansion.TRADERS_AND_BUILDERS,
+ Expansion.PRINCESS_AND_DRAGON,
+ //Expansion.TOWER, //causing error sometimes
+ Expansion.KING_AND_SCOUT,
+ Expansion.RIVER,
+ Expansion.RIVER_II,
+ Expansion.GQ11,
+ Expansion.CATAPULT,
+ Expansion.WIND_ROSE,
+ Expansion.CATHARS,
+ //Expansion.PHANTOM,
+ //only cards
+ Expansion.COUNT,
+ Expansion.PLAGUE
+ );
+ }
+
+
+
+ protected void initVars() {
+ packSize = getTilePack().totalSize();
+ enemyPlayers = game.getAllPlayers().length - 1;
+ myTurnsLeft = ((packSize-1) / (enemyPlayers+1)) + 1;
+ }
+
+ @Override
+ protected double rank() {
+ double ranking = 0;
+ initVars();
+
+ //trigger score
+ game.getPhase().next(ScorePhase.class);
+ game.getPhase().enter();
+
+ Arrays.fill(openCount, 0);
+
+ ranking += meepleRating();
+ ranking += pointRating();
+ ranking += openObjectRating();
+
+ ranking += rankPossibleFeatureConnections();
+ ranking += rankConvexity();
+ ranking += rankFairy();
+
+// // --- dbg print --
+// Tile tile = getGame().getCurrentTile();
+// Feature meeplePlacement = Iterables.find(tile.getFeatures(), new Predicate() {
+// @Override
+// public boolean apply(Feature f) {
+// return !f.getMeeples().isEmpty();
+// }
+// }, null);
+// Arrays.fill(openCount, 0);
+// System.err.println(
+// String.format("%8s (%4s) %10s %4s/%5s %8.3f = Mepl %.3f Poit %.3f Open %.3f Conn %.3f Covx %.3f Fair %.3f",
+// tile.getId(), tile.getRotation(), tile.getPosition(),
+// meeplePlacement == null ? "" : meeplePlacement.getClass().getSimpleName(),
+// meeplePlacement == null ? "" : meeplePlacement.getLocation(),
+// ranking, meepleRating(), pointRating(), openObjectRating(),
+// rankPossibleFeatureConnections(), rankConvexity(), rankFairy()));
+// // --- end of debug print
+
+ //objectRatings.clear();
+
+ return ranking;
+ }
+
+ protected double reducePoints(double points, Player p) {
+ if (isMe(p)) return points;
+ return -points/enemyPlayers;
+ }
+
+ protected double chanceToPlaceTile(Position pos) {
+ EdgePattern pattern = game.getBoard().getAvailMoveEdgePattern(pos);
+ if (pattern != null && pattern.wildcardSize() < 2) {
+ int remains = game.getTilePack().getSizeForEdgePattern(pattern);
+ if (remains == 0) return 0.0;
+ if (remains < game.getAllPlayers().length) {
+ if (remains == 0) return 0.0;
+ return 1.0 - Math.pow(1.0 - 1.0 / (game.getAllPlayers().length), remains);
+ }
+ }
+ return 1.0;
+ }
+
+ protected double meepleRating() {
+ double rating = 0;
+
+ for (Player p : game.getAllPlayers()) {
+ double meeplePoints = 0;
+ int limit = 0;
+ for (Follower f : Iterables.filter(p.getFollowers(), MeeplePredicates.deployed())) {
+ if (f instanceof SmallFollower) {
+ meeplePoints += 0.15;
+ } else if (f instanceof BigFollower) {
+ meeplePoints += 0.25;
+ }
+ if (++limit == myTurnsLeft) break;
+ }
+ rating += reducePoints(meeplePoints, p);
+ }
+ return rating;
+ }
+
+ class LegacyAiScoreAllCallback implements ScoreAllCallback {
+
+ private double rank = 0;
+
+ private Set towerDanger = new HashSet<>();
+
+ public LegacyAiScoreAllCallback() {
+ TowerCapability towerCap = game.getCapability(TowerCapability.class);
+ if (towerCap != null) {
+ for (Position towerPos : towerCap.getTowers()) {
+ int dangerDistance = 1 + game.getBoard().get(towerPos).getTower().getHeight();
+ towerDanger.add(towerPos);
+ for (int i = 1; i < dangerDistance; i++) {
+ towerDanger.add(towerPos.add(new Position(i, 0)));
+ towerDanger.add(towerPos.add(new Position(-i, 0)));
+ towerDanger.add(towerPos.add(new Position(0, i)));
+ towerDanger.add(towerPos.add(new Position(0, -i)));
+ }
+ }
+ }
+ }
+
+ private boolean isInTowerDanger(ScoreContext ctx) {
+ //not exact but it is easy heuristic at now
+ if (!towerDanger.isEmpty()) {
+ for (Follower f : ctx.getFollowers()) {
+ if (towerDanger.contains(f.getPosition())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void scoreCastle(Meeple meeple, Castle castle) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LegacyAiScoreContext getCompletableScoreContext(Completable completable) {
+ //TODO uncomment after invalidate implemeted
+// AiScoreContext ctx = getScoreCache().get(completable);
+// if (ctx != null && ctx.isValid()) {
+// return (CompletableScoreContext) ctx;
+// }
+ return new LegacyAiScoreContext(LegacyAiPlayer.this, completable.getScoreContext()/*, getScoreCache()*/);
+ }
+
+ @Override
+ public LegacyAiFarmScoreContext getFarmScoreContext(Farm farm) {
+ //TODO uncomment after invalidate implemeted
+// AiScoreContext ctx = getScoreCache().get(farm);
+// if (ctx != null && ctx.isValid()) {
+// return (FarmScoreContext) ctx;
+// }
+ return new LegacyAiFarmScoreContext(game/*, getScoreCache()*/);
+ }
+
+ @Override
+ public void scoreFarm(FarmScoreContext ctx, Player player) {
+ if (isInTowerDanger(ctx)) return;
+ double points = getFarmPoints((Farm) ctx.getMasterFeature(), player, ctx);
+ rank += reducePoints(points, player);
+ }
+
+ @Override
+ public void scoreBarn(FarmScoreContext ctx, Barn meeple) {
+ //prefer barn placement - magic constant
+ rank += reducePoints(1.2 * ctx.getBarnPoints(), meeple.getPlayer());
+ }
+
+ @Override
+ public void scoreCompletableFeature(CompletableScoreContext ctx) {
+ if (isInTowerDanger(ctx)) return;
+ rank += rankUnfishedCompletable(ctx.getMasterFeature(), (LegacyAiScoreContext) ctx);
+ rank += rankTrappedMeeples((LegacyAiScoreContext) ctx);
+ rank += rankSpecialFigures((LegacyAiScoreContext) ctx);
+ }
+
+ public double getRanking() {
+ return rank;
+ }
+
+ }
+
+ protected double pointRating() {
+ double rating = 0;
+
+ for (Player p : game.getAllPlayers()) {
+ rating += reducePoints(p.getPoints(), p);
+ }
+
+ ScoreAllFeatureFinder scoreAll = new ScoreAllFeatureFinder();
+ LegacyAiScoreAllCallback callback = new LegacyAiScoreAllCallback();
+ scoreAll.scoreAll(game, callback);
+ rating += callback.getRanking();
+
+ return rating;
+ }
+
+
+ public static final double[][] OPEN_PENALTY = {
+ { 0.0, 1.0, 2.5, 4.5, 7.5, 10.5, 14.5, 19.0, 29.0 }, //road
+ { 0.0, 0.5, 1.5, 3.0, 5.0, 8.0, 12.0, 17.0, 27.0 }, //city
+ { 0.0, 5.0, 10.0, 19.0, 28.0, 37.0, 47.0, 57.0, 67.0 }, //farm
+ { 0.0, 0.0, 0.4, 0.8, 1.2, 2.0, 4.0, 7.0, 11.0 } //cloister
+ };
+
+ protected double openObjectRating() {
+ double rating = 0;
+
+ for (int i = 0; i < OPEN_PENALTY.length; i++ ){
+ double penalty;
+ //fast fix for strange bug causes ArrayIndexOutOfBoundsException: 9
+ if (openCount[i] >= OPEN_PENALTY[i].length) {
+ penalty = OPEN_PENALTY[i][OPEN_PENALTY[i].length - 1];
+ } else {
+ penalty = OPEN_PENALTY[i][openCount[i]];
+ }
+ if (i == 2) {
+ //Farm
+ double modifier = (packSize - ((1+enemyPlayers) * 3)) / 20.0;
+ if (modifier < 1.0) modifier = 1.0;
+ rating -= modifier * penalty;
+ } else {
+ rating -= penalty;
+ }
+ }
+ return rating;
+ }
+
+ private ScoreContext futureConnectionCreateScoreContext(Feature feature) {
+ LegacyAiScoreAllCallback ctxHelper = new LegacyAiScoreAllCallback();
+ ScoreContext ctx;
+ if (feature instanceof Completable) {
+ ctx = ctxHelper.getCompletableScoreContext((Completable) feature);
+ } else {
+ ctx = ctxHelper.getFarmScoreContext((Farm) feature);
+ ((LegacyAiFarmScoreContext) ctx).setCityCache(new HashMap());
+ }
+ feature.walk(ctx);
+ return ctx;
+ }
+
+ private double futureConnectionGetFeaturePoints(Feature feature, ScoreContext featureCtx) {
+ if (featureCtx instanceof CompletableScoreContext) {
+ return getUnfinishedCompletablePoints((Completable) feature, (LegacyAiScoreContext) featureCtx);
+ } else {
+ return ((FarmScoreContext) featureCtx).getPoints(getPlayer());
+ }
+ }
+
+ private double futureConnectionRateConnection(Location toEmpty, Location toFeature, Position f2Pos, double chance) {
+ Tile tile1 = getGame().getCurrentTile();
+ Tile tile2 = getBoard().get(f2Pos);
+
+ double rating = 0;
+
+ Completable f1 = (Completable) tile1.getFeaturePartOf(toEmpty);
+ Completable f2 = (Completable) tile2.getFeaturePartOf(toFeature.rev());
+
+ if (f1 != null && f2 != null) {
+ if (f1.getClass().equals(f2.getClass())) {
+ // System.err.println(" " + tile1.getPosition() + " <-->" + f2Pos + " / " + f1 + " " + f2);
+ rating += futureConnectionRateFeatures(toEmpty, toFeature, chance, f1, f2);
+ } else {
+ rating += futureConnectionRateCrossing(toEmpty, toFeature, chance, f1, f2);
+ }
+ }
+
+ if (toEmpty != toFeature) {
+ boolean left = toEmpty.rotateCCW(Rotation.R90) == toFeature;
+ Farm farm1 = (Farm) tile2.getFeaturePartOf(left ? toEmpty.getLeftFarm() : toEmpty.getRightFarm());
+ Farm farm2 = (Farm) tile2.getFeaturePartOf(left ? toFeature.rev().getRightFarm() : toFeature.rev().getLeftFarm());
+
+ if (farm1 != null && farm2 != null) {
+// System.err.println(" " + tile1.getPosition() + " <-->" + f2Pos + " / " + farm1 + " " + farm2);
+ rating += futureConnectionRateFeatures(toEmpty, toFeature, chance, farm1, farm2);
+ }
+ }
+ return rating;
+ }
+
+ private double futureConnectionRateCrossing(Location toEmpty, Location toFeature, double chance, Feature f1, Feature f2) {
+ ScoreContext f1Ctx = futureConnectionCreateScoreContext(f1);
+ ScoreContext f2Ctx = futureConnectionCreateScoreContext(f2);
+ Map f1Powers = f1Ctx.getPowers();
+ Map f2Powers = f2Ctx.getPowers();
+
+ int[] powers = funtureConnectionSumPower(f2Powers, null);
+ int myPower = powers[0];
+ int bestEnemy = powers[1];
+
+ if (f1Powers.size() == 0) {
+ if (bestEnemy > myPower) {
+ return 0.2;
+ } else if (myPower > 0) {
+ return -2.5;
+ }
+ } else {
+ if (bestEnemy > myPower) {
+ return -0.1;
+ } else if (myPower > 0) {
+ return -0.5;
+ } else {
+ return -0.5;
+ }
+ }
+ return 0;
+ }
+
+
+ private double futureConnectionRateFeatures(Location toEmpty, Location toFeature, double chance, Feature f1, Feature f2) {
+ ScoreContext f1Ctx = futureConnectionCreateScoreContext(f1);
+ ScoreContext f2Ctx = futureConnectionCreateScoreContext(f2);
+ Map f1Powers = f1Ctx.getPowers();
+ Map f2Powers = f2Ctx.getPowers();
+
+ if (f1Powers.size() == 0) {
+ return 0;
+ }
+
+ if (f1Powers.size() == 1 && f2Powers.size() == 1 && f1Powers.containsKey(getPlayer()) && f2Powers.containsKey(getPlayer())) {
+// System.err.println(" !self merge");
+// System.err.println(f1Powers + " // " + f2Powers);
+ return -SELF_MERGE_PENALTY;
+ }
+
+ double myPoints = futureConnectionGetFeaturePoints(f1, f1Ctx);
+ double enemyPoints = futureConnectionGetFeaturePoints(f2, f2Ctx);
+
+ if (enemyPoints < (toEmpty == toFeature ? 7.0 : 5.0)) {
+// System.err.println("too small penalty: " + enemyPoints);
+ return -0.05; //small penalty
+ }
+
+ int[] powers = funtureConnectionSumPower(f1Powers, f2Powers);
+ int myPower = powers[0];
+ int bestEnemy = powers[1];
+
+ double coef = toEmpty != toFeature ? 0.7 : 0.4; //corner / straight connection
+
+// System.err.println("@@@ @@@ " + myPower + "/" + myPoints + " vs " + bestEnemy + "/" + enemyPoints);
+
+ if (myPower == bestEnemy) {
+ return coef * (enemyPoints - myPoints) * chance;
+ }
+ if (myPower > bestEnemy) {
+ return coef * enemyPoints * chance;
+
+ }
+ return -myPoints * chance; //no coef here
+
+ }
+
+ private int[] funtureConnectionSumPower(Map f1Powers, Map f2Powers) {
+ Map sum = new HashMap(f1Powers);
+ if (f2Powers != null) {
+ for (Entry epower : f2Powers.entrySet()) {
+ Integer val = sum.get(epower.getKey());
+ sum.put(epower.getKey(), val == null ? epower.getValue() : val + epower.getValue());
+ }
+ }
+ int myPower = 0;
+ int bestEnemy = 0;
+ for (Entry esum : sum.entrySet()) {
+ int value = esum.getValue();
+ if (esum.getKey().equals(getPlayer())) {
+ myPower = value;
+ } else {
+ if (value > bestEnemy) bestEnemy = value;
+ }
+ }
+ return new int[] { myPower, bestEnemy };
+ }
+
+ private double rankPossibleFeatureConnections() {
+ double rank = 0;
+
+ Tile tile = getGame().getCurrentTile();
+ Position placement = tile.getPosition();
+ assert placement != null;
+
+ for (Entry eplace : Position.ADJACENT.entrySet()) {
+ Position pos = placement.add(eplace.getValue());
+ if (getBoard().get(pos) != null) continue;
+
+ double chance = chanceToPlaceTile(pos);
+ if (chance < MIN_CHANCE) continue;
+
+ for (Entry econn : Position.ADJACENT.entrySet()) {
+ Position conn = pos.add(econn.getValue());
+ if (conn.equals(placement)) continue;
+ Tile connTile = getBoard().get(conn);
+ if (connTile == null) continue;
+
+ rank += futureConnectionRateConnection(eplace.getKey(), econn.getKey(), conn, chance);
+ }
+ }
+ return rank;
+ }
+
+ protected double rankFairy() {
+ if (!game.hasCapability(FairyCapability.class)) return 0;
+ FairyCapability fc = game.getCapability(FairyCapability.class);
+ Position fairyPos = fc.getFairyPosition();
+ if (fairyPos == null) return 0;
+
+ double rating = 0;
+
+// TODO more sophisticated rating
+ for (Meeple meeple : game.getDeployedMeeples()) {
+ if (!meeple.at(fairyPos)) continue;
+ if (!(meeple instanceof Follower)) continue;
+ if (meeple.getFeature() instanceof Castle) continue;
+
+ rating += reducePoints(1.0, meeple.getPlayer());
+ }
+
+ return rating;
+
+// //OLD legacy impl
+// Set onTile = gc.getPlacedFiguresForTile(board.get(fairyPos.x,fairyPos.y));
+// Set onePointPlayers = new HashSet<>();
+// for (PlacedFigure pfi : onTile) {
+// if (pfi != null && gi.get().equals(pfi.player)) {
+// onePointPlayers.add(pfi.player);
+// }
+// if (pfi.et == FeatureType.TOWER) continue;
+// LastPlaceInfo last = gc.getBoard().getLastPlacementInfo();
+// if (gc.getBoard().get(last.pos).getTrigger() == TileTrigger.DRAGON) {
+// int dist = last.pos.squareDistance(pfi.position);
+// if (dist == 1) {
+// //figurka hned vedle, heuristika
+// /*Tile lastTile = gc.getBoard().get(pfi.position);
+// if (pfi.et == ElementType.CLOISTER) {
+// //lastTile.getCloister().get ...
+// }*/
+// rating += reducePoints(4, pfi.player); //zatim proste odhadnem cenu figurky na 4 body
+// }
+// }
+// }
+// //kvuli brane a vice figurkam na jednom poli, aby kazdy hrac max +1 za kolo
+//
+// for (Player player : onePointPlayers) {
+// rating += reducePoints(0.8, player);
+// }
+ }
+
+ protected double rankConvexity() {
+ Position pos = game.getCurrentTile().getPosition();
+ return 0.001 * getBoard().getAdjacentAndDiagonalTiles(pos).size();
+ }
+
+ protected double rankUnfishedCompletable(Completable completable, LegacyAiScoreContext ctx) {
+ double rating = 0.0;
+ double points = getUnfinishedCompletablePoints(completable, ctx);
+ for (Player p : ctx.getMajorOwners()) {
+ rating += reducePoints(points, p);
+ }
+ return rating;
+ }
+
+ protected double getUnfinishedCompletablePoints(Completable complatable, LegacyAiScoreContext ctx) {
+ if (complatable instanceof City) {
+ return getUnfinishedCityPoints((City) complatable, ctx);
+ }
+ if (complatable instanceof Road) {
+ return getUnfinishedRoadPoints((Road) complatable, ctx);
+ }
+ if (complatable instanceof Cloister) {
+ return getUnfinishedCloisterPoints((Cloister) complatable, ctx);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ protected double getUnfinishedCityPoints(City city, LegacyAiScoreContext ctx) {
+ double chanceToClose = ctx.getChanceToClose();
+
+ if (chanceToClose > MIN_CHANCE && ctx.getMajorOwners().contains(getPlayer())) {
+ openCount[OPEN_COUNT_CITY]++;
+ }
+
+ //legacy heuristic
+ CityScoreContext cityCtx = (CityScoreContext) ctx.getCompletableScoreContext();
+ if (chanceToClose < MIN_CHANCE) {
+ return cityCtx.getPoints(false) + 3.0*chanceToClose;
+ } else {
+ return cityCtx.getPoints(true) - 3.0*(1.0-chanceToClose);
+ }
+ }
+
+ protected double getUnfinishedRoadPoints(Road road, LegacyAiScoreContext ctx) {
+ double chanceToClose = ctx.getChanceToClose();;
+
+ if (chanceToClose > MIN_CHANCE && ctx.getMajorOwners().contains(getPlayer())) {
+ openCount[OPEN_COUNT_ROAD]++;
+ }
+
+ //legacy heuristic
+ RoadScoreContext roadCtx = (RoadScoreContext) ctx.getCompletableScoreContext();
+ if (chanceToClose < MIN_CHANCE) {
+ return roadCtx.getPoints(false) + 3.0*chanceToClose;
+ } else {
+ return roadCtx.getPoints(true) - 3.0*(1.0-chanceToClose);
+ }
+
+ }
+
+ protected double getUnfinishedCloisterPoints(Cloister cloister, LegacyAiScoreContext ctx) {
+ List followers = cloister.getMeeples();
+ if (!followers.isEmpty() && isMe(followers.get(0).getPlayer())) {
+ openCount[OPEN_COUNT_CLOITSTER]++;
+ }
+ double chanceToClose = ctx.getChanceToClose();
+ int points = ctx.getPoints();
+ return points + (9-points)*chanceToClose;
+ }
+
+ protected double getFarmPoints(Farm farm, Player p, FarmScoreContext ctx) {
+ if (isMe(p)) {
+ openCount[OPEN_COUNT_FARM]++;
+ }
+ return ctx.getPoints(p);
+ }
+
+ protected double rankSpecialFigures(LegacyAiScoreContext ctx) {
+ double rating = 0.0;
+ for (Meeple m : ctx.getSpecialMeeples()) {
+ if (m instanceof Builder && isMe(m.getPlayer())) {
+ rating += rankBuilder((Builder) m, ctx);
+ }
+ }
+ return rating;
+ }
+
+ protected double rankBuilder(Builder builder, LegacyAiScoreContext ctx) {
+ if (!ctx.getMajorOwners().contains(getPlayer())) {
+ return -3.0; //builder in enemy object penalty
+ }
+ if (ctx.getChanceToClose() < 0.55) return 0.0;
+ double rating = 0.0;
+ //builder placed in object
+ if (builder.getFeature() instanceof City) {
+ rating += 1.5;
+ } else {
+ rating += 0.5;
+ }
+
+ BuilderCapability bc = game.getCapability(BuilderCapability.class);
+ //builder used on object
+ if (bc.getBuilderState() == BuilderState.ACTIVATED) {
+ rating += 3.5;
+ }
+ return rating;
+ }
+
+ private double rankTrappedMeeples(LegacyAiScoreContext ctx) {
+ //musi tu byt dolni mez - btw nestaci toto misto hodnoceni figurek, spis asi :)
+
+ //TODO lepe
+ if (myTurnsLeft < 8) return 0.0;
+
+ if (ctx.getChanceToClose() > 0.4) return 0.0;
+
+ double rating = 0.0;
+ for (Meeple m : ctx.getMeeples()) {
+ if (isMe(m.getPlayer())) {
+ rating += TRAPPED_MY_FIGURE_POINTS;
+ } else {
+ rating += TRAPPED_ENEMY_FIGURE_POINTS;
+ }
+ }
+ return (1.0 - ctx.getChanceToClose()) * rating; //no reduce
+ }
+
+
+ @Override
+ public void selectDragonMove(Set positions, int movesLeft) {
+ initVars();
+ Position dragonPosition = game.getCapability(DragonCapability.class).getDragonPosition();
+ double tensionX = 0, tensionY = 0;
+
+ for (Meeple m : game.getDeployedMeeples()) {
+ int distance = dragonPosition.squareDistance(m.getPosition());
+ if (distance == 0 || distance > movesLeft) continue;
+ if (m.getFeature() instanceof Castle) continue;
+
+ double weight = 1.0 / (distance * distance);
+ if (isMe(m.getPlayer())) {
+ weight *= -0.8; //co takhle 0.8
+ }
+ tensionX += weight * (m.getPosition().x - dragonPosition.x);
+ tensionY += weight * (m.getPosition().y - dragonPosition.y);
+ }
+
+ double minDiff = Double.MAX_VALUE;
+ Position result = null;
+ for (Position p : positions) {
+ double diff =
+ Math.abs(p.x - dragonPosition.x - tensionX) + Math.abs(p.y - dragonPosition.y - tensionY);
+ if (diff < minDiff) {
+ minDiff = diff;
+ result = p;
+ }
+ }
+ logger.info("Selected dragon move: {}", result);
+ getServer().moveDragon(result);
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiScoreContext.java b/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiScoreContext.java
index f5735c1e8..78eeda28f 100644
--- a/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiScoreContext.java
+++ b/src/main/java/com/jcloisterzone/ai/legacyplayer/LegacyAiScoreContext.java
@@ -1,171 +1,171 @@
-package com.jcloisterzone.ai.legacyplayer;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.jcloisterzone.Player;
-import com.jcloisterzone.ai.AiScoreContext;
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Position;
-import com.jcloisterzone.feature.Cloister;
-import com.jcloisterzone.feature.Completable;
-import com.jcloisterzone.feature.CompletableFeature;
-import com.jcloisterzone.feature.Feature;
-import com.jcloisterzone.feature.visitor.SelfReturningVisitor;
-import com.jcloisterzone.feature.visitor.score.CompletableScoreContext;
-import com.jcloisterzone.figure.Follower;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.figure.Special;
-
-class LegacyAiScoreContext extends SelfReturningVisitor implements CompletableScoreContext, AiScoreContext {
-
- public static class OpenEdge {
- double chanceToClose;
- Feature feature;
- Location location;
- }
-
- protected final transient Logger logger = LoggerFactory.getLogger(getClass());
-
- private final LegacyAiPlayer aiPlayer;
- private final CompletableScoreContext ctx;
- //private final Map scoreCache;
- private boolean valid = true;
-
- private Map openEdgesChanceToClose = new HashMap<>();
- private double chanceToClose = 1.0;
-
- public LegacyAiScoreContext(LegacyAiPlayer aiPlayer, CompletableScoreContext ctx/*, Map scoreCache*/) {
- this.aiPlayer = aiPlayer;
- this.ctx = ctx;
- //this.scoreCache = scoreCache;
- }
-
- @Override
- public boolean isValid() {
- return valid;
- };
-
- public void setValid(boolean valid) {
- this.valid = valid;
- }
-
- public double getChanceToClose() {
- if (chanceToClose > 0.95) {
- return 0.95;
- }
- return chanceToClose;
- }
-
- private double updateCompletableChanceToClose(CompletableFeature completable) {
- double result = 1.0;
- //TODO: this method using internal contract of MultiTileFeature - encapsulation is violated here
- int i = 0;
- for (Location side : Location.sides()) {
- if (side.intersect(completable.getLocation()) != null) {
- if (completable.getEdges()[i] == null) {
- //side is open
- Position p = completable.getTile().getPosition().add(side);
- if (!openEdgesChanceToClose.containsKey(p)) {
- OpenEdge edge = new OpenEdge();
- edge.chanceToClose = aiPlayer.chanceToPlaceTile(p);
- edge.feature = completable;
- edge.location = side;
- openEdgesChanceToClose.put(p, edge);
- result *= edge.chanceToClose;
- }
- }
- i++;
- }
- }
- return result;
- }
-
- public Map getOpenEdgesChanceToClose() {
- return openEdgesChanceToClose;
- }
-
- private double updateCloisterChanceToClose(Cloister cloister) {
- double result = 1.0;
- Position p = cloister.getTile().getPosition();
- for (Position adjacent: p.addMulti(Position.ADJACENT_AND_DIAGONAL.values())) {
- result *= aiPlayer.chanceToPlaceTile(adjacent);
- }
- //for "1.6-compatibility" - make it already sense ?
- if (result > 0.85) return 0.85;
- return result;
- }
-
- @Override
- public boolean visit(Feature feature) {
- //scoreCache.put(feature, this);
-
- if (feature instanceof CompletableFeature) {
- chanceToClose *= updateCompletableChanceToClose((CompletableFeature) feature);
- } else if (feature instanceof Cloister) {
- chanceToClose *= updateCloisterChanceToClose((Cloister) feature);
- }
- return ctx.visit(feature);
- }
-
- public CompletableScoreContext getCompletableScoreContext() {
- return ctx;
- }
-
- @Override
- public Follower getSampleFollower(Player player) {
- return ctx.getSampleFollower(player);
- }
-
- @Override
- public Set getMajorOwners() {
- return ctx.getMajorOwners();
- }
-
- @Override
- public List getFollowers() {
- return ctx.getFollowers();
- }
-
- @Override
- public List getSpecialMeeples() {
- return ctx.getSpecialMeeples();
- }
-
- @Override
- public Iterable getMeeples() {
- return ctx.getMeeples();
- }
-
-
- @Override
- public Completable getMasterFeature() {
- return ctx.getMasterFeature();
- }
-
- @Override
- public boolean isCompleted() {
- return ctx.isCompleted();
- }
-
- @Override
- public int getPoints() {
- return ctx.getPoints();
- }
-
- @Override
- public Set getPositions() {
- return ctx.getPositions();
- }
-
- @Override
- public Map getPowers() {
- return ctx.getPowers();
- }
-
+package com.jcloisterzone.ai.legacyplayer;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcloisterzone.Player;
+import com.jcloisterzone.ai.AiScoreContext;
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Position;
+import com.jcloisterzone.feature.Cloister;
+import com.jcloisterzone.feature.Completable;
+import com.jcloisterzone.feature.CompletableFeature;
+import com.jcloisterzone.feature.Feature;
+import com.jcloisterzone.feature.visitor.SelfReturningVisitor;
+import com.jcloisterzone.feature.visitor.score.CompletableScoreContext;
+import com.jcloisterzone.figure.Follower;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.figure.Special;
+
+class LegacyAiScoreContext extends SelfReturningVisitor implements CompletableScoreContext, AiScoreContext {
+
+ public static class OpenEdge {
+ double chanceToClose;
+ Feature feature;
+ Location location;
+ }
+
+ protected final transient Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final LegacyAiPlayer aiPlayer;
+ private final CompletableScoreContext ctx;
+ //private final Map scoreCache;
+ private boolean valid = true;
+
+ private Map openEdgesChanceToClose = new HashMap<>();
+ private double chanceToClose = 1.0;
+
+ public LegacyAiScoreContext(LegacyAiPlayer aiPlayer, CompletableScoreContext ctx/*, Map scoreCache*/) {
+ this.aiPlayer = aiPlayer;
+ this.ctx = ctx;
+ //this.scoreCache = scoreCache;
+ }
+
+ @Override
+ public boolean isValid() {
+ return valid;
+ };
+
+ public void setValid(boolean valid) {
+ this.valid = valid;
+ }
+
+ public double getChanceToClose() {
+ if (chanceToClose > 0.95) {
+ return 0.95;
+ }
+ return chanceToClose;
+ }
+
+ private double updateCompletableChanceToClose(CompletableFeature completable) {
+ double result = 1.0;
+ //TODO: this method using internal contract of MultiTileFeature - encapsulation is violated here
+ int i = 0;
+ for (Location side : Location.sides()) {
+ if (side.intersect(completable.getLocation()) != null) {
+ if (completable.getEdges()[i] == null) {
+ //side is open
+ Position p = completable.getTile().getPosition().add(side);
+ if (!openEdgesChanceToClose.containsKey(p)) {
+ OpenEdge edge = new OpenEdge();
+ edge.chanceToClose = aiPlayer.chanceToPlaceTile(p);
+ edge.feature = completable;
+ edge.location = side;
+ openEdgesChanceToClose.put(p, edge);
+ result *= edge.chanceToClose;
+ }
+ }
+ i++;
+ }
+ }
+ return result;
+ }
+
+ public Map getOpenEdgesChanceToClose() {
+ return openEdgesChanceToClose;
+ }
+
+ private double updateCloisterChanceToClose(Cloister cloister) {
+ double result = 1.0;
+ Position p = cloister.getTile().getPosition();
+ for (Position adjacent: p.addMulti(Position.ADJACENT_AND_DIAGONAL.values())) {
+ result *= aiPlayer.chanceToPlaceTile(adjacent);
+ }
+ //for "1.6-compatibility" - make it already sense ?
+ if (result > 0.85) return 0.85;
+ return result;
+ }
+
+ @Override
+ public boolean visit(Feature feature) {
+ //scoreCache.put(feature, this);
+
+ if (feature instanceof CompletableFeature) {
+ chanceToClose *= updateCompletableChanceToClose((CompletableFeature) feature);
+ } else if (feature instanceof Cloister) {
+ chanceToClose *= updateCloisterChanceToClose((Cloister) feature);
+ }
+ return ctx.visit(feature);
+ }
+
+ public CompletableScoreContext getCompletableScoreContext() {
+ return ctx;
+ }
+
+ @Override
+ public Follower getSampleFollower(Player player) {
+ return ctx.getSampleFollower(player);
+ }
+
+ @Override
+ public Set getMajorOwners() {
+ return ctx.getMajorOwners();
+ }
+
+ @Override
+ public List getFollowers() {
+ return ctx.getFollowers();
+ }
+
+ @Override
+ public List getSpecialMeeples() {
+ return ctx.getSpecialMeeples();
+ }
+
+ @Override
+ public Iterable getMeeples() {
+ return ctx.getMeeples();
+ }
+
+
+ @Override
+ public Completable getMasterFeature() {
+ return ctx.getMasterFeature();
+ }
+
+ @Override
+ public boolean isCompleted() {
+ return ctx.isCompleted();
+ }
+
+ @Override
+ public int getPoints() {
+ return ctx.getPoints();
+ }
+
+ @Override
+ public Set getPositions() {
+ return ctx.getPositions();
+ }
+
+ @Override
+ public Map getPowers() {
+ return ctx.getPowers();
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/com/jcloisterzone/ai/operation/MeepleUndeployedOperation.java b/src/main/java/com/jcloisterzone/ai/operation/MeepleUndeployedOperation.java
index 8a319a3cc..e0bb14156 100644
--- a/src/main/java/com/jcloisterzone/ai/operation/MeepleUndeployedOperation.java
+++ b/src/main/java/com/jcloisterzone/ai/operation/MeepleUndeployedOperation.java
@@ -1,32 +1,32 @@
-package com.jcloisterzone.ai.operation;
-
-import com.jcloisterzone.board.Location;
-import com.jcloisterzone.board.Tile;
-import com.jcloisterzone.feature.Feature;
-import com.jcloisterzone.figure.Meeple;
-import com.jcloisterzone.game.Game;
-
-public class MeepleUndeployedOperation implements Operation {
-
- private final Meeple meeple;
- private Tile tile;
- private Location loc;
-
- public MeepleUndeployedOperation(Meeple meeple) {
- this.meeple = meeple;
- this.tile = meeple.getFeature() == null ? null : meeple.getFeature().getTile(); //prison undeploy
- this.loc = meeple.getLocation();
- }
-
- @Override
- public void undo(Game game) {
- meeple.setLocation(loc);
- if (tile != null) {
- Feature feature = meeple.getPieceForDeploy(tile, loc);
- feature.addMeeple(meeple);
- meeple.setPosition(tile.getPosition());
- meeple.setFeature(feature);
- }
- }
-
-}
+package com.jcloisterzone.ai.operation;
+
+import com.jcloisterzone.board.Location;
+import com.jcloisterzone.board.Tile;
+import com.jcloisterzone.feature.Feature;
+import com.jcloisterzone.figure.Meeple;
+import com.jcloisterzone.game.Game;
+
+public class MeepleUndeployedOperation implements Operation {
+
+ private final Meeple meeple;
+ private Tile tile;
+ private Location loc;
+
+ public MeepleUndeployedOperation(Meeple meeple) {
+ this.meeple = meeple;
+ this.tile = meeple.getFeature() == null ? null : meeple.getFeature().getTile(); //prison undeploy
+ this.loc = meeple.getLocation();
+ }
+
+ @Override
+ public void undo(Game game) {
+ meeple.setLocation(loc);
+ if (tile != null) {
+ Feature feature = meeple.getPieceForDeploy(tile, loc);
+ feature.addMeeple(meeple);
+ meeple.setPosition(tile.getPosition());
+ meeple.setFeature(feature);
+ }
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/ai/operation/TilePlacedOperation.java b/src/main/java/com/jcloisterzone/ai/operation/TilePlacedOperation.java
index 1567b0b76..8035b2edd 100644
--- a/src/main/java/com/jcloisterzone/ai/operation/TilePlacedOperation.java
+++ b/src/main/java/com/jcloisterzone/ai/operation/TilePlacedOperation.java
@@ -1,24 +1,24 @@
-package com.jcloisterzone.ai.operation;
-
-import com.jcloisterzone.board.DefaultTilePack;
-import com.jcloisterzone.board.Tile;
-import com.jcloisterzone.board.TilePack;
-import com.jcloisterzone.game.Game;
-
-public class TilePlacedOperation implements Operation {
-
- private final Tile tile;
-
- public TilePlacedOperation(Tile tile) {
- this.tile = tile;
- }
-
- @Override
- public void undo(Game game) {
- game.getBoard().unmergeFeatures(tile);
- game.getBoard().remove(tile);
- if (tile.isAbbeyTile()) {
- ((DefaultTilePack)game.getTilePack()).addTile(tile, TilePack.INACTIVE_GROUP);
- }
- }
-}
+package com.jcloisterzone.ai.operation;
+
+import com.jcloisterzone.board.DefaultTilePack;
+import com.jcloisterzone.board.Tile;
+import com.jcloisterzone.board.TilePack;
+import com.jcloisterzone.game.Game;
+
+public class TilePlacedOperation implements Operation {
+
+ private final Tile tile;
+
+ public TilePlacedOperation(Tile tile) {
+ this.tile = tile;
+ }
+
+ @Override
+ public void undo(Game game) {
+ game.getBoard().unmergeFeatures(tile);
+ game.getBoard().remove(tile);
+ if (tile.isAbbeyTile()) {
+ ((DefaultTilePack)game.getTilePack()).addTile(tile, TilePack.INACTIVE_GROUP);
+ }
+ }
+}
diff --git a/src/main/java/com/jcloisterzone/board/Board.java b/src/main/java/com/jcloisterzone/board/Board.java
index ab889bb03..40493d69e 100644
--- a/src/main/java/com/jcloisterzone/board/Board.java
+++ b/src/main/java/com/jcloisterzone/board/Board.java
@@ -1,276 +1,276 @@
-package com.jcloisterzone.board;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import com.jcloisterzone.game.Game;
-import com.jcloisterzone.game.capability.BridgeCapability;
-
-
-/**
- * Board represent game board with any size, so Tile
instances ale
- * stored in Map
. Board supplies proper merging of score objects
- * (Road
, City
or Farm
)
- * and diagonal merge for Cloister instances.
-
- */
-public class Board {
- protected final Map tiles = new LinkedHashMap();
- protected final Map availMoves = new HashMap<>();
- protected final Map> currentAvailMoves = new HashMap<>();
- protected final Set holes = new HashSet<>();
-
- private int maxX, minX, maxY, minY;
-
- private final Game game;
-
-// protected Set tunnels = new HashSet<>();
-// protected Map openTunnels = new HashMap<>(); //tunnel with open one side
-
- protected List discardedTiles = new ArrayList<>();
-
-
- public Board(Game game) {
- this.game = game;
- }
-
- /**
- * Updates current avail moves for next turn
- * @param tile next tile
- */
- public void refreshAvailablePlacements(Tile tile) {
- Rotation tileRotation = tile.getRotation();
- currentAvailMoves.clear();
- for (Position p : availMoves.keySet()) {
- EnumSet allowed = EnumSet.noneOf(Rotation.class);
- for (Rotation rotation: Rotation.values()) {
- tile.setRotation(rotation);
- if (!isPlacementAllowed(tile, p)) {
- //not allowed according standard rules, must check if deployed bridge can allow it
- if (!game.hasCapability(BridgeCapability.class)) continue;
- if (!game.getCapability(BridgeCapability.class).isTilePlacementWithBridgePossible(tile, p)) continue;
- }
- if (!game.isTilePlacementAllowed(tile, p)) continue;
- allowed.add(rotation);
- }
- if (!allowed.isEmpty()) {
- currentAvailMoves.put(p, allowed);
- }
- }
- tile.setRotation(tileRotation); //reset rotation
- }
-
-
- protected void availMovesAdd(Position pos) {
- availMoves.put(pos, EdgePattern.forEmptyTile(this, pos));
- }
-
- protected void availMovesRemove(Position pos) {
- availMoves.remove(pos);
- }
-
- public EdgePattern getAvailMoveEdgePattern(Position pos) {
- return availMoves.get(pos);
- }
-
-
- /**
- * Place tile on given position. Check for correct placement (check if neigbours
- * edges match with tile edges according to Carcassonne rules
- * @param tile tile to place
- * @param p position to place
- * @throws IllegalMoveException if placement is violate game rules
- */
- public void add(Tile tile, Position p) {
- add(tile, p, false);
- }
-
- public void add(Tile tile, Position p, boolean unchecked) {
- if (!unchecked) {
- if (tile.isAbbeyTile()) {
- if (!holes.contains(p)) {
- throw new IllegalArgumentException("Abbey must be placed inside hole");
- }
- } else {
- if (!currentAvailMoves.containsKey(p)) {
- throw new IllegalArgumentException("Invalid position " + p);
- }
- if (!currentAvailMoves.get(p).contains(tile.getRotation())) {
- throw new IllegalArgumentException("Incorrect rotation " + tile.getRotation() + " "+ p);
- }
- }
- }
-
- tiles.put(p, tile);
- availMovesRemove(p);
-
- for (Position offset: Position.ADJACENT.values()) {
- Position next = p.add(offset);
- if (get(next) == null) {
- availMovesAdd(next);
- if (isHole(next)) {
- holes.add(next);
- }
- }
- }
- holes.remove(p);
- tile.setPosition(p);
- if (p.x > maxX) maxX = p.x;
- if (p.x < minX) minX = p.x;
- if (p.y > maxY) maxY = p.y;
- if (p.y < minY) minY = p.y;
- }
-
- public void mergeFeatures(Tile tile) {
- for (Entry e : getAdjacentTilesMap(tile.getPosition()).entrySet()) {
- tile.merge(e.getValue(), e.getKey());
- }
- }
-
- public void remove(Tile tile) {
- Position pos = tile.getPosition();
- assert pos != null;
- tiles.remove(pos);
- tile.setPosition(null);
- availMovesAdd(pos);
- if (isHole(pos)) holes.add(pos);
- for (Position offset: Position.ADJACENT.values()) {
- Position next = pos.add(offset);
- holes.remove(next);
- if (getAdjacentCount(next) == 0) {
- availMoves.remove(next);
- }
- }
- }
-
- public void unmergeFeatures(Tile tile) {
- assert tile.getPosition() != null;
- for (Entry e : getAdjacentTilesMap(tile.getPosition()).entrySet()) {
- tile.unmerge(e.getValue(), e.getKey());
- }
- }
-
- public void discardTile(Tile tile) {
- discardedTiles.add(tile);
- game.fireGameEvent().tileDiscarded(tile);
- }
-
-
- public List getDiscardedTiles() {
- return discardedTiles;
- }
-
- private boolean isHole(Position p) {
- for (Position offset: Position.ADJACENT.values()) {
- Position next = p.add(offset);
- if (get(next) == null) {
- return false;
- }
- }
- return true;
- }
-
- private int getAdjacentCount(Position p) {
- int count = 0;
- for (Position offset: Position.ADJACENT.values()) {
- Position next = p.add(offset);
- if (get(next) != null) {
- count++;
- }
- }
- return count;
- }
-
- public Map> getAvailablePlacements() {
- return currentAvailMoves;
- }
-
- public Set getAvailablePlacementPositions() {
- return currentAvailMoves.keySet();
- }
-
- public Set getHoles() {
- return holes;
- }
-
-
- /**
- * Returns tile on position with cordinates x
,y
.
- * @param x x-coordinate
- * @param y y-coordinate
- * @return demand tile
- */
- public Tile get(int x, int y) {
- return tiles.get(new Position(x, y));
- }
-
- public Tile get(Position p) {
- return tiles.get(p);
- }
-
- public Collection getAllTiles() {
- return tiles.values();
- }
-
- /*
- * Check if placement is legal against orthonogal neigbours. */
- public boolean isPlacementAllowed(Tile tile, Position p) {
- for (Entry e : getAdjacentTilesMap(p).entrySet()) {
- if (!tile.check(e.getValue(), e.getKey(), this)) {
- return false;
- }
- }
- return true;
- }
-
- public int getMaxX() {
- return maxX;
- }
-
- public int getMinX() {
- return minX;
- }
-
- public int getMaxY() {
- return maxY;
- }
-
- public int getMinY() {
- return minY;
- }
-
- public List getMulti(Position[] positions) {
- List tiles = new ArrayList<>();
- for (Position p : positions) {
- Tile t = get(p);
- if (t != null) {
- tiles.add(t);
- }
- }
- return tiles;
- }
-
- public Map getAdjacentTilesMap(Position pos) {
- Map tiles = new HashMap(4);
- for (Entry e: Position.ADJACENT.entrySet()) {
- Tile tile = get(e.getValue().add(pos));
- if (tile != null) {
- tiles.put(e.getKey(), tile);
- }
- }
- return tiles;
- }
-
- public List getAdjacentAndDiagonalTiles(Position pos) {
- return getMulti(pos.addMulti(Position.ADJACENT_AND_DIAGONAL.values()));
- }
-
-}
+package com.jcloisterzone.board;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.jcloisterzone.game.Game;
+import com.jcloisterzone.game.capability.BridgeCapability;
+
+
+/**
+ * Board represent game board with any size, so Tile
instances ale
+ * stored in Map
. Board supplies proper merging of score objects
+ * (Road
, City
or Farm
)
+ * and diagonal merge for Cloister instances.
+
+ */
+public class Board {
+ protected final Map tiles = new LinkedHashMap();
+ protected final Map availMoves = new HashMap<>();
+ protected final Map> currentAvailMoves = new HashMap<>();
+ protected final Set holes = new HashSet<>();
+
+ private int maxX, minX, maxY, minY;
+
+ private final Game game;
+
+// protected Set tunnels = new HashSet<>();
+// protected Map openTunnels = new HashMap<>(); //tunnel with open one side
+
+ protected List discardedTiles = new ArrayList<>();
+
+
+ public Board(Game game) {
+ this.game = game;
+ }
+
+ /**
+ * Updates current avail moves for next turn
+ * @param tile next tile
+ */
+ public void refreshAvailablePlacements(Tile tile) {
+ Rotation tileRotation = tile.getRotation();
+ currentAvailMoves.clear();
+ for (Position p : availMoves.keySet()) {
+ EnumSet allowed = EnumSet.noneOf(Rotation.class);
+ for (Rotation rotation: Rotation.values()) {
+ tile.setRotation(rotation);
+ if (!isPlacementAllowed(tile, p)) {
+ //not allowed according standard rules, must check if deployed bridge can allow it
+ if (!game.hasCapability(BridgeCapability.class)) continue;
+ if (!game.getCapability(BridgeCapability.class).isTilePlacementWithBridgePossible(tile, p)) continue;
+ }
+ if (!game.isTilePlacementAllowed(tile, p)) continue;
+ allowed.add(rotation);
+ }
+ if (!allowed.isEmpty()) {
+ currentAvailMoves.put(p, allowed);
+ }
+ }
+ tile.setRotation(tileRotation); //reset rotation
+ }
+
+
+ protected void availMovesAdd(Position pos) {
+ availMoves.put(pos, EdgePattern.forEmptyTile(this, pos));
+ }
+
+ protected void availMovesRemove(Position pos) {
+ availMoves.remove(pos);
+ }
+
+ public EdgePattern getAvailMoveEdgePattern(Position pos) {
+ return availMoves.get(pos);
+ }
+
+
+ /**
+ * Place tile on given position. Check for correct placement (check if neigbours
+ * edges match with tile edges according to Carcassonne rules
+ * @param tile tile to place
+ * @param p position to place
+ * @throws IllegalMoveException if placement is violate game rules
+ */
+ public void add(Tile tile, Position p) {
+ add(tile, p, false);
+ }
+
+ public void add(Tile tile, Position p, boolean unchecked) {
+ if (!unchecked) {
+ if (tile.isAbbeyTile()) {
+ if (!holes.contains(p)) {
+ throw new IllegalArgumentException("Abbey must be placed inside hole");
+ }
+ } else {
+ if (!currentAvailMoves.containsKey(p)) {
+ throw new IllegalArgumentException("Invalid position " + p);
+ }
+ if (!currentAvailMoves.get(p).contains(tile.getRotation())) {
+ throw new IllegalArgumentException("Incorrect rotation " + tile.getRotation() + " "+ p);
+ }
+ }
+ }
+
+ tiles.put(p, tile);
+ availMovesRemove(p);
+
+ for (Position offset: Position.ADJACENT.values()) {
+ Position next = p.add(offset);
+ if (get(next) == null) {
+ availMovesAdd(next);
+ if (isHole(next)) {
+ holes.add(next);
+ }
+ }
+ }
+ holes.remove(p);
+ tile.setPosition(p);
+ if (p.x > maxX) maxX = p.x;
+ if (p.x < minX) minX = p.x;
+ if (p.y > maxY) maxY = p.y;
+ if (p.y < minY) minY = p.y;
+ }
+
+ public void mergeFeatures(Tile tile) {
+ for (Entry e : getAdjacentTilesMap(tile.getPosition()).entrySet()) {
+ tile.merge(e.getValue(), e.getKey());
+ }
+ }
+
+ public void remove(Tile tile) {
+ Position pos = tile.getPosition();
+ assert pos != null;
+ tiles.remove(pos);
+ tile.setPosition(null);
+ availMovesAdd(pos);
+ if (isHole(pos)) holes.add(pos);
+ for (Position offset: Position.ADJACENT.values()) {
+ Position next = pos.add(offset);
+ holes.remove(next);
+ if (getAdjacentCount(next) == 0) {
+ availMoves.remove(next);
+ }
+ }
+ }
+
+ public void unmergeFeatures(Tile tile) {
+ assert tile.getPosition() != null;
+ for (Entry e : getAdjacentTilesMap(tile.getPosition()).entrySet()) {
+ tile.unmerge(e.getValue(), e.getKey());
+ }
+ }
+
+ public void discardTile(Tile tile) {
+ discardedTiles.add(tile);
+ game.fireGameEvent().tileDiscarded(tile);
+ }
+
+
+ public List getDiscardedTiles() {
+ return discardedTiles;
+ }
+
+ private boolean isHole(Position p) {
+ for (Position offset: Position.ADJACENT.values()) {
+ Position next = p.add(offset);
+ if (get(next) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private int getAdjacentCount(Position p) {
+ int count = 0;
+ for (Position offset: Position.ADJACENT.values()) {
+ Position next = p.add(offset);
+ if (get(next) != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public Map> getAvailablePlacements() {
+ return currentAvailMoves;
+ }
+
+ public Set getAvailablePlacementPositions() {
+ return currentAvailMoves.keySet();
+ }
+
+ public Set getHoles() {
+ return holes;
+ }
+
+
+ /**
+ * Returns tile on position with cordinates x
,y
.
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @return demand tile
+ */
+ public Tile get(int x, int y) {
+ return tiles.get(new Position(x, y));
+ }
+
+ public Tile get(Position p) {
+ return tiles.get(p);
+ }
+
+ public Collection getAllTiles() {
+ return tiles.values();
+ }
+
+ /*
+ * Check if placement is legal against orthonogal neigbours. */
+ public boolean isPlacementAllowed(Tile tile, Position p) {
+ for (Entry e : getAdjacentTilesMap(p).entrySet()) {
+ if (!tile.check(e.getValue(), e.getKey(), this)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int getMaxX() {
+ return maxX;
+ }
+
+ public int getMinX() {
+ return minX;
+ }
+
+ public int getMaxY() {
+ return maxY;
+ }
+
+ public int getMinY() {
+ return minY;
+ }
+
+ public List getMulti(Position[] positions) {
+ List tiles = new ArrayList<>();
+ for (Position p : positions) {
+ Tile t = get(p);
+ if (t != null) {
+ tiles.add(t);
+ }
+ }
+ return tiles;
+ }
+
+ public Map getAdjacentTilesMap(Position pos) {
+ Map tiles = new HashMap(4);
+ for (Entry e: Position.ADJACENT.entrySet()) {
+ Tile tile = get(e.getValue().add(pos));
+ if (tile != null) {
+ tiles.put(e.getKey(), tile);
+ }
+ }
+ return tiles;
+ }
+
+ public List getAdjacentAndDiagonalTiles(Position pos) {
+ return getMulti(pos.addMulti(Position.ADJACENT_AND_DIAGONAL.values()));
+ }
+
+}
diff --git a/src/main/java/com/jcloisterzone/board/DefaultTilePack.java b/src/main/java/com/jcloisterzone/board/DefaultTilePack.java
index 826b7ad57..74d7a9b29 100644
--- a/src/main/java/com/jcloisterzone/board/DefaultTilePack.java
+++ b/src/main/java/com/jcloisterzone/board/DefaultTilePack.java
@@ -1,194 +1,194 @@
-package com.jcloisterzone.board;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class DefaultTilePack implements TilePack {
-
- protected final transient Logger logger = LoggerFactory.getLogger(getClass());
-
- private static final String INACTIVE_GROUP = "inactive";
-
- private Map> groups = new HashMap<>();
- private Set activeGroups = new HashSet<>();
-
- private Map edgePatterns = new HashMap<>();
-
- @Override
- public int totalSize() {
- int n = 0;
- for (Entry> entry: groups.entrySet()) {
- if (!entry.getKey().equals(INACTIVE_GROUP)) {
- n += entry.getValue().size();
- }
- }
- return n;
- }
-
- @Override
- public boolean isEmpty() {
- return size() == 0;
- }
-
- @Override
- public int size() {
- int n = 0;
- for (String key: activeGroups) {
- n += groups.get(key).size();
- }
- return n;
- }
-
- @Override
- public Tile drawTile(int index) {
- for (String key: activeGroups) {
- ArrayList group = groups.get(key);
- if (index < group.size()) {
- Tile currentTile = group.remove(index);
- decreaseSideMaskCounter(currentTile, key);
- return currentTile;
- } else {
- index -= group.size();
- }
- }
- throw new ArrayIndexOutOfBoundsException();
- }
-
- private void increaseSideMaskCounter(Tile tile, String groupId) {
- if (!INACTIVE_GROUP.equals(groupId) && tile.getPosition() == null) {
- Integer countForSideMask = edgePatterns.get(tile.getEdgePattern());
- if (countForSideMask == null) {
- edgePatterns.put(tile.getEdgePattern(), 1);
- } else {
- edgePatterns.put(tile.getEdgePattern(), countForSideMask + 1);
- }
- }
- }
-
- private void decreaseSideMaskCounter(Tile tile, String groupId) {
- if (tile == null || groupId.equals(INACTIVE_GROUP)) return;
- Integer count = edgePatterns.get(tile.getEdgePattern());
- if (count == null) {
- logger.error("Inconsistent edge mask statistics. Cannot decrease: " + tile.getEdgePattern().toString());
- return;
- }
- if (count == 1) {
- edgePatterns.remove(tile.getEdgePattern());
- } else {
- edgePatterns.put(tile.getEdgePattern(), count - 1);
- }
- }
-
- @Override
- public Tile drawTile(String groupId, String tileId) {
- ArrayList group = groups.get(groupId);
- Iterator i = group.iterator();
- while(i.hasNext()) {
- Tile tile = i.next();
- if (tile.getId().equals(tileId)) {
- i.remove();
- decreaseSideMaskCounter(tile, groupId);
- return tile;
- }
- }
- return null;
- }
-
- @Override
- public Tile drawTile(String tileId) {
- for (String groupId: groups.keySet()) {
- Tile tile = drawTile(groupId, tileId);
- if (tile != null) return tile;
- }
- logger.warn("Tile pack does not contain {}", tileId);
- return null;
- }
-
- public List drawPrePlacedActiveTiles() {
- List result = new ArrayList<>();
- for (Entry> entry: groups.entrySet()) {
- ArrayList group = entry.getValue();
- Iterator