diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..062ee87 --- /dev/null +++ b/.classpath @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 94eab12..107264a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,59 @@ -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.next -release.properties +#ignore thumbnails created by windows +Thumbs.db + +#Ignore files build by Visual Studio +*.obj +*.user +*.aps +*.pch +*.vspscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.cache +*.ilk +*.log +[Bb]in +[Dd]ebug*/ +*.lib +*.sbr +obj/ +[Rr]elease*/ +[Tt]est[Rr]esult* +*.opensdf +*.sdf +ipch/ +.DS_Store + +# Resharper +_ReSharper*/ +*.docstates + +# Maven +target + +# Convention +output +download + +# vim +*.swp + +# eclipse +.settings +.cache + +# emacs +*~ +\#*# + +# project specific +*.zip +*.rep +log/ +*.csv +*.jar diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8468fc4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "JavaChallenge2012"] + path = JavaChallenge2012 + url = git@github.com:JavaChallenge2012/JavaChallenge2012.git diff --git a/.project b/.project new file mode 100644 index 0000000..2c125b7 --- /dev/null +++ b/.project @@ -0,0 +1,24 @@ + + + JavaChallenge2012 + + + + + + org.scala-ide.sdt.core.scalabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/README.md b/README.md index d9eb8e2..3df8f63 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,30 @@ -JavaChallenge2012 -================= +# Prepare Eclipse environment +1. Install Eclipse + * For Juno (4.2) +http://www.eclipse.org/downloads/ +2. Run Eclipse +3. Menu > Help > Install new software +4. Install Scala IDE (For Scala 2.9.x) on Eclipse +http://download.scala-ide.org/sdk/e37/scala29/stable/site (for 3.7 Indigo) or +http://download.scala-ide.org/sdk/e38/scala29/stable/site (for 3.8/4.2 Juno). +5. Install m2e-scala connector on Eclipse +http://alchim31.free.fr/m2e-scala/update-site/ +6. Edit eclipse.ini ("eclipse/eclipse.ini" on Windows, "Eclipse.app/Contents/MacOS/eclipse.ini" on Mac OS) +-Xmx???m => -Xmx2048m -A game platform of AI competition at ACM ICPC JavaChallenge 2012. +# Import the maven project into your Eclipse workspace +You can import maven projects with the following steps: + +1. Import > Existing Maven Projects +2. Set Root Directory containing pom.xml +3. Select Projects +4. Push Finish +5. Right click the imported project > Maven > Update Project Configuration > OK + +# Build with Maven 3 +1. run 'build.bat' + +# Dcouments +* https://github.com/JavaChallenge2012/JavaChllaenge2012 +* http://www.slideshare.net/exKAZUu/javachallenge-2012-result +* http://www.slideshare.net/exKAZUu/javachallenge-2012-special-league diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..72a714e --- /dev/null +++ b/build.bat @@ -0,0 +1 @@ +mvn package & cp target/JavaChallenge2012-*-dependencies.jar JavaChallenge2012ForDevelopingAI & cp target/JavaChallenge2012-*-dependencies.jar JavaChallenge2012ForRecording diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..678523c --- /dev/null +++ b/pom.xml @@ -0,0 +1,219 @@ + + 4.0.0 + + net.javachallenge + JavaChallenge2012 + 1.0.0 + jar + + ${project.artifactId} + A game platform for JavaChallenge2012. + 2012 + + + 1.7 + 1.7 + UTF-8 + UTF-8 + UTF-8 + UTF-8 + 2.9.1 + + + + + jp.ac.waseda.cs.washi + http://oss.sonatype.org/content/groups/public/ + + true + + + true + + + + + + + org.scala-lang + scala-library + ${scala.version} + + + commons-cli + commons-cli + 1.2 + + + jp.ac.waseda.cs.washi + GameAIArena + 1.4.4 + + + commons-lang + commons-lang + 2.6 + + + com.google.guava + guava + 13.0.1 + + + + + junit + junit + 4.10 + test + + + org.hamcrest + hamcrest-all + 1.1 + test + + + org.mockito + mockito-core + 1.9.0-rc1 + test + + + org.scala-tools.testing + specs_${scala.version} + 1.6.9 + test + + + org.scalatest + scalatest + 1.2 + test + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2.2 + + + jar-with-dependencies + + + + net.javachallenge.Main + + + + + + make-assembly + package + + attached + + + + + + + org.scala-tools + maven-scala-plugin + 2.15.2 + + + scala-compile-first + process-resources + + add-source + compile + + + + -make:transitive + -dependencyfile + ${project.build.directory}/.scala_dependencies + + + + + scala-test-compile + process-test-resources + + testCompile + + + + -make:transitive + -dependencyfile + ${project.build.directory}/.scala_dependencies + + + + + + + + main + net.javachallenge.Main + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.6 + + false + true + + + + **/*Test.* + **/*Suite.* + + + + + + + + diff --git a/src/main/config/config.xml b/src/main/config/config.xml new file mode 100644 index 0000000..574931a --- /dev/null +++ b/src/main/config/config.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/java/net/javachallenge/api/AlienTrade.java b/src/main/java/net/javachallenge/api/AlienTrade.java new file mode 100644 index 0000000..da0a8be --- /dev/null +++ b/src/main/java/net/javachallenge/api/AlienTrade.java @@ -0,0 +1,25 @@ +package net.javachallenge.api; + +/** + * An interface which represents the alien trade for providing market information. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + */ +public interface AlienTrade { + /** + * Returns the price for buying the specified material from the alien. + * + * @param material a material to buy + * @return the price for buying the specified material from the alien + */ + int getBuyPriceOf(Material material); + + /** + * Returns the price for selling the specified material to the alien. + * + * @param material a material to sell + * @return the price for selling the specified material to the alien. + */ + int getSellPriceOf(Material material); +} diff --git a/src/main/java/net/javachallenge/api/ComputerPlayer.java b/src/main/java/net/javachallenge/api/ComputerPlayer.java new file mode 100644 index 0000000..75bd68e --- /dev/null +++ b/src/main/java/net/javachallenge/api/ComputerPlayer.java @@ -0,0 +1,84 @@ +package net.javachallenge.api; + +import java.util.List; + +import net.javachallenge.api.command.Command; + +/** + * An abstract class which represents an AI player. Please extend this class for developing your AI + * player. + */ +public abstract class ComputerPlayer { + private List commands; + private TrianglePoint location; + + /** + * Saves the temporal commands for controlling your player. Note that the temporal commands are + * accepted when your AI program does not decide the commands within the limiting time (1000 ms). + * + * @param commands the temporal commands to save + */ + public final void saveTemporalCommands(List commands) { + this.commands = commands; + } + + /** + * Returns the saved temporal commands. + * + * @return the saved temporal commands + */ + public final Command[] getTemporalCommands() { + Command[] empty = new Command[0]; + return commands != null ? commands.toArray(empty) : empty; + } + + /** + * Saves the temporal vein location to occupy first for selecting your vein. Note that the + * temporal vein location is accepted when your AI program does not decide the vein location + * within the limiting time (10000[ms]). + * + * @param location the temporal vein location to save + */ + public final void saveTemporalVeinLocation(TrianglePoint location) { + this.location = location; + } + + /** + * Returns the saved temporal vein location to occupy first. + * + * @return the saved temporal vein location to occupy first + */ + public final TrianglePoint getTemporalVeinLocation() { + return location != null ? location : new net.javachallenge.entity.TrianglePoint( + Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + /** + * Returns the selected vein location to occupy first. This method should terminate within + * 10000[ms]. The saved temporal vein location is used when this method exceed the limiting time. + * Note that the vein location are selected irresponsibly when an invalid vein location is + * selected. + * + * @param game the {@link Game} instance of the current turn + * @return the selected vein location to occupy first + */ + public abstract TrianglePoint selectVein(Game game); + + /** + * Returns the decided command list to control your player. This method should terminate within + * 10000[ms]. The saved temporal command list is used when this method exceed the limiting time. + * Note that the command to do nothing is accepted when an invalid or empty command is decided. + * Invalid commands are ignored, i.e. skipped to execute next commands in the command list. + * + * @param game the {@link Game} instance of the current turn + * @return the decided command list to control your player + */ + public abstract List selectActions(Game game); + + /** + * Return the team name. + * + * @return the team name + */ + public abstract String getName(); +} diff --git a/src/main/java/net/javachallenge/api/Field.java b/src/main/java/net/javachallenge/api/Field.java new file mode 100644 index 0000000..dd389e4 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Field.java @@ -0,0 +1,130 @@ +package net.javachallenge.api; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Map; +import java.util.TreeMap; + +/** + * A class which represents the field which contains the veins and the squads. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + */ +public interface Field { + /** + * Returns all veins as a copied mutable list. + * + * @return all veins as a copied mutable list + */ + ArrayList getVeins(); + + /** + * Returns all veins which the specified player owns as a copied mutable list. + * + * @param playerId the player to filter veins + * @return all veins which the specified player owns as a copied mutable list + */ + ArrayList getVeins(int playerId); + + /** + * Returns the veins of the same owner for the specified vein ordered by the distance in ascending + * order as a copied mutable list. + * + * @param origination the origination to calculate distances + * @return the veins of the same owner ordered by the distance in ascending order as a copied + * mutable list + */ + ArrayList getVeinsOfSameOwnerOrderedByDistance(Vein origination); + + /** + * Returns the veins of the other owners for the specified vein ordered by the distance in + * ascending order as a copied mutable list. + * + * @param origination the origination to calculate distances + * @return the veins of the other owners ordered by the distance in ascending order as a copied + * mutable list + */ + ArrayList getVeinsOfOtherOwnersOrderedByDistance(Vein origination); + + /** + * Returns the {@link Map} instance of the locations (key) and the veins (value) as a copied + * mutable map. + * + * @return the {@link Map} instance with the locations (key) and the veins (value) as a copied + * mutable map + */ + TreeMap getVeinMap(); + + /** + * Returns the filtered {@link Map} instance of the locations (key) and the veins (value) by the + * specified id of the owner player as a copied mutable map. + * + * @return the filtered {@link Map} instance of the locations (key) and the veins (value) as a + * copied mutable map + */ + TreeMap getVeinMap(int playerId); + + /** + * Returns the vein with the specified location. + * + * @param location the location to locate the vein + * @return the vein with the specified location + */ + Vein getVein(TrianglePoint location); + + /** + * Returns the all squads as a copied mutable list. + * + * @return the all squads as a copied mutable list + */ + ArrayList getSquads(); + + /** + * Returns the filtered squads by the specified id of the owner player as a copied mutable list. + * + * @return the filtered squads by the specified id of the owner player as a copied mutable list + */ + ArrayList getSquads(int playerId); + + /** + * Returns the valid {@link TrianglePoint} instances as a copied mutable set. + * + * @return the valid {@link TrianglePoint} instances as a copied mutable set + */ + HashSet getValidCoords(); + + /** + * Returns the number of veins which the specified player owns. + * + * @param plyaerId the player id to filter veins + * @return the number of veins which the specified player owns + */ + int countVeins(int plyaerId); + + /** + * Sums and returns the number of the robots of the specified player on the vertexes and the + * sides. + * + * @param plyaerId the player to sum the number of the veins + * @return the number of robots including robots on the vertexes and the sides + */ + int sumRobots(int plyaerId); + + /** + * Sums and returns the productivity of the specified player and the specified material. + * + * @param plyaerId the player to sum the material productivity + * @param material the material to sum the material productivity + * @return the productivity of the specified player and the specified material + */ + int sumCurrentMaterialProductivity(int plyaerId, Material material); + + /** + * Sums and returns the robot productivity of the specified player. + * + * @param plyaerId the player to sum the robot productivity + * @return the robot productivity of the specified player + */ + int sumCurrentRobotProductivity(int plyaerId); +} diff --git a/src/main/java/net/javachallenge/api/Game.java b/src/main/java/net/javachallenge/api/Game.java new file mode 100644 index 0000000..d3763a6 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Game.java @@ -0,0 +1,181 @@ +package net.javachallenge.api; + +import java.util.ArrayList; + +/** + * A class that represents the whole state of the game. The all information of the game can be + * retrieved from the instance of this class. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + */ +public interface Game { + /** + * Returns the neutral player id, which is constant value. + * + * @return the neutral player id + */ + int getNeutralPlayerId(); + + /** + * Returns your player id, which is the active player, because your program is executed when your + * player is active. + * + * @return your player id, which is the active player + */ + int getMyPlayerId(); + + /** + * Returns the all players participating in the game without the neutral player as a copied + * mutable list. + * + * @return the all players participating in the game without the neutral player as a copied + * mutable list + */ + ArrayList getPlayers(); + + /** + * Please use getSurvivingPlayers instead of this method. + */ + @Deprecated + ArrayList getSurvivedPlayers(); + + /** + * Returns the surviving players participating in the game without the neutral player as a copied + * mutable list. + * + * @return the surviving players participating in the game without the neutral player as a copied + * mutable list + */ + ArrayList getSurvivingPlayers(); + + /** + * Returns the {@link Player} instance with the specified player id. + * + * @param id the player id to retrieve the {@link Player} instance + * @return the player + */ + Player getPlayer(int id); + + /** + * Returns your player, which is the active player, because your program is executed when your + * player is active. + * + * @return your player, which is the active player + */ + Player getMyPlayer(); + + /** + * Returns the boolean whether the specified player is surviving. + * + * @param playerId the player id to check surviving + * @return the boolean whether the specified player is surviving + */ + boolean isSurvivingPlayer(int playerId); + + /** + * Returns the alien trade for providing the market rate information. + * + * @return the alien trade for providing the market rate information + */ + AlienTrade getAlienTrade(); + + /** + * Returns the field which contains the veins and the squads. + * + * @return the field which contains the veins and the squads + */ + Field getField(); + + /** + * Returns the existing offer list with the specified material as a copied mutable list. + * + * @param material the material to get offers + * @return the existing offer list with the specified material as a copied mutable list + */ + ArrayList getOffers(Material material); + + /** + * Returns the existing demand list with the specified material as a copied mutable list. + * + * @param material the material to get demands + * @return the existing demand list with the specified material as a copied mutable list + */ + ArrayList getDemands(Material material); + + /** + * Returns the existing offer list with the specified player as a copied mutable list. + * + * @param playerId the player id to get offers + * @return the existing offer list with the specified player as a copied mutable list + */ + ArrayList getOffers(int playerId); + + /** + * Returns the existing demand list with the specified player as a copied mutable list. + * + * @param playerId the player id to get demands + * @return the existing demand list with the specified player as a copied mutable list + */ + ArrayList getDemands(int playerId); + + /** + * Returns the existing offer with the specified material from the specified player or, null if no + * offer. + * + * @param playerId the player id to get offer + * @param material the material to get offer + * @return the existing offer with the specified material from the specified player or, null if no + * offer + */ + PlayerTrade getOffer(int playerId, Material material); + + /** + * Returns the existing demand with the specified material from the specified player or, null if + * no offer. + * + * @param playerId the player id to get demand + * @param material the material to get demand + * @return the existing demand with the specified material from the specified player or, null if + * no offer + */ + PlayerTrade getDemand(int playerId, Material material); + + /** + * Returns the existing trades (offers and demands) as a copied mutable list. + * + * @return the existing trades (offers and demands) as a copied mutable list + */ + ArrayList getPlayerTrades(); + + /** + * Returns the settings of this game. + * + * @return the settings of this game + */ + GameSetting getSetting(); + + /** + * Returns the number of the players. + * + * @return the number of the players + */ + int getPlayerCount(); + + /** + * Returns the number of the current round. Note that you can get the maximum round number from + * the {@link GameSetting} instance. + * + * @return the number of the current round + */ + int getRound(); + + /** + * Returns the total money which is sum of the and all own materials of the bank with the + * specified player id. + * + * @param playerId player id to calculate total money + * @return the total money + */ + int getTotalMoneyWhenSellingAllMaterials(int playerId); +} diff --git a/src/main/java/net/javachallenge/api/GameSetting.java b/src/main/java/net/javachallenge/api/GameSetting.java new file mode 100644 index 0000000..59bbb6d --- /dev/null +++ b/src/main/java/net/javachallenge/api/GameSetting.java @@ -0,0 +1,69 @@ +package net.javachallenge.api; + + +/** + * A class which represents game setting. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + */ +public interface GameSetting { + /** + * Returns the maximum round number. Note that the round number is more than or equal to 0 and + * less than or equal to the maximum number. + */ + int getMaxRound(); + + /** + * Returns the initial money for the players. + */ + int getInitialMoney(); + + /** + * Returns the initial amount of the specified material for the players. + */ + int getInitialMaterial(Material material); + + /** + * Returns the amount of the specified material required to upgrade material rank from 1 to 2. + * + * @return the amount of the specified material required to upgrade material rank from 1 to 2 + */ + int getMaterialsForUpgradingMaterialRankFrom1To2(Material material); + + /** + * Returns the amount of the specified material required to upgrade material rank from 2 to 3. + * + * @return the amount of the specified material required to upgrade material rank from 2 to 3 + */ + int getMaterialsForUpgradingMaterialRankFrom2To3(Material material); + + /** + * Returns the amount of the specified material required to upgrade robot rank from 1 to 2. + * + * @return the amount of the specified material required to upgrade robot rank from 1 to 2 + */ + int getMaterialsForUpgradingRobotRankFrom1To2(Material material); + + /** + * Returns the amount of the specified material required to upgrade robot rank from 2 to 3. + * + * @return the amount of the specified material required to upgrade robot rank from 2 to 3 + */ + int getMaterialsForUpgradingRobotRankFrom2To3(Material material); + + /** + * Returns the map size. Note that the size must be 10 in JavaChallenge2012. + */ + int getMapSize(); + + /** + * Returns the number of the veins. Note that the number must be 40 in JavaChallenge2012. + */ + int getVeinCount(); + + /** + * Return the margin of the alien trade. This margin is used to calculate only selling price. + */ + int getAlienTradeMargin(); +} diff --git a/src/main/java/net/javachallenge/api/GameSettingBuilder.java b/src/main/java/net/javachallenge/api/GameSettingBuilder.java new file mode 100644 index 0000000..fda3aff --- /dev/null +++ b/src/main/java/net/javachallenge/api/GameSettingBuilder.java @@ -0,0 +1,132 @@ +package net.javachallenge.api; + +/** + * The {@link GameSettingBuilder} class is used to initialize the settings of the game. + */ +public class GameSettingBuilder { + private int alienTradeMargin; + private int initialMoney; + private int mapSize; + private int maxRound; + private int veinCount; + + /** + * Constructs a {@link GameSettingBuilder} with default settings. + */ + public GameSettingBuilder() { + GameSetting gs = net.javachallenge.entity.GameSetting$.MODULE$.defaultInstance(); + alienTradeMargin = gs.getAlienTradeMargin(); + initialMoney = gs.getInitialMoney(); + mapSize = gs.getMapSize(); + maxRound = gs.getMaxRound(); + veinCount = gs.getVeinCount(); + } + + /** + * Returns a {@link GameSetting} instance with the settings of this instance. + * + * @return settings for the game + */ + public GameSetting build() { + return net.javachallenge.entity.GameSetting$.MODULE$.build(this); + } + + /** + * Returns the alien trade margin. + * + * @return the alien trade margin + */ + public int getAlienTradeMargin() { + return alienTradeMargin; + } + + /** + * Set the alien trade margin to alienTradeMargin. + * + * @param alienTradeMargin the margin of the alien during trades + */ + public GameSettingBuilder setAlienTradeMargin(int alienTradeMargin) { + this.alienTradeMargin = alienTradeMargin; + return this; + } + + /** + * Return the initial amount of money. + * + * @return the initial amount of money + */ + public int getInitialMoney() { + return initialMoney; + } + + /** + * Set the initial amount of money. + * + * @param initialMoney the initial amount of money + * @return the updated instance of GameSettingBuilder + */ + public GameSettingBuilder setInitialMoney(int initialMoney) { + this.initialMoney = initialMoney; + return this; + } + + /** + * Returns the size of an edge of the map in tiles number. + * + * @return the size of an edge of the map in tiles number + */ + public int getMapSize() { + return mapSize; + } + + /** + * Set the size of the map with the size of an edge in tiles number. + * + * @param mapSize the number of the tiles in an edge + * @return the updated instance of GameSettingBuilder + */ + public GameSettingBuilder setMapSize(int mapSize) { + this.mapSize = mapSize; + return this; + } + + /** + * Returns the maximum number of rounds in a game. + * + * @return the maximum number of rounds in a game + */ + public int getMaxRound() { + return maxRound; + } + + /** + * Set the maxiumum numbers of round in a game. + * + * @param maxRound the maxiumum numbers of round in a game + * @return the updated instance of GameSettingBuilder + */ + public GameSettingBuilder setMaxRound(int maxRound) { + this.maxRound = maxRound; + return this; + } + + /** + * Returns the number of veins on the field. + * + * @return the number of veins on the field. + */ + public int getVeinCount() { + return veinCount; + } + + /** + * Set the number of veins on the field. + * + * @param veinCount the number of veins on the field. + * @return the updated instance of GameSettingBuilder + */ + public GameSettingBuilder setVeinCount(int veinCount) { + this.veinCount = veinCount; + return this; + } +} diff --git a/src/main/java/net/javachallenge/api/Logger.java b/src/main/java/net/javachallenge/api/Logger.java new file mode 100644 index 0000000..0ca0e06 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Logger.java @@ -0,0 +1,16 @@ +package net.javachallenge.api; + +/** + * The Logger class is used to print logs of the operations occurring during the game. + */ +public class Logger { + + /** + * Logs text using the current display function. + * + * @param text the text to log + */ + public static void log(String text) { + net.javachallenge.Main$.MODULE$.log(text); + } +} diff --git a/src/main/java/net/javachallenge/api/Make.java b/src/main/java/net/javachallenge/api/Make.java new file mode 100644 index 0000000..b8b9949 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Make.java @@ -0,0 +1,36 @@ +package net.javachallenge.api; + +/** + * A utility class to construct some instances. + */ +public class Make { + + private Make() {} + + /** + * Constructs a builder for a game setting. + * + * @return a builder for a game setting + */ + public static GameSettingBuilder gameSettingsBuilder() { + return new GameSettingBuilder(); + } + + /** + * Constructs a builder for a play mode. + * + * @return a builder for a play mode + * */ + public static PlayModeBuilder playModeBuilder() { + return new PlayModeBuilder(); + } + + /** + * Constructs a location which represents a vertex of a tile. + * + * @return a location which represents a vertex of a tile + */ + public static TrianglePoint point(int x, int y) { + return new net.javachallenge.entity.TrianglePoint(x, y); + } +} diff --git a/src/main/java/net/javachallenge/api/Material.java b/src/main/java/net/javachallenge/api/Material.java new file mode 100644 index 0000000..62d8d78 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Material.java @@ -0,0 +1,21 @@ +package net.javachallenge.api; + +/** + * The {@link Material} enum represents the three types of material available in the game. + */ +public enum Material { + /** + * A gas material. + */ + Gas, + + /** + * A stone material. + */ + Stone, + + /** + * A metal material. + */ + Metal; +} diff --git a/src/main/java/net/javachallenge/api/MockPlayer.java b/src/main/java/net/javachallenge/api/MockPlayer.java new file mode 100644 index 0000000..7ddda8f --- /dev/null +++ b/src/main/java/net/javachallenge/api/MockPlayer.java @@ -0,0 +1,44 @@ +package net.javachallenge.api; + +import java.util.List; + +import net.javachallenge.api.command.Command; + +/** + * The {@link MockPlayer} class represents a dummy player controlled by the computer. + */ +public class MockPlayer extends ComputerPlayer { + + private String name; + + /** + * Constructs a MockPlayer with a dummy name. + */ + public MockPlayer() { + this("MockPlayer"); + } + + /** + * Constructs a MockPlayer with the given name. + * + * @param name the name to give to the MockPlayer + */ + public MockPlayer(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public TrianglePoint selectVein(Game game) { + return null; + } + + @Override + public List selectActions(Game game) { + return null; + } +} diff --git a/src/main/java/net/javachallenge/api/PlayMode.java b/src/main/java/net/javachallenge/api/PlayMode.java new file mode 100644 index 0000000..0d24d31 --- /dev/null +++ b/src/main/java/net/javachallenge/api/PlayMode.java @@ -0,0 +1,76 @@ +package net.javachallenge.api; + +/** + * The {@link PlayMode} class is used to control the different game external parameters. + */ +public class PlayMode { + + private int fps; + private int availableVeinSelectMilliseconds; + private int availableTurnMilliseconds; + private UserInterfaceMode userInterfaceMode; + private boolean ignoringExceptions; + + /** + * Returns the number of FPS of the game. + * + * @return the number of FPS of the game + */ + public int getFps() { + return fps; + } + + /** + * Returns the maximum milliseconds per a turn. + * + * @return the maximum milliseconds per a turn + */ + public int getAvailableTurnMilliseconds() { + return availableTurnMilliseconds; + } + + /** + * Returns the maximum milliseconds per a vein selection. + * + * @return the maximum milliseconds per a vein selection + */ + public int getAvailableVeinSelectMilliseconds() { + return availableVeinSelectMilliseconds; + } + + /** + * Returns the mode of the user interface. + * + * @return the mode of the user interface + */ + public UserInterfaceMode getUserInterfaceMode() { + return userInterfaceMode; + } + + /** + * Returns the ignoring exception status. + * + * @return the ignoring exception status + */ + public boolean isIgnoringExceptions() { + return ignoringExceptions; + } + + /** + * Constructs a PlayMode with the given parameters. + * + * @param fps the fps which is speed for advancing the game + * @param availableVeinSelectMilliseconds the maximum milliseconds per a vein selection + * @param availableTurnMilliseconds the maximum milliseconds per a turn. + * @param userInterfaceMode the user interface mode + * @param ignoringExceptions the ignoring exception status (true to ignore) + */ + public PlayMode(int fps, int availableVeinSelectMilliseconds, int availableTurnMilliseconds, + UserInterfaceMode userInterfaceMode, boolean ignoringExceptions) { + this.fps = fps; + this.availableVeinSelectMilliseconds = availableVeinSelectMilliseconds; + this.availableTurnMilliseconds = availableTurnMilliseconds; + this.userInterfaceMode = userInterfaceMode; + this.ignoringExceptions = ignoringExceptions; + } +} diff --git a/src/main/java/net/javachallenge/api/PlayModeBuilder.java b/src/main/java/net/javachallenge/api/PlayModeBuilder.java new file mode 100644 index 0000000..d49f026 --- /dev/null +++ b/src/main/java/net/javachallenge/api/PlayModeBuilder.java @@ -0,0 +1,131 @@ +package net.javachallenge.api; + +/** + * The {@link PlayModeBuilder} class is used to construct instances of the PlayMode class. + */ +public class PlayModeBuilder { + private int fps; + private int availableTurnMilliseconds; + private int availableVeinSelectMilliseconds; + private UserInterfaceMode userInterfaceMode; + private boolean ignoringExceptions; + + /** + * Constructs a {@link PlayModeBuilder} with default parameters. + */ + public PlayModeBuilder() { + PlayMode mode = net.javachallenge.PlayModeHelper.defaultInstance(); + this.fps = mode.getFps(); + this.availableTurnMilliseconds = mode.getAvailableTurnMilliseconds(); + this.availableVeinSelectMilliseconds = mode.getAvailableVeinSelectMilliseconds(); + this.userInterfaceMode = mode.getUserInterfaceMode(); + this.ignoringExceptions = mode.isIgnoringExceptions(); + } + + /** + * Builds a {@link PlayMode} instance with this instance parameters. + */ + public PlayMode build() { + return net.javachallenge.PlayModeHelper.build(this); + } + + /** + * Returns the fps which is speed for advancing the game. + * + * @return the fps which is speed for advancing the game + */ + public int getFps() { + return fps; + } + + /** + * Set the fps which is speed for advancing the game. + * + * @param fps the fps to set + * @return the updated instance of PlayModeBuilder + */ + public PlayModeBuilder setFps(int fps) { + this.fps = fps; + return this; + } + + /** + * Returns the maximum milliseconds per a turn. + * + * @return the maximum milliseconds per a turn + */ + public int getAvailableTurnMilliseconds() { + return availableTurnMilliseconds; + } + + /** + * Set the maximum milliseconds per a turn. + * + * @param availableTurnMilliseconds the maximum milliseconds per a turn + * @return the updated instance of PlayModeBuilder + */ + public PlayModeBuilder setAvailableTurnMilliseconds(int availableTurnMilliseconds) { + this.availableTurnMilliseconds = availableTurnMilliseconds; + return this; + } + + /** + * Returns the maximum milliseconds per a vein selection. + * + * @return the maximum milliseconds per a vein selection + */ + public int getAvailableVeinSelectMilliseconds() { + return availableVeinSelectMilliseconds; + } + + /** + * Set the maximum milliseconds per a vein selection. + * + * @param availableVeinSelectMilliseconds the maximum milliseconds per a vein selection. + * @return the updated instance of PlayModeBuilder + */ + public PlayModeBuilder setAvailableVeinSelectMilliseconds(int availableVeinSelectMilliseconds) { + this.availableVeinSelectMilliseconds = availableVeinSelectMilliseconds; + return this; + } + + /** + * Returns the mode of the user interface. + * + * @return the mode of the user interface + */ + public UserInterfaceMode getUserInterfaceMode() { + return userInterfaceMode; + } + + /** + * Set the mode of the user interface. + * + * @param userInterfaceMode the mode of the user interface + * @return the updated instance of PlayModeBuilder + */ + public PlayModeBuilder setUserInterfaceMode(UserInterfaceMode userInterfaceMode) { + this.userInterfaceMode = userInterfaceMode; + return this; + } + + /** + * Returns the ignoring exception status. + * + * @return the ignoring exception status + */ + public boolean isIgnoringExceptions() { + return ignoringExceptions; + } + + /** + * Set the ignoring exception status. + * + * @param ignoringExceptions the ignoring exception status + * @return the updated instance of PlayModeBuilder + */ + public PlayModeBuilder setIgnoringExceptions(boolean ignoringExceptions) { + this.ignoringExceptions = ignoringExceptions; + return this; + } +} diff --git a/src/main/java/net/javachallenge/api/Player.java b/src/main/java/net/javachallenge/api/Player.java new file mode 100644 index 0000000..ed12cb2 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Player.java @@ -0,0 +1,46 @@ +package net.javachallenge.api; + +/** + * The player interface is the abstract representation of a player in the game. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + */ +public interface Player { + + /** + * Returns the id of the player. + * + * @return the id of the player + */ + int getId(); + + /** + * Returns the quantity of the given material owned by the player. + * + * @param material the material to get informations about + * @return the quantity of the given material owned by the player + */ + int getMaterial(Material material); + + /** + * Returns the amount of money owned by the player. + * + * @return the amount of money owned by the player + */ + int getMoney(); + + /** + * Returns the time to live, i.e., the number of the last round where the player were active + * (considered as living). + * + * @return the time to live + */ + int getTimeToLive(); + + /** + * Please use getTimeToLive instead of this method. + */ + @Deprecated + int getLastActiveRound(); +} diff --git a/src/main/java/net/javachallenge/api/PlayerTrade.java b/src/main/java/net/javachallenge/api/PlayerTrade.java new file mode 100644 index 0000000..8db9816 --- /dev/null +++ b/src/main/java/net/javachallenge/api/PlayerTrade.java @@ -0,0 +1,46 @@ +package net.javachallenge.api; + +/** + * The {@link PlayerTrade} class represents a trade between players. It can be an offer or a demand. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + * + */ +public interface PlayerTrade { + + /** + * Returns the id of the player doing the trade. + * + * @return the id of the player doing the trade + */ + int getPlayerId(); + + /** + * Returns the material of the trade. + * + * @return the material of the trade + */ + Material getMaterial(); + + /** + * Returns the amount of material in the trade. + * + * @return the amount of material in the trade + */ + int getAmount(); + + /** + * Returns the price per unit of material in this trade. + * + * @return the price per unit of material in this trade + */ + int getPricePerOneMaterial(); + + /** + * Returns the type of the trade. + * + * @return the type of the trade + */ + TradeType getTradeType(); +} diff --git a/src/main/java/net/javachallenge/api/Squad.java b/src/main/java/net/javachallenge/api/Squad.java new file mode 100644 index 0000000..68d9285 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Squad.java @@ -0,0 +1,48 @@ +package net.javachallenge.api; + +import java.util.ArrayList; + +/** + * The {@link Squad} interface represents a squad of robots in the game. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + */ +public interface Squad { + /** + * Returns the id of the owner of the squad. + * + * @return the id of the owner of the squad + */ + int getOwnerId(); + + /** + * Returns the number of robots in the squad. + * + * @return the number of robots in the squad + */ + int getRobot(); + + /** + * Returns the path of the squad from the current location to the destination as a copied mutable + * list. + * + * @return the path of the squad from the current location to the destination as a copied mutable + * list + */ + ArrayList getPath(); + + /** + * Returns the current location of the squad. + * + * @return the current location of the squad + */ + TrianglePoint getCurrentLocation(); + + /** + * Returns the destination of the squad. + * + * @return the destination of the squad + */ + TrianglePoint getDestinationLocation(); +} diff --git a/src/main/java/net/javachallenge/api/TradeType.java b/src/main/java/net/javachallenge/api/TradeType.java new file mode 100644 index 0000000..13fe05f --- /dev/null +++ b/src/main/java/net/javachallenge/api/TradeType.java @@ -0,0 +1,15 @@ +package net.javachallenge.api; + +/** + * The {@link TradeType} enum is used to express the type of the Trade. + */ +public enum TradeType { + /** + * The offer for selling items. + */ + Offer, + /** + * The demand for buying items. + */ + Demand, +} diff --git a/src/main/java/net/javachallenge/api/TriangleComparator.java b/src/main/java/net/javachallenge/api/TriangleComparator.java new file mode 100644 index 0000000..9d2b52f --- /dev/null +++ b/src/main/java/net/javachallenge/api/TriangleComparator.java @@ -0,0 +1,16 @@ +package net.javachallenge.api; + +import java.util.Comparator; + +/** + * A {@link Comparator} for {@link TrianglePoint}. + */ +public class TriangleComparator implements Comparator { + public int compare(TrianglePoint p1, TrianglePoint p2) { + if (p1.getY() == p2.getY()) { + return p1.getX() - p2.getX(); + } else { + return p1.getY() - p2.getY(); + } + } +} diff --git a/src/main/java/net/javachallenge/api/TrianglePoint.java b/src/main/java/net/javachallenge/api/TrianglePoint.java new file mode 100644 index 0000000..0cfd72c --- /dev/null +++ b/src/main/java/net/javachallenge/api/TrianglePoint.java @@ -0,0 +1,71 @@ +package net.javachallenge.api; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * The {@link TrianglePoint} class represents the coordinates of the game map. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + * + */ +public interface TrianglePoint extends Serializable { + /** + * Returns the horizontal coordinate of the point. + * + * @return the horizontal coordinate of the point + */ + int getX(); + + /** + * Returns the vertical coordinate of the point. + * + * @return the vertical coordinate of the point. + */ + int getY(); + + /** + * Returns true if the triangle is upward, false otherwise + * + * @return true if the triangle is upward, false otherwise + */ + boolean isUpwardTriangle(); + + /** + * Returns true if the triangle is downward, false otherwise + * + * @return true if the triangle is downward, false otherwise + */ + boolean isDownwardTriangle(); + + /** + * Returns true if this and that are connected, else otherwise + * + * @return true if this and that are connected, else otherwise + */ + boolean isConnected(TrianglePoint that); + + /** + * Returns the distance to the point to + * + * @param to the target point + * @return the distance to the point to + */ + int getDistance(TrianglePoint to); + + /** + * Returns the shortest path from this position to the specified position as a copied mutable + * list. + * + * @param to the target point + * @return the shortest path from this position to the specified position as a copied mutable + * list. + */ + ArrayList getShortestPath(TrianglePoint to); + + /** + * Returns a string representation of the point to display in commands. + */ + String toStringForCommand(); +} diff --git a/src/main/java/net/javachallenge/api/UserInterfaceMode.java b/src/main/java/net/javachallenge/api/UserInterfaceMode.java new file mode 100644 index 0000000..86b4f68 --- /dev/null +++ b/src/main/java/net/javachallenge/api/UserInterfaceMode.java @@ -0,0 +1,12 @@ +package net.javachallenge.api; + +/** + * The {@link UserInterfaceMode} enum represents the different user interface modes available for + * the game. + */ +public enum UserInterfaceMode { + /** + * The different user interface modes available. + */ + CharacterBased, LargeGraphical, SmallGraphical, +} diff --git a/src/main/java/net/javachallenge/api/Vein.java b/src/main/java/net/javachallenge/api/Vein.java new file mode 100644 index 0000000..b49e3d7 --- /dev/null +++ b/src/main/java/net/javachallenge/api/Vein.java @@ -0,0 +1,100 @@ +package net.javachallenge.api; + +import java.util.ArrayList; + +/** + * The {@link Vein} class represents a vein of the field in the game. + * + * Note that this interface is immutable, thus, its object stores the information of just this turn + * and will not be updated. + * + */ +public interface Vein { + /** + * Returns the id of the owner of the vein. + * + * Note that a neutral vein's owner is equal to Game.getNeutralPlayerId(). + * + * @return the id of the owner of the vein + */ + int getOwnerId(); + + /** + * Returns the location of the vein. + * + * @return the location of the vein + */ + TrianglePoint getLocation(); + + /** + * Returns the material produced by the vein. + * + * @return the material produced by the vein + */ + Material getMaterial(); + + /** + * Returns the number of robots in the vein. + * + * @return Returns the number of robots in the vein + */ + int getNumberOfRobots(); + + /** + * Returns the current material productivity. + * + * @return the current material productivity + */ + int getCurrentMaterialProductivity(); + + /** + * Returns the current robot productivity (reproduction rate). + * + * @return the current robot productivity (reproduction rate) + */ + int getCurrentRobotProductivity(); + + /** + * Returns the initial material productivity. + * + * @return the initial material productivity + */ + int getInitialMaterialProductivity(); + + /** + * Returns the initial robot productivity (reproduction rate). + * + * @return the initial robot productivity (reproduction rate) + */ + int getInitialRobotProductivity(); + + /** + * Returns the material rank. + * + * @return the material rank + */ + int getMaterialRank(); + + /** + * Returns the robot rank. + * + * @return the robot rank. + */ + int getRobotRank(); + + /** + * Returns the distance to the vein to. + * + * @param to the target vein for the distance + * @return the distance between this and to + */ + int getDistance(Vein to); + + /** + * Returns the shortest path from this vein to the specified vein as a copied mutable list. + * + * @param to the target vein + * @return the shortest path from this vein to the specified vein as a copied mutable list. + */ + ArrayList getShortestPath(Vein to); +} diff --git a/src/main/java/net/javachallenge/api/command/BuyFromAlienTradeCommand.java b/src/main/java/net/javachallenge/api/command/BuyFromAlienTradeCommand.java new file mode 100644 index 0000000..beb6418 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/BuyFromAlienTradeCommand.java @@ -0,0 +1,31 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.Material; + +import com.google.common.base.Preconditions; + +/** + * The {@link BuyFromAlienTradeCommand} represents a command to buy the given material from aliens. + */ +class BuyFromAlienTradeCommand implements Command { + private Material material; + private int amount; + + /** + * Constructs a {@link BuyFromAlienTradeCommand} with the given material and amount of it. + * + * @param material the material to buy + * @param amount the amount of material to buy + */ + BuyFromAlienTradeCommand(Material material, int amount) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(material); + this.material = material; + this.amount = amount; + } + + @Override + public String toString() { + return String.format("bank buy %s %d", material, amount); + } +} diff --git a/src/main/java/net/javachallenge/api/command/BuyFromOfferCommand.java b/src/main/java/net/javachallenge/api/command/BuyFromOfferCommand.java new file mode 100644 index 0000000..e2c672a --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/BuyFromOfferCommand.java @@ -0,0 +1,34 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.PlayerTrade; + +import com.google.common.base.Preconditions; + +/** + * The {@link BuyFromOfferCommand} class represents a command to buy the given material from another + * player. + */ +class BuyFromOfferCommand implements Command { + + private PlayerTrade trade; + private int amount; + + /** + * Constructs a {@link BuyFromOfferCommand} with the given trade and the amount of material to buy + * from it. + * + * @param trade the offer to buy from + * @param amount the amount of material to buy + */ + BuyFromOfferCommand(PlayerTrade trade, int amount) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(trade); + this.trade = trade; + this.amount = amount; + } + + @Override + public String toString() { + return String.format("buy %d %s %d", trade.getPlayerId(), trade.getMaterial(), amount); + } +} diff --git a/src/main/java/net/javachallenge/api/command/Command.java b/src/main/java/net/javachallenge/api/command/Command.java new file mode 100644 index 0000000..cb07add --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/Command.java @@ -0,0 +1,6 @@ +package net.javachallenge.api.command; + +/** + * The {@link Command} interface represents a command that can be executed by the player. + */ +public interface Command {} diff --git a/src/main/java/net/javachallenge/api/command/Commands.java b/src/main/java/net/javachallenge/api/command/Commands.java new file mode 100644 index 0000000..6ddab04 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/Commands.java @@ -0,0 +1,155 @@ +package net.javachallenge.api.command; + +import java.util.List; + +import net.javachallenge.api.Material; +import net.javachallenge.api.PlayerTrade; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; + +/** + * A factory class to construct commands. + */ +public class Commands { + private Commands() {} + + /** + * Constructs a command to buy the given material from aliens. + * + * @param material the material to buy + * @param amount the amount of material to buy + * @return the command to buy the material + */ + public static Command buyFromAlienTrade(Material material, int amount) { + return new BuyFromAlienTradeCommand(material, amount); + } + + /** + * Constructs a command to sell the given material to aliens. + * + * @param material the material to sell to the aliens + * @param amount the amount of material to sell + * @return the command to sell the material + */ + public static Command sellToAlienTrade(Material material, int amount) { + return new SellToAlienTradeCommand(material, amount); + } + + /** + * Constructs a command to buy the given material from another player. + * + * @param trade the offer to buy from + * @param amount the amount of material to buy + * @return the command to buy from the given offer + */ + public static Command buyFromPlayerTrade(PlayerTrade trade, int amount) { + return new BuyFromOfferCommand(trade, amount); + } + + /** + * Constructs a command to make an offer to other players. + * + * @param trade the demand to respond to + * @param amount the amount of material to sell. Should be less or equal to the amount of the + * demand. + * @return the command to sell in response to the given demand + */ + public static Command sellToPlayerTrade(PlayerTrade trade, int amount) { + return new SellToDemandCommand(trade, amount); + } + + /** + * Constructs a command to create an offer to sell material to other players. + * + * @param material the material to sell + * @param amount the amount of material to sell + * @param pricePerSingleItem the price of a unit of the material to sell + * @return the command to make the offer + */ + public static Command offer(Material material, int amount, int pricePerSingleItem) { + return new OfferCommand(material, amount, pricePerSingleItem); + } + + /** + * Constructs a command to create a demand to buy material from other players. + * + * @param material the material to buy + * @param amount the amount of material to buy + * @param pricePerSingleItem the price of a unit of the material to buy + * @return the command to make the demand + */ + public static Command demand(Material material, int amount, int pricePerSingleItem) { + return new DemandCommand(material, amount, pricePerSingleItem); + } + + /** + * Constructs a command to send robot to occupy a vein. + * + * @param robotsNumber the number of robots to send to the vein. There should be enough robots at + * the vein from where to send the robots. + * @param from the coordinates of the vein to send the robots from. The location shall point to an + * existing vein owned by the caller. + * @param to the coordinates of the vein to send the robots to. The location shall point to a vein + * owned by the caller. + * @return the command to send the robots + */ + public static Command launch(int robotsNumber, TrianglePoint from, TrianglePoint to) { + return new LaunchCommand(robotsNumber, from, to); + } + + /** + * Constructs a command with the number of robots to send, and the coordinates of the vein from + * and to where the robots should go to. + * + * @param robotsNumber the number of robots to send to the vein. There should be enough robots at + * the vein from where to send the robots. vein owned by the caller. + * @param path a list of points containing the path to take. The path should at least contain the + * departure point and the destination. + * @return the command to send the robots + */ + public static Command launchWithPath(int robotsNumber, List path) { + return new LaunchWithPathCommand(robotsNumber, path); + } + + /** + * Constructs a command to upgrade the productivity of the vein at the given location. + * + * @param location the location of the vein to upgrade. The location shall point to a vein owned + * by the caller. + * @return the command to upgrade material rank at the given location + */ + public static Command upgradeMaterial(TrianglePoint location) { + return new UpgradeMaterialCommand(location); + } + + /** + * Constructs a command to upgrade the reproduction rate of the robots at the given vein. + * + * @param location the location of the vein to upgrade. The location shall point to a vein owned + * by the caller. + * @return the command to upgrade material rank at the given location + */ + public static Command upgradeRobot(TrianglePoint location) { + return new UpgradeRobotCommand(location); + } + + /** + * Constructs a command to upgrade the productivity of the vein at the given location. + * + * @param vein the vein to upgrade. The vein shall point to a vein owned by the caller. + * @return the command to upgrade the vein at the given location + */ + public static Command upgradeMaterial(Vein vein) { + return new UpgradeMaterialCommand(vein.getLocation()); + } + + /** + * Constructs a command to upgrade the reproduction rate of the robots at the given vein. + * + * @param vein the vein to upgrade. The vein shall point to a vein owned by the caller. + * @return the command to upgrade robot rank of the vein at the given location + */ + public static Command upgradeRobot(Vein vein) { + return new UpgradeRobotCommand(vein.getLocation()); + } +} diff --git a/src/main/java/net/javachallenge/api/command/DemandCommand.java b/src/main/java/net/javachallenge/api/command/DemandCommand.java new file mode 100644 index 0000000..f0df7e8 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/DemandCommand.java @@ -0,0 +1,37 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.Material; + +import com.google.common.base.Preconditions; + +/** + * The {@link DemandCommand} class represents a command to create a demand to buy material from + * other players. + */ +class DemandCommand implements Command { + + private Material material; + private int amount; + private int pricePerSingleItem; + + /** + * Constructs a {@link DemandCommand} with the material and amount to buy and the price for a unit + * of this material. + * + * @param material the material to buy + * @param amount the amount of material to buy + * @param pricePerSingleitem the price of a unit of the material to buy + */ + DemandCommand(Material material, int amount, int pricePerSingleItem) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(material); + this.material = material; + this.amount = amount; + this.pricePerSingleItem = pricePerSingleItem; + } + + @Override + public String toString() { + return String.format("demand %s %d %d", material, amount, pricePerSingleItem); + } +} diff --git a/src/main/java/net/javachallenge/api/command/LaunchCommand.java b/src/main/java/net/javachallenge/api/command/LaunchCommand.java new file mode 100644 index 0000000..411b9e0 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/LaunchCommand.java @@ -0,0 +1,41 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.TrianglePoint; + +import com.google.common.base.Preconditions; + +/** + * The {@link LaunchCommand} class represents a command to send robot to occupy a vein. + */ +class LaunchCommand implements Command { + + private int robotsNumber; + private TrianglePoint from; + private TrianglePoint to; + + /** + * Constructs a {@link LaunchCommand} with the number of robots to send, and the coordinates of + * the vein from and to where the robots should go to. + * + * @param robotsNumber the number of robots to send to the vein. There should be enough robots at + * the vein from where to send the robots. + * @param from the coordinates of the vein to send the robots from. The location shall point to a + * vein owned by the caller. + * @param to the coordinates of the vein to send the robots to. The location shall point to a vein + * owned by the caller. + */ + LaunchCommand(int robotsNumber, TrianglePoint from, TrianglePoint to) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + this.robotsNumber = robotsNumber; + this.from = from; + this.to = to; + } + + @Override + public String toString() { + return String.format("launch %d %s %s", robotsNumber, from.toStringForCommand(), + to.toStringForCommand()); + } +} diff --git a/src/main/java/net/javachallenge/api/command/LaunchWithPathCommand.java b/src/main/java/net/javachallenge/api/command/LaunchWithPathCommand.java new file mode 100644 index 0000000..f33612d --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/LaunchWithPathCommand.java @@ -0,0 +1,45 @@ +package net.javachallenge.api.command; + +import java.util.List; + +import net.javachallenge.api.TrianglePoint; + +import com.google.common.base.Preconditions; + +/** + * The {@link LaunchWithPathCommand} class represents a command to send robot to occupy a vein. + */ +class LaunchWithPathCommand implements Command { + + private int robotsNumber; + private List path; + + /** + * Constructs a {@link LaunchWithPathCommand} with the number of robots to send, and the + * coordinates of the vein from and to where the robots should go to. + * + * @param robotsNumber the number of robots to send to the vein. There should be enough robots at + * the vein from where to send the robots. vein owned by the caller. + * @param path a list of points containing the path to take. The path should at least contain the + * departure point and the destination. + */ + + LaunchWithPathCommand(int robotsNumber, List path) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(path); + for (TrianglePoint p : path) { + Preconditions.checkNotNull(p); + } + this.robotsNumber = robotsNumber; + this.path = path; + } + + @Override + public String toString() { + String s = "launch " + String.valueOf(this.robotsNumber); + for (TrianglePoint p : this.path) { + s += " " + p.toStringForCommand(); + } + return s; + } +} diff --git a/src/main/java/net/javachallenge/api/command/OfferCommand.java b/src/main/java/net/javachallenge/api/command/OfferCommand.java new file mode 100644 index 0000000..79c8f08 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/OfferCommand.java @@ -0,0 +1,38 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.Material; + +import com.google.common.base.Preconditions; + +/** + * The {@link OfferCommand} class represents a command to create an offer to sell material to other + * players. + */ +class OfferCommand implements Command { + + private Material material; + private int amount; + private int pricePerSingleItem; + + /** + * Constructs an {@link OfferCommand} with the given material and amount to sell and the price for + * a unit of this material. + * + * @param material the material to sell + * @param amount the amount of material to sell + * @param pricePerSingleitem the price of a unit of the material to sell + * + */ + OfferCommand(Material material, int amount, int pricePerSingleItem) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(material); + this.material = material; + this.amount = amount; + this.pricePerSingleItem = pricePerSingleItem; + } + + @Override + public String toString() { + return String.format("offer %s %d %d", material, amount, pricePerSingleItem); + } +} diff --git a/src/main/java/net/javachallenge/api/command/SellToAlienTradeCommand.java b/src/main/java/net/javachallenge/api/command/SellToAlienTradeCommand.java new file mode 100644 index 0000000..2955464 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/SellToAlienTradeCommand.java @@ -0,0 +1,33 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.Material; + +import com.google.common.base.Preconditions; + +/** + * The {@link SellToAlienTradeCommand} class represents a command to sell the given material to + * aliens. + */ +class SellToAlienTradeCommand implements Command { + + private Material material; + private int amount; + + /** + * Constructs a {@link SellToAlienTradeCommand} with the given material and amount of it. + * + * @param material the material to sell to the aliens + * @param amount the amount of material to sell + */ + SellToAlienTradeCommand(Material material, int amount) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(material); + this.material = material; + this.amount = amount; + } + + @Override + public String toString() { + return String.format("bank sell %s %d", material, amount); + } +} diff --git a/src/main/java/net/javachallenge/api/command/SellToDemandCommand.java b/src/main/java/net/javachallenge/api/command/SellToDemandCommand.java new file mode 100644 index 0000000..fbbc368 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/SellToDemandCommand.java @@ -0,0 +1,33 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.PlayerTrade; + +import com.google.common.base.Preconditions; + +/** + * The {@link SellToDemandCommand} class represents a command to make an offer to other players. + */ +class SellToDemandCommand implements Command { + + private PlayerTrade trade; + private int amount; + + /** + * Constructs a {@link SellToDemandCommand} with the demand to respond to and the amount to sell. + * + * @param trade the demand to respond to + * @param amount the amount of material to sell. Should be less or equal to the amount of the + * demand. + */ + SellToDemandCommand(PlayerTrade trade, int amount) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(trade); + this.trade = trade; + this.amount = amount; + } + + @Override + public String toString() { + return String.format("sell %d %s %d", trade.getPlayerId(), trade.getMaterial(), amount); + } +} diff --git a/src/main/java/net/javachallenge/api/command/UpgradeMaterialCommand.java b/src/main/java/net/javachallenge/api/command/UpgradeMaterialCommand.java new file mode 100644 index 0000000..7b81dc7 --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/UpgradeMaterialCommand.java @@ -0,0 +1,31 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.TrianglePoint; + +import com.google.common.base.Preconditions; + +/** + * The {@link UpgradeMaterialCommand} class represents a command to upgrade the productivity of the + * vein at the given location. + */ +class UpgradeMaterialCommand implements Command { + + private TrianglePoint location; + + /** + * Constructs an {@link UpgradeMaterialCommand} for the vein at the given location. + * + * @param location the location of the vein to upgrade. The location shall point to a vein owned + * by the caller. + */ + UpgradeMaterialCommand(TrianglePoint location) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(location); + this.location = location; + } + + @Override + public String toString() { + return String.format("upgrade material %s", location.toStringForCommand()); + } +} diff --git a/src/main/java/net/javachallenge/api/command/UpgradeRobotCommand.java b/src/main/java/net/javachallenge/api/command/UpgradeRobotCommand.java new file mode 100644 index 0000000..aea70bf --- /dev/null +++ b/src/main/java/net/javachallenge/api/command/UpgradeRobotCommand.java @@ -0,0 +1,32 @@ +package net.javachallenge.api.command; + +import net.javachallenge.api.TrianglePoint; + +import com.google.common.base.Preconditions; + +/** + * The {@link UpgradeRobotCommand} class represents a command to upgrade the reproduction rate of + * the robots at the given vein. + */ +class UpgradeRobotCommand implements Command { + + private TrianglePoint location; + + /** + * Constructs an {@link UpgradeRobotCommand} to upgrade the robots reproduction rate of the vein + * at the given location. + * + * @param location the location of the vein in which robots should be upgraded. The location shall + * point to a vein owned by the caller. + */ + UpgradeRobotCommand(TrianglePoint location) { + // Check toString method works well with no exception. + Preconditions.checkNotNull(location); + this.location = location; + } + + @Override + public String toString() { + return String.format("upgrade robot %s", location.toStringForCommand()); + } +} diff --git a/src/main/java/net/javachallenge/contest/FinalContestReplayer.java b/src/main/java/net/javachallenge/contest/FinalContestReplayer.java new file mode 100644 index 0000000..9ca6384 --- /dev/null +++ b/src/main/java/net/javachallenge/contest/FinalContestReplayer.java @@ -0,0 +1,24 @@ +package net.javachallenge.contest; + +import java.util.List; + +import net.javachallenge.api.Make; +import net.javachallenge.api.PlayMode; + +import com.google.common.collect.Lists; + +public class FinalContestReplayer { + public static void main(String[] args) { + PlayMode playMode = Make.playModeBuilder().setFps(20).build(); + List fileNames = + Lists + .newArrayList( + "replay-final/2012_11_18_8_46_36__oshieteZukky_not_shiokawa_Mi_Sawa2012_wakaba_Gunma_s_Ambition___o______o__.rep", + "replay-final/2012_11_18_8_46_40__not_shiokawa_Gunma_s_Ambition_Mi_Sawa2012_oshieteZukky_wakaba___o______o__.rep", + "replay-final/2012_11_18_8_46_43__wakaba_not_shiokawa_oshieteZukky_Mi_Sawa2012___o______o___Gunma_s_Ambition.rep", + "replay-final/2012_11_18_11_25_35____o______o___oshieteZukky.rep"); + for (String fileName : fileNames) { + net.javachallenge.Main.startReplayGame(fileName, playMode); + } + } +} diff --git a/src/main/java/net/javachallenge/contest/GuestFinalContestReplayer.java b/src/main/java/net/javachallenge/contest/GuestFinalContestReplayer.java new file mode 100644 index 0000000..f591f59 --- /dev/null +++ b/src/main/java/net/javachallenge/contest/GuestFinalContestReplayer.java @@ -0,0 +1,24 @@ +package net.javachallenge.contest; + +import java.util.List; + +import net.javachallenge.api.Make; +import net.javachallenge.api.PlayMode; + +import com.google.common.collect.Lists; + +public class GuestFinalContestReplayer { + public static void main(String[] args) { + PlayMode playMode = Make.playModeBuilder().setFps(20).build(); + List fileNames = + Lists + .newArrayList( + "replay-guest-final/2012_11_18_9_8_46__methane1_not_shiokawa_wakaba_oshieteZukky___o______o___hasi.rep", + "replay-guest-final/2012_11_18_9_8_51__wakaba_methane1_oshieteZukky_hasi___o______o___not_shiokawa.rep", + "replay-guest-final/2012_11_18_9_8_53__wakaba___o______o___oshieteZukky_not_shiokawa_methane1_hasi.rep"); + + for (String fileName : fileNames) { + net.javachallenge.Main.startReplayGame(fileName, playMode); + } + } +} diff --git a/src/main/java/net/javachallenge/contest/GuestQualificationContestReplayer.java b/src/main/java/net/javachallenge/contest/GuestQualificationContestReplayer.java new file mode 100644 index 0000000..e53eb80 --- /dev/null +++ b/src/main/java/net/javachallenge/contest/GuestQualificationContestReplayer.java @@ -0,0 +1,31 @@ +package net.javachallenge.contest; + +import java.util.List; + +import net.javachallenge.api.Make; +import net.javachallenge.api.PlayMode; + +import com.google.common.collect.Lists; + +public class GuestQualificationContestReplayer { + public static void main(String[] args) { + PlayMode playMode = Make.playModeBuilder().setFps(20).build(); + List fileNames = + Lists + .newArrayList( + "replay-guest-qual/2012_11_18_8_57_0__mecha_g3_Wand_Player_JoeJack_Oyososan_hasi_methane1.rep", + "replay-guest-qual/2012_11_18_8_57_4__JoeJack_methane1_hasi_Wand_Player_Oyososan_mecha_g3.rep", + "replay-guest-qual/2012_11_18_8_57_6__Oyososan_Wand_Player_mecha_g3_hasi_JoeJack_methane1.rep", + "replay-guest-qual/2012_11_18_8_57_9__methane1_Wand_Player_JoeJack_Oyososan_hasi_mecha_g3.rep", + "replay-guest-qual/2012_11_18_8_57_12__hasi_mecha_g3_Wand_Player_methane1_JoeJack_Oyososan.rep", + "replay-guest-qual/2012_11_18_8_57_15__methane1_hasi_Wand_Player_Oyososan_mecha_g3_JoeJack.rep", + "replay-guest-qual/2012_11_18_8_57_17__methane1_JoeJack_Wand_Player_hasi_Oyososan_mecha_g3.rep", + "replay-guest-qual/2012_11_18_8_57_19__JoeJack_Oyososan_methane1_hasi_mecha_g3_Wand_Player.rep", + "replay-guest-qual/2012_11_18_8_57_22__methane1_mecha_g3_Wand_Player_JoeJack_Oyososan_hasi.rep", + "replay-guest-qual/2012_11_18_8_56_56__methane1_JoeJack_Oyososan_hasi_Wand_Player_mecha_g3.rep"); + + for (String fileName : fileNames) { + net.javachallenge.Main.startReplayGame(fileName, playMode); + } + } +} diff --git a/src/main/java/net/javachallenge/contest/QualificationContestReplayer.java b/src/main/java/net/javachallenge/contest/QualificationContestReplayer.java new file mode 100644 index 0000000..e8c802a --- /dev/null +++ b/src/main/java/net/javachallenge/contest/QualificationContestReplayer.java @@ -0,0 +1,26 @@ +package net.javachallenge.contest; + +import java.util.List; + +import net.javachallenge.api.Make; +import net.javachallenge.api.PlayMode; + +import com.google.common.collect.Lists; + +public class QualificationContestReplayer { + public static void main(String[] args) { + PlayMode playMode = Make.playModeBuilder().setFps(20).build(); + List fileNames = + Lists + .newArrayList( + "replay-qual/2012_11_18_8_19_11__You_And_Java__Download_Freely____w___Gunma_s_Ambition_Sendy_Enumerable_C_hokudai.rep", + "replay-qual/2012_11_18_8_19_14__mofu_txt_not_shiokawa_GlasgowHaskellPlayer_Chrome_0xFF7_oshieteZukky.rep", + "replay-qual/2012_11_18_8_19_18_____3__________usagisan_Mi_Sawa2012_Amadeus___d__0w0__b_Hikikomori____Okubyoumono_wakaba.rep", + "replay-qual/2012_11_18_8_19_19__icp_py_Myu_TeamTakapt_muteki_shogun_ma_bo______w____________.rep", + "replay-qual/2012_11_18_8_19_21__There_s_more_than_one_WA_to_do_AC__JoeJack_Guan_Wun_CityU_EEngineer_UECoders_zerohachi.rep", + "replay-qual/2012_11_18_8_19_23__hiyokko_team_Otoshigami_tmt514___o______o___THE_2DM_STER_Mr__Tantan.rep"); + for (String fileName : fileNames) { + net.javachallenge.Main.startReplayGame(fileName, playMode); + } + } +} diff --git a/src/main/java/net/javachallenge/players/Main.java b/src/main/java/net/javachallenge/players/Main.java new file mode 100644 index 0000000..32fbcbf --- /dev/null +++ b/src/main/java/net/javachallenge/players/Main.java @@ -0,0 +1,38 @@ +package net.javachallenge.players; + +import java.util.ArrayList; +import java.util.Collections; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.GameSetting; +import net.javachallenge.api.Make; +import net.javachallenge.api.PlayMode; +import net.javachallenge.players.guests.Hasi; +import net.javachallenge.players.others.JoeJack; +import net.javachallenge.players.others.Myu; +import net.javachallenge.players.others.NearPlayer; +import net.javachallenge.players.others.Sabateur; +import net.javachallenge.players.others.Tokoharu; +import net.javachallenge.players.others.Wand; + +import com.google.common.collect.Lists; + +public class Main { + + public static void main(String[] args) { + ArrayList players = + Lists.newArrayList(new Hasi(), new JoeJack(), new Myu(), new Sabateur(), new Tokoharu(), + new NearPlayer()); + Collections.shuffle(players); + + // You can customize game setting. + GameSetting setting = Make.gameSettingsBuilder().build(); + // You can customize play setting which is independent on game rule. + PlayMode playMode = Make.playModeBuilder().setFps(9999) + // .setIgnoringExceptions(false) for debugging + // .setUserInterfaceMode(UserInterfaceMode.CharacterBased) + .build(); + + net.javachallenge.Main.startAIGame(players.toArray(new ComputerPlayer[0]), setting, playMode); + } +} diff --git a/src/main/java/net/javachallenge/players/guests/Hasi.java b/src/main/java/net/javachallenge/players/guests/Hasi.java new file mode 100644 index 0000000..63a7e92 --- /dev/null +++ b/src/main/java/net/javachallenge/players/guests/Hasi.java @@ -0,0 +1,397 @@ +package net.javachallenge.players.guests; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Game; +import net.javachallenge.api.Make; +import net.javachallenge.api.Material; +import net.javachallenge.api.Squad; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class Hasi extends ComputerPlayer { + Random rand = new Random("hasi".hashCode()); + int median; + HashMap> G = null; + + @Override + public String getName() { + return "hasi"; + } + + @Override + public TrianglePoint selectVein(Game game) { + this.saveTemporalVeinLocation(Make.point(0, 0)); + + if (G == null) { + G = new HashMap>(); + ArrayList veinsAll = game.getField().getVeins(); + int N = veinsAll.size(); + int[][] distanceTable = new int[N][N]; + for (int i = 1; i < N; ++i) { + for (int j = 0; j < i; ++j) { + distanceTable[i][j] = distanceTable[j][i] = veinsAll.get(i) + .getDistance(veinsAll.get(j)); + } + } + boolean[][] notShortest = new boolean[N][N]; + for (int i = 1; i < N; ++i) { + for (int j = 0; j < i; ++j) { + for (int k = 0; k < N; ++k) + if (k != i && k != j) { + if (distanceTable[i][j] >= distanceTable[i][k] + + distanceTable[k][j]) { + notShortest[i][j] = notShortest[j][i] = true; + break; + } + } + } + } + ArrayList cnt = new ArrayList(); + for (int i = 0; i < N; ++i) { + ArrayList a = new ArrayList(); + for (int j = 0; j < N; ++j) { + if (j != i && !notShortest[i][j]) { + a.add(veinsAll.get(j).getLocation()); + } + } + G.put(veinsAll.get(i).getLocation(), a); + cnt.add(a.size()); + } + Collections.sort(cnt); + median = cnt.get(cnt.size() / 2); + } + + ArrayList veins = new ArrayList(); + for (Vein vein : game.getField().getVeins(game.getNeutralPlayerId())) { + veins.add(vein); + } + if (!veins.isEmpty()) { + Collections.sort(veins, new SelectVeinComparator(game)); + return veins.get(0).getLocation(); + } + for (Vein vein : game.getField().getVeins()) { + if (vein.getOwnerId() == game.getNeutralPlayerId()) { + veins.add(vein); + } + } + return veins.get(rand.nextInt(veins.size())).getLocation(); + } + + @Override + public List selectActions(Game game) { + List commands = new ArrayList(); + // Upgrade + ArrayList veins = new ArrayList(); + for (Vein vein : game.getField().getVeins(game.getMyPlayerId())) { + veins.add(vein); + } + Collections.sort(veins, new UpgradeVeinComparator()); + HashMap amount = new HashMap(); + for (Material material : Material.values()) { + amount.put(material, game.getMyPlayer().getMaterial(material)); + } + loop1: for (Vein vein : veins) { + if (vein.getRobotRank() == 1) { + for (Material material : Material.values()) { + if (amount.get(material) < game + .getSetting() + .getMaterialsForUpgradingRobotRankFrom1To2(material)) + continue loop1; + } + for (Material material : Material.values()) { + amount.put( + material, + amount.get(material) + - game.getSetting() + .getMaterialsForUpgradingRobotRankFrom1To2( + material)); + } + commands.add(Commands.upgradeRobot(vein)); + } else if (vein.getRobotRank() == 2) { + for (Material material : Material.values()) { + if (amount.get(material) < game + .getSetting() + .getMaterialsForUpgradingRobotRankFrom2To3(material)) + continue loop1; + } + for (Material material : Material.values()) { + amount.put( + material, + amount.get(material) + - game.getSetting() + .getMaterialsForUpgradingRobotRankFrom2To3( + material)); + } + commands.add(Commands.upgradeRobot(vein)); + } + } + // Trade + if (amount.get(Material.Metal) + amount.get(Material.Gas) < 1500 + && amount.get(Material.Stone) > 0) { + commands.add(Commands.sellToAlienTrade(Material.Stone, + amount.get(Material.Stone))); + } + if (game.getMyPlayer().getMoney() > 0) { + Material minMaterial = amount.get(Material.Metal) < 1000 ? Material.Metal + : Material.Gas; + int x = game.getMyPlayer().getMoney() + / game.getAlienTrade().getBuyPriceOf(minMaterial); + if (x > 0) + commands.add(Commands.buyFromAlienTrade(minMaterial, x)); + } + // Launch + HashMap posNeighbors = new HashMap<>(); + HashMap hasNeighbors = new HashMap<>(); + int numRobots = 0; + for (Vein vein : game.getField().getVeins(game.getMyPlayerId())) { + numRobots += vein.getNumberOfRobots(); + for (TrianglePoint pos : G.get(vein.getLocation())) { + Vein toVein = game.getField().getVein(pos); + if (toVein.getOwnerId() != game.getMyPlayerId()) { + posNeighbors.put(pos, toVein.getNumberOfRobots()); + hasNeighbors.put(vein.getLocation(), true); + } + } + } + ArrayList frontline = new ArrayList(); + for (Vein vein : game.getField().getVeins(game.getMyPlayerId())) { + if (!hasNeighbors.containsKey(vein.getLocation())) { + int x = vein.getNumberOfRobots() - numRobots + / game.getSetting().getVeinCount(); + if (x > 0) { + int minDist = Integer.MAX_VALUE; + ArrayList targetVein = new ArrayList(); + for (Vein v : game.getField() + .getVeins(game.getMyPlayerId())) { + if (hasNeighbors.containsKey(v.getLocation())) { + if (vein.getDistance(v) < minDist) { + minDist = vein.getDistance(v); + targetVein = new ArrayList(); + } + if (vein.getDistance(v) <= minDist) { + targetVein.add(v.getLocation()); + } + } + } + HashSet toVein = new HashSet(); + for (TrianglePoint p : targetVein) { + Vein v = game.getField().getVein(p); + int d = Integer.MAX_VALUE; + ArrayList a = new ArrayList(); + for (Vein t : game.getField().getVeins( + game.getMyPlayerId())) { + if (t != vein + && vein.getDistance(t) <= d + && vein.getDistance(t) + t.getDistance(v) <= vein + .getDistance(v)) { + if (vein.getDistance(t) < d) { + d = vein.getDistance(t); + a = new ArrayList(); + } + a.add(t); + } + } + if (a.isEmpty()) { + toVein.add(v.getLocation()); + } else { + for (Vein t : a) + toVein.add(t.getLocation()); + } + } + if (x >= toVein.size()) + for (TrianglePoint p : toVein) { + Vein v = game.getField().getVein(p); + commands.add(Commands.launch(x / toVein.size(), + vein.getLocation(), v.getLocation())); + } + } + } else { + frontline.add(vein); + } + } + ArrayList targets = new ArrayList(); + for (Map.Entry e : posNeighbors.entrySet()) { + targets.add(game.getField().getVein(e.getKey())); + } + HashMap squads = new HashMap(); + for (Squad squad : game.getField().getSquads(game.getMyPlayerId())) { + TrianglePoint p = squad.getDestinationLocation(); + if (!squads.containsKey(p)) { + squads.put(p, 0); + } + squads.put(p, squads.get(p) + squad.getRobot()); + } + for (Vein vein : frontline) { + int num = 0; + int minDist = Integer.MAX_VALUE; + Vein toVein = null; + boolean existsNeutral = false; + Collections.sort(targets, new TargetVeinComparator(game, vein, + squads)); + for (Vein v : targets) { + if (existsNeutral + && v.getOwnerId() == game.getNeutralPlayerId()) { + int distance = vein.getDistance(v); + if (distance < minDist) { + minDist = distance; + num = 1; + toVein = v; + } else if (distance == minDist) { + if (rand.nextInt(++num) == 0) { + toVein = v; + } + } + } else if (v.getOwnerId() == game.getNeutralPlayerId()) { + existsNeutral = true; + minDist = vein.getDistance(v); + num = 1; + toVein = v; + } else if (v.getOwnerId() != game.getMyPlayerId()) { + int distance = vein.getDistance(v); + if (distance < minDist) { + minDist = distance; + num = 1; + toVein = v; + } else if (distance == minDist) { + if (rand.nextInt(++num) == 0) { + toVein = v; + } + } + } + } + if (num > 0) { + int x = toVein.getNumberOfRobots(); + if (toVein.getOwnerId() != game.getNeutralPlayerId()) { + x += toVein.getCurrentRobotProductivity() + * vein.getDistance(toVein); + } + if (x < vein.getNumberOfRobots() - 1) { + TrianglePoint p = toVein.getLocation(); + x = Math.max(x + 1, vein.getNumberOfRobots() / 2); + commands.add(Commands.launch(x, vein.getLocation(), p)); + if (!squads.containsKey(p)) { + squads.put(p, 0); + } + squads.put(p, squads.get(p) + x); + } + } + } + + return commands; + } + + class SelectVeinComparator implements Comparator { + HashMap weightMatrial = new HashMap(); + Game game; + + SelectVeinComparator(Game game) { + this.game = game; + for (Material material : Material.values()) { + weightMatrial.put(material, 0); + } + if (game.getField().getVeins(game.getMyPlayerId()).isEmpty()) { + weightMatrial.put(Material.Gas, 2); + weightMatrial.put(Material.Metal, 1); + } else { + weightMatrial.put(Material.Metal, 2); + weightMatrial.put(Material.Gas, 1); + } + } + + public int score(Vein v) { + int score = 0; + for (Vein to : game.getField().getVeins()) { + if (to.getOwnerId() != v.getOwnerId()) { + if (to.getOwnerId() == game.getNeutralPlayerId()) { + score += (20 - v.getDistance(to)) * 100; + } else { + score -= (20 - v.getDistance(to)) * 500; + } + } else if (to != v) { + score -= (20 - v.getDistance(to)) * 100; + } + } + score += weightMatrial.get(v.getMaterial()) * 1000; + score += v.getInitialRobotProductivity() * 100; + score += v.getNumberOfRobots(); + return score; + } + + @Override + public int compare(Vein a, Vein b) { + return -new Integer(score(a)).compareTo(score(b)); + } + + }; + + class UpgradeVeinComparator implements Comparator { + + @Override + public int compare(Vein a, Vein b) { + if (a.getCurrentRobotProductivity() != b + .getCurrentRobotProductivity()) { + return -new Integer(a.getCurrentRobotProductivity()) + .compareTo(b.getCurrentRobotProductivity()); + } + return 0; + } + + }; + + class TargetVeinComparator implements Comparator { + Game game; + Vein from; + HashMap squads; + + TargetVeinComparator(Game game, Vein from, + HashMap squads) { + this.game = game; + this.from = from; + this.squads = squads; + } + + public int score(Vein v) { + int score = 0; + if (v.getOwnerId() == game.getNeutralPlayerId()) { + if (squads.containsKey(v.getLocation())) { + if (squads.get(v.getLocation()) <= v.getNumberOfRobots()) { + score += 10000; + } else { + score += 5000; + } + } else { + score += 10000; + } + } else { + if (squads.containsKey(v.getLocation())) { + if (squads.get(v.getLocation()) + 100 <= v + .getNumberOfRobots()) { + score += 500; + } + } else { + score += 500; + } + } + score -= from.getDistance(v); + return score; + } + + @Override + public int compare(Vein a, Vein b) { + return -new Integer(score(a)).compareTo(score(b)); + } + + }; + +} diff --git a/src/main/java/net/javachallenge/players/guests/MechaPlayer.java b/src/main/java/net/javachallenge/players/guests/MechaPlayer.java new file mode 100644 index 0000000..9255fef --- /dev/null +++ b/src/main/java/net/javachallenge/players/guests/MechaPlayer.java @@ -0,0 +1,138 @@ +package net.javachallenge.players.guests; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Field; +import net.javachallenge.api.Game; +import net.javachallenge.api.Make; +import net.javachallenge.api.Material; +import net.javachallenge.api.Player; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class MechaPlayer extends ComputerPlayer { + Game game = null; + List enemyVeins = null; + List emptyVeins = null; + + private void update(Game game){ + this.game = game; + this.enemyVeins = new ArrayList(); + this.emptyVeins = new ArrayList(); + List allVeins = game.getField().getVeins(); + for (Vein vein : allVeins) { + if(vein.getOwnerId() == game.getNeutralPlayerId()) + emptyVeins.add(vein); + else if(vein.getOwnerId() != game.getMyPlayer().getId()) + enemyVeins.add(vein); + } + } + + private Integer getInitialScore(Vein vein){ + Integer score = 0; + for (Vein evein : enemyVeins){ + score += (vein.getDistance(evein))*(vein.getDistance(evein)) * 100; + } + for (Vein evein : emptyVeins){ + score -= (vein.getDistance(evein))*(vein.getDistance(evein)) * 5; + } + score += vein.getLocation().getDistance(Make.point(0, 0)) * 100000; + return score; + } + + @Override + public String getName() { + return "mecha_g3"; + } + + @Override + public TrianglePoint selectVein(Game game) { + update(game); + this.saveTemporalVeinLocation(Make.point(0, 0)); + Vein selectVein = emptyVeins.get(0); + Integer maxScore = Integer.MIN_VALUE; + for (Vein vein : emptyVeins) { + Integer score = getInitialScore(vein); + if(maxScore < score){ + maxScore = score; + selectVein = vein; + } + } + return selectVein.getLocation(); + } + + @Override + public List selectActions(Game game) { + update(game); + List commands = new ArrayList(); + + Field field = game.getField(); + List myVeinList = field.getVeins(game.getMyPlayer().getId()); + + List copiedVeinList = new ArrayList(); + for (Vein vein : enemyVeins) { + copiedVeinList.add(vein); + } + for (Vein vein : emptyVeins) { + copiedVeinList.add(vein); + } + + // Launch + for (Vein myVein : myVeinList) { + Collections.sort(copiedVeinList, new VeinDistanceComparator(myVein)); + Vein to = copiedVeinList.get(0); + int diff = myVein.getNumberOfRobots() - to.getNumberOfRobots(); + if ( (to.getOwnerId() == game.getNeutralPlayerId() && (diff>1)) || + (to.getOwnerId() != game.getNeutralPlayerId() && (diff>30)) ){ + commands.add(Commands.launch(myVein.getNumberOfRobots()/2, + myVein.getLocation(), + to.getLocation())); + } + } + + // Upgrade + for(Vein myVein : myVeinList){ + commands.add(Commands.upgradeRobot(myVein)); + commands.add(Commands.upgradeMaterial(myVein)); + } + + // Trade + for (Material material : Material.values()) { + int amount = game.getMyPlayer().getMaterial(material); + if (amount > 1000) { + commands.add(Commands.sellToAlienTrade(material, amount - 500)); + } + if (amount < 500) { + commands.add(Commands.buyFromAlienTrade(material, 500)); + } + } + return commands; + } + + class VeinDistanceComparator implements Comparator { + Vein myVein; + + VeinDistanceComparator(Vein myVein) { + this.myVein = myVein; + } + + @Override + public int compare(Vein o1, Vein o2) { + + if (o1.equals(myVein)) return 1; + if (o2.equals(myVein)) return -1; + + if (o1.getOwnerId() == myVein.getOwnerId()) return 1; + if (o2.getOwnerId() == myVein.getOwnerId()) return -1; + return myVein.getDistance(o1) - myVein.getDistance(o2); + } + } +} diff --git a/src/main/java/net/javachallenge/players/guests/Methane1Player.java b/src/main/java/net/javachallenge/players/guests/Methane1Player.java new file mode 100644 index 0000000..01f575b --- /dev/null +++ b/src/main/java/net/javachallenge/players/guests/Methane1Player.java @@ -0,0 +1,587 @@ +package net.javachallenge.players.guests; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.TreeMap; +import java.util.TreeSet; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Field; +import net.javachallenge.api.Game; +import net.javachallenge.api.Make; +import net.javachallenge.api.Material; +import net.javachallenge.api.Player; +import net.javachallenge.api.Squad; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class Methane1Player extends ComputerPlayer { + private Player self; + private List commands; + private Game game; + int turn; + + int stone, gas, metal; + + class OwnVein { + Vein vein; + int robots; + TrianglePoint location; + int robotRank; + int materialRank; + int commingSquads; + boolean emerge = false; + + OwnVein(Vein v) { + vein = v; + robots = v.getNumberOfRobots(); + location = v.getLocation(); + robotRank = v.getRobotRank(); + materialRank = v.getMaterialRank(); + commingSquads = 0; + for (Squad s : game.getField().getSquads()) { + if (s.getDestinationLocation().equals(location)) { + commingSquads += s.getRobot(); + if (s.getCurrentLocation().getDistance(location) == 1) { + if (s.getRobot() > robots + + v.getCurrentRobotProductivity()) { + emerge = true; + } + } + } + } + } + + boolean tryUpgradeRobot() { + if (robotRank == 1 && metal >= 200 && gas >= 200) { + commands.add(Commands.upgradeRobot(vein)); + metal -= 200; + gas -= 200; + robotRank++; + return true; + } else if (robotRank == 2 && metal >= 500 && stone >= 300) { + commands.add(Commands.upgradeRobot(vein)); + metal -= 500; + stone -= 300; + robotRank++; + return true; + } + return false; + } + + boolean tryUpgradeMaterial() { + if (materialRank == 1 && gas >= 200 && stone >= 100) { + commands.add(Commands.upgradeMaterial(vein)); + gas -= 200; + stone -= 100; + return true; + } else if (materialRank == 2 && gas >= 100 && stone >= 300) { + commands.add(Commands.upgradeMaterial(vein)); + gas -= 100; + stone -= 300; + return true; + } + return false; + } + + void attack(Vein target, int squadSize, boolean force) { + if (squadSize > robots) { + System.err.println("Warn: too big squad size."); + squadSize = robots; + } + List route = makeRoute(location, + target.getLocation()); + // List route = + // location.getShortestPath(target.getLocation()); + if (route == null) { + if (force) { + commands.add(Commands.launch(squadSize, location, + target.getLocation())); + saveTemporalCommands(commands); + return; + } else + return; + } + + robots -= squadSize; + commands.add(Commands.launch(squadSize, location, + target.getLocation())); + // commands.add(Commands.launchWithPath(squadSize, route)); + saveTemporalCommands(commands); + System.err.println("Move from " + location + " to " + + target.getLocation() + " with " + squadSize + " robots."); + // System.err.println("Route:" + route); + } + + public boolean equals(Object other) { + OwnVein ov = (OwnVein) other; + return ov.location == this.location; + } + + public int hashCode() { + return location.hashCode(); + } + + ArrayList nearFriends() { + ArrayList veins = new ArrayList<>(); + for (Vein v : game.getField().getVeinsOfSameOwnerOrderedByDistance( + vein)) { + veins.add(myVeinsByLocation.get(v.getLocation())); + } + return veins; + } + + List nearEnemies() { + return game.getField().getVeinsOfOtherOwnersOrderedByDistance(vein); + } + } + + TreeMap myVeinsByLocation = new TreeMap<>(); + ArrayList myVeins = new ArrayList<>(); + + @Override + public String getName() { + return "methane1"; + } + + Vein firstVein = null; + + @Override + public TrianglePoint selectVein(Game game) { + int gas_ratio = 2; + int metal_ratio = 3; + TrianglePoint center = Make.point(1, 0); + + if (firstVein != null) { + switch (firstVein.getMaterial()) { + case Metal: + metal_ratio = 1; + break; + case Gas: + gas_ratio = 1; + break; + default: + break; + } + } + int bestScore = Integer.MIN_VALUE; + Vein bestVein = null; + + for (Vein v : game.getField().getVeins()) { + if (v.getOwnerId() != -1) { + continue; + } + int score = 0; + + score += center.getDistance(v.getLocation()); + score += v.getNumberOfRobots() * 8; + score += v.getInitialRobotProductivity() * 40; + switch (v.getMaterial()) { + case Gas: + score += gas_ratio * v.getInitialMaterialProductivity(); + break; + case Metal: + score += metal_ratio * v.getInitialMaterialProductivity(); + break; + default: + break; + } + for (Vein ov : game.getField().getVeins()) { + if (ov.getOwnerId() == -1) + continue; + int d = ov.getDistance(v); + if (d < 16) { + score -= 16 - d; + if (d < 10) { + score -= 10 - d; + } + if (d < 6) { + score -= 6 - d; + } + } + score -= ov.getDistance(v); + + } + if (score > bestScore) { + bestVein = v; + bestScore = score; + } + } + return bestVein.getLocation(); + } + + @Override + public List selectActions(Game game) { + this.game = game; + turn = game.getRound(); + self = game.getMyPlayer(); + gas = self.getMaterial(Material.Gas); + stone = self.getMaterial(Material.Stone); + metal = self.getMaterial(Material.Metal); + + myVeins.clear(); + myVeinsByLocation.clear(); + for (Vein v : game.getField().getVeins(self.getId())) { + OwnVein ov = new OwnVein(v); + myVeins.add(ov); + myVeinsByLocation.put(v.getLocation(), ov); + } + commands = new ArrayList<>(); + + upgradeVeins(); + saveTemporalCommands(commands); + + doAttack(); + return commands; + } + + private static class LargerRobotProducer implements Comparator { + @Override + public int compare(OwnVein o1, OwnVein o2) { + return o2.vein.getInitialRobotProductivity() + - o1.vein.getInitialRobotProductivity(); + } + } + + private static class MoreMaterialProducer implements Comparator { + @Override + public int compare(OwnVein o1, OwnVein o2) { + int v1 = o1.vein.getInitialMaterialProductivity(); + int v2 = o2.vein.getInitialMaterialProductivity(); + // TODO: 資源の種類に対して重み付け + // TODO: vein の安全さに対して重み付け + return v2 - v1; + } + } + + void doTrade() { + if (stone > 1200) { + commands.add(Commands + .sellToAlienTrade(Material.Stone, stone - 1200)); + } + if (gas > 1200) { + commands.add(Commands.sellToAlienTrade(Material.Gas, gas - 1200)); + } + if (metal > 1600) { + commands.add(Commands + .sellToAlienTrade(Material.Metal, metal - 1600)); + } + if (metal < 400) { + commands.add(Commands.buyFromAlienTrade(Material.Metal, 400)); + } + if (gas < 300) { + commands.add(Commands.buyFromAlienTrade(Material.Gas, 300)); + } + if (metal < 400) { + commands.add(Commands.buyFromAlienTrade(Material.Metal, 400)); + } + if (gas < 300) { + commands.add(Commands.buyFromAlienTrade(Material.Gas, 300)); + } + if (stone < 300) { + commands.add(Commands.buyFromAlienTrade(Material.Stone, 300)); + } + } + + /** 所有している vein の rank up を行う. */ + private void upgradeVeins() { + Collections.sort(myVeins, new LargerRobotProducer()); + for (OwnVein vein : myVeins) { + vein.tryUpgradeRobot(); + } + Collections.sort(myVeins, new MoreMaterialProducer()); + for (OwnVein vein : myVeins) { + vein.tryUpgradeMaterial(); + } + doTrade(); + } + + private static class MoreRobots implements Comparator { + @Override + public int compare(OwnVein v1, OwnVein v2) { + return v2.robots - v1.robots; + } + } + + private static int getRemainTurn(Squad s) { + TrianglePoint loc = s.getCurrentLocation(); + TrianglePoint dest = s.getDestinationLocation(); + List path = s.getPath(); + + for (int i = 0; i < path.size(); ++i) { + if (path.get(i).equals(dest)) { + return path.size() - i; + } + } + System.err.println("Warn: can't calculate remain turn"); + return loc.getDistance(s.getDestinationLocation()); + } + + private static class NearDestination implements Comparator { + @Override + public int compare(Squad s1, Squad s2) { + return getRemainTurn(s1) - getRemainTurn(s2); + } + } + + private boolean _makeRouteSub(TrianglePoint from, TrianglePoint to, + List route, int t) { + int x = from.getX(); + int y = from.getY(); + + if (from.equals(to)) + return true; + + if (t > 0) { + for (Squad s : game.getField().getSquads()) { + int now; + TrianglePoint cur = s.getCurrentLocation(); + List sp = s.getPath(); + for (now = 0; now < sp.size(); ++now) { + if (sp.get(now).equals(cur)) { + break; + } + } + // System.err.println("now="+now+" path="+sp.size() + " t="+t); + if (now + t - 1 < sp.size()) { + if (sp.get(now + t - 1).equals(from)) { + System.err.println("Boom: " + from); + return false; + } + } + if (now + t < sp.size()) { + if (sp.get(now + t).equals(from)) { + System.err.println("Boom: " + from); + return false; + } + } + } + } + + int dist = from.getDistance(to); + + TrianglePoint next = Make.point(x - 1, y); + if (next.getDistance(to) < dist) { + route.add(next); + if (_makeRouteSub(next, to, route, t + 1)) { + return true; + } + route.remove(t + 1); + } + + next = Make.point(x + 1, y); + if (next.getDistance(to) < dist) { + route.add(next); + if (_makeRouteSub(next, to, route, t + 1)) { + return true; + } + route.remove(t + 1); + } + + if ((x + y) % 2 == 0) { + // if (from.isUpwardTriangle()) { + next = Make.point(x, y + 1); + } else { + next = Make.point(x, y - 1); + } + if (next.getDistance(to) < dist) { + route.add(next); + if (_makeRouteSub(next, to, route, t + 1)) { + return true; + } + route.remove(t + 1); + } + return false; + } + + /** 攻撃するときのルート計算 */ + List makeRoute(TrianglePoint from, TrianglePoint to) { + List route = new ArrayList<>(); + route.add(from); + if (_makeRouteSub(from, to, route, 0)) { + return route; + } + return null; + } + + /** 攻撃を行う */ + private void doAttack() { + Collections.sort(myVeins, new MoreRobots()); + Field field = game.getField(); + List squads = field.getSquads(); + + TreeSet targetted = new TreeSet<>(); + + int defence = 0; + int veins = 0; + for (Vein v : field.getVeinsOfOtherOwnersOrderedByDistance(myVeins + .get(0).vein)) { + if (v.getOwnerId() == game.getNeutralPlayerId()) + continue; + veins++; + defence += v.getNumberOfRobots(); + } + if (game.getRound() > 100) { + defence *= 200 - game.getRound(); + } else { + defence *= game.getRound(); + } + defence /= 100 * veins; + defence += 5; + + // defence = (int)(defence * (1.0 + ((double) game.getRound() / 200.0 - + // 0.5)) / veins); + + System.err.println("Round: " + game.getRound() + " / Defence: " + + defence); + + for (OwnVein vein : myVeins) { + int mindist = Integer.MAX_VALUE; + int def2 = defence; + List nearEnemies = vein.nearEnemies(); + for (Vein ev : nearEnemies) { + if (ev.getOwnerId() == game.getNeutralPlayerId()) + continue; + int d = ev.getDistance(vein.vein); + if (d > 8) + break; + int x = ev.getNumberOfRobots() - d + * vein.vein.getCurrentRobotProductivity(); + if (x > 0) + def2 += x; + def2 += vein.commingSquads; + } + + targetloop: for (Vein enemyVein : nearEnemies) { + TrianglePoint loc = enemyVein.getLocation(); + int enemyRobots = enemyVein.getNumberOfRobots(); + if (targetted.contains(loc)) { + continue; + } + + int dist = loc.getDistance(vein.location); + if (dist < mindist) + mindist = dist; + if (dist > 16) { + break; + } + + ArrayList targetSquads = new ArrayList<>(); + for (Squad s : squads) { + if (s.getDestinationLocation().equals(loc)) { + if (s.getCurrentLocation().getDistance(loc) >= dist) { + continue targetloop; + } + if (s.getOwnerId() == self.getId()) { + enemyRobots -= s.getRobot(); + if (enemyRobots < 0) + enemyRobots = 0; + } + targetSquads.add(s); + } + } + + Collections.sort(targetSquads, new NearDestination()); + int enemyId = enemyVein.getOwnerId(); + int t = 0; + for (Squad s : targetSquads) { + int r = getRemainTurn(s); + if (r >= dist) { + continue targetloop; + } + if (enemyId != game.getNeutralPlayerId()) { + enemyRobots += (r - t) + * enemyVein.getCurrentRobotProductivity(); + } + t = r; + if (enemyRobots < s.getRobot()) { + enemyId = s.getOwnerId(); + enemyRobots = s.getRobot() - enemyRobots; + } else { + enemyRobots -= s.getRobot(); + } + } + if (enemyId != game.getNeutralPlayerId()) { + enemyRobots += (dist - t) + * enemyVein.getCurrentRobotProductivity(); + } + enemyRobots += (dist / 2); + + // ターゲットの周辺の敵基地の強さを勘案する. 取ってすぐ取り返されるなら最初から取らない. + if (turn > 4 || dist > 6 + || enemyVein.getOwnerId() == game.getNeutralPlayerId()) { + for (Vein v : game.getField() + .getVeinsOfOtherOwnersOrderedByDistance(enemyVein)) { + int r = v.getDistance(enemyVein); + if (r > 6) + break; + if (v.getOwnerId() != self.getId() + && v.getOwnerId() != game.getNeutralPlayerId()) { + int x = v.getNumberOfRobots() + - enemyVein.getCurrentRobotProductivity() + * r; + if (x > 0) + enemyRobots += x / 2; + } + } + for (Vein v : game.getField() + .getVeinsOfSameOwnerOrderedByDistance(enemyVein)) { + int r = v.getDistance(enemyVein); + if (r > 6) + break; + int x = v.getNumberOfRobots() + - enemyVein.getCurrentRobotProductivity() * r; + if (x > 0) + enemyRobots += x / 2; + } + } + + if (vein.emerge && enemyRobots < vein.robots) { + targetted.add(loc); + vein.attack(enemyVein, vein.robots, false); + break; + } + if (enemyRobots + def2 < vein.robots) { + targetted.add(loc); + int squadSize = vein.robots - def2; + vein.attack(enemyVein, squadSize, false); + break; + } + } + + int distBonus = mindist * vein.vein.getCurrentRobotProductivity() + / 2; + if (distBonus >= defence) + distBonus = defence - 1; + if (mindist > 10 + && (vein.robots - vein.commingSquads) > (defence - distBonus)) { + // move to mine. + for (OwnVein nearVein : vein.nearFriends()) { + if (nearVein.nearEnemies().get(0) + .getDistance(nearVein.vein) < mindist) { + int squadSize = vein.robots - vein.commingSquads + - defence + distBonus; + System.err.println("Move to friend: from=" + + vein.location + " to=" + nearVein.location + + " robots=" + vein.robots + " mindist=" + + mindist + " defence=" + defence + + " distBonus=" + distBonus + "squad=" + + squadSize); + vein.attack(nearVein.vein, vein.robots - defence + + distBonus, true); + break; + } + } + } + + // if (vein.emerge && vein.robots > 0) { + // System.err.println("WARNING! WARNING! WARNING!"); + // vein.attack(vein.nearFriends().get(0).vein, vein.robots, false); + // } + } + } +} diff --git a/src/main/java/net/javachallenge/players/guests/Oyososan.java b/src/main/java/net/javachallenge/players/guests/Oyososan.java new file mode 100644 index 0000000..0aeec94 --- /dev/null +++ b/src/main/java/net/javachallenge/players/guests/Oyososan.java @@ -0,0 +1,392 @@ +package net.javachallenge.players.guests; + +import static java.lang.Math.abs; +import static java.lang.Math.exp; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Field; +import net.javachallenge.api.Game; +import net.javachallenge.api.Material; +import net.javachallenge.api.Player; +import net.javachallenge.api.Squad; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class Oyososan extends ComputerPlayer { + + public static class Constant { + public static final int[] WEIGHTS = { 50, 10, 646, 20, 480, 200, 5, 4, + 5, 4, 8, 200, 5, 3, 4, 3, 50, 4, 1000, 1600, 12, 7, 1000, 5, + 14, 10, }; + } + + private static final int LAUNCH_ROBOT_BEHIND_NUMBER_RATIO = Constant.WEIGHTS[0]; + private static final int LAUNCH_ROBOT_BEHIND_DISTANCE_THRESHOLD = Constant.WEIGHTS[1]; + private static final int LAUNCH_ROBOT_BEHIND_NUMBER_THRESHOLD = Constant.WEIGHTS[2]; + private static final int LAUNCH_ROBOT_BEHIND_BEGIN_ROUND = Constant.WEIGHTS[3]; + private static final int LAUNCH_ROBOT_ENEMY_ROUND_WEIGHT = Constant.WEIGHTS[4]; + private static final int LAUNCH_ROBOT_ENEMY_NUMBER_WEIGHT = Constant.WEIGHTS[5]; + private static final int LAUNCH_ROBOT_ENEMY_Y_ROUND_WEIGHT = Constant.WEIGHTS[6]; + private static final int LAUNCH_ROBOT_ENEMY_Y_OFFSET = Constant.WEIGHTS[7]; + private static final int LAUNCH_ROBOT_ENEMY_X_ROUND_WEIGHT = Constant.WEIGHTS[8]; + private static final int LAUNCH_ROBOT_ENEMY_X_OFFSET = Constant.WEIGHTS[9]; + private static final int LAUNCH_ROBOT_ENEMY_BEGIN_ROUND = Constant.WEIGHTS[10]; + private static final int LAUNCH_ROBOT_OTHER_ROUND_WEIGHT = Constant.WEIGHTS[11]; + private static final int LAUNCH_ROBOT_OTHER_Y_ROUND_WEIGHT = Constant.WEIGHTS[12]; + private static final int LAUNCH_ROBOT_OTHER_X_ROUND_WEIGHT = Constant.WEIGHTS[13]; + private static final int LAUNCH_ROBOT_OTHER_Y_OFFSET = Constant.WEIGHTS[14]; + private static final int LAUNCH_ROBOT_OTHER_X_OFFSET = Constant.WEIGHTS[15]; + private static final int SELL_MATERIALS_AMOUNT_RATIO = Constant.WEIGHTS[16]; + private static final int SELL_MATERIALS_MIN_MAX_RATIO = Constant.WEIGHTS[17]; + private static final int SELL_MATERIALS_AMOUNT_THRESHOLD = Constant.WEIGHTS[18]; + private static final int SELECT_VEIN_CENTER_SCORE = Constant.WEIGHTS[19]; + private static final int SELECT_VEIN_MY_VEIN_FAVORITE_DISTANCE = Constant.WEIGHTS[20]; + private static final int SELECT_VEIN_MY_VEIN_SCORE = Constant.WEIGHTS[21]; + private static final int SELECT_VEIN_ENEMY_VEIN_SCORE = Constant.WEIGHTS[22]; + private static final int SELECT_VEIN_OTHER_VEIN_SCORE = Constant.WEIGHTS[23]; + private static final int CHOOSE_BY_ROBOT_PRODUCTIVITY = Constant.WEIGHTS[24]; + private static final int CHOOSE_BY_MATERIAL_PRODUCTIVITY = Constant.WEIGHTS[25]; + + public static class PointComparator implements Comparator { + public final int compare(TrianglePoint left, TrianglePoint right) { + if (left.getX() == right.getX()) + return left.getX() - right.getX(); + return left.getY() - right.getY(); + } + } + + @Override + public String getName() { + return "Oyososan"; + } + + @Override + public TrianglePoint selectVein(Game game) { + Vein bestVein = null; + double bestScore = Double.NEGATIVE_INFINITY; + + for (Vein vein : game.getField().getVeins()) { + if (vein.getOwnerId() != -1) { + continue; + } + + double score = 0.0; + for (Vein otherVein : game.getField().getVeins()) { + if (vein.equals(otherVein)) { + continue; + } + + if (otherVein.getOwnerId() == -1) { + // Other vein. + score += SELECT_VEIN_OTHER_VEIN_SCORE + * exp(-vein.getDistance(otherVein)); + } else if (otherVein.getOwnerId() != game.getMyPlayer().getId()) { + // Enemy vein. + score -= SELECT_VEIN_ENEMY_VEIN_SCORE + * exp(-vein.getDistance(otherVein)); + } else { + // My vein. + score += SELECT_VEIN_MY_VEIN_SCORE + * exp(-abs(SELECT_VEIN_MY_VEIN_FAVORITE_DISTANCE + - vein.getDistance(otherVein))); + } + } + + // Distance from the center. + score -= SELECT_VEIN_CENTER_SCORE + * exp(-abs(vein.getLocation().getX()) + - abs(vein.getLocation().getY())); + + // Productivity + score += vein.getInitialRobotProductivity() + * CHOOSE_BY_ROBOT_PRODUCTIVITY / 100; + score += vein.getInitialMaterialProductivity() + * CHOOSE_BY_MATERIAL_PRODUCTIVITY / 100; + + if (bestScore < score) { + bestScore = score; + bestVein = vein; + } + } + + return bestVein.getLocation(); + } + + @Override + public List selectActions(final Game game) { + Player myPlayer = game.getMyPlayer(); + + List commands = new ArrayList(); + this.saveTemporalCommands(commands); + + List myVeins = new ArrayList(); + List enemyVeins = new ArrayList(); + List otherVeins = new ArrayList(); + + for (Vein vein : game.getField().getVeins()) { + if (vein.getOwnerId() == myPlayer.getId()) { + myVeins.add(vein); + } else if (vein.getOwnerId() != -1) { + enemyVeins.add(vein); + } else { + otherVeins.add(vein); + } + } + Collections.sort(myVeins, new Comparator() { + @Override + public int compare(Vein o1, Vein o2) { + Field field = game.getField(); + return -(o1.getDistance(field + .getVeinsOfOtherOwnersOrderedByDistance(o1).get(0)) - o2 + .getDistance(field + .getVeinsOfOtherOwnersOrderedByDistance(o2) + .get(0))); + } + }); + + Map materials = new HashMap(); + int myMoney = myPlayer.getMoney(); + + Material minMaterial = Material.Gas; + Material maxMaterial = Material.Gas; + for (Material material : Material.values()) { + int amount = myPlayer.getMaterial(material); + materials.put(material, new AtomicInteger(amount)); + + if (myPlayer.getMaterial(minMaterial) > myPlayer + .getMaterial(material)) { + minMaterial = material; + } + if (myPlayer.getMaterial(maxMaterial) < myPlayer + .getMaterial(material)) { + maxMaterial = material; + } + } + + // Sell materials. + if (materials.get(maxMaterial).get() > SELL_MATERIALS_AMOUNT_THRESHOLD + && materials.get(minMaterial).get() + * SELL_MATERIALS_MIN_MAX_RATIO < materials.get( + maxMaterial).get()) { + int price = game.getAlienTrade().getSellPriceOf(maxMaterial); + int sellAmount = materials.get(maxMaterial).get() + * SELL_MATERIALS_AMOUNT_RATIO / 100; + commands.add(Commands.sellToAlienTrade(maxMaterial, sellAmount)); + materials.get(maxMaterial).addAndGet(-sellAmount); + myMoney += price * sellAmount; + } + + // Buy materials. + int price = game.getAlienTrade().getBuyPriceOf(minMaterial); + int buyAmount = myMoney / price; + if (buyAmount > 0) { + commands.add(Commands.buyFromAlienTrade(minMaterial, buyAmount)); + materials.get(minMaterial).addAndGet(buyAmount); + } + + this.saveTemporalCommands(commands); + + // Upgrade robots. + { + final int[] gasCost = { 0, 200, 0, Integer.MAX_VALUE }; + final int[] stoneCost = { 0, 0, 300, Integer.MAX_VALUE }; + final int[] metalCost = { 0, 200, 500, Integer.MAX_VALUE }; + + Vein toUpdate = null; + int distMin = Integer.MAX_VALUE; + for (Vein vein : myVeins) { + int rank = vein.getRobotRank(); + if (materials.get(Material.Gas).get() < gasCost[rank] + || materials.get(Material.Metal).get() < metalCost[rank] + || materials.get(Material.Stone).get() < stoneCost[rank]) { + continue; + } + List enemies = game.getField() + .getVeinsOfOtherOwnersOrderedByDistance(vein); + int dist = enemies.get(0).getDistance(vein); + + if (dist < distMin && dist > 3) { + toUpdate = vein; + distMin = dist; + } + } + if (toUpdate != null) { + int rank = toUpdate.getRobotRank(); + commands.add(Commands.upgradeRobot(toUpdate)); + materials.get(Material.Gas).addAndGet(-gasCost[rank]); + materials.get(Material.Metal).addAndGet(-metalCost[rank]); + materials.get(Material.Stone).addAndGet(-stoneCost[rank]); + } + } + this.saveTemporalCommands(commands); + + // Upgrade materials. + { + final int[] gasCost = { 0, 200, 100, Integer.MAX_VALUE }; + final int[] stoneCost = { 0, 100, 300, Integer.MAX_VALUE }; + Vein toUpdate = null; + int distMin = Integer.MAX_VALUE; + for (Vein vein : myVeins) { + int rank = vein.getMaterialRank(); + if (materials.get(Material.Gas).get() < gasCost[rank] + || materials.get(Material.Stone).get() < stoneCost[rank]) { + continue; + } + List enemies = game.getField() + .getVeinsOfOtherOwnersOrderedByDistance(vein); + int dist = enemies.get(0).getDistance(vein); + if (dist < distMin && dist > 3) { + toUpdate = vein; + distMin = dist; + } + } + if (toUpdate != null) { + int rank = toUpdate.getMaterialRank(); + commands.add(Commands.upgradeMaterial(toUpdate)); + materials.get(Material.Gas).addAndGet(-gasCost[rank]); + materials.get(Material.Stone).addAndGet(-stoneCost[rank]); + } + } + this.saveTemporalCommands(commands); + + Map numberOfRobots = new HashMap(); + for (Vein myVein : myVeins) { + numberOfRobots.put(myVein, + new AtomicInteger(myVein.getNumberOfRobots())); + } + + // Launch robots to other veins. + Collections.shuffle(myVeins); + for (final Vein myVein : myVeins) { + Collections.sort(otherVeins, new Comparator() { + @Override + public int compare(Vein o1, Vein o2) { + return myVein.getDistance(o1) - myVein.getDistance(o2); + } + }); + + for (Vein otherVein : otherVeins) { + if (abs(myVein.getLocation().getX() + - otherVein.getLocation().getX()) < LAUNCH_ROBOT_OTHER_X_OFFSET + + game.getRound() + * LAUNCH_ROBOT_OTHER_X_ROUND_WEIGHT + / 100 + && abs(myVein.getLocation().getY() + - otherVein.getLocation().getY()) < LAUNCH_ROBOT_OTHER_Y_OFFSET + + game.getRound() + * LAUNCH_ROBOT_OTHER_Y_ROUND_WEIGHT / 100) { + if (numberOfRobots.get(myVein).get() > otherVein + .getNumberOfRobots() + + game.getRound() + * LAUNCH_ROBOT_OTHER_ROUND_WEIGHT / 100) { + int robots = otherVein.getNumberOfRobots() + 1; + commands.add(Commands.launch(robots, + myVein.getLocation(), otherVein.getLocation())); + numberOfRobots.get(myVein).addAndGet(-robots); + } + } + } + } + this.saveTemporalCommands(commands); + + // Launch robots to enemy veins. + Collections.shuffle(myVeins); + if (game.getRound() > LAUNCH_ROBOT_ENEMY_BEGIN_ROUND) { + for (Vein myVein : myVeins) { + for (Vein enemyVein : game.getField() + .getVeinsOfOtherOwnersOrderedByDistance(myVein)) { + if (abs(myVein.getLocation().getX() + - enemyVein.getLocation().getX()) < LAUNCH_ROBOT_ENEMY_X_OFFSET + + game.getRound() + * LAUNCH_ROBOT_ENEMY_X_ROUND_WEIGHT / 100 + && abs(myVein.getLocation().getY() + - enemyVein.getLocation().getY()) < LAUNCH_ROBOT_ENEMY_Y_OFFSET + + game.getRound() + * LAUNCH_ROBOT_ENEMY_Y_ROUND_WEIGHT / 100) { + if (numberOfRobots.get(myVein).get() > enemyVein + .getNumberOfRobots() + * LAUNCH_ROBOT_ENEMY_NUMBER_WEIGHT + / 100 + - game.getRound() + * LAUNCH_ROBOT_ENEMY_ROUND_WEIGHT / 100 + && numberOfRobots.get(myVein).get() > enemyVein + .getNumberOfRobots() + + enemyVein + .getCurrentRobotProductivity() + * myVein.getDistance(enemyVein) + 1) { + int robots = enemyVein.getNumberOfRobots() + + enemyVein.getCurrentRobotProductivity() + * myVein.getDistance(enemyVein) + 1; + commands.add(Commands.launch(robots, + myVein.getLocation(), + enemyVein.getLocation())); + numberOfRobots.get(myVein).addAndGet(-robots); + } + } + } + } + } + this.saveTemporalCommands(commands); + + // Launch robots from behind to front enemy veins. + Collections.shuffle(myVeins); + if (game.getRound() > LAUNCH_ROBOT_BEHIND_BEGIN_ROUND) { + for (Vein myVein : myVeins) { + if (numberOfRobots.get(myVein).get() < LAUNCH_ROBOT_BEHIND_NUMBER_THRESHOLD) { + continue; + } + + for (Vein enemyVein : game.getField() + .getVeinsOfOtherOwnersOrderedByDistance(myVein)) { + if (myVein.getDistance(enemyVein) < LAUNCH_ROBOT_BEHIND_DISTANCE_THRESHOLD) { + continue; + } + + int robots = numberOfRobots.get(myVein).get() + * LAUNCH_ROBOT_BEHIND_NUMBER_RATIO / 100; + commands.add(Commands.launch(robots, myVein.getLocation(), + enemyVein.getLocation())); + numberOfRobots.get(myVein).addAndGet(-robots); + break; + } + } + } + this.saveTemporalCommands(commands); + + // TODO(tzik): Launch robot to veins under attacked to protect them. + // calcInvasionList(myPlayer.getId(), game.getField().getSquads()); + + return commands; + } + + private static final class Invasion { + public TrianglePoint destination; + public int delay; + public int number; + } + + List calcInvasionList(int myId, List squads) { + List invasions = new ArrayList(); + for (Squad squad : squads) { + Invasion invasion = new Invasion(); + invasion.destination = squad.getDestinationLocation(); + invasion.delay = squad.getPath().size() - 1; + invasion.number = squad.getRobot(); + + if (squad.getOwnerId() == myId) + invasion.number *= -1; + invasions.add(invasion); + } + return invasions; + } +} diff --git a/src/main/java/net/javachallenge/players/others/JoeJack.java b/src/main/java/net/javachallenge/players/others/JoeJack.java new file mode 100644 index 0000000..b31149a --- /dev/null +++ b/src/main/java/net/javachallenge/players/others/JoeJack.java @@ -0,0 +1,335 @@ +package net.javachallenge.players.others; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Game; +import net.javachallenge.api.GameSetting; +import net.javachallenge.api.Material; +import net.javachallenge.api.PlayerTrade; +import net.javachallenge.api.Squad; +import net.javachallenge.api.TradeType; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class JoeJack extends ComputerPlayer { + private Map allVeins; + private Map sentRobots; + private Map launchedRobots; + private Map usedMaterials; + private int usedMoney; + private Game game; + private final int MaxRank = 3; + + private void initialize(Game game) { + this.game = game; + allVeins = game.getField().getVeinMap(); + sentRobots = new HashMap(); + launchedRobots = new HashMap(); + usedMaterials = new HashMap(); + usedMoney = 0; + for (Squad squad : game.getField().getSquads()) { + TrianglePoint destination = squad.getDestinationLocation(); + increment(sentRobots, destination, squad.getRobot() + * (squad.getOwnerId() == game.getMyPlayer().getId() ? 1 : -1)); + } + } + + @Override + public String getName() { + return "JoeJack"; + } + + private Vein firstSelectedVein; + + @Override + public TrianglePoint selectVein(Game game) { + initialize(game); + List all = new ArrayList(); + all.addAll(allVeins.keySet()); + Collections.sort(all, veinValueComparator); + TrianglePoint best = null; + for (TrianglePoint point : all) { + if (allVeins.get(point).getOwnerId() == game.getNeutralPlayerId() + && (firstSelectedVein == null || allVeins.get(point).getShortestPath(firstSelectedVein) + .size() < game.getSetting().getMapSize())) { + best = point; + break; + } + } + if (firstSelectedVein == null && best != null) { + firstSelectedVein = allVeins.get(best); + } + return best; + } + + private void launch(List commands, int robotsToLaunch, TrianglePoint from, + TrianglePoint to) { + addCommand(commands, Commands.launch(robotsToLaunch, from, to)); + increment(sentRobots, to, robotsToLaunch); + increment(launchedRobots, from, robotsToLaunch); + } + + @Override + public List selectActions(Game game) { + // initialize + initialize(game); + List commands = new ArrayList(); + ArrayList myVeinPoints = new ArrayList(); + ArrayList otherVeinPoints = new ArrayList(); + for (TrianglePoint point : allVeins.keySet()) { + if (allVeins.get(point).getOwnerId() == game.getMyPlayer().getId()) { + myVeinPoints.add(point); + } else { + otherVeinPoints.add(point); + } + } + + // launch + Collections.sort(myVeinPoints, frontVeinComparator); + Collections.reverse(myVeinPoints); + for (TrianglePoint myPoint : myVeinPoints) { + TargetVeinComparator targetVeinComparator = new TargetVeinComparator(); + targetVeinComparator.setSource(myPoint); + Collections.sort(otherVeinPoints, targetVeinComparator); + for (TrianglePoint otherPoint : otherVeinPoints.subList(0, + otherVeinPoints.size() / 4)) { + int robotsToLaunch = + getRequiredRobots(myPoint, otherPoint) + 1 - getValue(sentRobots, otherPoint); + if (robotsToLaunch > 0 + && robotsToLaunch <= getRemainingRobots(myPoint) - getValue(launchedRobots, myPoint)) { + launch(commands, robotsToLaunch, myPoint, otherPoint); + } + } + final int NumberOfRobotsToHold = 100; + for (TrianglePoint otherPoint : otherVeinPoints) { + int robotsToLaunch = getRemainingRobots(myPoint) - NumberOfRobotsToHold; + if (robotsToLaunch <= 0) { + break; + } else { + launch(commands, robotsToLaunch, myPoint, otherPoint); + } + } + } + + // upgrade + Collections.sort(myVeinPoints, veinValueComparator); + for (TrianglePoint point : myVeinPoints) { + GameSetting setting = game.getSetting(); + + int currentMaterialRank = allVeins.get(point).getMaterialRank(); + Map requiredMaterialsForMaterial = new HashMap(); + for (Material material : Material.values()) { + int requiredAmount; + if (currentMaterialRank == 1) { + requiredAmount = setting.getMaterialsForUpgradingMaterialRankFrom1To2(material); + } else { + requiredAmount = setting.getMaterialsForUpgradingMaterialRankFrom2To3(material); + } + requiredMaterialsForMaterial.put(material, requiredAmount); + } + upgrade(requiredMaterialsForMaterial, Commands.upgradeMaterial(point), point, + currentMaterialRank, commands); + + int currentRobotRank = allVeins.get(point).getRobotRank(); + Map requiredMaterialsForRobot = new HashMap(); + for (Material material : Material.values()) { + int requiredAmount; + if (currentRobotRank == 1) { + requiredAmount = setting.getMaterialsForUpgradingRobotRankFrom1To2(material); + } else { + requiredAmount = setting.getMaterialsForUpgradingRobotRankFrom2To3(material); + } + requiredMaterialsForRobot.put(material, requiredAmount); + } + upgrade(requiredMaterialsForRobot, Commands.upgradeRobot(point), point, currentRobotRank, + commands); + } + + // trade : prepare + final int MaxTradeAmount = 100; + final int MaterialAmountToHold = 300; + List materialsToBuy = new ArrayList(); + List materialsToSell = new ArrayList(); + for (Material material : Material.values()) { + if (getRemainingMaterials(material) < MaterialAmountToHold) { + materialsToBuy.add(material); + } else if (getRemainingMaterials(material) > MaterialAmountToHold) { + materialsToSell.add(material); + } + } + + // trade : accept their trades + for (PlayerTrade trade : game.getPlayerTrades()) { + Material material = trade.getMaterial(); + if (trade.getTradeType() == TradeType.Offer + && trade.getPricePerOneMaterial() < getDefaultPrice(material) + && materialsToBuy.contains(material)) { + addCommand(commands, Commands.buyFromPlayerTrade(trade, Math.min(trade.getAmount(), + Math.min(getMaximumAmountFromRemainingMoney(trade.getPricePerOneMaterial()), + MaxTradeAmount)))); + } else if (trade.getTradeType() == TradeType.Demand + && trade.getPricePerOneMaterial() > getDefaultPrice(material) + && materialsToSell.contains(material)) { + addCommand( + commands, + Commands.sellToPlayerTrade( + trade, + Math.min(trade.getAmount(), + Math.min(getRemainingMaterials(material), MaxTradeAmount)))); + } + } + + // trade : send our trade requests + for (Material material : materialsToBuy) { + int amount = Math.min(getRemainingMoney() / getDefaultPrice(material), MaxTradeAmount); + if (amount > 0) { + addCommand(commands, Commands.demand(material, amount, getDefaultPrice(material))); + } + } + for (Material material : materialsToSell) { + int amount = Math.min(getRemainingMaterials(material), MaxTradeAmount); + if (amount > 0) { + addCommand(commands, Commands.offer(material, amount, getDefaultPrice(material))); + } + } + + return commands; + } + + private int getDefaultPrice(Material material) { + return game.getAlienTrade().getBuyPriceOf(material) / 2; + } + + private int getMaximumAmountFromRemainingMoney(int price) { + return getRemainingMoney() / price; + } + + private void upgrade(Map requiredMaterials, Command upgradeCommand, + TrianglePoint point, int currentRank, List commands) { + if (currentRank < MaxRank) { + boolean hasEnoughMaterials = true; + for (Material material : requiredMaterials.keySet()) { + if (getRemainingMaterials(material) < requiredMaterials.get(material)) { + hasEnoughMaterials = false; + break; + } + } + if (hasEnoughMaterials) { + addCommand(commands, upgradeCommand); + for (Material material : requiredMaterials.keySet()) { + increment(usedMaterials, material, requiredMaterials.get(material)); + } + } + } + } + + private void addCommand(List commands, Command command) { + commands.add(command); + this.saveTemporalCommands(commands); + } + + private void increment(Map map, K key, Integer value) { + map.put(key, (map.containsKey(key) ? map.get(key) : 0) + value); + } + + private Integer getValue(Map map, K key) { + if (map.containsKey(key)) { + return map.get(key); + } else { + return 0; + } + } + + private int evaluateVein(TrianglePoint from, TrianglePoint to) { + if (getRequiredRobots(from, to) <= 0) { + return Integer.MIN_VALUE / 10; + } + int distance = from.getShortestPath(to).size(); + if (distance > game.getSetting().getMapSize()) { + return Integer.MIN_VALUE / 10*2; + } + int evaluation = 0; + evaluation -= getRequiredRobots(from, to); + evaluation += allVeins.get(from).getNumberOfRobots(); + evaluation -= + distance + * (allVeins.get(to).getOwnerId() == game.getNeutralPlayerId() ? 5 : allVeins.get(to) + .getCurrentRobotProductivity()); + return evaluation; + } + + private int getRequiredRobots(TrianglePoint from, TrianglePoint to) { + Vein targetVein = allVeins.get(to); + int productivity = + (targetVein.getOwnerId() == game.getNeutralPlayerId() ? 0 : targetVein + .getCurrentRobotProductivity()); + int robots = targetVein.getNumberOfRobots() + from.getShortestPath(to).size() * productivity; + return robots; + } + + private int getRemainingMaterials(Material material) { + return game.getMyPlayer().getMaterial(material) - getValue(usedMaterials, material); + } + + private int getRemainingMoney() { + return game.getMyPlayer().getMoney() - usedMoney; + } + + private int getRemainingRobots(TrianglePoint vein) { + return allVeins.get(vein).getNumberOfRobots() - getValue(launchedRobots, vein); + } + + public Comparator veinValueComparator = new Comparator() { + private int getValue(Vein vein) { + return vein.getCurrentMaterialProductivity() + vein.getCurrentRobotProductivity(); + } + + public int compare(TrianglePoint left, TrianglePoint right) { + return getValue(allVeins.get(right)) - getValue(allVeins.get(left)); + } + }; + + public class TargetVeinComparator implements Comparator { + private TrianglePoint source; + + public void setSource(TrianglePoint source) { + this.source = source; + } + + public int compare(TrianglePoint left, TrianglePoint right) { + return evaluateVein(source, right) - evaluateVein(source, left); + } + }; + + public Comparator frontVeinComparator = new Comparator() { + private int getValue(TrianglePoint from) { + int value = 0; + int nearFriendlies = 0; + for (TrianglePoint to : allVeins.keySet()) { + if (allVeins.get(to).getOwnerId() != game.getMyPlayer().getId()) { + value -= from.getShortestPath(to).size(); + } else { + value -= game.getSetting().getMapSize() * 2; + nearFriendlies++; + } + } + if (nearFriendlies < 2) { + return Integer.MIN_VALUE / 10; + } else { + return value; + } + } + + public int compare(TrianglePoint left, TrianglePoint right) { + return getValue(right) - getValue(left); + } + }; +} diff --git a/src/main/java/net/javachallenge/players/others/Myu.java b/src/main/java/net/javachallenge/players/others/Myu.java new file mode 100644 index 0000000..4663212 --- /dev/null +++ b/src/main/java/net/javachallenge/players/others/Myu.java @@ -0,0 +1,244 @@ +package net.javachallenge.players.others; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Field; +import net.javachallenge.api.Game; +import net.javachallenge.api.Material; +import net.javachallenge.api.Player; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class Myu extends ComputerPlayer { + private List veinPoints; + private List myVeinPoints = new ArrayList(); + private static boolean isFirst = true; + + @Override + public String getName() { + return "Myu"; + } + + @Override + public TrianglePoint selectVein(Game game) { + Field field = game.getField(); + Map veins = field.getVeinMap(); + Object[] tp = veins.keySet().toArray(); + Point edge = getEdgeVeins(tp); + + veinPoints = categolizeVeins(tp, edge); + return selectPoint(veins); + } + + private Point getEdgeVeins(Object[] tp) { + int maxX = 0; + int minX = 100; + int maxY = 0; + int minY = 0; + + for (int i = tp.length - 1; i >= 0; i--) { + TrianglePoint t = (TrianglePoint) tp[i]; + int tmpX = t.getX(); + int tmpY = t.getY(); + + if (maxX < tmpX) { + maxX = tmpX; + } else if (minX > tmpX) { + minX = tmpX; + } + + if (maxY < tmpY) { + maxY = tmpY; + } else if (minY > tmpY) { + minY = tmpY; + } + } + return new Point(maxX + minX, maxY + minY); + } + + private List categolizeVeins(Object[] tp, Point edge) { + int maxNum = 0; + int zone = 0; + List seVeins = new ArrayList(); + List swVeins = new ArrayList(); + List neVeins = new ArrayList(); + List nwVeins = new ArrayList(); + + for (int i = tp.length - 1; i >= 0; i--) { + TrianglePoint t = (TrianglePoint) tp[i]; + if (t.getX() >= edge.x / 2) { + if (t.getY() >= edge.y / 2) { + seVeins.add(t); + if (seVeins.size() > maxNum) { + maxNum = seVeins.size(); + zone = 0; + } + } else { + neVeins.add(t); + if (neVeins.size() > maxNum) { + maxNum = neVeins.size(); + zone = 3; + } + } + } else { + if (t.getY() >= edge.x / 2) { + swVeins.add(t); + if (swVeins.size() > maxNum) { + maxNum = swVeins.size(); + zone = 2; + } + } else { + nwVeins.add(t); + if (nwVeins.size() > maxNum) { + maxNum = nwVeins.size(); + zone = 1; + } + } + } + } + + List points = new ArrayList(); + switch (zone) { + case 0: + points.addAll(seVeins); + points.addAll(swVeins); + points.addAll(neVeins); + points.addAll(nwVeins); + return points; + case 1: + points.addAll(nwVeins); + points.addAll(neVeins); + points.addAll(swVeins); + points.addAll(seVeins); + return points; + case 2: + points.addAll(swVeins); + points.addAll(seVeins); + points.addAll(nwVeins); + points.addAll(neVeins); + return points; + case 3: + points.addAll(neVeins); + points.addAll(nwVeins); + points.addAll(seVeins); + points.addAll(swVeins); + return points; + } + return seVeins; + } + + private TrianglePoint selectPoint(Map veins) { + int maxRP = 0; + Vein vein; + TrianglePoint point = veinPoints.get(0); + for (TrianglePoint t : veinPoints) { + vein = veins.get(t); + int rp = vein.getInitialRobotProductivity(); + if (isFirst) { + if (maxRP <= rp && vein.getOwnerId() == -1 && vein.getMaterial() == Material.Gas) { + maxRP = rp; + point = t; + } + } else { + if (maxRP <= rp && vein.getOwnerId() == -1 && vein.getMaterial() == Material.Metal) { + maxRP = rp; + point = t; + } + } + } + + isFirst = false; + myVeinPoints.add(point); + return point; + } + + @Override + public List selectActions(Game game) { + Player myself = game.getMyPlayer(); + Field field = game.getField(); + Map veinlist = field.getVeinMap(); + int robots = + (int) ((veinlist.get(myVeinPoints.get(0)).getNumberOfRobots() + veinlist.get( + myVeinPoints.get(1)).getNumberOfRobots()) * 0.8); + + List commands = new ArrayList(); + boolean ugrbt = true; + boolean ugmtl = true; + for (TrianglePoint tp : myVeinPoints) { + Vein v = veinlist.get(tp); + int rank = v.getRobotRank(); + + if (ugmtl + && myself.getMaterial(Material.Stone) > game.getSetting() + .getMaterialsForUpgradingMaterialRankFrom2To3(Material.Stone) + && myself.getMaterial(Material.Gas) > game.getSetting() + .getMaterialsForUpgradingRobotRankFrom2To3(Material.Gas)) { + if (rank < 3) { + commands.add(Commands.upgradeMaterial(v.getLocation())); + ugmtl = false; + } + } + + if (ugmtl + && myself.getMaterial(Material.Stone) > game.getSetting() + .getMaterialsForUpgradingMaterialRankFrom1To2(Material.Stone) + && myself.getMaterial(Material.Gas) > game.getSetting() + .getMaterialsForUpgradingRobotRankFrom1To2(Material.Gas)) { + if (rank < 2) { + commands.add(Commands.upgradeMaterial(v.getLocation())); + ugmtl = false; + } + } + + if (ugrbt + && myself.getMaterial(Material.Metal) > game.getSetting() + .getMaterialsForUpgradingRobotRankFrom2To3(Material.Metal) + && myself.getMaterial(Material.Gas) > game.getSetting() + .getMaterialsForUpgradingRobotRankFrom2To3(Material.Gas)) { + if (rank < 3) { + commands.add(Commands.upgradeRobot(v.getLocation())); + ugrbt = false; + } + } + + if (ugrbt + && myself.getMaterial(Material.Metal) > game.getSetting() + .getMaterialsForUpgradingRobotRankFrom1To2(Material.Metal) + && myself.getMaterial(Material.Gas) > game.getSetting() + .getMaterialsForUpgradingRobotRankFrom1To2(Material.Gas)) { + if (rank < 2) { + commands.add(Commands.upgradeRobot(v.getLocation())); + ugrbt = false; + } + } + + } + + Iterator tp = veinPoints.iterator(); + while (tp.hasNext()) { + TrianglePoint location = tp.next(); + if (veinlist.get(location).getOwnerId() == game.getNeutralPlayerId()) { + if (veinlist.get(location).getNumberOfRobots() < robots * 0.8) { + for (TrianglePoint p : myVeinPoints) { + Vein v = veinlist.get(p); + commands.add(Commands.launch((int) (v.getNumberOfRobots() * 0.9), v.getLocation(), + location)); + } + myVeinPoints.add(location); + tp.remove(); + break; + } + } + } + + this.saveTemporalCommands(commands); + return commands; + } +} diff --git a/src/main/java/net/javachallenge/players/others/NearPlayer.java b/src/main/java/net/javachallenge/players/others/NearPlayer.java new file mode 100644 index 0000000..ed83434 --- /dev/null +++ b/src/main/java/net/javachallenge/players/others/NearPlayer.java @@ -0,0 +1,425 @@ +package net.javachallenge.players.others; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Game; +import net.javachallenge.api.Material; +import net.javachallenge.api.Squad; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class NearPlayer extends ComputerPlayer { + static final double LAUNCH_RATE = 2.5; + + static final int REQUIRED_MINIMUM_ROBOTS_TO_INVADE = 100; + + static final int REQUIRED_MAXIMUM_SQUADS_TO_INVADE = 4; + + static final int NEAR_THRESHOLD = 12; + + class VeinWithPoint { + Vein vein; + TrianglePoint point; + int distance; + + VeinWithPoint(Vein v, TrianglePoint p, TrianglePoint t) { + vein = v; + point = p; + distance = p.getShortestPath(t).size(); + } + } + + static Random random = new Random(1000000009); + + @Override + public String getName() { + return "near-player"; + } + + @Override + public TrianglePoint selectVein(Game game) { + TrianglePoint bestPoint = null; + int bestRobot = -1; + + Map map = filterVein(game, -1); + Material firstMaterial = null; + if (filterMyVein(game).size() == 0) { + firstMaterial = Material.Stone; + } else { + Vein selectedVein = filterMyVein(game).values().iterator().next(); + firstMaterial = selectedVein.getMaterial(); + } + + for (Entry entry : map.entrySet()) { + TrianglePoint pt = entry.getKey(); + Vein vein = entry.getValue(); + boolean wantMaterial = + (vein.getMaterial() == Material.Gas || vein.getMaterial() == Material.Metal); + if (firstMaterial == vein.getMaterial()) { + wantMaterial = false; + } + if (wantMaterial) { + int score = evaluateInitialVeinScore(game, vein, pt); + if (bestRobot < score) { + bestRobot = score; + bestPoint = pt; + } + } + } + if (bestPoint != null) { + return bestPoint; + } + return map.entrySet().iterator().next().getKey(); + } + + /** + * Evaluate and return initial value of specified vein. + * + * @param game + * @param vein + * @param pt + * @return initial evaluation value of specified vein + */ + private int evaluateInitialVeinScore(Game game, Vein vein, TrianglePoint pt) { + Map map = filterVein(game, -1); + Map enemyMap = filterEnemyVein(game); + int score = evaluateBaseProductivity(vein); + for (Entry entryTo : map.entrySet()) { + TrianglePoint topt = entryTo.getKey(); + if (pt.equals(topt)) { + continue; + } + int d = topt.getShortestPath(pt).size(); + score += (Math.max(0, NEAR_THRESHOLD - d)); + } + for (Entry entryTo : enemyMap.entrySet()) { + TrianglePoint topt = entryTo.getKey(); + if (pt.equals(topt)) { + continue; + } + int d = topt.getShortestPath(pt).size(); + score -= (Math.max(0, NEAR_THRESHOLD - d)) * 100; + } + return score; + } + + + @Override + public List selectActions(Game game) { + List commands = new ArrayList(); + + commands.addAll(generateLaunchCommands(game)); + + commands.addAll(generateUpgradeCommands(game)); + + commands.addAll(generateTradeCommands(game)); + + return commands; + } + + /** + * Generate and return trade commands. + * + * @param game + * @return trade commands + */ + private List generateTradeCommands(Game game) { + List ret = new ArrayList(); + return ret; + } + + /** + * Return evaluation value of the vein based on initial productivity. + * + * @param vein + * @return evaluation value of the vein based on initial productivity + */ + private int evaluateBaseProductivity(Vein vein) { + return vein.getInitialRobotProductivity() + vein.getInitialMaterialProductivity(); + } + + /** + * Generate and return upgrade commands. + * + * @param game + * @return upgrade commands + */ + private List generateUpgradeCommands(Game game) { + List ret = new ArrayList(); + + Map myVeins = filterVein(game, game.getMyPlayer().getId()); + for (Entry myEntry : myVeins.entrySet()) { + TrianglePoint myPoint = myEntry.getKey(); + ret.add(Commands.upgradeRobot(myPoint)); + } + for (Entry myEntry : myVeins.entrySet()) { + TrianglePoint myPoint = myEntry.getKey(); + ret.add(Commands.upgradeMaterial(myPoint)); + } + return ret; + } + + /** + * Generate and return launch commands. + * + * @param game + * @return upgrade commands + */ + private List generateLaunchCommands(Game game) { + List ret = new ArrayList(); + Map myVeins = filterVein(game, game.getMyPlayer().getId()); + Set sentSet = new HashSet(); + Set sentToSet = new HashSet(); + + ret.addAll(launchSingleToSingle(game, myVeins, sentSet, sentToSet)); + + ret.addAll(launchMultipleToSingle(game, myVeins, sentSet, sentToSet)); + + return ret; + } + + /** + * Return list of unused veins that owned by me + * + * @param myVeins + * @param sentSet + * @return list of unused veins that owned by me + */ + private Map unusedMyVeins(Map myVeins, + Set sentSet) { + Map unusedMyVeins = new HashMap(); + for (Entry myEntry : myVeins.entrySet()) { + TrianglePoint myPoint = myEntry.getKey(); + if (sentSet.contains(myPoint)) { + continue; + } + if (myEntry.getValue().getNumberOfRobots() <= REQUIRED_MINIMUM_ROBOTS_TO_INVADE) { + continue; + } + unusedMyVeins.put(myPoint, myEntry.getValue()); + } + return unusedMyVeins; + } + + /** + * Return commands that launch robots multiple vein to enemy single vein. + * + * @param game + * @param myVeins + * @param sentSet + * @param sentToSet + * @return commands that launch robots multiple vein to enemy single vein. + */ + private List launchMultipleToSingle(Game game, Map myVeins, + Set sentSet, Set sentToSet) { + List multipleLaunchCommands = new ArrayList(); + for (Entry enemyEntry : filterEnemyVein(game).entrySet()) { + Vein enemyVein = enemyEntry.getValue(); + TrianglePoint enemyPoint = enemyEntry.getKey(); + + Map unusedMyVeins = unusedMyVeins(myVeins, sentSet); + + List nearPoints = nearVeins(game, enemyPoint, unusedMyVeins); + int cnt = 0; + int totalMyRobot = 0; + for (VeinWithPoint vwp : nearPoints) { + totalMyRobot += vwp.vein.getNumberOfRobots() / 2; + cnt++; + int expectedEnemyRobot = + enemyVein.getNumberOfRobots() + enemyVein.getCurrentRobotProductivity() * vwp.distance; + if (totalMyRobot >= expectedEnemyRobot * LAUNCH_RATE / 2) { + break; + } + } + + if (cnt <= Math.min(nearPoints.size() - 1, REQUIRED_MAXIMUM_SQUADS_TO_INVADE) + && nearPoints.get(0).distance <= NEAR_THRESHOLD) { + for (int d = 0; d < cnt; d++) { + VeinWithPoint vwp = nearPoints.get(d); + multipleLaunchCommands.add(Commands.launch(vwp.vein.getNumberOfRobots() / 2, vwp.point, + enemyPoint)); + sentSet.add(vwp.point); + } + } + } + return multipleLaunchCommands; + } + + /** + * Return enemy squads going to specified points sorted by distance. + * + * @param game + * @param vwp + * @return enemy squads going to specified points sorted by distance + */ + private List enemySquadsSortedByDistance(Game game, VeinWithPoint vwp) { + List squads = new ArrayList(); + for (Squad squad : game.getField().getSquads()) { + if (squad.getOwnerId() == game.getMyPlayer().getId()) { + continue; + } + if (squad.getDestinationLocation().equals(vwp.point)) { + squads.add(squad); + } + } + Collections.sort(squads, new Comparator() { + @Override + public int compare(Squad o1, Squad o2) { + int z1 = o1.getCurrentLocation().getShortestPath(o1.getDestinationLocation()).size(); + int z2 = o2.getCurrentLocation().getShortestPath(o2.getDestinationLocation()).size(); + return z1 - z2; + } + }); + return squads; + } + + /** + * Return expected number of enemy robots. + * + * @param game + * @param squads + * @param length + * @return expected number of enemy robots + */ + private int expectedEnemyRobot(Game game, List squads, int length) { + int squadIndex = squads.size() - 1; + int expectedEnemyRobot = 0; + while (squadIndex >= 0) { + Squad squad = squads.get(squadIndex); + if (squad.getOwnerId() != game.getMyPlayer().getId()) { + if (squad.getPath().size() > length) { + expectedEnemyRobot += squad.getRobot(); + } else { + expectedEnemyRobot = Math.max(expectedEnemyRobot, squad.getRobot()); + } + } + squadIndex--; + } + return expectedEnemyRobot; + } + + /** + * Return commands that launch robots single vein to another single vein. + * + * @param game + * @param myVeins + * @param sentSet + * @param sentToSet + * @return commands that launch robots single vein to another single vein + */ + private List launchSingleToSingle(Game game, Map myVeins, + Set sentSet, Set sentToSet) { + List singleLaunchCommands = new ArrayList(); + + for (Entry myEntry : myVeins.entrySet()) { + TrianglePoint fromPoint = myEntry.getKey(); + Vein myVein = myEntry.getValue(); + + Map emptyVeins = filterVein(game, -1); + List nearVeins = nearVeins(game, fromPoint, emptyVeins); + for (VeinWithPoint vwp : nearVeins) { + if (sentToSet.contains(vwp.point)) { + continue; + } + int length = vwp.point.getShortestPath(fromPoint).size(); + List squads = enemySquadsSortedByDistance(game, vwp); + int expectedEnemyRobot = + expectedEnemyRobot(game, squads, length) + vwp.vein.getNumberOfRobots(); + if (length <= NEAR_THRESHOLD + && myVein.getNumberOfRobots() >= expectedEnemyRobot * LAUNCH_RATE) { + singleLaunchCommands.add(Commands.launch((int) (expectedEnemyRobot * LAUNCH_RATE / 2), + fromPoint, vwp.point)); + sentSet.add(fromPoint); + sentToSet.add(vwp.point); + break; + } + } + } + return singleLaunchCommands; + } + + /** + * Return veins owned by me. + * + * @param game + * @return my veins + */ + private Map filterMyVein(Game game) { + return filterVein(game, game.getMyPlayer().getId()); + } + + /** + * Return veins owned by enemies. + * + * @param game + * @return enemy veins + */ + private Map filterEnemyVein(Game game) { + return filterNotVein(game, game.getMyPlayer().getId(), -1); + } + + private Map filterVein(Game game, int tid) { + return game.getField().getVeinMap(tid); + } + + /** + * Returns the vein owned by tid nor tid2. + * + * @param game + * @param tid + * @param tid2 + * @return + */ + private Map filterNotVein(Game game, int tid, int tid2) { + Map map = game.getField().getVeinMap(); + Map returnMap = new HashMap(); + for (Entry entry : map.entrySet()) { + TrianglePoint pt = entry.getKey(); + Vein vein = entry.getValue(); + if (vein.getOwnerId() != tid && vein.getOwnerId() != tid2) { + returnMap.put(pt, vein); + } + } + return returnMap; + } + + /** + * Return the map of vein and point that exist near the specified point. + * + * @param game + * @param p + * @param targetVeins + * @return the map of vein and point that exist near the specified point + */ + private List nearVeins(Game game, TrianglePoint p, + Map targetVeins) { + List nearPoints = new ArrayList(); + for (Entry myEntry : targetVeins.entrySet()) { + TrianglePoint fromPoint = myEntry.getKey(); + Vein myVein = myEntry.getValue(); + nearPoints.add(new VeinWithPoint(myVein, fromPoint, p)); + } + + Collections.sort(nearPoints, new Comparator() { + public int compare(VeinWithPoint o1, VeinWithPoint o2) { + if (o1.distance == o2.distance) { + return o2.vein.getNumberOfRobots() - o1.vein.getNumberOfRobots(); + } + return o1.distance - o2.distance; + } + }); + return nearPoints; + } +} diff --git a/src/main/java/net/javachallenge/players/others/Sabateur.java b/src/main/java/net/javachallenge/players/others/Sabateur.java new file mode 100644 index 0000000..08b0128 --- /dev/null +++ b/src/main/java/net/javachallenge/players/others/Sabateur.java @@ -0,0 +1,120 @@ +package net.javachallenge.players.others; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Game; +import net.javachallenge.api.Material; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class Sabateur extends ComputerPlayer { + + @Override + public String getName() { + return "Sabateur"; + } + + @Override + public TrianglePoint selectVein(Game game) { + Map allVeins = game.getField().getVeinMap(); + TrianglePoint bestPoint = null; + + for (TrianglePoint point : allVeins.keySet()) { + if (bestPoint == null) { + bestPoint = point; + } else if (allVeins.get(point).getInitialRobotProductivity() * 5 + + allVeins.get(point).getNumberOfRobots() > allVeins.get(bestPoint) + .getInitialRobotProductivity() * 5 + allVeins.get(bestPoint).getNumberOfRobots()) { + bestPoint = point; + } + } + + return bestPoint; + } + + @Override + public List selectActions(Game game) { + List commands = new ArrayList(); + + Map allVeins = game.getField().getVeinMap(); + ArrayList myVeinPoints = new ArrayList(); + ArrayList enemyVeinPoints = new ArrayList(); + ArrayList otherVeinPoints = new ArrayList(); + + for (TrianglePoint point : game.getField().getVeinMap().keySet()) { + if (allVeins.get(point).getOwnerId() == game.getMyPlayer().getId()) { + myVeinPoints.add(point); + } else if (allVeins.get(point).getOwnerId() != -1) { + enemyVeinPoints.add(point); + } else { + otherVeinPoints.add(point); + } + } + + this.saveTemporalCommands(commands); + + for (TrianglePoint point : myVeinPoints) { + int tmp = 0; + for (TrianglePoint opoint : otherVeinPoints) { + if (Math.abs(point.getX() - opoint.getX()) < 4 + game.getRound() / 20 + && Math.abs(point.getY() - opoint.getY()) < 4 + game.getRound() / 20) { + if (game.getField().getVein(point).getNumberOfRobots() - tmp > game.getField() + .getVein(opoint).getNumberOfRobots() + + game.getRound() * 2) { + tmp += game.getField().getVein(opoint).getNumberOfRobots() + 1; + commands.add(Commands.launch(game.getField().getVein(opoint).getNumberOfRobots() + 1, + point, opoint)); + } + } + } + } + + if (game.getRound() > 20) { + for (TrianglePoint point : myVeinPoints) { + int tmp = 0; + for (TrianglePoint epoint : enemyVeinPoints) { + if (Math.abs(point.getX() - epoint.getX()) < 4 + game.getRound() / 20 + && Math.abs(point.getY() - epoint.getY()) < 4 + game.getRound() / 20) { + if (game.getField().getVein(point).getNumberOfRobots() - tmp > game.getField() + .getVein(epoint).getNumberOfRobots() + * 2 - game.getRound() * 3) { + if (game.getField().getVein(point).getNumberOfRobots() - tmp > game.getField() + .getVein(epoint).getNumberOfRobots()) { + tmp += game.getField().getVein(epoint).getNumberOfRobots() + 1; + commands.add(Commands.launch( + game.getField().getVein(epoint).getNumberOfRobots() + 1, point, epoint)); + + } + } + } + } + } + } + + this.saveTemporalCommands(commands); + + for (TrianglePoint point : myVeinPoints) { + if (game.getField().getVein(point).getRobotRank() == 1) { + if (game.getMyPlayer().getMaterial(Material.Gas) > 200 + && game.getMyPlayer().getMaterial(Material.Stone) > 200) { + commands.add(Commands.upgradeRobot(point)); + break; + } + } else { + if (game.getMyPlayer().getMaterial(Material.Stone) > 300 + && game.getMyPlayer().getMaterial(Material.Metal) > 500) { + commands.add(Commands.upgradeRobot(point)); + break; + } + } + } + + return commands; + } + +} diff --git a/src/main/java/net/javachallenge/players/others/TN821Player.java b/src/main/java/net/javachallenge/players/others/TN821Player.java new file mode 100644 index 0000000..42e4050 --- /dev/null +++ b/src/main/java/net/javachallenge/players/others/TN821Player.java @@ -0,0 +1,117 @@ +package net.javachallenge.players.others; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Game; +import net.javachallenge.api.Make; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class TN821Player extends ComputerPlayer { + @Override + public String getName() { + return "TN821"; + } + + @Override + public TrianglePoint selectVein(Game game) { + return Make.point(0, 0); + } + + @Override + public List selectActions(Game game) { + return do1(game); + } + + private List do1(Game game) { + + List commands = new ArrayList(); + + Random rnd = new Random(100); + TriTriDistance TTD = new TriTriDistance(game); + int myId = game.getMyPlayer().getId(); + Map veinMap = game.getField().getVeinMap(); + for (TrianglePoint myTp : game.getField().getVeinMap(myId).keySet()) { + TrianglePoint targetTp = null; + int minDist = Integer.MAX_VALUE; + Vein myVein = veinMap.get(myTp); + for (TrianglePoint tp : game.getField().getVeinMap().keySet()) { + if (veinMap.get(tp).getOwnerId() == myId) continue; + if (veinMap.get(tp).getOwnerId() != -1 && rnd.nextInt() < 10) continue; + if (minDist > TTD.getDistance(myTp, tp)) { + targetTp = tp; + minDist = TTD.getDistance(myTp, tp); + } + } + if (targetTp != null) { + int RobotNum = myVein.getNumberOfRobots(); + int x = 20; + while (RobotNum > x) { + commands.add(Commands.launch(x, myTp, targetTp)); + RobotNum -= x; + } + } else { + commands.add(Commands.upgradeMaterial(myTp)); + commands.add(Commands.upgradeRobot(myTp)); + } + } + + return commands; + } + + + + public static int getDistance(TrianglePoint t1, TrianglePoint t2) { + return Math.abs(t1.getX() - t2.getX()) + Math.abs(t1.getY() - t2.getY()); + } + + + class TriTriDistance { + private Game game; + private Map> triTriDistanceMap; + + public TriTriDistance(Game game) { + this.game = game; + this.triTriDistanceMap = getTriTriMap(); + } + + /** + * Map> trianglePoint1=departure trianglePoint2=destination + * int=distance + */ + private Map> getTriTriMap() { + // Store the locations of all veins. + // Vein locations are fixed after starting the game. + Map veinMap = game.getField().getVeinMap(); + Set triPoints = veinMap.keySet(); + + Map> map = + new HashMap>(); + for (TrianglePoint t1 : triPoints) { + HashMap subMap = new HashMap(); + for (TrianglePoint t2 : triPoints) { + int distance = TN821Player.getDistance(t1, t2); + subMap.put(t2, distance); + } + map.put(t1, subMap); + } + + return map; + + } + + public int getDistance(TrianglePoint source, TrianglePoint target) { + return triTriDistanceMap.get(source).get(target); + } + + } + +} diff --git a/src/main/java/net/javachallenge/players/others/Tokoharu.java b/src/main/java/net/javachallenge/players/others/Tokoharu.java new file mode 100644 index 0000000..cdbae80 --- /dev/null +++ b/src/main/java/net/javachallenge/players/others/Tokoharu.java @@ -0,0 +1,505 @@ +package net.javachallenge.players.others; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.javachallenge.api.ComputerPlayer; +import net.javachallenge.api.Field; +import net.javachallenge.api.Game; +import net.javachallenge.api.Make; +import net.javachallenge.api.Material; +import net.javachallenge.api.Player; +import net.javachallenge.api.Squad; +import net.javachallenge.api.TrianglePoint; +import net.javachallenge.api.Vein; +import net.javachallenge.api.command.Command; +import net.javachallenge.api.command.Commands; + +public class Tokoharu extends ComputerPlayer { + @Override + public String getName() { + return "tokoharuAI"; + } + + @Override + public TrianglePoint selectVein(Game game) { + Field f = game.getField(); + Player me = game.getMyPlayer(); + Map veinMap = f.getVeinMap(); + + ArrayList veins = new ArrayList(); + + for (int i = -20; i < 20; i++) + for (int j = -20; j <= 20; j++) { + TrianglePoint tp = Make.point(i, j); + if (veinMap.containsKey(tp)) veins.add(veinMap.get(tp)); + } + + TrianglePoint besttp = Make.point(0, 0); + double bestscore = 0; + for (int i = 0; i < veins.size(); i++) { + + Vein myVein = veins.get(i); + + TrianglePoint startLocation = myVein.getLocation(); + if (myVein.getOwnerId() != -1) continue; + if (myVein.getOwnerId() == me.getId()) continue; + + double div = 1; + + double score = 0; + for (int j = 0; j < veins.size(); j++) + if (i != j) { + Vein targetVein = veins.get(j); + TrianglePoint toLocation = targetVein.getLocation(); + + int ownerId = targetVein.getOwnerId(); + int shortestPathLength = startLocation.getShortestPath(toLocation).size(); + + double baseScore = (double) 1 / shortestPathLength; + baseScore *= baseScore; + + if (ownerId != -1) + score += baseScore; + else if (ownerId == me.getId()) + div = shortestPathLength; + else + score += baseScore * 5; + + } + + double hoge = + (double) myVein.getInitialMaterialProductivity() + myVein.getInitialRobotProductivity(); + score *= hoge / div; + if (score > bestscore) { + bestscore = score; + besttp = startLocation; + } + + } + // System.err.println(bestscore); + + return besttp; + } + + /* + * private ArrayList generateList(Map veinsMy) { ArrayList ret = + * new ArrayList(); for(Entry pointAndVein : veinsMy.entrySet() ) { + * + * // TrianglePoint p = pointAndVein.getKey(); Vein vein = pointAndVein.getValue(); ret.add(vein); + * } return ret; + * + * } + */ + + private void sortingDistanceToVeins(int[] nums, int iNo, List veinsNoOwner, + List veinsMy) { + TrianglePoint fromLocation = veinsNoOwner.get(iNo).getLocation(); + + for (int iMy = 0; iMy < veinsMy.size(); iMy++) + for (int jMy = 0; jMy + 1 < veinsMy.size(); jMy++) { + TrianglePoint ALocation = veinsMy.get(nums[jMy]).getLocation(); + TrianglePoint BLocation = veinsMy.get(nums[jMy + 1]).getLocation(); + + int a = fromLocation.getShortestPath(ALocation).size(); + int b = fromLocation.getShortestPath(BLocation).size(); + + if (a > b) { + int tmp = nums[jMy]; + nums[jMy] = nums[jMy + 1]; + nums[jMy + 1] = tmp; + } + } + + // System.out.println(); + + } + + private List genNoOneGoThere(int myId, List veinsNoOwner, List veinsMy, + int[] nokoriRobot, List squads) { + + List ret = new ArrayList(); + + while (true) { + + int minTime = 1000; + int bestiNo = 1000; + for (int iNo = 0; iNo < veinsNoOwner.size(); iNo++) { + int require = veinsNoOwner.get(iNo).getNumberOfRobots(); + + int myTotalSquad = 0; + for (int iSq = 0; iSq < squads.size(); iSq++) { + Squad thisSquad = squads.get(iSq); + if (thisSquad.getOwnerId() == myId) { + if (thisSquad.getDestinationLocation().equals(veinsNoOwner.get(iNo).getLocation())) { + myTotalSquad += thisSquad.getRobot(); + } + } + } + require = Math.max(-1, require - myTotalSquad); + + int nums[] = new int[veinsMy.size()]; + for (int iMy = 0; iMy < veinsMy.size(); iMy++) + nums[iMy] = iMy; + sortingDistanceToVeins(nums, iNo, veinsNoOwner, veinsMy); + + int now = 10000; + if (require > 0) for (int iMy = 0; iMy < veinsMy.size(); iMy++) + if (nokoriRobot[nums[iMy]] > 0) { + require -= nokoriRobot[nums[iMy]]; + if (require < 0) { + // now = iMy; + now = + veinsNoOwner.get(iNo).getLocation() + .getShortestPath(veinsMy.get(nums[iMy]).getLocation()).size(); + + if (now > 7) now = 10000; + break; + } + } + + if (now < minTime) { + minTime = now; + bestiNo = iNo; + } + } + + boolean update = false; + if (minTime < 300) { + update = true; + + int nums[] = new int[veinsMy.size()]; + for (int i = 0; i < veinsMy.size(); i++) + nums[i] = i; + sortingDistanceToVeins(nums, bestiNo, veinsNoOwner, veinsMy); + + int require = veinsNoOwner.get(bestiNo).getNumberOfRobots(); + // System.out.println("GO "+ bestiNo + " "+require); + for (int iMy = 0; iMy < veinsMy.size(); iMy++) { + int iter = nums[iMy]; + + int used = nokoriRobot[iter]; + require -= used; + nokoriRobot[nums[iMy]] = 0; + if (require < 0) used += require + 1; + nokoriRobot[nums[iMy]] -= used; + ret.add(Commands.launch(used, veinsMy.get(nums[iMy]).getLocation(), + veinsNoOwner.get(bestiNo).getLocation())); + if (require < 0) break; + + // System.out.println(iter+" "+bestiNo); + + } + // System.out.println("END"); + } + if (!update) break; + } + + return ret; + + } + + private List genAssistCommand(int[] veinsRobot, List veins, int myID, + List squads) { + int maxDist = 7; + ArrayList ret = new ArrayList(); + int veinCnt = veins.size(); + int[] nokoriRobot = veinsRobot; + + for (int i = 0; i < squads.size(); i++) { + Squad thisSquad = squads.get(i); + if (thisSquad.getOwnerId() == myID) { + for (int j = 0; j < veinCnt; j++) + if (thisSquad.getDestinationLocation().equals(veins.get(j).getLocation())) { + nokoriRobot[j] += thisSquad.getRobot(); + } + } + } + + for (int iFrom = 0; iFrom < veinCnt; iFrom++) + if (nokoriRobot[iFrom] > 0) { + for (int iTo = 0; iTo < veinCnt; iTo++) + if (iFrom != iTo) { + Vein veinFrom = veins.get(iFrom); + Vein veinTo = veins.get(iTo); + int robotFrom = nokoriRobot[iFrom]; + int robotTo = nokoriRobot[iTo]; + int sa = (robotFrom - robotTo) / 2; + if (sa > 10) { + int dist = veinFrom.getLocation().getShortestPath(veinTo.getLocation()).size(); + if (maxDist > dist) { + ret.add(Commands.launch(sa, veinFrom.getLocation(), veinTo.getLocation())); + nokoriRobot[iFrom] -= sa; + nokoriRobot[iTo] += sa; + + } + } + } + } + return ret; + } + + private List genIkamusume(Game game, List veinsMy, List veinsEnemy, + int enemyId, int[] nokoriRobot, int myId, List squads) { + List ret = new ArrayList(); + int cnt = 0; + + while (true) { + cnt++; + if (cnt > 3) break; + boolean update = false; + for (int iEn = 0; iEn < veinsEnemy.size(); iEn++) { + // System.out.println(iEn+" "+veinsEnemy.size()); + Vein thisVein = veinsEnemy.get(iEn); + + int suff = minimumSufficientRobots(thisVein, veinsMy, false); + int real = thisVein.getNumberOfRobots(); + if (suff + 5 > real) { + update = true; + int require = suff + 5; + int nums[] = new int[veinsMy.size()]; + for (int i = 0; i < veinsMy.size(); i++) + nums[i] = i; + sortingDistanceToVeins(nums, iEn, veinsEnemy, veinsMy); + + for (int iMy = 0; iMy < veinsMy.size(); iMy++) + if (nokoriRobot[nums[iMy]] > 0) { + int iter = nums[iMy]; + + int used = nokoriRobot[iter]; + require -= used; + nokoriRobot[nums[iMy]] = 0; + if (require < 0) used += require + 1; + nokoriRobot[nums[iMy]] -= used; + ret.add(Commands.launch(used, veinsMy.get(nums[iMy]).getLocation(), + veinsEnemy.get(iEn).getLocation())); + if (require < 0) break; + } + } + } + if (!update) break; + } + return ret; + + } + + private int minimumSufficientRobots(Vein myVein, List aPlayerVein, boolean noceil) { + int maxDist = 7; + if (noceil) maxDist = 30; + + TrianglePoint myVeinTp = myVein.getLocation(); + + int[] distRobotSequence = new int[maxDist]; + for (int i = 0; i < maxDist; i++) + distRobotSequence[i] = 0; + + for (int i = 0; i < aPlayerVein.size(); i++) { + Vein aVein = aPlayerVein.get(i); + int robots = aVein.getNumberOfRobots(); + int dist = myVeinTp.getShortestPath(aVein.getLocation()).size(); + + if (dist < maxDist) { + distRobotSequence[dist] += robots; + } + } + + int[] distRobotSum = new int[distRobotSequence.length]; + distRobotSum[0] = 0; + for (int i = 1; i < maxDist; i++) + distRobotSum[i] = distRobotSum[i - 1] + distRobotSequence[i]; + + int retval = 0; + int myRobotProduct = myVein.getCurrentRobotProductivity(); + for (int i = 1; i < maxDist; i++) { + boolean ok = true; + int nowCntRobot = distRobotSequence[i]; + for (int j = i - 1; j >= 0; j--) { + nowCntRobot -= myRobotProduct; + if (nowCntRobot < distRobotSequence[j]) ok = false; + } + if (ok) retval = Math.max(retval, nowCntRobot); + } + return retval; + + } + + private int valueDamage(TrianglePoint tp, List squads) { + int ret = 0; + for (int i = 0; i < squads.size(); i++) { + Squad someSquad = squads.get(i); + TrianglePoint dest = someSquad.getDestinationLocation(); + if (tp == dest) ret += someSquad.getRobot(); + } + return ret; + } + + public List updateCommand(List veinsMy, Player myPlayer) { + List ret = new ArrayList(); + int cnt = 0; + + int myGas = myPlayer.getMaterial(Material.Gas); + int myStone = myPlayer.getMaterial(Material.Stone); + int myMetal = myPlayer.getMaterial(Material.Metal); + while (true) { + if (cnt > 5) break; + cnt++; + boolean update = false; + + if (cnt > 1 || myStone <= myMetal) if (myGas >= 100 && myStone >= 200) { + update = true; + myGas -= 100; + myStone -= 200; + + int bestVein = 0; + int bestScore = -50; + for (int i = 0; i < veinsMy.size(); i++) { + Vein targetVein = veinsMy.get(i); + int matLevel = targetVein.getMaterialRank(); + int matNum = targetVein.getCurrentMaterialProductivity(); + + if (matLevel != 1) continue; + + int score = matNum; + + if (bestScore < score) { + bestScore = score; + bestVein = i; + } + + } + if (bestScore > 0) ret.add(Commands.upgradeMaterial(veinsMy.get(bestVein).getLocation())); + } + + else if (cnt > 1 || myStone >= myMetal) if (myGas >= 100 && myMetal >= 200) { + update = true; + myGas -= 100; + myMetal -= 200; + + int bestVein = 0; + int bestScore = -50; + for (int i = 0; i < veinsMy.size(); i++) { + Vein targetVein = veinsMy.get(i); + int robLevel = targetVein.getRobotRank(); + int robNum = targetVein.getCurrentRobotProductivity(); + + if (robLevel != 1) continue; + + int score = robNum; + + if (bestScore < score) { + bestScore = score; + bestVein = i; + } + + } + if (bestScore > 0) ret.add(Commands.upgradeRobot(veinsMy.get(bestVein).getLocation())); + } + + else if (cnt > 1 || myStone <= myMetal) if (myGas >= 200 && myStone >= 400) { + + update = true; + myGas -= 200; + myStone -= 400; + + int bestVein = 0; + int bestScore = -50; + for (int i = 0; i < veinsMy.size(); i++) { + Vein targetVein = veinsMy.get(i); + int matLevel = targetVein.getMaterialRank(); + int matNum = targetVein.getCurrentMaterialProductivity(); + + if (matLevel != 2) continue; + + int score = matNum; + + if (bestScore < score) { + bestScore = score; + bestVein = i; + } + + } + if (bestScore > 0) ret.add(Commands.upgradeMaterial(veinsMy.get(bestVein).getLocation())); + } + + else if (cnt > 1 || myStone >= myMetal) if (myGas >= 200 && myMetal >= 400) { + update = true; + int bestVein = 0; + int bestScore = -50; + for (int i = 0; i < veinsMy.size(); i++) { + Vein targetVein = veinsMy.get(i); + int robLevel = targetVein.getRobotRank(); + int robNum = targetVein.getCurrentRobotProductivity(); + + if (robLevel != 2) continue; + + int score = robNum; + + if (bestScore < score) { + bestScore = score; + bestVein = i; + } + + } + if (bestScore > 0) ret.add(Commands.upgradeRobot(veinsMy.get(bestVein).getLocation())); + } + + if (!update) break; + } + + return ret; + } + + @Override + public List selectActions(Game game) { + List commands = new ArrayList(); + + List squads = game.getField().getSquads(); + + int myId = game.getMyPlayer().getId(); + + List veinsMy = game.getField().getVeins(myId); + List veinsNoOwner = game.getField().getVeins(-1); + + int[] nokoriRobot = new int[veinsMy.size()]; + for (int i = 0; i < veinsMy.size(); i++) { + Vein thisVein = veinsMy.get(i); + + int maximumSufficient = 0; + for (int id = 0; id < game.getPlayerCount(); id++) + if (id != myId) + maximumSufficient = + Math.max(maximumSufficient, + minimumSufficientRobots(thisVein, game.getField().getVeins(id), false)); + + nokoriRobot[i] = thisVein.getNumberOfRobots() - maximumSufficient; + nokoriRobot[i] = nokoriRobot[i] - valueDamage(thisVein.getLocation(), squads); + } + + List now = genNoOneGoThere(myId, veinsNoOwner, veinsMy, nokoriRobot, squads); + for (int i = 0; i < now.size(); i++) + commands.add(now.get(i)); + + List assistance = genAssistCommand(nokoriRobot, veinsMy, myId, squads); + for (int i = 0; i < assistance.size(); i++) + commands.add(assistance.get(i)); + + List update = updateCommand(veinsMy, game.getMyPlayer()); + for (int i = 0; i < update.size(); i++) + commands.add(update.get(i)); + + for (int id = 0; id < game.getPlayerCount(); id++) + if (id != myId) { + List ika = + genIkamusume(game, veinsMy, game.getField().getVeins(id), id, nokoriRobot, myId, squads); + for (int j = 0; j < ika.size(); j++) + commands.add(ika.get(j)); + } + + this.saveTemporalCommands(commands); + + commands.add(Commands.buyFromAlienTrade(Material.Stone, 1)); + + return commands; + } +} diff --git a/src/main/java/net/javachallenge/players/others/Wand.java b/src/main/java/net/javachallenge/players/others/Wand.java new file mode 100644 index 0000000..2b316c9 --- /dev/null +++ b/src/main/java/net/javachallenge/players/others/Wand.java @@ -0,0 +1,208 @@ +package net.javachallenge.players.others; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.javachallenge.api.*; +import net.javachallenge.api.command.*; + +public class Wand extends ComputerPlayer { + + static final int NEUTRAL_OWNER_ID = -1; + + @Override + public String getName() { + return "Wand Player"; + } + + @Override + public TrianglePoint selectVein(Game game) { + // Store your temporal selection. When your program exceeds the time + // limit (10 second), + // this selection are accepted. + this.saveTemporalVeinLocation(Make.point(0, 0)); + + Field field = game.getField(); + List allVeins = field.getVeins(); + Vein selectVein = allVeins.get(0); + + Map veinScores = new HashMap(); + + for (Vein vein : allVeins) { + if (vein.getOwnerId() != NEUTRAL_OWNER_ID) { + veinScores.put(vein, Integer.MIN_VALUE); + continue; + } + Integer score = + (vein.getCurrentRobotProductivity() + vein.getCurrentMaterialProductivity()) * 25 + + vein.getNumberOfRobots(); + veinScores.put(vein, score); + } + + Integer maxScore = Integer.MIN_VALUE; + for (Vein vein : allVeins) { + Integer veinScore = veinScores.get(vein); + if (veinScore > maxScore) { + selectVein = vein; + maxScore = veinScore; + } + } + + return selectVein.getLocation(); + + } + + @Override + public List selectActions(Game game) { + List commands = new ArrayList(); + + Field field = game.getField(); + Player myPlayer = game.getMyPlayer(); + // AlienTrade alienTrade = game.getAlienTrade(); + + List veinList = field.getVeins(); + List myVeinList = field.getVeins(game.getMyPlayer().getId()); + List copiedVeinList = new ArrayList(); + List copiedMyVeinList = new ArrayList(); + + for (Vein vein : veinList) { + copiedVeinList.add(vein); + } + + for (Vein vein : myVeinList) { + copiedMyVeinList.add(vein); + } + + // Launch + for (Vein myVein : myVeinList) { + Collections.sort(copiedVeinList, new VeinComparator(VeinComparator.DISTANCE, myVein)); + Vein to = copiedVeinList.get(0); + + if (myVein.getNumberOfRobots() > 100) { + commands.add(Commands.launch(myVein.getNumberOfRobots() - 50, myVein.getLocation(), + to.getLocation())); + } + + } + + // Upgrade + // Robot upgrade + Collections.sort(copiedMyVeinList, new VeinComparator(VeinComparator.ROBOT_PRODUCTIVITY)); + for (Vein myVein : copiedMyVeinList) { + if (myVein.getRobotRank() >= 3) { + continue; + } + boolean flag = true; + for (Material material : Material.values()) { + int materialAmount = myPlayer.getMaterial(material); + if ((myVein.getRobotRank() == 1 && game.getSetting() + .getMaterialsForUpgradingRobotRankFrom1To2(material) > materialAmount) + || (myVein.getRobotRank() == 2 && game.getSetting() + .getMaterialsForUpgradingRobotRankFrom2To3(material) > materialAmount)) { + flag = false; + } + } + if (flag) { + commands.add(Commands.upgradeRobot(myVein)); + break; + } + } + + // Material Upgrade + Collections.sort(copiedMyVeinList, new VeinComparator(VeinComparator.MATERIAL_PRODUCTIVITY)); + for (Vein myVein : copiedMyVeinList) { + if (myVein.getMaterialRank() >= 3) { + continue; + } + boolean flag = true; + for (Material material : Material.values()) { + int materialAmount = myPlayer.getMaterial(material); + if ((myVein.getRobotRank() == 1 && game.getSetting() + .getMaterialsForUpgradingMaterialRankFrom1To2(material) > materialAmount) + || (myVein.getRobotRank() == 2 && game.getSetting() + .getMaterialsForUpgradingMaterialRankFrom2To3(material) > materialAmount)) { + flag = false; + } + } + if (flag) { + commands.add(Commands.upgradeMaterial(myVein)); + } + } + + // Trade + for (Material material : Material.values()) { + int amount = game.getMyPlayer().getMaterial(material); + if (amount > 1000) { + commands.add(Commands.sellToAlienTrade(material, amount - 500)); + } + if (amount < 200) { + commands.add(Commands.buyFromAlienTrade(material, + Math.min(200, myPlayer.getMoney() / game.getAlienTrade().getBuyPriceOf(material)))); + } + } + + return commands; + } + + class VeinComparator implements Comparator { + + public static final int DISTANCE = 0; + public static final int MATERIAL_PRODUCTIVITY = 1; + public static final int ROBOT_PRODUCTIVITY = 2; + public static final int AIM_SCORE = 3; + + Vein originVein; + int type; + + VeinComparator(int type, Vein originVein) { + this.type = type; + this.originVein = originVein; + } + + VeinComparator(int type) { + this.type = type; + } + + @Override + public int compare(Vein o1, Vein o2) { + switch (type) { + case DISTANCE: + return compareDistance(o1, o2); + case MATERIAL_PRODUCTIVITY: + return compareMaterialProductivity(o1, o2); + case ROBOT_PRODUCTIVITY: + return compareRobotProductivity(o1, o2); + case AIM_SCORE: + return compareAimScore(o1, o2); + default: + return 0; + } + } + + public int compareDistance(Vein o1, Vein o2) { + if (o1.equals(originVein)) return 1; + if (o2.equals(originVein)) return -1; + + if (o1.getOwnerId() == originVein.getOwnerId()) return 1; + if (o2.getOwnerId() == originVein.getOwnerId()) return -1; + return originVein.getDistance(o1) - originVein.getDistance(o2); + } + + public int compareAimScore(Vein o1, Vein o2) { + return 0; + } + + public int compareMaterialProductivity(Vein o1, Vein o2) { + return (o1.getCurrentMaterialProductivity() - o2.getCurrentMaterialProductivity()) * -1; + } + + public int compareRobotProductivity(Vein o1, Vein o2) { + return (o1.getCurrentMaterialProductivity() - o2.getCurrentMaterialProductivity()) * -1; + } + + } +} diff --git a/src/main/resources/img/bank.png b/src/main/resources/img/bank.png new file mode 100644 index 0000000..c458993 Binary files /dev/null and b/src/main/resources/img/bank.png differ diff --git a/src/main/resources/img/bar0.png b/src/main/resources/img/bar0.png new file mode 100644 index 0000000..e58c966 Binary files /dev/null and b/src/main/resources/img/bar0.png differ diff --git a/src/main/resources/img/bar1.png b/src/main/resources/img/bar1.png new file mode 100644 index 0000000..9f4b964 Binary files /dev/null and b/src/main/resources/img/bar1.png differ diff --git a/src/main/resources/img/bar2.png b/src/main/resources/img/bar2.png new file mode 100644 index 0000000..2280748 Binary files /dev/null and b/src/main/resources/img/bar2.png differ diff --git a/src/main/resources/img/bar3.png b/src/main/resources/img/bar3.png new file mode 100644 index 0000000..bc64905 Binary files /dev/null and b/src/main/resources/img/bar3.png differ diff --git a/src/main/resources/img/bar4.png b/src/main/resources/img/bar4.png new file mode 100644 index 0000000..04b87f5 Binary files /dev/null and b/src/main/resources/img/bar4.png differ diff --git a/src/main/resources/img/bar5.png b/src/main/resources/img/bar5.png new file mode 100644 index 0000000..d9e40ae Binary files /dev/null and b/src/main/resources/img/bar5.png differ diff --git a/src/main/resources/img/hex32.png b/src/main/resources/img/hex32.png new file mode 100644 index 0000000..f138069 Binary files /dev/null and b/src/main/resources/img/hex32.png differ diff --git a/src/main/resources/img/hex48.png b/src/main/resources/img/hex48.png new file mode 100644 index 0000000..6f5f5fd Binary files /dev/null and b/src/main/resources/img/hex48.png differ diff --git a/src/main/resources/img/map32.png b/src/main/resources/img/map32.png new file mode 100644 index 0000000..c632ba1 Binary files /dev/null and b/src/main/resources/img/map32.png differ diff --git a/src/main/resources/img/map48.png b/src/main/resources/img/map48.png new file mode 100644 index 0000000..1891be6 Binary files /dev/null and b/src/main/resources/img/map48.png differ diff --git a/src/main/resources/img/mat01.png b/src/main/resources/img/mat01.png new file mode 100644 index 0000000..027a6c6 Binary files /dev/null and b/src/main/resources/img/mat01.png differ diff --git a/src/main/resources/img/mat02.png b/src/main/resources/img/mat02.png new file mode 100644 index 0000000..8285d51 Binary files /dev/null and b/src/main/resources/img/mat02.png differ diff --git a/src/main/resources/img/mat03.png b/src/main/resources/img/mat03.png new file mode 100644 index 0000000..e62318d Binary files /dev/null and b/src/main/resources/img/mat03.png differ diff --git a/src/main/resources/img/mat04.png b/src/main/resources/img/mat04.png new file mode 100644 index 0000000..9b670d8 Binary files /dev/null and b/src/main/resources/img/mat04.png differ diff --git a/src/main/resources/img/mine0.png b/src/main/resources/img/mine0.png new file mode 100644 index 0000000..7322c85 Binary files /dev/null and b/src/main/resources/img/mine0.png differ diff --git a/src/main/resources/img/mine1.png b/src/main/resources/img/mine1.png new file mode 100644 index 0000000..63e6e63 Binary files /dev/null and b/src/main/resources/img/mine1.png differ diff --git a/src/main/resources/img/mine2.png b/src/main/resources/img/mine2.png new file mode 100644 index 0000000..b9bdb30 Binary files /dev/null and b/src/main/resources/img/mine2.png differ diff --git a/src/main/resources/img/mine3.png b/src/main/resources/img/mine3.png new file mode 100644 index 0000000..97e08e4 Binary files /dev/null and b/src/main/resources/img/mine3.png differ diff --git a/src/main/resources/img/mine4.png b/src/main/resources/img/mine4.png new file mode 100644 index 0000000..f4962be Binary files /dev/null and b/src/main/resources/img/mine4.png differ diff --git a/src/main/resources/img/mine5.png b/src/main/resources/img/mine5.png new file mode 100644 index 0000000..3d1d30c Binary files /dev/null and b/src/main/resources/img/mine5.png differ diff --git a/src/main/resources/img/number/num-10.png b/src/main/resources/img/number/num-10.png new file mode 100644 index 0000000..b9ccebb Binary files /dev/null and b/src/main/resources/img/number/num-10.png differ diff --git a/src/main/resources/img/number/num-11.png b/src/main/resources/img/number/num-11.png new file mode 100644 index 0000000..c5ccdfa Binary files /dev/null and b/src/main/resources/img/number/num-11.png differ diff --git a/src/main/resources/img/number/num-12.png b/src/main/resources/img/number/num-12.png new file mode 100644 index 0000000..a015220 Binary files /dev/null and b/src/main/resources/img/number/num-12.png differ diff --git a/src/main/resources/img/number/num-13.png b/src/main/resources/img/number/num-13.png new file mode 100644 index 0000000..8f934b1 Binary files /dev/null and b/src/main/resources/img/number/num-13.png differ diff --git a/src/main/resources/img/number/num-14.png b/src/main/resources/img/number/num-14.png new file mode 100644 index 0000000..79922f4 Binary files /dev/null and b/src/main/resources/img/number/num-14.png differ diff --git a/src/main/resources/img/number/num-15.png b/src/main/resources/img/number/num-15.png new file mode 100644 index 0000000..344878e Binary files /dev/null and b/src/main/resources/img/number/num-15.png differ diff --git a/src/main/resources/img/number/num-16.png b/src/main/resources/img/number/num-16.png new file mode 100644 index 0000000..8cde9e5 Binary files /dev/null and b/src/main/resources/img/number/num-16.png differ diff --git a/src/main/resources/img/number/num-17.png b/src/main/resources/img/number/num-17.png new file mode 100644 index 0000000..74bb28c Binary files /dev/null and b/src/main/resources/img/number/num-17.png differ diff --git a/src/main/resources/img/number/num-18.png b/src/main/resources/img/number/num-18.png new file mode 100644 index 0000000..99477ef Binary files /dev/null and b/src/main/resources/img/number/num-18.png differ diff --git a/src/main/resources/img/number/num-19.png b/src/main/resources/img/number/num-19.png new file mode 100644 index 0000000..8a9c714 Binary files /dev/null and b/src/main/resources/img/number/num-19.png differ diff --git a/src/main/resources/img/number/num00.png b/src/main/resources/img/number/num00.png new file mode 100644 index 0000000..81b602a Binary files /dev/null and b/src/main/resources/img/number/num00.png differ diff --git a/src/main/resources/img/number/num01.png b/src/main/resources/img/number/num01.png new file mode 100644 index 0000000..2f2ec7a Binary files /dev/null and b/src/main/resources/img/number/num01.png differ diff --git a/src/main/resources/img/number/num02.png b/src/main/resources/img/number/num02.png new file mode 100644 index 0000000..43c9650 Binary files /dev/null and b/src/main/resources/img/number/num02.png differ diff --git a/src/main/resources/img/number/num03.png b/src/main/resources/img/number/num03.png new file mode 100644 index 0000000..2f038df Binary files /dev/null and b/src/main/resources/img/number/num03.png differ diff --git a/src/main/resources/img/number/num04.png b/src/main/resources/img/number/num04.png new file mode 100644 index 0000000..87a7bc4 Binary files /dev/null and b/src/main/resources/img/number/num04.png differ diff --git a/src/main/resources/img/number/num05.png b/src/main/resources/img/number/num05.png new file mode 100644 index 0000000..40fd834 Binary files /dev/null and b/src/main/resources/img/number/num05.png differ diff --git a/src/main/resources/img/number/num06.png b/src/main/resources/img/number/num06.png new file mode 100644 index 0000000..ce5751f Binary files /dev/null and b/src/main/resources/img/number/num06.png differ diff --git a/src/main/resources/img/number/num07.png b/src/main/resources/img/number/num07.png new file mode 100644 index 0000000..1831602 Binary files /dev/null and b/src/main/resources/img/number/num07.png differ diff --git a/src/main/resources/img/number/num08.png b/src/main/resources/img/number/num08.png new file mode 100644 index 0000000..6626cc0 Binary files /dev/null and b/src/main/resources/img/number/num08.png differ diff --git a/src/main/resources/img/number/num09.png b/src/main/resources/img/number/num09.png new file mode 100644 index 0000000..0ee4e90 Binary files /dev/null and b/src/main/resources/img/number/num09.png differ diff --git a/src/main/resources/img/number/num10.png b/src/main/resources/img/number/num10.png new file mode 100644 index 0000000..92fa4bb Binary files /dev/null and b/src/main/resources/img/number/num10.png differ diff --git a/src/main/resources/img/number/num11.png b/src/main/resources/img/number/num11.png new file mode 100644 index 0000000..9e6570a Binary files /dev/null and b/src/main/resources/img/number/num11.png differ diff --git a/src/main/resources/img/number/num12.png b/src/main/resources/img/number/num12.png new file mode 100644 index 0000000..afb9b43 Binary files /dev/null and b/src/main/resources/img/number/num12.png differ diff --git a/src/main/resources/img/number/num13.png b/src/main/resources/img/number/num13.png new file mode 100644 index 0000000..143fb7d Binary files /dev/null and b/src/main/resources/img/number/num13.png differ diff --git a/src/main/resources/img/number/num14.png b/src/main/resources/img/number/num14.png new file mode 100644 index 0000000..da1ca4d Binary files /dev/null and b/src/main/resources/img/number/num14.png differ diff --git a/src/main/resources/img/number/num15.png b/src/main/resources/img/number/num15.png new file mode 100644 index 0000000..a6984c3 Binary files /dev/null and b/src/main/resources/img/number/num15.png differ diff --git a/src/main/resources/img/number/num16.png b/src/main/resources/img/number/num16.png new file mode 100644 index 0000000..2d15cf8 Binary files /dev/null and b/src/main/resources/img/number/num16.png differ diff --git a/src/main/resources/img/number/num17.png b/src/main/resources/img/number/num17.png new file mode 100644 index 0000000..24cb7fe Binary files /dev/null and b/src/main/resources/img/number/num17.png differ diff --git a/src/main/resources/img/number/num18.png b/src/main/resources/img/number/num18.png new file mode 100644 index 0000000..bf10ed4 Binary files /dev/null and b/src/main/resources/img/number/num18.png differ diff --git a/src/main/resources/img/number/num19.png b/src/main/resources/img/number/num19.png new file mode 100644 index 0000000..077d18d Binary files /dev/null and b/src/main/resources/img/number/num19.png differ diff --git a/src/main/resources/img/number/num20.png b/src/main/resources/img/number/num20.png new file mode 100644 index 0000000..5bc84d4 Binary files /dev/null and b/src/main/resources/img/number/num20.png differ diff --git a/src/main/resources/img/number/num21.png b/src/main/resources/img/number/num21.png new file mode 100644 index 0000000..56df438 Binary files /dev/null and b/src/main/resources/img/number/num21.png differ diff --git a/src/main/resources/img/number/num22.png b/src/main/resources/img/number/num22.png new file mode 100644 index 0000000..6df6acc Binary files /dev/null and b/src/main/resources/img/number/num22.png differ diff --git a/src/main/resources/img/number/num23.png b/src/main/resources/img/number/num23.png new file mode 100644 index 0000000..a6784a4 Binary files /dev/null and b/src/main/resources/img/number/num23.png differ diff --git a/src/main/resources/img/number/num24.png b/src/main/resources/img/number/num24.png new file mode 100644 index 0000000..a39fd28 Binary files /dev/null and b/src/main/resources/img/number/num24.png differ diff --git a/src/main/resources/img/number/num25.png b/src/main/resources/img/number/num25.png new file mode 100644 index 0000000..d467a97 Binary files /dev/null and b/src/main/resources/img/number/num25.png differ diff --git a/src/main/resources/img/number/num26.png b/src/main/resources/img/number/num26.png new file mode 100644 index 0000000..0f5dd82 Binary files /dev/null and b/src/main/resources/img/number/num26.png differ diff --git a/src/main/resources/img/number/num27.png b/src/main/resources/img/number/num27.png new file mode 100644 index 0000000..bc683a2 Binary files /dev/null and b/src/main/resources/img/number/num27.png differ diff --git a/src/main/resources/img/number/num28.png b/src/main/resources/img/number/num28.png new file mode 100644 index 0000000..81c100b Binary files /dev/null and b/src/main/resources/img/number/num28.png differ diff --git a/src/main/resources/img/number/num29.png b/src/main/resources/img/number/num29.png new file mode 100644 index 0000000..862e919 Binary files /dev/null and b/src/main/resources/img/number/num29.png differ diff --git a/src/main/resources/img/number/num30.png b/src/main/resources/img/number/num30.png new file mode 100644 index 0000000..a8c4af4 Binary files /dev/null and b/src/main/resources/img/number/num30.png differ diff --git a/src/main/resources/img/number/num31.png b/src/main/resources/img/number/num31.png new file mode 100644 index 0000000..1101097 Binary files /dev/null and b/src/main/resources/img/number/num31.png differ diff --git a/src/main/resources/img/number/num32.png b/src/main/resources/img/number/num32.png new file mode 100644 index 0000000..ad88e9f Binary files /dev/null and b/src/main/resources/img/number/num32.png differ diff --git a/src/main/resources/img/number/num33.png b/src/main/resources/img/number/num33.png new file mode 100644 index 0000000..229a4e8 Binary files /dev/null and b/src/main/resources/img/number/num33.png differ diff --git a/src/main/resources/img/number/num34.png b/src/main/resources/img/number/num34.png new file mode 100644 index 0000000..d43298b Binary files /dev/null and b/src/main/resources/img/number/num34.png differ diff --git a/src/main/resources/img/number/num35.png b/src/main/resources/img/number/num35.png new file mode 100644 index 0000000..5d9e6d7 Binary files /dev/null and b/src/main/resources/img/number/num35.png differ diff --git a/src/main/resources/img/number/num36.png b/src/main/resources/img/number/num36.png new file mode 100644 index 0000000..b0bf5f3 Binary files /dev/null and b/src/main/resources/img/number/num36.png differ diff --git a/src/main/resources/img/number/num37.png b/src/main/resources/img/number/num37.png new file mode 100644 index 0000000..ac7d31b Binary files /dev/null and b/src/main/resources/img/number/num37.png differ diff --git a/src/main/resources/img/number/num38.png b/src/main/resources/img/number/num38.png new file mode 100644 index 0000000..6c50ea8 Binary files /dev/null and b/src/main/resources/img/number/num38.png differ diff --git a/src/main/resources/img/number/num39.png b/src/main/resources/img/number/num39.png new file mode 100644 index 0000000..629fdc6 Binary files /dev/null and b/src/main/resources/img/number/num39.png differ diff --git a/src/main/resources/img/number/num40.png b/src/main/resources/img/number/num40.png new file mode 100644 index 0000000..e2443c4 Binary files /dev/null and b/src/main/resources/img/number/num40.png differ diff --git a/src/main/resources/img/number/num41.png b/src/main/resources/img/number/num41.png new file mode 100644 index 0000000..1a140c9 Binary files /dev/null and b/src/main/resources/img/number/num41.png differ diff --git a/src/main/resources/img/number/num42.png b/src/main/resources/img/number/num42.png new file mode 100644 index 0000000..489fc61 Binary files /dev/null and b/src/main/resources/img/number/num42.png differ diff --git a/src/main/resources/img/number/num43.png b/src/main/resources/img/number/num43.png new file mode 100644 index 0000000..67a819b Binary files /dev/null and b/src/main/resources/img/number/num43.png differ diff --git a/src/main/resources/img/number/num44.png b/src/main/resources/img/number/num44.png new file mode 100644 index 0000000..61cbef0 Binary files /dev/null and b/src/main/resources/img/number/num44.png differ diff --git a/src/main/resources/img/number/num45.png b/src/main/resources/img/number/num45.png new file mode 100644 index 0000000..264322a Binary files /dev/null and b/src/main/resources/img/number/num45.png differ diff --git a/src/main/resources/img/number/num46.png b/src/main/resources/img/number/num46.png new file mode 100644 index 0000000..78d81ef Binary files /dev/null and b/src/main/resources/img/number/num46.png differ diff --git a/src/main/resources/img/number/num47.png b/src/main/resources/img/number/num47.png new file mode 100644 index 0000000..bc062ed Binary files /dev/null and b/src/main/resources/img/number/num47.png differ diff --git a/src/main/resources/img/number/num48.png b/src/main/resources/img/number/num48.png new file mode 100644 index 0000000..bf69901 Binary files /dev/null and b/src/main/resources/img/number/num48.png differ diff --git a/src/main/resources/img/number/num49.png b/src/main/resources/img/number/num49.png new file mode 100644 index 0000000..236a36b Binary files /dev/null and b/src/main/resources/img/number/num49.png differ diff --git a/src/main/resources/img/number/num50.png b/src/main/resources/img/number/num50.png new file mode 100644 index 0000000..001d228 Binary files /dev/null and b/src/main/resources/img/number/num50.png differ diff --git a/src/main/resources/img/number/num51.png b/src/main/resources/img/number/num51.png new file mode 100644 index 0000000..809e3a3 Binary files /dev/null and b/src/main/resources/img/number/num51.png differ diff --git a/src/main/resources/img/number/num52.png b/src/main/resources/img/number/num52.png new file mode 100644 index 0000000..4847a41 Binary files /dev/null and b/src/main/resources/img/number/num52.png differ diff --git a/src/main/resources/img/number/num53.png b/src/main/resources/img/number/num53.png new file mode 100644 index 0000000..1955402 Binary files /dev/null and b/src/main/resources/img/number/num53.png differ diff --git a/src/main/resources/img/number/num54.png b/src/main/resources/img/number/num54.png new file mode 100644 index 0000000..046e428 Binary files /dev/null and b/src/main/resources/img/number/num54.png differ diff --git a/src/main/resources/img/number/num55.png b/src/main/resources/img/number/num55.png new file mode 100644 index 0000000..e7d2a24 Binary files /dev/null and b/src/main/resources/img/number/num55.png differ diff --git a/src/main/resources/img/number/num56.png b/src/main/resources/img/number/num56.png new file mode 100644 index 0000000..6a199c3 Binary files /dev/null and b/src/main/resources/img/number/num56.png differ diff --git a/src/main/resources/img/number/num57.png b/src/main/resources/img/number/num57.png new file mode 100644 index 0000000..9ea718b Binary files /dev/null and b/src/main/resources/img/number/num57.png differ diff --git a/src/main/resources/img/number/num58.png b/src/main/resources/img/number/num58.png new file mode 100644 index 0000000..53a97f1 Binary files /dev/null and b/src/main/resources/img/number/num58.png differ diff --git a/src/main/resources/img/number/num59.png b/src/main/resources/img/number/num59.png new file mode 100644 index 0000000..b69dda4 Binary files /dev/null and b/src/main/resources/img/number/num59.png differ diff --git a/src/main/resources/img/pactive.png b/src/main/resources/img/pactive.png new file mode 100644 index 0000000..353de40 Binary files /dev/null and b/src/main/resources/img/pactive.png differ diff --git a/src/main/resources/img/pickel.png b/src/main/resources/img/pickel.png new file mode 100644 index 0000000..e9a5980 Binary files /dev/null and b/src/main/resources/img/pickel.png differ diff --git a/src/main/resources/img/playerInfo/buy.png b/src/main/resources/img/playerInfo/buy.png new file mode 100644 index 0000000..c22d9bb Binary files /dev/null and b/src/main/resources/img/playerInfo/buy.png differ diff --git a/src/main/resources/img/playerInfo/p0.png b/src/main/resources/img/playerInfo/p0.png new file mode 100644 index 0000000..4e08993 Binary files /dev/null and b/src/main/resources/img/playerInfo/p0.png differ diff --git a/src/main/resources/img/playerInfo/p1.png b/src/main/resources/img/playerInfo/p1.png new file mode 100644 index 0000000..c7b1052 Binary files /dev/null and b/src/main/resources/img/playerInfo/p1.png differ diff --git a/src/main/resources/img/playerInfo/p2.png b/src/main/resources/img/playerInfo/p2.png new file mode 100644 index 0000000..efb48bf Binary files /dev/null and b/src/main/resources/img/playerInfo/p2.png differ diff --git a/src/main/resources/img/playerInfo/p3.png b/src/main/resources/img/playerInfo/p3.png new file mode 100644 index 0000000..5877104 Binary files /dev/null and b/src/main/resources/img/playerInfo/p3.png differ diff --git a/src/main/resources/img/playerInfo/p4.png b/src/main/resources/img/playerInfo/p4.png new file mode 100644 index 0000000..79d42c1 Binary files /dev/null and b/src/main/resources/img/playerInfo/p4.png differ diff --git a/src/main/resources/img/playerInfo/p5.png b/src/main/resources/img/playerInfo/p5.png new file mode 100644 index 0000000..0f35566 Binary files /dev/null and b/src/main/resources/img/playerInfo/p5.png differ diff --git a/src/main/resources/img/playerInfo/sell.png b/src/main/resources/img/playerInfo/sell.png new file mode 100644 index 0000000..c849f88 Binary files /dev/null and b/src/main/resources/img/playerInfo/sell.png differ diff --git a/src/main/resources/img/plus-1.png b/src/main/resources/img/plus-1.png new file mode 100644 index 0000000..8f95db4 Binary files /dev/null and b/src/main/resources/img/plus-1.png differ diff --git a/src/main/resources/img/plus0.png b/src/main/resources/img/plus0.png new file mode 100644 index 0000000..093c71f Binary files /dev/null and b/src/main/resources/img/plus0.png differ diff --git a/src/main/resources/img/plus1.png b/src/main/resources/img/plus1.png new file mode 100644 index 0000000..aa7892c Binary files /dev/null and b/src/main/resources/img/plus1.png differ diff --git a/src/main/resources/img/plus2.png b/src/main/resources/img/plus2.png new file mode 100644 index 0000000..eff58e7 Binary files /dev/null and b/src/main/resources/img/plus2.png differ diff --git a/src/main/resources/img/plus3.png b/src/main/resources/img/plus3.png new file mode 100644 index 0000000..f99a8ec Binary files /dev/null and b/src/main/resources/img/plus3.png differ diff --git a/src/main/resources/img/plus4.png b/src/main/resources/img/plus4.png new file mode 100644 index 0000000..74c136a Binary files /dev/null and b/src/main/resources/img/plus4.png differ diff --git a/src/main/resources/img/plus5.png b/src/main/resources/img/plus5.png new file mode 100644 index 0000000..99e44a0 Binary files /dev/null and b/src/main/resources/img/plus5.png differ diff --git a/src/main/resources/img/result/48_0.png b/src/main/resources/img/result/48_0.png new file mode 100644 index 0000000..6ed10ed Binary files /dev/null and b/src/main/resources/img/result/48_0.png differ diff --git a/src/main/resources/img/result/48_1.png b/src/main/resources/img/result/48_1.png new file mode 100644 index 0000000..72e1a2e Binary files /dev/null and b/src/main/resources/img/result/48_1.png differ diff --git a/src/main/resources/img/result/48_2.png b/src/main/resources/img/result/48_2.png new file mode 100644 index 0000000..593be1d Binary files /dev/null and b/src/main/resources/img/result/48_2.png differ diff --git a/src/main/resources/img/result/48_3.png b/src/main/resources/img/result/48_3.png new file mode 100644 index 0000000..463f2ba Binary files /dev/null and b/src/main/resources/img/result/48_3.png differ diff --git a/src/main/resources/img/result/48_4.png b/src/main/resources/img/result/48_4.png new file mode 100644 index 0000000..5f98175 Binary files /dev/null and b/src/main/resources/img/result/48_4.png differ diff --git a/src/main/resources/img/result/48_5.png b/src/main/resources/img/result/48_5.png new file mode 100644 index 0000000..072fb88 Binary files /dev/null and b/src/main/resources/img/result/48_5.png differ diff --git a/src/main/resources/img/result/48_6.png b/src/main/resources/img/result/48_6.png new file mode 100644 index 0000000..9e0d6ac Binary files /dev/null and b/src/main/resources/img/result/48_6.png differ diff --git a/src/main/resources/img/result/48_7.png b/src/main/resources/img/result/48_7.png new file mode 100644 index 0000000..5d2cfc8 Binary files /dev/null and b/src/main/resources/img/result/48_7.png differ diff --git a/src/main/resources/img/result/48_8.png b/src/main/resources/img/result/48_8.png new file mode 100644 index 0000000..af36197 Binary files /dev/null and b/src/main/resources/img/result/48_8.png differ diff --git a/src/main/resources/img/result/48_9.png b/src/main/resources/img/result/48_9.png new file mode 100644 index 0000000..9bd1ba9 Binary files /dev/null and b/src/main/resources/img/result/48_9.png differ diff --git a/src/main/resources/img/result/64_0.png b/src/main/resources/img/result/64_0.png new file mode 100644 index 0000000..d91878f Binary files /dev/null and b/src/main/resources/img/result/64_0.png differ diff --git a/src/main/resources/img/result/64_1.png b/src/main/resources/img/result/64_1.png new file mode 100644 index 0000000..024caef Binary files /dev/null and b/src/main/resources/img/result/64_1.png differ diff --git a/src/main/resources/img/result/64_2.png b/src/main/resources/img/result/64_2.png new file mode 100644 index 0000000..8478688 Binary files /dev/null and b/src/main/resources/img/result/64_2.png differ diff --git a/src/main/resources/img/result/64_3.png b/src/main/resources/img/result/64_3.png new file mode 100644 index 0000000..72e270a Binary files /dev/null and b/src/main/resources/img/result/64_3.png differ diff --git a/src/main/resources/img/result/64_4.png b/src/main/resources/img/result/64_4.png new file mode 100644 index 0000000..7e6d227 Binary files /dev/null and b/src/main/resources/img/result/64_4.png differ diff --git a/src/main/resources/img/result/64_5.png b/src/main/resources/img/result/64_5.png new file mode 100644 index 0000000..f95b13d Binary files /dev/null and b/src/main/resources/img/result/64_5.png differ diff --git a/src/main/resources/img/result/64_6.png b/src/main/resources/img/result/64_6.png new file mode 100644 index 0000000..347ded2 Binary files /dev/null and b/src/main/resources/img/result/64_6.png differ diff --git a/src/main/resources/img/result/64_7.png b/src/main/resources/img/result/64_7.png new file mode 100644 index 0000000..7e0ed26 Binary files /dev/null and b/src/main/resources/img/result/64_7.png differ diff --git a/src/main/resources/img/result/64_8.png b/src/main/resources/img/result/64_8.png new file mode 100644 index 0000000..dbcaf25 Binary files /dev/null and b/src/main/resources/img/result/64_8.png differ diff --git a/src/main/resources/img/result/64_9.png b/src/main/resources/img/result/64_9.png new file mode 100644 index 0000000..66049cd Binary files /dev/null and b/src/main/resources/img/result/64_9.png differ diff --git a/src/main/resources/img/result/lamp_0.png b/src/main/resources/img/result/lamp_0.png new file mode 100644 index 0000000..9a26b72 Binary files /dev/null and b/src/main/resources/img/result/lamp_0.png differ diff --git a/src/main/resources/img/result/lamp_1.png b/src/main/resources/img/result/lamp_1.png new file mode 100644 index 0000000..b9f6b93 Binary files /dev/null and b/src/main/resources/img/result/lamp_1.png differ diff --git a/src/main/resources/img/result/lamp_2.png b/src/main/resources/img/result/lamp_2.png new file mode 100644 index 0000000..08ebd26 Binary files /dev/null and b/src/main/resources/img/result/lamp_2.png differ diff --git a/src/main/resources/img/result/lamp_3.png b/src/main/resources/img/result/lamp_3.png new file mode 100644 index 0000000..0903f9d Binary files /dev/null and b/src/main/resources/img/result/lamp_3.png differ diff --git a/src/main/resources/img/result/lamp_4.png b/src/main/resources/img/result/lamp_4.png new file mode 100644 index 0000000..fd7043b Binary files /dev/null and b/src/main/resources/img/result/lamp_4.png differ diff --git a/src/main/resources/img/result/lamp_5.png b/src/main/resources/img/result/lamp_5.png new file mode 100644 index 0000000..8c77a39 Binary files /dev/null and b/src/main/resources/img/result/lamp_5.png differ diff --git a/src/main/resources/img/result/lamp_6.png b/src/main/resources/img/result/lamp_6.png new file mode 100644 index 0000000..8c77a39 Binary files /dev/null and b/src/main/resources/img/result/lamp_6.png differ diff --git a/src/main/resources/img/result/memo.jpg b/src/main/resources/img/result/memo.jpg new file mode 100644 index 0000000..2c7a92d Binary files /dev/null and b/src/main/resources/img/result/memo.jpg differ diff --git a/src/main/resources/img/result/result_BG.jpg b/src/main/resources/img/result/result_BG.jpg new file mode 100644 index 0000000..3d0602c Binary files /dev/null and b/src/main/resources/img/result/result_BG.jpg differ diff --git a/src/main/resources/img/result/result_BG.png b/src/main/resources/img/result/result_BG.png new file mode 100644 index 0000000..84486ae Binary files /dev/null and b/src/main/resources/img/result/result_BG.png differ diff --git a/src/main/resources/img/result/sample.jpg b/src/main/resources/img/result/sample.jpg new file mode 100644 index 0000000..472295b Binary files /dev/null and b/src/main/resources/img/result/sample.jpg differ diff --git a/src/main/resources/img/result/sample_memo.jpg b/src/main/resources/img/result/sample_memo.jpg new file mode 100644 index 0000000..dc65ce9 Binary files /dev/null and b/src/main/resources/img/result/sample_memo.jpg differ diff --git a/src/main/resources/img/robot0.png b/src/main/resources/img/robot0.png new file mode 100644 index 0000000..6d577d0 Binary files /dev/null and b/src/main/resources/img/robot0.png differ diff --git a/src/main/resources/img/robot1.png b/src/main/resources/img/robot1.png new file mode 100644 index 0000000..42e8c87 Binary files /dev/null and b/src/main/resources/img/robot1.png differ diff --git a/src/main/resources/img/robot2.png b/src/main/resources/img/robot2.png new file mode 100644 index 0000000..9f3e632 Binary files /dev/null and b/src/main/resources/img/robot2.png differ diff --git a/src/main/resources/img/robot3.png b/src/main/resources/img/robot3.png new file mode 100644 index 0000000..2135c74 Binary files /dev/null and b/src/main/resources/img/robot3.png differ diff --git a/src/main/resources/img/robot4.png b/src/main/resources/img/robot4.png new file mode 100644 index 0000000..69f1bd3 Binary files /dev/null and b/src/main/resources/img/robot4.png differ diff --git a/src/main/resources/img/robot5.png b/src/main/resources/img/robot5.png new file mode 100644 index 0000000..99980bf Binary files /dev/null and b/src/main/resources/img/robot5.png differ diff --git a/src/main/resources/img/robot_sample.png b/src/main/resources/img/robot_sample.png new file mode 100644 index 0000000..aa1b773 Binary files /dev/null and b/src/main/resources/img/robot_sample.png differ diff --git a/src/main/resources/img/round/rn0.png b/src/main/resources/img/round/rn0.png new file mode 100644 index 0000000..4c5c6c6 Binary files /dev/null and b/src/main/resources/img/round/rn0.png differ diff --git a/src/main/resources/img/round/rn1.png b/src/main/resources/img/round/rn1.png new file mode 100644 index 0000000..a86a308 Binary files /dev/null and b/src/main/resources/img/round/rn1.png differ diff --git a/src/main/resources/img/round/rn2.png b/src/main/resources/img/round/rn2.png new file mode 100644 index 0000000..c959964 Binary files /dev/null and b/src/main/resources/img/round/rn2.png differ diff --git a/src/main/resources/img/round/rn3.png b/src/main/resources/img/round/rn3.png new file mode 100644 index 0000000..b51a957 Binary files /dev/null and b/src/main/resources/img/round/rn3.png differ diff --git a/src/main/resources/img/round/rn4.png b/src/main/resources/img/round/rn4.png new file mode 100644 index 0000000..ddb9777 Binary files /dev/null and b/src/main/resources/img/round/rn4.png differ diff --git a/src/main/resources/img/round/rn5.png b/src/main/resources/img/round/rn5.png new file mode 100644 index 0000000..280990b Binary files /dev/null and b/src/main/resources/img/round/rn5.png differ diff --git a/src/main/resources/img/round/rn6.png b/src/main/resources/img/round/rn6.png new file mode 100644 index 0000000..6beed83 Binary files /dev/null and b/src/main/resources/img/round/rn6.png differ diff --git a/src/main/resources/img/round/rn7.png b/src/main/resources/img/round/rn7.png new file mode 100644 index 0000000..bec36c1 Binary files /dev/null and b/src/main/resources/img/round/rn7.png differ diff --git a/src/main/resources/img/round/rn8.png b/src/main/resources/img/round/rn8.png new file mode 100644 index 0000000..ea0c4b6 Binary files /dev/null and b/src/main/resources/img/round/rn8.png differ diff --git a/src/main/resources/img/round/rn9.png b/src/main/resources/img/round/rn9.png new file mode 100644 index 0000000..5aba5ea Binary files /dev/null and b/src/main/resources/img/round/rn9.png differ diff --git a/src/main/resources/img/round/rnslash.png b/src/main/resources/img/round/rnslash.png new file mode 100644 index 0000000..7f26dbd Binary files /dev/null and b/src/main/resources/img/round/rnslash.png differ diff --git a/src/main/resources/img/round/round.png b/src/main/resources/img/round/round.png new file mode 100644 index 0000000..abe4537 Binary files /dev/null and b/src/main/resources/img/round/round.png differ diff --git a/src/main/resources/img/star.png b/src/main/resources/img/star.png new file mode 100644 index 0000000..8729b00 Binary files /dev/null and b/src/main/resources/img/star.png differ diff --git a/src/main/resources/img/stars.png b/src/main/resources/img/stars.png new file mode 100644 index 0000000..354e968 Binary files /dev/null and b/src/main/resources/img/stars.png differ diff --git a/src/main/resources/img/start/title.png b/src/main/resources/img/start/title.png new file mode 100644 index 0000000..812095f Binary files /dev/null and b/src/main/resources/img/start/title.png differ diff --git a/src/main/resources/img/vein-1.png b/src/main/resources/img/vein-1.png new file mode 100644 index 0000000..8295494 Binary files /dev/null and b/src/main/resources/img/vein-1.png differ diff --git a/src/main/resources/img/vein0.png b/src/main/resources/img/vein0.png new file mode 100644 index 0000000..c86a33a Binary files /dev/null and b/src/main/resources/img/vein0.png differ diff --git a/src/main/resources/img/vein1.png b/src/main/resources/img/vein1.png new file mode 100644 index 0000000..e9bc020 Binary files /dev/null and b/src/main/resources/img/vein1.png differ diff --git a/src/main/resources/img/vein2.png b/src/main/resources/img/vein2.png new file mode 100644 index 0000000..46ddca4 Binary files /dev/null and b/src/main/resources/img/vein2.png differ diff --git a/src/main/resources/img/vein3.png b/src/main/resources/img/vein3.png new file mode 100644 index 0000000..dade994 Binary files /dev/null and b/src/main/resources/img/vein3.png differ diff --git a/src/main/resources/img/vein4.png b/src/main/resources/img/vein4.png new file mode 100644 index 0000000..8801590 Binary files /dev/null and b/src/main/resources/img/vein4.png differ diff --git a/src/main/resources/img/vein5.png b/src/main/resources/img/vein5.png new file mode 100644 index 0000000..ad79a5f Binary files /dev/null and b/src/main/resources/img/vein5.png differ diff --git a/src/main/resources/img/veinSample.png b/src/main/resources/img/veinSample.png new file mode 100644 index 0000000..a37a968 Binary files /dev/null and b/src/main/resources/img/veinSample.png differ diff --git a/src/main/resources/img/vein_sample.png b/src/main/resources/img/vein_sample.png new file mode 100644 index 0000000..3ced481 Binary files /dev/null and b/src/main/resources/img/vein_sample.png differ diff --git a/src/main/resources/translations/messages_ja.xml b/src/main/resources/translations/messages_ja.xml new file mode 100644 index 0000000..c54c106 --- /dev/null +++ b/src/main/resources/translations/messages_ja.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/GameEnvironment.scala b/src/main/scala/net/javachallenge/GameEnvironment.scala new file mode 100644 index 0000000..7fcaa78 --- /dev/null +++ b/src/main/scala/net/javachallenge/GameEnvironment.scala @@ -0,0 +1,41 @@ +package net.javachallenge; + +import scala.math._ +import jp.ac.waseda.cs.washi.gameaiarena.gui.Environment +import jp.ac.waseda.cs.washi.gameaiarena.gui.GamePanel +import net.javachallenge.entity.Game +import jp.ac.waseda.cs.washi.gameaiarena.api.Point2 +import net.javachallenge.entity.TrianglePoint + +/** + * ゲーム全体の情報を統括するクラスです。 + */ +class GameEnvironment(panel: GamePanel, var game: Game, val tileSize: Int) extends Environment(panel) { + private var _trianglePointToPixelPoint: Map[TrianglePoint, Point2] = Map.empty + + val offsetX = 20 + val offsetY = 8 + + def toHexPoint(x: Int, y: Int) = (abs(y) * tileSize / 2 + (x + 9) * tileSize, (y + 9) * tileSize / 4 * 3) + + def trianglePointToPixelPoint = { + if (_trianglePointToPixelPoint.size == 0 && game != null) { + val (cpx, cpy) = toHexPoint(0, 0) + _trianglePointToPixelPoint = game.field.validCoords.map( + p => { + val oddx = abs(p.x) % 2 + val oddy = abs(p.y) % 2 + val px = (p.x >> 1) * tileSize + tileSize / 2 * oddx + val py = (p.y >> 1) * tileSize / 2 * 3 + (oddy * 2 - 1) * tileSize / 4 * (oddx + 1) + tileSize / 2 + p -> new Point2(px + cpx + offsetX, py + cpy + offsetY) + }).toMap + } + _trianglePointToPixelPoint + } +} + +object GameEnvironment { + def apply(panel: GamePanel = null, game: Game = null, tileSize: Int = 48) = { + new GameEnvironment(panel, game, tileSize) + } +} diff --git a/src/main/scala/net/javachallenge/Main.scala b/src/main/scala/net/javachallenge/Main.scala new file mode 100644 index 0000000..80f71c5 --- /dev/null +++ b/src/main/scala/net/javachallenge/Main.scala @@ -0,0 +1,290 @@ +package net.javachallenge + +import net.javachallenge.util.settings.Defaults +import net.javachallenge.api.PlayMode +import net.javachallenge.api.UserInterfaceMode +import com.google.common.io.Files +import scala.collection.mutable +import java.util.Random +import java.awt._ +import java.awt.event._ +import javax.swing._ +import jp.ac.waseda.cs.washi.gameaiarena.gui.GamePanels +import net.javachallenge.entity._ +import net.javachallenge.scene._ +import net.javachallenge.util.misc.ImageLoader +import net.javachallenge.scene.graphic._ +import net.javachallenge.scene.console._ +import net.javachallenge.entity.GameSetting +import net.javachallenge.api.ComputerPlayer +import net.javachallenge.api.MockPlayer +import jp.ac.waseda.cs.washi.gameaiarena.gui._ +import jp.ac.waseda.cs.washi.gameaiarena.api._ +import jp.ac.waseda.cs.washi.gameaiarena.key._ +import jp.ac.waseda.cs.washi.gameaiarena.runner.Runners +import net.javachallenge.printer.Printer +import org.apache.commons.cli._ +import net.javachallenge.runner._ + +object Main { + + val HELP = "h" + val CUI_MODE = "c" + val LARGE_MODE = "l" + var logFunction: String => Unit = null + + def log(text: String) = logFunction(text) + + def printHelp(options: Options) { + val help = new HelpFormatter(); + help.printHelp( + "java -jar JavaChallenge2012-X.X.X.jar [OPTIONS]\n" + + "[OPTIONS]: ", "", options, "", true); + } + + def main(args: Array[String]) { + val options = new Options() + .addOption(HELP, false, "Print this help.") + .addOption(CUI_MODE, false, "Enable CUI mode instead of GUI mode.") + .addOption(LARGE_MODE, false, "Enable GUI mode with large images.") + try { + val parser = new BasicParser(); + val cl = parser.parse(options, args); + if (cl.hasOption(HELP)) { + printHelp(options); + } else { + startGame(options, cl); + } + } catch { + case e: ParseException => { + System.err.println("Error: " + e.getMessage()); + printHelp(options); + System.exit(-1); + } + } + } + + def startConsoleGame() = { + val env = GameEnvironment() + env.getSceneManager().setFps(1000) + val endScene = new EmptyScene(null) with ResultScene with ConsoleScene + val mainScene = new MainScene(endScene) with ConsoleScene + val veinScene = new VeinScene(mainScene) with ConsoleScene + val playerScene = new PlayerScene(veinScene) with ConsoleScene + env.start(playerScene) + } + + def startGame(options: Options, cl: CommandLine) { + if (cl.hasOption(CUI_MODE)) { + startConsoleGame() + } else { + val (window, env) = initializeComponents(cl.hasOption(LARGE_MODE)) + val setting = GameSetting(mapSize = 10, veinCount = 40) + + val endScene = new WaitingScene(null) with ResultScene with GraphicalScene with TextBoxScene + val mainScene = new MainScene(endScene) with GraphicalScene with TextBoxScene + val veinScene = new VeinScene(mainScene) with GraphicalScene with TextBoxScene + val playerScene = new PlayerScene(veinScene, setting) with GraphicalScene with TextBoxScene + + val f: Function1[String, Unit] = mainScene.displayCore(_) + + env.start(playerScene) + + window.dispose() + } + } + + def consoleRunnerScenes(nextScene: Scene[GameEnvironment]) = { + val mainScene = new MainScene(nextScene) with ConsoleScene with MainRunnerScene + val veinScene = new VeinScene(mainScene) with ConsoleScene with InitialRunnerScene + val titleScene = new WaitingScene(veinScene) with ConsoleScene + logFunction = mainScene.displayCore(_) + (titleScene, veinScene, mainScene, nextScene) + } + + def graphicalRunnerScenes(nextScene: Scene[GameEnvironment]) = { + val mainScene = new MainScene(nextScene) with GraphicalScene with TextBoxScene with MainRunnerScene + val waitScene = new WaitingScene(mainScene) with GraphicalScene with TextBoxScene + val veinScene = new VeinScene(waitScene) with GraphicalScene with TextBoxScene with InitialRunnerScene + val titleScene = new WaitingScene(veinScene) with CompositeGraphicalScene with TextBoxScene + titleScene.addScene(new EmptyScene(null) with TitleScene with TextBoxScene) + logFunction = mainScene.displayCore(_) + (titleScene, veinScene, mainScene, nextScene) + } + + def initializeWindowAndEnvironment(playMode: PlayMode) = + playMode.getUserInterfaceMode() match { + case UserInterfaceMode.SmallGraphical => + initializeComponents(false) + case UserInterfaceMode.LargeGraphical => + initializeComponents(true) + case UserInterfaceMode.CharacterBased => + (null, GameEnvironment()) + } + + def startAIGame(comPlayers: Array[ComputerPlayer], apiSetting: net.javachallenge.api.GameSetting, playMode: PlayMode) = { + val (window, env) = initializeWindowAndEnvironment(playMode) + val setting = apiSetting.asInstanceOf[GameSetting] + val random = new Random() + + val ((_, veinScene, mainScene, _), resultScene) = + if (playMode.getUserInterfaceMode == UserInterfaceMode.CharacterBased) { + val resultScene_ = new EmptyScene(null) with ResultScene with ConsoleScene + (consoleRunnerScenes(resultScene_), resultScene_) + } else { + val resultScene_ = new WaitingScene(null) with ResultScene with GraphicalScene with TextBoxScene + (graphicalRunnerScenes(resultScene_), resultScene_) + } + val (irs, mrs, rand) = RunnerInitializer.initialize(comPlayers, setting, playMode) + mainScene.runners = Vector(mrs: _*) + veinScene.runners = Vector(irs: _*) + + env.game = Game(comPlayers.map(_.getName()), setting, Field(setting, rand)) + env.getSceneManager().setFps(playMode.getFps()) + env.start(veinScene) + + if (window != null) { + window.dispose() + } + resultScene.sortedPlayers + } + + def startReplayGame(replayFileName: String, playMode: PlayMode) { + val (window, env) = initializeWindowAndEnvironment(playMode) + + val ((titleScene, veinScene, mainScene, _), resultScene) = + if (playMode.getUserInterfaceMode == UserInterfaceMode.CharacterBased) { + val resultScene_ = new EmptyScene(null) with ResultScene with ConsoleScene + (consoleRunnerScenes(resultScene_), resultScene_) + } else { + val resultScene_ = new WaitingScene(null) with ResultScene with TextBoxScene + val finalScene_ = new WaitingScene(resultScene_) with GraphicalScene with TextBoxScene + (graphicalRunnerScenes(finalScene_), resultScene_) + } + + val (irs, mrs, names, setting, rand) = RunnerInitializer.initializeReplay(replayFileName) + mainScene.runners = Vector(mrs: _*) + veinScene.runners = Vector(irs: _*) + + env.game = Game(names, setting, Field(setting, rand)) + env.getSceneManager().setFps(playMode.getFps()) + env.start(titleScene) + + if (window != null) { + window.dispose() + } + } + + def initializeComponents(isLargeMode: Boolean) = { + // TODO: Use scala.swing package instead of javax.swing package + + // Initialize layout components + val mainPanel = new JPanel() + val layout = new SpringLayout(); + mainPanel.setLayout(layout) + + // Initialize each component + val window = new JFrame() + window.setTitle("JavaChallenge2012") + val gamePanel = GamePanels.newWithDefaultImage() + if (isLargeMode) { + window.setSize(1280, 1000) + gamePanel.setPreferredSize(new Dimension(1280, 720)) + } else { + window.setSize(1024, 740) + gamePanel.setPreferredSize(new Dimension(1024, 495)) + } + mainPanel.add(gamePanel); + val logArea = new JTextArea(); + val logScrollPane = new JScrollPane(logArea) + logScrollPane.setPreferredSize(new Dimension(0, 0)) + mainPanel.add(logScrollPane); + logArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + logArea.setEditable(false) + val commandField = new JTextField(); + commandField.setPreferredSize(new Dimension(0, 20)) + commandField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + commandField.addActionListener(new ActionListener() { + def actionPerformed(e: ActionEvent) = { + val command = commandField.getText() + commandField.setText("") + TextBoxScene.addCommand(command) + } + }) + TextBoxScene.display = (text) => { + logArea.append(text) + logArea.setCaretPosition(logArea.getText().length()) + } + mainPanel.add(commandField); + + // Layout compo6nents + layout.putConstraint(SpringLayout.NORTH, gamePanel, 0, SpringLayout.NORTH, mainPanel); + layout.putConstraint(SpringLayout.NORTH, logScrollPane, 0, SpringLayout.SOUTH, gamePanel); + layout.putConstraint(SpringLayout.SOUTH, logScrollPane, 0, SpringLayout.NORTH, commandField); + layout.putConstraint(SpringLayout.SOUTH, commandField, 0, SpringLayout.SOUTH, mainPanel); + layout.putConstraint(SpringLayout.WEST, logScrollPane, 0, SpringLayout.WEST, mainPanel); + layout.putConstraint(SpringLayout.WEST, commandField, 0, SpringLayout.WEST, mainPanel); + layout.putConstraint(SpringLayout.EAST, logScrollPane, 0, SpringLayout.EAST, mainPanel); + layout.putConstraint(SpringLayout.EAST, commandField, 0, SpringLayout.EAST, mainPanel); + + window.getContentPane().add(mainPanel) + //window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) + + // Show the window + window.setVisible(true); + gamePanel.initializeAfterShowing() + + val env = GameEnvironment(gamePanel, tileSize = if (isLargeMode) 48 else 32) + ImageLoader.prefetch(env.getRenderer) + env.getSceneManager.addWindowListenerForTerminating(window); + env.getSceneManager().setFps(5) + .addWindowListenerForTerminating(window) + initializeListener(gamePanel, env, commandField, logArea) + + commandField.requestFocus() + + (window, env) + } + + def initializeListener(gamePanel: JPanel, env: GameEnvironment, commandField: JTextField, logArea: JTextArea) { + gamePanel.addMouseListener(new MouseAdapter() { + override def mouseClicked(e: MouseEvent) = { + if (env.game != null) { + val p = new Point2(e.getPoint()) + val range = 16 + val dx = -4 + val dy = -4 + for ( + (tp, px) <- env.trianglePointToPixelPoint.filter { + case (tp, px) => + px.x + dx <= p.x && p.x <= px.x + dx + range && + px.y + dy <= p.y && p.y <= px.y + dy + range + } + ) { + TextBoxScene.display("Your clicked location is " + tp.cmdStr) + TextBoxScene.display(Defaults.NEW_LINE) + if (env.game.field.veins.toMap.contains(tp)) { + val v = env.game.field.veins(tp) + TextBoxScene.display("There is a vein: ") + Printer.vein(tp, v, TextBoxScene.display) + TextBoxScene.display(Defaults.NEW_LINE) + for (squad <- env.game.field.squads.filter(s => s.current == tp)) { + TextBoxScene.display("There is a squad: " + squad) + TextBoxScene.display(Defaults.NEW_LINE) + } + } + if (e.getButton() == 3) { + commandField.setText(commandField.getText + " " + tp.cmdStr + " ") + } + commandField.requestFocus() + } + } + } + }); + val memorizer = new AwtKeyMemorizer() + gamePanel.addKeyListener(memorizer) + logArea.addKeyListener(memorizer); + commandField.addKeyListener(memorizer) + env.getInputer().add(0, memorizer.getKeyPressChecker(KeyEvent.VK_ENTER)) + } +} diff --git a/src/main/scala/net/javachallenge/PlayModeHelper.scala b/src/main/scala/net/javachallenge/PlayModeHelper.scala new file mode 100644 index 0000000..2130da5 --- /dev/null +++ b/src/main/scala/net/javachallenge/PlayModeHelper.scala @@ -0,0 +1,10 @@ +package net.javachallenge + +import net.javachallenge.api.UserInterfaceMode +import net.javachallenge.api.PlayModeBuilder +import net.javachallenge.api.PlayMode + +object PlayModeHelper { + def build(b: PlayModeBuilder) = new PlayMode(b.getFps, b.getAvailableVeinSelectMilliseconds(), b.getAvailableTurnMilliseconds(), b.getUserInterfaceMode, b.isIgnoringExceptions) + val defaultInstance = new PlayMode(10, 10000, 1000, UserInterfaceMode.SmallGraphical, true) +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/RunnerInitializer.scala b/src/main/scala/net/javachallenge/RunnerInitializer.scala new file mode 100644 index 0000000..baa426f --- /dev/null +++ b/src/main/scala/net/javachallenge/RunnerInitializer.scala @@ -0,0 +1,90 @@ +package net.javachallenge + +import java.io.ObjectOutputStream +import java.io.FileOutputStream +import java.io.FileNotFoundException +import java.io.IOException +import org.apache.commons.lang.StringUtils +import jp.ac.waseda.cs.washi.gameaiarena.io.InputStreams +import java.io.BufferedInputStream +import java.io.ObjectInputStream +import net.javachallenge.runner.InitialRunner +import net.javachallenge.api.MockPlayer +import net.javachallenge.runner.InitialRunner +import net.javachallenge.runner.MainRunner +import java.util.Calendar +import net.javachallenge.api.ComputerPlayer +import net.javachallenge.entity.GameSetting +import java.util.Random +import net.javachallenge.api.PlayMode +import net.javachallenge.util.misc.DateUtils +import java.io.File + +object RunnerInitializer { + + def initialize(comPlayers: Array[ComputerPlayer], setting: GameSetting, playMode: PlayMode) = { + val names = comPlayers.map(_.getName.map(c => if (Character.isJavaIdentifierPart(c)) c else '_')) + val fileName = DateUtils.dateStringForFileName + "__" + names.mkString("_") + ".rep" + val rand = new Random() + new File("replay").mkdir() + val oos = openObjectOutputStream("replay/" + fileName, comPlayers.map(_.getName), setting, rand) + + var initialRunners = comPlayers.map(cmp => new InitialRunner(cmp)) + .map(r => if (playMode.isIgnoringExceptions()) r.ignoringException() else r) + .map(r => r.limittingTime(playMode.getAvailableVeinSelectMilliseconds())) + .map(r => r.recordingStream(oos)) + var mainRunners = comPlayers.map(cmp => (new MainRunner(cmp))) + .map(r => if (playMode.isIgnoringExceptions()) r.ignoringException() else r) + .map(r => r.limittingTime(playMode.getAvailableTurnMilliseconds())) + .map(r => r.recordingStream(oos)) + (initialRunners, mainRunners, rand) + } + + def initializeReplay(filePath: String) = { + val stream = InputStreams.openFileOrResource(filePath); + if (stream == null) { + throw new IOException("Cant open the replay file:\n" + filePath); + } + var version = 0; + if (stream.read() == 'V') { + version = stream.read() - '0'; + } + val bis = new BufferedInputStream(stream); + val ois = new ObjectInputStream(bis); + val (names, setting, rand) = version match { + case 0 => { + val names = ois.readObject().asInstanceOf[Seq[String]] + val setting = ois.readObject().asInstanceOf[GameSetting] + val rand = ois.readObject().asInstanceOf[Random] + (names, setting, rand) + } + case _ => + throw new IOException("Unsupported replay file.") + } + val initialRunners = names.map(name => (new InitialRunner(new MockPlayer(name))).replayingStream(ois)).toSeq + val mainRunners = names.map(name => (new MainRunner(new MockPlayer(name))).replayingStream(ois)).toSeq + (initialRunners, mainRunners, names, setting, rand) + } + + private def openObjectOutputStream(recordFileName: String, names: Seq[String], setting: GameSetting, rand: Random) = { + try { + if (!StringUtils.isEmpty(recordFileName)) { + val fos = new FileOutputStream(recordFileName); + fos.write('V'); + fos.write('0'); + val oos = new ObjectOutputStream(fos); + oos.writeObject(names) + oos.writeObject(setting) + oos.writeObject(rand) + oos + } else { + null + } + } catch { + case e: FileNotFoundException => + null + case e: IOException => + null + } + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/entity/AlienTrade.scala b/src/main/scala/net/javachallenge/entity/AlienTrade.scala new file mode 100644 index 0000000..f97cd15 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/AlienTrade.scala @@ -0,0 +1,66 @@ +package net.javachallenge.entity + +import net.javachallenge.util.settings.Defaults +import scala.Mutable +import scala.math + +case class AlienTrade(val prices: Map[Material, Int], val margin: Int) + extends net.javachallenge.api.AlienTrade { + require(!prices.exists { case (m, p) => p <= 0 }, "Prices of materials should be grater than 0.") + require(prices.size == Material.all.size, "Prices of materials should be prepared for all materials.") + + override def getBuyPriceOf(material: net.javachallenge.api.Material) = { + require(material != null) + + buyPriceOf(material) + } + + override def getSellPriceOf(material: net.javachallenge.api.Material) = { + require(material != null) + + sellPriceOf(material) + } + + /** + * Returns buying price of the material + * + * @param material + */ + def buyPriceOf(material: Material): Int = { + prices(material) + } + + /** + * Returns selling price of the material + * + * @param material + */ + def sellPriceOf(material: Material): Int = { + prices(material) / margin + } + + /** + * Update bank value using player materials + * + */ + def update(game: Game) = { + // compute each player's total amount of materials + var totalMaterials = Map[Material, Long]().withDefaultValue(0L) + var total = 0L + for (player <- game.survivedPlayers) { + for ((m, amount) <- player.materials) { + totalMaterials += (m -> (totalMaterials(m) + amount)) + total += amount + } + } + this.copy(prices = totalMaterials + .map { + case (m, amount) => + (m, (math.min(100L * (total + 300L) / (amount + 100) * (total + 300L) / (amount + 100) / 9, Int.MaxValue)).toInt) + }.toMap) + } +} + +object AlienTrade { + def apply(settings: GameSetting): AlienTrade = new AlienTrade(Material.all.map(m => (m, 100)).toMap, settings.bankMargin) +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/entity/ApiConversion.scala b/src/main/scala/net/javachallenge/entity/ApiConversion.scala new file mode 100644 index 0000000..62eaa73 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/ApiConversion.scala @@ -0,0 +1,11 @@ +package net.javachallenge.entity + +object ApiConversion { + implicit def field(field: net.javachallenge.api.Field) = field.asInstanceOf[Field] + implicit def game(game: net.javachallenge.api.Game) = game.asInstanceOf[Game] + implicit def gameSetting(setting: net.javachallenge.api.GameSetting) = setting.asInstanceOf[GameSetting] + implicit def player(player: net.javachallenge.api.Player) = player.asInstanceOf[Player] + implicit def squad(squad: net.javachallenge.api.Squad) = squad.asInstanceOf[Squad] + implicit def trianglePoint(trianglePoint: net.javachallenge.api.TrianglePoint) = trianglePoint.asInstanceOf[TrianglePoint] + implicit def vein(vein: net.javachallenge.api.Vein) = vein.asInstanceOf[Vein] +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/entity/AuctionHall.scala b/src/main/scala/net/javachallenge/entity/AuctionHall.scala new file mode 100644 index 0000000..463c136 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/AuctionHall.scala @@ -0,0 +1,65 @@ +package net.javachallenge.entity + +import scala.collection.mutable +import scala.collection.immutable + +/** + * A container class to stock and manage trades between players. + * + * @constructor creates a hall auction + * @param The trades currently available. + * @param The trades that have been completed or canceled. + */ +case class AuctionHall(val trades: Map[(Int, Material), Trade] = Map.empty) { + require(trades != null) + + /** + * Clear the specified player's trades. + */ + def clear(player: Player) = { + var newTrades = trades + var newPlayer = player + for (material <- Material.all) { + newPlayer = newTrades.get((player.id, material)) match { + case Some(t) => t.cancel(newPlayer) + case _ => newPlayer + } + newTrades -= ((player.id, material)) + } + (this.copy(trades = newTrades), newPlayer) + } + + /** + * Adds a trade to the auction hall. + */ + private def addTrade(trade: Trade) = { + if (trades.contains((trade.publisherId, trade.material))) { + throw new IllegalArgumentException("You have already porposed the trade for the " + trade.material); + } + this.copy(trades = trades + ((trade.publisherId, trade.material) -> trade)) + } + + /** + * Creates and adds an offer to the hall auction. + */ + def addOffer(game: Game, publisherId: Int, material: Material, amount: Int, price: Int) = { + val (offer, player) = Offer.publish(game, publisherId, material, amount, price) + (addTrade(offer), player) + } + + /** + * Creates and adds a demand to the hall auction. + */ + def addDemand(game: Game, publisherId: Int, material: Material, amount: Int, price: Int) = { + val (demand, player) = Demand.publish(game, publisherId, material, amount, price) + (addTrade(demand), player) + } + + /** + * Executes a transaction and archives the trade if done. + */ + def makeTransaction(game: Game, customerId: Int, trade: Trade, amount: Int) = { + val (trades, publisher, customer) = trade.makeTransaction(game, customerId, amount) + (this.copy(trades = this.trades - ((trade.publisherId, trade.material)) ++ trades), publisher, customer) + } +} diff --git a/src/main/scala/net/javachallenge/entity/Field.scala b/src/main/scala/net/javachallenge/entity/Field.scala new file mode 100644 index 0000000..925d237 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/Field.scala @@ -0,0 +1,314 @@ +package net.javachallenge.entity + +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.immutable.TreeMap +import scala.collection.immutable.TreeSet +import java.util.Random +import scala.collection.JavaConverters._ +import net.javachallenge.entity.ApiConversion._ +import com.google.common.collect.Lists +import java.util.ArrayList +import java.util.HashMap +import java.util.HashSet +import net.javachallenge.api.TriangleComparator + +/** + * A case class which represents a filed including veins and squads. + * + * @constructor Create a field. + * @param size the size of the field + * @param veins the vein map of locations and veins + * @param squads the squad set + */ +case class Field(val veins: TreeMap[TrianglePoint, Vein], val squads: List[Squad], val validCoords: TreeSet[TrianglePoint]) + extends net.javachallenge.api.Field { + + require(veins != null) + require(squads != null) + require(validCoords != null) + + override def getVein(location: net.javachallenge.api.TrianglePoint) = { + require(location != null) + + veins(location) + } + + override def getVeins = { + val javaVeins: Iterable[net.javachallenge.api.Vein] = veins.values + new java.util.ArrayList(javaVeins.toSeq.asJava) + } + + override def getVeins(playerId: Int) = { + val javaVeins: Iterable[net.javachallenge.api.Vein] = veins.values + .filter(_.ownerId == playerId) + new java.util.ArrayList(javaVeins.toSeq.asJava) + } + + override def getVeinMap = { + val javaVeins: Map[net.javachallenge.api.TrianglePoint, net.javachallenge.api.Vein] = veins + .map(v => v._1 -> v._2) + val treeMap: java.util.TreeMap[net.javachallenge.api.TrianglePoint, net.javachallenge.api.Vein] = new java.util.TreeMap(new TriangleComparator) + treeMap.putAll(javaVeins.asJava) + treeMap + } + + override def getVeinMap(playerId: Int) = { + val javaVeins: Map[net.javachallenge.api.TrianglePoint, net.javachallenge.api.Vein] = veins + .filter(_._2.ownerId == playerId) + .toMap + val treeMap: java.util.TreeMap[net.javachallenge.api.TrianglePoint, net.javachallenge.api.Vein] = new java.util.TreeMap(new TriangleComparator) + treeMap.putAll(javaVeins.asJava) + treeMap + } + + override def getSquads = { + val javaSquads: List[net.javachallenge.api.Squad] = squads + new ArrayList(javaSquads.asJava) + } + + override def getSquads(playerId: Int) = { + val javaSquads: List[net.javachallenge.api.Squad] = squads + .filter(_.ownerId == playerId) + new ArrayList(javaSquads.asJava) + } + + override def getValidCoords = { + val javaValidCoords: Set[net.javachallenge.api.TrianglePoint] = validCoords + .map(p => p) + new java.util.HashSet(javaValidCoords.asJava) + } + + override def countVeins(playerId: Int) = veinCounts(playerId) + + override def sumRobots(playerId: Int) = totalVeinRobotAndIncomes(playerId)._1 + totalSquadRobots(playerId) + + override def sumCurrentMaterialProductivity(playerId: Int, material: net.javachallenge.api.Material) = { + require(material != null) + + totalMaterialIncomes(playerId)(material) + } + + override def sumCurrentRobotProductivity(playerId: Int) = totalVeinRobotAndIncomes(playerId)._1 + totalSquadRobots(playerId) + + override def getVeinsOfSameOwnerOrderedByDistance(origination: net.javachallenge.api.Vein) = { + require(origination != null) + + val vs = veins.values + .filter(v => v != origination && v.ownerId == origination.getOwnerId()) + .map((v: net.javachallenge.api.Vein) => (v, origination.getDistance(v))) + .toList + new java.util.ArrayList(vs.sortBy(_._2).map(_._1).asJava) + } + + override def getVeinsOfOtherOwnersOrderedByDistance(origination: net.javachallenge.api.Vein) = { + require(origination != null) + + val vs = veins.values + .filter(v => v != origination && v.ownerId != origination.getOwnerId()) + .map((v: net.javachallenge.api.Vein) => (v, origination.getDistance(v))) + .toList + new java.util.ArrayList(vs.sortBy(_._2).map(_._1).asJava) + } + + def totalMaterialIncomes = { + veins.groupBy(_._2.ownerId) + .map { + case (pid, vs) => + (pid, vs.groupBy(_._2.material) + .map { case (m, vs) => (m, vs.map(_._2.materialIncome).sum) } + .toMap + .withDefaultValue(0)) + } + .toMap + .withDefaultValue(Map.empty.withDefaultValue(0)) + } + + def veinCounts = { + veins.groupBy(_._2.ownerId) + .map { + case (pid, vs) => + (pid, vs.size) + } + .toMap + .withDefaultValue(0) + } + + def totalVeinRobotAndIncomes = { + veins.groupBy(_._2.ownerId) + .map { + case (pid, vs) => + (pid, (vs.map(_._2.robot).sum, vs.map(_._2.robotIncome).sum)) + } + .toMap + .withDefaultValue((0, 0)) + } + + def totalSquadRobots = { + squads.groupBy(_.ownerId) + .map { + case (pid, ss) => + (pid, ss.map(_.robot).sum) + } + .toMap + .withDefaultValue(0) + } + + def owingVeins(player: Player) = { + veins.filter(_._2.ownerId == player.id) + } + + def occupy(location: TrianglePoint, playerId: Int) = { + if (!veins.contains(location)) { + throw new InvalidCommandException("The specified vertex has no vein.") + } + this.copy(veins = veins + (location -> veins(location).occupy(playerId))) + } + + /** + * Returns a new filed where a new squad added with the specified arguments. + * Add squad in this field and returns new field. + * @param game the game instance + * @param robot the robot of the new squad + * @param path the path from the vein to the other vein + * @return the new filed where a new squad added + */ + def sendSquad(game: Game, robot: Int, path: List[TrianglePoint]) = { + val from = path.head + val to = path.last + if (from == to) { + throw new InvalidCommandException("You can not select the same vein as a departure and a destination.") + } + if (!(veins.contains(from) && veins.contains(to))) { + throw new InvalidCommandException("You can not select a vertext without a vein.") + } + if (!(0 < robot && robot <= veins(from).robot)) { + throw new InvalidCommandException("You don't have enough robots.") + } + if (veins(from).ownerId != game.currentPlayerId) { + throw new InvalidCommandException("You cannot select other player's vein as a departure.") + } + if (path.zip(path.tail).exists { case (p1, p2) => !p1.isConnected(p2) }) { + throw new InvalidCommandException("You should select a connected path from a depature to a destination.") + } + + this.copy(squads = Squad(game, robot, path) :: squads, + veins = veins + (from -> veins(from).changeRobot(-robot))) + } + + def upgrade(location: TrianglePoint, isMaterialRank: Boolean) = { + val newVein = + if (isMaterialRank) + veins(location).upgradeMaterialRank() + else + veins(location).upgradeRobotRank() + this.copy(veins = veins + (location -> newVein)) + } + + /** + * Returns a new filed where the specified squad removed + * @param squad the squad to be removed + * @return the new filed where the specified squad removed + */ + def removeSquad(squad: Squad) = { + this.copy(squads = squads - squad) + } + + /** + * Returns a new field where the squads advance. + * @param game the {@link Game} instance + * @return the new field where the squads advance + */ + def advanceSquads(game: Game) = { + val newSquadsAndVeins = Squad.advanceAll(game) + this.copy(squads = newSquadsAndVeins._1, veins = newSquadsAndVeins._2) + } + + /** + * Returns a new field where the new vein map replaced with old one. + * @param newVeins the new vein map + * @return the new field where the new vein map replaced with old one + */ + def setVeins(newVeins: TreeMap[TrianglePoint, Vein]) = { + this.copy(veins = newVeins) + } +} + +/** + * A object containing helper methods for creating new fields. + */ +object Field { + /** + * Create a new field with the specified size. + * @param size the size of the field + * @return the new field with the specified size + */ + def apply(setting: GameSetting, rand: Random): Field = { + val validCoords = getValidCoords(setting.mapSize) + val coords: mutable.Set[TrianglePoint] = mutable.Set() + while (coords.size < setting.veinCount) { + val coord = randomCoods(rand, setting.mapSize) + .filter(p => validCoords.contains(p)) + .filter(p => !coords.contains(p)) + .filter(p => !coords.exists(p2 => p2.shortestPath(p).size <= 3)) + .head + coords.add(coord) + } + + val veins = TreeMap((coords.zipWithIndex.map { + case (p, i) => { + val robot = 50 + rand.nextInt(50) + val rProd = 5 + rand.nextInt(5) + val mProd = 5 + rand.nextInt(5) + val material = Material.all(i % Material.all.size) + (p, Vein(p, material, robot, mProd, rProd)) + } + }).toStream: _*) + new Field(veins, List(), validCoords) + } + + /** + * Create a new field with the specified size and vein map. + * @param size the size of the field + * @param veins the vein map of the locations and the veins + * @return the new field with the specified size + */ + def apply(setting: GameSetting, veins: TreeMap[TrianglePoint, Vein]): Field = { + new Field(veins, List(), getValidCoords(setting.mapSize)) + } + + def getValidCoords(size: Int) = { + val validCoords: mutable.Set[TrianglePoint] = mutable.Set() + for (y <- 0 until size) { + for (x <- Range(-size * 2 + 2 + y, size * 2 - y + 1)) { + validCoords.add(TrianglePoint(x, y)) + validCoords.add(TrianglePoint(x, y + 1)) + validCoords.add(TrianglePoint(x, -y)) + validCoords.add(TrianglePoint(x, -y + 1)) + } + } + TreeSet(validCoords.toStream: _*) + } + + /** + * Returns random coordinates for a field with the specified size. + * @param rand the {@link Random} instance + * @param size the size of the field + * @return the random coordinates for a field with the specified size + */ + def randomCood(rand: Random, size: Int) = { + val x = rand.nextInt(size * 4) - size * 2 + 1 + val y = rand.nextInt(size * 2) - size + 1 + TrianglePoint(x, y) + } + + /** + * Returns random coordinates for a field with the specified size. + * @param rand the {@link Random} instance + * @param size the size of the field + * @return the random coordinates for a field with the specified size + */ + def randomCoods(rand: Random, size: Int): Stream[TrianglePoint] = { + randomCood(rand, size) #:: randomCoods(rand, size) + } +} diff --git a/src/main/scala/net/javachallenge/entity/Game.scala b/src/main/scala/net/javachallenge/entity/Game.scala new file mode 100644 index 0000000..f4b7bb3 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/Game.scala @@ -0,0 +1,418 @@ +package net.javachallenge.entity + +import scala.collection.mutable +import scala.collection.immutable +import net.javachallenge.util.internationalization.I18n +import scala.collection.JavaConverters._ +import java.util.Random + +/** + * A case class which represents a whole game states. + * + * @constructor Create a new game. + * @param players the player map of the indexes and the players + * @param auctionHall the {@link newAuctionHall} instance + * @param maxRounds the maximum round number + * @param field the field + * @param playerIndex the index of the current player + * @param round the current round number + */ +case class Game(val players: Vector[Player], val auctionHall: AuctionHall, val alienTrade: AlienTrade, + val field: Field, val setting: GameSetting, val currentPlayerId: Int, val round: Int) + extends net.javachallenge.api.Game { + + require(players != null, "players shoud be not null.") + require(players.size > 0, "players has more than or equal to one player.") + require(auctionHall != null, "auctionHall shoud be not null.") + require(alienTrade != null, "alienTrade shoud be not null.") + require(field != null, "field shoud be not null.") + require(setting != null, "setting shoud be not null.") + require(currentPlayerId >= 0, "currentPlayerId shoud be grater than or equal to 0.") + require(round >= 0, "round shoud be grater than or equal to 0.") + + override def getNeutralPlayerId = Player.neutralPlayer.id + + override def getMyPlayerId = currentPlayerId + + override def getPlayers = { + val javaPlayers: Vector[net.javachallenge.api.Player] = players + new java.util.ArrayList(javaPlayers.asJava) + } + + override def getSurvivedPlayers = getSurvivingPlayers + + override def getSurvivingPlayers = { + val javaPlayers: Vector[net.javachallenge.api.Player] = survivedPlayers + new java.util.ArrayList(javaPlayers.asJava) + } + + override def getPlayer(id: Int) = { + require(0 <= id && id < players.size, "id should be grater than or equal to 0 and less than playerCount"); + players(id) + } + + override def getMyPlayer = currentPlayer + + override def getAlienTrade = alienTrade + + override def getField = field + + override def getOffer(playerId: Int, material: net.javachallenge.api.Material) = { + require(0 <= playerId && playerId < players.size, "playerId should be grater than or equal to 0 and less than playerCount"); + require(material != null, "material should be not null.") + + val m = Material(material) + auctionHall.trades.values.filter(_ match { + case trade: Offer => + trade.publisherId == playerId && trade.material == m + case _ => false + }).headOption.getOrElse(null) + } + + override def getOffers(playerId: Int) = { + require(0 <= playerId && playerId < players.size, "playerId should be grater than or equal to 0 and less than playerCount"); + + val trades: Seq[net.javachallenge.api.PlayerTrade] = auctionHall.trades.values.filter(_ match { + case trade: Offer => + trade.publisherId == playerId + case _ => false + }).toSeq + new java.util.ArrayList(trades.asJava) + } + + override def getOffers(material: net.javachallenge.api.Material) = { + require(material != null, "material should be not null.") + + val m = Material(material) + val trades: Seq[net.javachallenge.api.PlayerTrade] = auctionHall.trades.values.filter(_ match { + case trade: Offer => + trade.material == m + case _ => false + }).toSeq + new java.util.ArrayList(trades.asJava) + } + + override def getDemand(playerId: Int, material: net.javachallenge.api.Material) = { + require(0 <= playerId && playerId < players.size, "playerId should be grater than or equal to 0 and less than playerCount"); + require(material != null, "material should be not null.") + + val m = Material(material) + auctionHall.trades.values.filter(_ match { + case trade: Demand => + trade.publisherId == playerId && trade.material == m + case _ => false + }).headOption.getOrElse(null) + } + + override def getDemands(playerId: Int) = { + require(0 <= playerId && playerId < players.size, "playerId should be grater than or equal to 0 and less than playerCount"); + + val trades: Seq[net.javachallenge.api.PlayerTrade] = auctionHall.trades.values.filter(_ match { + case trade: Demand => + trade.publisherId == playerId + case _ => false + }).toSeq + new java.util.ArrayList(trades.asJava) + } + + override def getDemands(material: net.javachallenge.api.Material) = { + require(material != null, "material should be not null.") + + val m = Material(material) + val trades: Seq[net.javachallenge.api.PlayerTrade] = auctionHall.trades.values.filter(_ match { + case trade: Demand => + trade.material == m + case _ => false + }).toSeq + new java.util.ArrayList(trades.asJava) + } + + override def getPlayerTrades = { + val javaTrades: List[net.javachallenge.api.PlayerTrade] = auctionHall.trades.values.toList + new java.util.ArrayList(javaTrades.asJava) + } + + override def getSetting = setting + + override def getPlayerCount = players.size + + override def getRound = round + + override def getTotalMoneyWhenSellingAllMaterials(playerId: Int) = { + require(0 <= playerId && playerId < players.size, "playerId should be grater than or equal to 0 and less than playerCount"); + + val p = players(playerId) + p.money + p.materials.map { case (m, a) => alienTrade.sellPriceOf(m) * a }.sum + } + + override def isSurvivingPlayer(playerId: Int) = field.veins.exists(_._2.ownerId == playerId) + + def survivedPlayers = players.filter(p => field.veins.exists(_._2.ownerId == p.id)) + def currentPlayer = players(currentPlayerId) + def playerCount = players.size + + def isEnded = { + if (round > setting.maxRound) + true + else + survivedPlayers.size <= 1 + } + + def advanceSelectVein(nextPlayerId: Int) = { + this.copy(currentPlayerId = nextPlayerId) + } + + /** + * Advances to the next turn. + * Ends the last turn and starts the next turn. + */ + def advanceTurn(): Game = { + if (isEnded) + return this + + var game = this + do { + println(toString()) + // Change to the next player + val newPlayerId = (game.currentPlayerId + 1) % game.playerCount + val newRound = if (game.currentPlayerId == game.playerCount - 1) game.round + 1 else game.round + if (game.round != newRound) { + // Process starting the next round + } + // Process starting the next turn + game = if (game.isSurvivingPlayer(game.currentPlayerId)) { + game + .earnMaterials() + .earnRobots() + .updateTimeToLive() + .updateAlienTrade() + .advanceSquads() + .advanceAuctionHall(newPlayerId) + .copy(round = newRound, currentPlayerId = newPlayerId) + } else { + game + .updateAlienTrade() + .advanceSquads() + .advanceAuctionHall(newPlayerId) + .copy(round = newRound, currentPlayerId = newPlayerId) + } + } while (!game.isEnded && !game.isSurvivingPlayer(game.currentPlayerId)) + if (game.isEnded) + game.copy(currentPlayerId = (game.currentPlayerId - 1 + game.playerCount) % game.playerCount) + else + game + } + + def clearAuctionHall() = { + var newGame = this + for (i <- 0 until players.size) { + newGame = newGame.advanceAuctionHall(i) + } + newGame + } + + private def earnMaterials() = this.copy(players = players.updated(currentPlayerId, currentPlayer.earnMaterials(this))) + private def updateTimeToLive() = this.copy(players = players.updated(currentPlayerId, currentPlayer.updateTimeToLive(this))) + private def earnRobots() = this.copy(field = field.setVeins(currentPlayer.earnRobots(this))) + private def updateAlienTrade() = this.copy(alienTrade = alienTrade.update(this)) + private def advanceSquads() = this.copy(field = field.advanceSquads(this)) + private def advanceAuctionHall(newPlayerId: Int) = { + val (newAuctionHall, newPlayer) = auctionHall.clear(players(newPlayerId)) + this.copy(auctionHall = newAuctionHall, players = players.updated(newPlayerId, newPlayer)) + } + + def occupy(selectCount: Int, location: TrianglePoint) = { + require(selectCount >= 0) + require(location != null) + + (selectCount + 1, this.copy(field = field.occupy(location, currentPlayerId), + currentPlayerId = math.max(veinSelectPlayerIndex(selectCount + 1), 0))) + } + + def veinSelectPlayerIndex(selectCount: Int) = { + require(selectCount >= 0) + + if (selectCount < playerCount) selectCount else playerCount * 2 - selectCount - 1 + } + + def tradeWithAlien(material: Material, amount: Int, isBuy: Boolean) = { + require(Material != null) + + val newPlayer = if (isBuy) { + currentPlayer.buyMaterial(material, amount, alienTrade.buyPriceOf(material)) + } else { + currentPlayer.sellMaterial(material, amount, alienTrade.sellPriceOf(material)) + } + this.copy(players = players.updated(currentPlayerId, newPlayer)) + } + + /** + * Returns a new game sending a squad with the given robot from the vein vein to the given vein. + * @param robot the robot of the new squad + * @param from the from location of the new squad + * @param to the to location of the new squad + * @return the new filed where a new squad added with the specified parameters + */ + def sendSquad(robot: Int, from: TrianglePoint, to: TrianglePoint): Game = { + require(from != null) + require(to != null) + + sendSquad(robot, from.shortestPath(to)) + } + + /** + * Returns a new game sending a squad with the given robot and the given path. + * @param robot the robot of the new squad + * @param path the locations from the owned vein to other vein + * @return the new filed where a new squad added with the specified parameters + */ + def sendSquad(robot: Int, path: List[TrianglePoint]): Game = { + require(path != null) + require(!path.isEmpty) + + this.copy(field = field.sendSquad(this, robot, path)) + } + + def upgrade(location: TrianglePoint, vein: Vein, isMaterialRank: Boolean) = { + require(location != null) + require(vein != null) + + val (rank, required) = + if (isMaterialRank) + (vein.materialRank, setting.materialsForUpgradingMaterial) + else + (vein.robotRank, setting.materialsForUpgradingRobot) + val newField = field.upgrade(location, isMaterialRank) + val newPlayer = Material.all.foldLeft(currentPlayer)((player, material) => + player.changeMaterial(material, -required(rank - 1)(material))) + this.copy(field = newField, players = players.updated(currentPlayerId, newPlayer)) + } + + def buy(trade: Offer, amount: Int) = { + val (newAuctionHall, publisher, customer) = + auctionHall.makeTransaction(this, currentPlayerId, trade, amount) + val newPlayers = players.updated(publisher.id, publisher) + .updated(customer.id, customer) + this.copy(auctionHall = newAuctionHall, players = newPlayers) + } + + def sell(trade: Demand, amount: Int) = { + val (newAuctionHall, publisher, customer) = + auctionHall.makeTransaction(this, currentPlayerId, trade, amount) + val newPlayers = players.updated(publisher.id, publisher) + .updated(customer.id, customer) + this.copy(auctionHall = newAuctionHall, players = newPlayers) + } + + def offer(material: Material, amount: Int, price: Int) = { + val (newAuctionHall, newPlayer) = + auctionHall.addOffer(this, currentPlayerId, material, amount, price) + this.copy(auctionHall = newAuctionHall, players = players.updated(currentPlayerId, newPlayer)) + } + + def demand(material: Material, amount: Int, price: Int) = { + val (newAuctionHall, newPlayer) = + auctionHall.addDemand(this, currentPlayerId, material, amount, price) + this.copy(auctionHall = newAuctionHall, players = players.updated(currentPlayerId, newPlayer)) + } + + def print(num: Int) = { + var data="" + data += num + data += "," + data + } + override def toString() = { + var data = "" + data += print(this.getRound()) + data += print(this.getPlayerCount()) + for(player <- this.players) { + data += player.name+"," + for(i <- 0 until 3) { + data += print(player.getMaterial(Material.all(i))) + } + data += print(player.getMoney()) + data += print(player.getTimeToLive()) + } + + + val veinList = this.getField().getVeins() + val veinSize = veinList.size() + data += print(veinSize); + + for(veincnt <- 0 until veinSize) { + val vein = veinList.get(veincnt) + data += print(vein.getOwnerId()) + data += print(vein.getLocation().getX()) + data += print(vein.getLocation().getY()) + data += print(vein.getMaterial().ordinal()) + data += print(vein.getNumberOfRobots()) + data += print(vein.getCurrentMaterialProductivity()) + data += print(vein.getCurrentRobotProductivity()) + data += print(vein.getMaterialRank()) + data += print(vein.getRobotRank()) + } + + val squadList = this.getField().getSquads() + val squadSize = squadList.size() +// data += "squad" + data += print(squadSize) + for(squadcnt <- 0 until squadSize) { + val squad = squadList.get(squadcnt) + data += print(squad.getOwnerId()) + data += print(squad.getRobot()) + val path = squad.getPath() + data += print(path.size()) + for(i <- 0 until path.size()) { + data += print(path.get(i).getX()) + data += print(path.get(i).getY()) + } + + data += print(squad.getCurrentLocation().getX()) + data += print(squad.getCurrentLocation().getY()) + data += print(squad.getDestinationLocation().getX()) + data += print(squad.getDestinationLocation().getY()) + + } + val alien = this.getAlienTrade() + for(i <- 0 until 3) { + data += print(alien.getBuyPriceOf(Material.all(i)) ) + data += print(alien.getSellPriceOf(Material.all(i)) ) + } + for(mat <- 0 until this.playerCount) { + val offerList = this.getOffers(mat) + val ofSize = offerList.size() + data += print(ofSize) + for(i <- 0 until ofSize) { + val ptrade = offerList.get(i) + data += print(ptrade.getPlayerId()) + data += print(ptrade.getAmount()) + data += print(ptrade.getPricePerOneMaterial()) + data += print(ptrade.getMaterial().ordinal()) + } + val demandList = this.getDemands(mat) + val demSize = demandList.size() + data += print(demSize) + for(i <- 0 until demSize) { + val ptrade = demandList.get(i) + data += print(ptrade.getPlayerId()) + data += print(ptrade.getAmount()) + data += print(ptrade.getPricePerOneMaterial()) + data += print(ptrade.getMaterial().ordinal()) + } + + } + + //return のところ + data +} + +} + +object Game { + def apply(names: Iterable[String], setting: GameSetting = GameSetting(), field: Field = null) = { + val nonNullField = if (field == null) Field(setting, new Random()) else field + new Game(Vector(names.zipWithIndex.map { case (n, i) => Player(i, n, setting) }.toStream: _*), + AuctionHall(), AlienTrade(setting), nonNullField, setting, 0, 1) + } +} diff --git a/src/main/scala/net/javachallenge/entity/GameSetting.scala b/src/main/scala/net/javachallenge/entity/GameSetting.scala new file mode 100644 index 0000000..8581971 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/GameSetting.scala @@ -0,0 +1,49 @@ +package net.javachallenge.entity + +import net.javachallenge.api.GameSettingBuilder +import scala.collection.JavaConverters._ + +/** + * @constructor + * @param initialMaterials the quantity of material owned by players when starting the game. + */ +@SerialVersionUID(0l) +case class GameSetting(val maxRound: Int = 200, val initialMoney: Int = 0, + val initialMaterials: Map[Material, Int] = Map(Gas -> 0, Stone -> 0, Metal -> 0), + val materialsForUpgradingMaterial: Vector[Map[Material, Int]] = Vector(Map(Gas -> 200, Stone -> 100, Metal -> 0), + Map(Gas -> 100, Stone -> 300, Metal -> 0)), + val materialsForUpgradingRobot: Vector[Map[Material, Int]] = Vector(Map(Gas -> 200, Stone -> 0, Metal -> 200), + Map(Gas -> 0, Stone -> 300, Metal -> 500)), + val mapSize: Int = 10, val veinCount: Int = 40, val bankMargin: Int = 4, val moveTurn: Int = 1) + extends net.javachallenge.api.GameSetting { + + require(maxRound > 0, "maxRound must be greater than zero") + require(initialMoney >= 0, "initialMoney must be greater than or equal to zero") + require(bankMargin > 0, "bankMargin must be greater than zero") + require(moveTurn > 0, "moveTurn must be greater than zero") + require(mapSize > 0, "mapSize must be greater than zero") + require(veinCount > 0, "veinCount must be greater than zero") + + override def getMaxRound() = maxRound + override def getInitialMoney() = initialMoney + override def getInitialMaterial(material: net.javachallenge.api.Material) = initialMaterials(Material(material)) + override def getMapSize() = mapSize + override def getVeinCount() = veinCount + override def getAlienTradeMargin() = bankMargin + override def getMaterialsForUpgradingMaterialRankFrom1To2(m: net.javachallenge.api.Material) = materialsForUpgradingMaterial(0)(m) + override def getMaterialsForUpgradingMaterialRankFrom2To3(m: net.javachallenge.api.Material) = materialsForUpgradingMaterial(1)(m) + override def getMaterialsForUpgradingRobotRankFrom1To2(m: net.javachallenge.api.Material) = materialsForUpgradingRobot(0)(m) + override def getMaterialsForUpgradingRobotRankFrom2To3(m: net.javachallenge.api.Material) = materialsForUpgradingRobot(1)(m) + + def toJavaMap(map: Map[Material, Int]) = map.map(t => { + val i: java.lang.Integer = t._2; + t._1 -> i + }).asJava +} + +object GameSetting { + def build(b: GameSettingBuilder) = + GameSetting(bankMargin = b.getAlienTradeMargin(), initialMoney = b.getInitialMoney(), + mapSize = b.getMapSize(), maxRound = b.getMaxRound(), veinCount = b.getVeinCount()) + val defaultInstance = GameSetting() +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/entity/InvalidCommandException.scala b/src/main/scala/net/javachallenge/entity/InvalidCommandException.scala new file mode 100644 index 0000000..10231f3 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/InvalidCommandException.scala @@ -0,0 +1,4 @@ +package net.javachallenge.entity + +class InvalidCommandException(val message: String) extends Exception(message) { +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/entity/Material.scala b/src/main/scala/net/javachallenge/entity/Material.scala new file mode 100644 index 0000000..ce072eb --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/Material.scala @@ -0,0 +1,88 @@ +package net.javachallenge.entity + +import net.javachallenge.util.internationalization.I18n + +/** + * Helpers and defaults values for material class. + */ +object Material { + case class UnknownMaterialException extends Exception + + val all = List(Gas, Stone, Metal) + + def apply(name: String) = name.toLowerCase() match { + case "gas" => Gas + case "stone" => Stone + case "metal" => Metal + case _ => throw new UnknownMaterialException + } + + implicit def apply(material: net.javachallenge.api.Material): Material = material match { + case net.javachallenge.api.Material.Gas => Gas + case net.javachallenge.api.Material.Stone => Stone + case net.javachallenge.api.Material.Metal => Metal + case _ => throw new UnknownMaterialException + } + + implicit def toJava(material: Material) = material.toJava + + def unapply(name: String) = name.toLowerCase() match { + case "gas" => Some(Gas) + case "stone" => Some(Stone) + case "metal" => Some(Metal) + case _ => None + } +} + +/** + * A material that can be found in veins. + */ +abstract sealed class Material { + /** + * The name of the material. + */ + val name: String + + protected def toJava(): net.javachallenge.api.Material + + override def toString: String = name +} + +/** + * A case object representing a 'gas'. + */ +@SerialVersionUID(0l) +case object Gas extends Material { + /** + * {@inheritDoc} + */ + val name: String = I18n("gas") + + override def toJava() = net.javachallenge.api.Material.Gas +} + +/** + * A case object representing a 'stone'. + */ +@SerialVersionUID(0l) +case object Stone extends Material { + /** + * {@inheritDoc} + */ + val name: String = I18n("stone") + + override def toJava() = net.javachallenge.api.Material.Stone +} + +/** + * A case object representing a 'metal'. + */ +@SerialVersionUID(0l) +case object Metal extends Material { + /** + * {@inheritDoc} + */ + val name: String = I18n("metal") + + override def toJava() = net.javachallenge.api.Material.Metal +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/entity/Player.scala b/src/main/scala/net/javachallenge/entity/Player.scala new file mode 100644 index 0000000..fcf4f23 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/Player.scala @@ -0,0 +1,135 @@ +package net.javachallenge.entity + +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.immutable.TreeMap + +/** + * A player of the game. + * + * @constructor creates a player + * @param name the name of the player to be created + * @param money the money owned by the player + * @param materials owned by the player. + */ +case class Player(val id: Int, val name: String, val money: Int, val materials: Map[Material, Int], val timeToLive: Int) + extends net.javachallenge.api.Player { + + require(money >= 0, "initialMoney must be greater than or equal to zero") + require(!materials.values.exists { amount => amount < 0 }, + "material amount must be greater than or equal to zero") + + override def getId = id + + override def getMaterial(material: net.javachallenge.api.Material) = { + require(material != null) + + materials(Material(material.name())) + } + + override def getMoney = money + + override def getLastActiveRound = getTimeToLive + + override def getTimeToLive = timeToLive + + override def toString = { + id + ". " + name + "(" + money + "J): " + materials + ", timeToLive: " + timeToLive + } + + def isNeutral = id == Player.neutralPlayer.id + + def earnRobots(game: Game) = { + val veins = mutable.Map(game.field.veins.toStream: _*) + for ((point, vein) <- game.field.owingVeins(this)) { + val newVein = vein.earnRobot() + veins += (point -> newVein) + } + TreeMap(veins.toStream: _*) + } + + /** + * Starts this player's turn. + * + * @param game the game instance containing all states + */ + def earnMaterials(game: Game) = { + val newMaterials = mutable.Map(materials.toStream: _*) + for ((point, vein) <- game.field.owingVeins(this)) { + newMaterials(vein.material) += vein.materialIncome + } + this.copy(materials = newMaterials.toMap) + } + + def updateTimeToLive(game: Game) = { + this.copy(timeToLive = game.round) + } + + def sellMaterial(material: Material, amount: Int, pricePerEachMaterial: Int) = { + if (amount <= 0) { + throw new InvalidCommandException("The material amount should be grater than 0.") + } + if (pricePerEachMaterial <= 0) { + throw new InvalidCommandException("The price should be grater than 0.") + } + + this.changeMaterial(material, -amount) + .changeMoney(pricePerEachMaterial * amount) + } + + def buyMaterial(material: Material, amount: Int, pricePerEachMaterial: Int) = { + if (amount <= 0) { + throw new InvalidCommandException("The material amount should be grater than 0.") + } + if (pricePerEachMaterial <= 0) { + throw new InvalidCommandException("The price should be grater than 0.") + } + + this.changeMaterial(material, amount) + .changeMoney(-pricePerEachMaterial * amount) + } + + /** + * Adds money of this player to the current money. + * + * @param addedAmount the amount to be added + */ + def changeMoney(addedAmount: Int) = { + val newMoney = money + addedAmount + if (newMoney < 0) { + throw new InvalidCommandException("You don't have enough money.") + } + this.copy(money = newMoney) + } + + /** + * Adds the specified material amount of this player to the current amount. + * + * @param materialKind the kind of the material to be added + * @param addedAmount the amount to be added + */ + def changeMaterial(materialKind: Material, addedAmount: Int) = { + val newAmount = materials(materialKind) + addedAmount + if (newAmount < 0) { + throw new InvalidCommandException("You don't have enough materials.") + } + this.copy(materials = materials + (materialKind -> newAmount)) + } +} + +/** + * Companion object for Player class containing factory method. + */ +object Player { + /** + * Neutral player who does nothing (it just holds the vein). + */ + val neutralPlayer = new Player(-1, "neutral", 0, Map().withDefaultValue(0), 0) + + /** + * Creates a player. + */ + def apply(id: Int, name: String, setting: GameSetting): Player = { + new Player(id, name, setting.initialMoney, setting.initialMaterials, 0) + } +} diff --git a/src/main/scala/net/javachallenge/entity/Squad.scala b/src/main/scala/net/javachallenge/entity/Squad.scala new file mode 100644 index 0000000..683e2b6 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/Squad.scala @@ -0,0 +1,229 @@ +package net.javachallenge.entity + +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.immutable.TreeMap +import scala.collection.JavaConverters._ + +/** + * A case class which represents a squad. + * @constructor Create a new squad with the specified arguments. + * @param ownerId the owner id of the squad + * @param robot the robot amount + * @param path the path from the current position to the goal + * @param onRoadTurn the number of the consumed turns to walk a side of a tile + */ +case class Squad(val ownerId: Int, val robot: Int, val onRoadRemainingTurn: Int, val path: List[TrianglePoint]) + extends net.javachallenge.api.Squad { + require(robot > 0, "robot must be greater than zero") + require(onRoadRemainingTurn > 0, "onRoadRemainingTurn must be greater than zero") + + override def getOwnerId() = ownerId + + override def getRobot() = robot + + override def getPath() = { + val ps: List[net.javachallenge.api.TrianglePoint] = path + new java.util.ArrayList(ps.asJava) + } + + override def getCurrentLocation() = path.head + + override def getDestinationLocation() = path.last + + /** + * The current location on the path. + * + * @return the current location + */ + def current = path.head + + /** + * Returns the value which indicates whether this squad is on a vertex. + * + * @return the value which indicates whether this squad is on a vertex + */ + def onVertex(game: Game) = onRoadRemainingTurn == game.setting.moveTurn + + def owner(game: Game) = game.players(ownerId) + + /** + * Returns the value which indicates whether this squad is conflicted enemy squads + * on a vertex. + * + * @return the value which indicates whether this squad is conflicted enemy squads + * on a vertex + */ + private def conflictedOnVertex(game: Game, that: Squad) = { + onRoadRemainingTurn + that.onRoadRemainingTurn == game.setting.moveTurn * 2 && + current == that.current + } + + /** + * Returns the value which indicates whether this squad is conflicted enemy squads + * on a road. + * + * @return the value which indicates whether this squad is conflicted enemy squads + * on a road + */ + private def conflictedOnRoad(game: Game, that: Squad) = { + onRoadRemainingTurn + that.onRoadRemainingTurn == game.setting.moveTurn && + current.isConnected(that.current) && + current == that.path.tail.head && path.tail.head == that.current + } +} + +object Squad { + + def apply(game: Game, robot: Int, path: List[TrianglePoint]) = { + new Squad(game.currentPlayerId, robot, game.setting.moveTurn, path) + } + + /** + * Advances the all squads of the current player. + * + * @param game the game instance containing whole states + */ + def advanceAll(game: Game) = { + val newVeins = mutable.Map(game.field.veins.toStream: _*) + val friends = game.field.squads.filter(_.ownerId == game.currentPlayerId) + val enemies = game.field.squads.filter(_.ownerId != game.currentPlayerId) + val (newFriends, newEnemies) = battle(game, move(game, friends), enemies, newVeins) + (newFriends ++ newEnemies, TreeMap(newVeins.toStream: _*)) + } + + /** + * Moves the all squads of the current player. + * + * @param ownSquads the squads of the current player + * @param newSquads new squads of the current player + */ + private def move(game: Game, squads: List[Squad], newSquads: List[Squad] = List()): List[Squad] = { + squads match { + case squad :: rest => { + val newSquad = if (squad.ownerId == game.currentPlayerId) { + var onRoadRemainingTurn = squad.onRoadRemainingTurn - 1 + var path = squad.path + if (onRoadRemainingTurn == 0) { + path = path.tail + onRoadRemainingTurn = game.setting.moveTurn + } + squad.copy(path = path, onRoadRemainingTurn = onRoadRemainingTurn) + } else { + squad + } + move(game, rest, newSquad :: newSquads) + } + case Nil => { + newSquads + } + } + } + + /** + * Battles the all squads of the current player with enemy squads. + * + * @param game the game instance containing whole states + * @param ownSquads the squads of the current player + * @param newSquads new squads of the current player + */ + private def battle(game: Game, friends: List[Squad], enemies: List[Squad], newVeins: mutable.Map[TrianglePoint, Vein], newFriends: List[Squad] = List()): (List[Squad], List[Squad]) = { + friends match { + case squad :: rest => { + if (squad.path.size == 1) { + newVeins += squad.current -> reach(game, squad, newVeins) + battle(game, rest, enemies, newVeins, newFriends) + } else { + val (newrobot, updatedEnemies) = + if (squad.onVertex(game)) + conflict(game, squad.robot, enemies, squad.conflictedOnVertex(game, _)) + else + conflict(game, squad.robot, enemies, squad.conflictedOnRoad(game, _)) + val updatedFriends = if (newrobot > 0) { + (squad.copy(robot = newrobot) :: newFriends) + } else { + newFriends + } + battle(game, rest, updatedEnemies, newVeins, updatedFriends) + } + } + case Nil => { + (newFriends, enemies) + } + } + } + + /** + * Processes that the given squads conflicts the enemy squads. + * + * @param squad the squad conflicting the enemy squads + * @param enemySquads the squads of the other players + * @param newSquads new squads of the current player + */ + private def conflict(game: Game, robot: Int, enemies: List[Squad], conflicted: Squad => Boolean, newEnemies: List[Squad] = List()): (Int, List[Squad]) = { + if (robot <= 0) { + (robot, enemies ++ newEnemies) + } else { + enemies match { + case enemy :: rest => { + if (conflicted(enemy)) { + var enemyrobot = enemy.robot + val damage = math.min(robot, enemyrobot) + val newrobot = robot - damage + enemyrobot -= damage + val updatedEnemies = if (enemyrobot > 0) { + enemy.copy(robot = enemyrobot) :: newEnemies + } else { + newEnemies + } + conflict(game, newrobot, rest, conflicted, updatedEnemies) + } else { + conflict(game, robot, rest, conflicted, enemy :: newEnemies) + } + } + case Nil => { + (robot, newEnemies) + } + } + } + } + + /** + * Processes that the given squad reaches the veins. + * + * @param game the game instance containing whole states + * @param squad the squad reaching the vein + */ + private def reach(game: Game, squad: Squad, newVeins: mutable.Map[TrianglePoint, Vein]) = { + val vein = newVeins(squad.current) + if (vein.ownerId == game.currentPlayerId) { + reachOwnVein(squad, vein) + } else { + reachEnemyVein(squad, vein) + } + } + + /** + * Processes that the given squad reaches the specified own vein. + * + * @param squad the squad reaching the vein + * @param vein the own vein where the squad reaches + */ + private def reachOwnVein(squad: Squad, vein: Vein) = { + vein.changeRobot(squad.robot) + } + + /** + * Processes that the given squad reaches the specified enemy vein. + * + * @param squad the squad reaching the vein + * @param vein the enemy vein where the squad reaches + */ + private def reachEnemyVein(squad: Squad, vein: Vein) = { + if (vein.robot >= squad.robot) { + vein.changeRobot(-squad.robot) + } else { + vein.conquer(squad) + } + } +} diff --git a/src/main/scala/net/javachallenge/entity/Trade.scala b/src/main/scala/net/javachallenge/entity/Trade.scala new file mode 100644 index 0000000..556a5e1 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/Trade.scala @@ -0,0 +1,146 @@ +package net.javachallenge.entity + +import net.javachallenge.api.TradeType + +/** + * Abstract class to represent a trade. + * + * @param id the id of the trade + * @param publisherId the id of the player publishing the trade + * @param material the material of the offer + * @param amount the amount of material in the offer + * @param price the price for ONE unity of the material + * @param done the boolean indicating if the transaction has been completed or not + * @param canceled the boolean indicating if the transaction has been canceled + */ +abstract sealed class Trade private[entity] (val publisherId: Int, val material: Material, val amount: Int, val price: Int) + extends net.javachallenge.api.PlayerTrade { + + require(material != null) + require(amount > 0, "Amount must be positive") + require(price > 0, "Price must be positive") + + override def getPlayerId = publisherId + override def getMaterial = material + override def getAmount = amount + override def getPricePerOneMaterial = price + override def getTradeType = if (this.isInstanceOf[Offer]) TradeType.Offer else TradeType.Demand + + def publisher(game: Game) = game.players(publisherId) + + /** + * Executes the core of the transaction, changing the amount of material left in the trade. + * + * @param game the game instance which contains whole game states + * @param customerId the id of the customer who wants to make the transaction with the trade + * @param transactionAmount the amount of material to be traded in the transaction + * @throws TradeException if the transaction cannot be achieved + */ + protected def makeCoreTransaction(game: Game, customerId: Int, transactionAmount: Int): (Map[(Int, Material), Trade], Player, Player) + + /** + * Executes the transaction, changing the amount of material left in the trade. + * + * @param game the game instance which contains whole game states + * @param customerId the id of the customer who wants to make the transaction with the trade + * @param transactionAmount the amount of material to be traded in the transaction + * @throws TradeException if the transaction cannot be achieved + */ + def makeTransaction(game: Game, customerId: Int, transactionAmount: Int) = { + if (transactionAmount > amount) { + throw new InvalidCommandException("The material amound for selling/buying should be less than the offered/demanded amount.") + } + makeCoreTransaction(game, customerId, transactionAmount) + } + + /** + * Cancels the transaction. + */ + def cancel(player: Player): Player +} + +/** + * An offer put by a person who wants to sell material. + * {@inheritdoc} + * @constructor creates a new offer + */ +case class Offer private[entity] (sellerId: Int, offerMaterial: Material, offerAmount: Int, offerPrice: Int) + extends Trade(sellerId, offerMaterial, offerAmount, offerPrice) { + + /** + * {@inheritdoc} + * Changes the amount of money and material of both players in the transaction. + */ + def makeCoreTransaction(game: Game, buyerId: Int, transactionAmount: Int) = { + val totalPrice = transactionAmount * price + val buyer = game.players(buyerId) + val newSeller = publisher(game).changeMoney(totalPrice) + val newBuyer = buyer.changeMoney(-totalPrice).changeMaterial(material, transactionAmount) + val newAmount = offerAmount - transactionAmount + + (if (newAmount == 0) Map.empty else Map((publisherId, material) -> this.copy(offerAmount = newAmount)), + newSeller, newBuyer) + } + + /** + * {@inheritdoc} + */ + override def cancel(player: Player) = { + require(player.id == sellerId) + player.changeMaterial(material, amount) + } +} + +/** + * Companion object for Offer class containing factory method. + */ +object Offer { + def publish(game: Game, sellerId: Int, material: Material, amount: Int, price: Int) = { + val seller = game.players(sellerId) + (new Offer(sellerId, material, amount, price), + seller.changeMaterial(material, -amount)) + } +} + +/** + * A demand put by a person who wants to buy material. + * {@inheritdoc} + * @constructor creates a new demand + */ +case class Demand private[entity] (buyerId: Int, demandMaterial: Material, demandAmount: Int, demandPrice: Int) + extends Trade(buyerId, demandMaterial, demandAmount, demandPrice) { + + /** + * {@inheritdoc} + * Changes the amount of money and material of both players in the transaction. + */ + override def makeCoreTransaction(game: Game, sellerId: Int, transactionAmount: Int) = { + val totalPrice = transactionAmount * price + val seller = game.players(sellerId) + val newBuyer = publisher(game).changeMaterial(material, transactionAmount) + val newSeller = seller.changeMoney(totalPrice).changeMaterial(material, -transactionAmount) + val newAmount = demandAmount - transactionAmount + + (if (newAmount == 0) Map.empty else Map((publisherId, material) -> this.copy(demandAmount = newAmount)), + newBuyer, newSeller) + } + + /** + * {@inheritdoc} + */ + override def cancel(player: Player) = { + require(player.id == buyerId) + player.changeMoney(price * amount) + } +} + +/** + * Companion object for Demand class containing factory method. + */ +object Demand { + def publish(game: Game, buyerId: Int, material: Material, amount: Int, price: Int) = { + val buyer = game.players(buyerId) + val totalPrice: Int = price * amount + (new Demand(buyerId, material, amount, price), buyer.changeMoney(-totalPrice)) + } +} diff --git a/src/main/scala/net/javachallenge/entity/TrianglePoint.scala b/src/main/scala/net/javachallenge/entity/TrianglePoint.scala new file mode 100644 index 0000000..5656546 --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/TrianglePoint.scala @@ -0,0 +1,112 @@ +package net.javachallenge.entity + +import net.javachallenge.entity.ApiConversion._ +import scala.collection.JavaConverters._ +import jp.ac.waseda.cs.washi.gameaiarena.api.Point2 + +/** + * A case class which represents a coordinate in triangle coordinate with the {@link Point2} instance. + */ +@SerialVersionUID(0l) +case class TrianglePoint(_x: Int, _y: Int) extends Point2(_x, _y) with net.javachallenge.api.TrianglePoint { + override def getX() = x + + override def getY() = y + + override def isUpwardTriangle = isUpward + + override def isDownwardTriangle = isDownward + + override def isConnected(that: net.javachallenge.api.TrianglePoint): Boolean = isConnected(that) + + override def getDistance(to: net.javachallenge.api.TrianglePoint) = getShortestPath(to).size - 1 + + override def getShortestPath(to: net.javachallenge.api.TrianglePoint) = { + val ps: List[net.javachallenge.api.TrianglePoint] = shortestPath(to) + new java.util.ArrayList(ps.asJava) + } + + override def toStringForCommand = cmdStr + + /** + * Returns a boolean whether this location is on an upward triangle. + * @return the a boolean whether this location is on an upward triangle + */ + def isUpward = ((x + y) & 1) == 0 + + /** + * Returns a boolean whether this location is on a downward triangle. + * @return the a boolean whether this location is on a downward triangle + */ + def isDownward = ((x + y) & 1) != 0 + + /** + * Returns a boolean whether this location connects with the specified location. + * @param that the location to be checked with this location + * @return the boolean whether this location connects with the specified location + */ + def isConnected(that: TrianglePoint): Boolean = { + val dx = x - that.x + val dy = y - that.y + if (dy == 0 && (dx == -1 || dx == 1)) { + true + } else if (isUpward) { + dx == 0 && dy == -1 + } else { + dx == 0 && dy == 1 + } + } + + /** + * Returns the list of the shortest path from this location to the specified location. + * @param to the destination location + * @return the list of the shortest path from this location to the specified location + */ + def shortestPath(to: TrianglePoint): List[TrianglePoint] = { + if (x < to.x || (x == to.x && y <= to.y)) { + val dy = y - to.y + val absdy = math.abs(dy) + val signedDy = if (absdy != 0) dy / absdy else 0 + shortestPath(signedDy, to :: Nil) + } else { + to.shortestPath(this).reverse + } + } + + private def shortestPath(signedDy: Int, path: List[TrianglePoint]): List[TrianglePoint] = { + val to = path.head + if (to.y == y) { + if (to.x == x) { + path + } else if (to.x < x) { + shortestPath(signedDy, TrianglePoint(to.x + 1, to.y) :: path) + } else { + shortestPath(signedDy, TrianglePoint(to.x - 1, to.y) :: path) + } + } else if (to.isConnected(TrianglePoint(to.x, to.y + signedDy))) { + shortestPath(signedDy, TrianglePoint(to.x, to.y + signedDy) :: path) + } else { + if (to.x < x) { + shortestPath(signedDy, TrianglePoint(to.x + 1, to.y) :: path) + } else { + shortestPath(signedDy, TrianglePoint(to.x - 1, to.y) :: path) + } + } + } + + def ==(that: TrianglePoint) = { + equals(that) + } + + def cmdStr = x + "," + y +} + +object TrianglePoint { + implicit def trianglePointOrdering: Ordering[TrianglePoint] = Ordering.fromLessThan((p1, p2) => { + if (p1.y == p2.y) { + p1.x < p2.x + } else { + p1.y < p2.y + } + }) +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/entity/Vein.scala b/src/main/scala/net/javachallenge/entity/Vein.scala new file mode 100644 index 0000000..6678d3f --- /dev/null +++ b/src/main/scala/net/javachallenge/entity/Vein.scala @@ -0,0 +1,101 @@ +package net.javachallenge.entity + +import net.javachallenge.util.internationalization.I18n +import scala.math._ +import scala.util._ + +/** + * A vein in the map. + * @param owner the owner of the vein + * @param materialRank the current mining level of the vein + * @param robotRank the current productivity level of the vein + */ +case class Vein(val location: TrianglePoint, val ownerId: Int, val material: Material, val robot: Int, + val materialProductivity: Int, val robotProductivity: Int, val materialRank: Int, val robotRank: Int) + extends net.javachallenge.api.Vein { + require(robot >= 0, "robot must be greater than or eqaul to zero") + require(robotProductivity >= 0, "robotProductivity must be greater than or equal to zero") + require(materialProductivity >= 0, "materialProductivity must be greater than or equal to zero") + require(1 <= robotRank && robotRank <= 3, "robotRank must be greater than 0 and less than 4") + require(1 <= materialRank && materialRank <= 3, "materialRank must be greater than 0 and less than 4") + + override def getOwnerId = ownerId + override def getLocation = location + override def getMaterial = material + override def getNumberOfRobots = robot + override def getCurrentMaterialProductivity = materialIncome + override def getCurrentRobotProductivity = robotIncome + override def getInitialMaterialProductivity = materialProductivity + override def getInitialRobotProductivity = robotProductivity + override def getMaterialRank = materialRank + override def getRobotRank = robotRank + override def getDistance(to: net.javachallenge.api.Vein) = location.getDistance(to.getLocation()) + override def getShortestPath(to: net.javachallenge.api.Vein) = location.getShortestPath(to.getLocation()) + + def materialIncome = materialProductivity * (1 << (materialRank - 1)) + + def robotIncome = robotProductivity * (1 << (robotRank - 1)) + + def owner(game: Game) = game.players(ownerId) + + def earnRobot() = this.copy(robot = robot + robotIncome) + + def changeRobot(addedRobot: Int) = this.copy(robot = robot + addedRobot) + + def conquer(squad: Squad) = { + require(ownerId != squad.ownerId) + this.copy(ownerId = squad.ownerId, robot = squad.robot - robot, materialRank = max(1, materialRank - 1), robotRank = max(1, robotRank - 1)) + } + + def occupy(playerId: Int) = { + if (ownerId != Player.neutralPlayer.id) { + throw new InvalidCommandException("The specified vein has been already taken.") + } + this.copy(ownerId = playerId) + } + + /** + * Upgrades material rank. + * + * @param new material rank + */ + def upgradeMaterialRank() = { + if (materialRank == 3) { + throw new InvalidCommandException("The material rank is already maximum.") + } + this.copy(materialRank = materialRank + 1) + } + + /** + * Upgrades robot rank. + * + * @param new robot rank + */ + def upgradeRobotRank() = { + if (robotRank == 3) { + throw new InvalidCommandException("The robot rank is already maximum.") + } + this.copy(robotRank = robotRank + 1) + } + + /** + * Returns vein information in String + * + * @return vein information in String + */ + override def toString: String = { + "owner=" + ownerId + + " material=" + material + + " robot=" + robot + + " materialRank=" + materialRank + + " robotRank=" + robotRank + + " materialProductivity=" + materialProductivity + + " robotProductivity=" + robotProductivity + } +} + +object Vein { + def apply(location: TrianglePoint, material: Material, robot: Int, materialProductivity: Int, robotProductivity: Int) = { + new Vein(location, Player.neutralPlayer.id, material, robot, materialProductivity, robotProductivity, 1, 1) + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/printer/Printer.scala b/src/main/scala/net/javachallenge/printer/Printer.scala new file mode 100644 index 0000000..60b446e --- /dev/null +++ b/src/main/scala/net/javachallenge/printer/Printer.scala @@ -0,0 +1,80 @@ +package net.javachallenge.printer + +import jp.ac.waseda.cs.washi.gameaiarena.api.Point2 +import net.javachallenge.entity.Vein +import net.javachallenge.entity.Game +import net.javachallenge.util.internationalization.I18n +import net.javachallenge.entity.Material + +/** + * An console printer for game objects. + */ +object Printer { + + /** + * Prints all the veins. + * + * @param veins the veins to display + */ + def veins(game: Game, print: String => Unit) { + for ((p, vein) <- game.field.veins) { + print("(%+3d,%+3d): %s".format(p.x, p.y, vein)) + } + } + + /** + * Prints the given location and the given vein. + * + * @param p the location of the vein to print + * @param vein the vein to print + */ + def vein(p: Point2, vein: Vein, print: String => Unit) { + print("(%+3d,%+3d): %s".format(p.x, p.y, vein)) + } + + /** + * Prints all the veins. + * + * @param veins the veins to display + */ + def squads(game: Game, print: String => Unit) { + for ((squad, index) <- game.field.squads.zipWithIndex) { + print("> %s".format(index, squad)) + } + } + + /** + * Prints all the veins. + * + * @param veins the veins to display + */ + def players(game: Game, print: String => Unit) { + for (player <- game.players) { + print("> %s".format(player.id, player)) + } + } + + /** + * Displays all the trades. + * + * @param trades a map containing all the available trades with their index + */ + def trades(game: Game, print: String => Unit) { + print("-" * 50) + print("%s: %s %s %s".format(I18n.get("id"), I18n.get("material"), + I18n.get("amount"), I18n.get("price"))) + print("-" * 50) + game.auctionHall.trades.values.zipWithIndex foreach { + case (trade, id) => + print("%d: %s %d %d".format(id, trade.material, trade.amount, trade.price)) + } + print("-" * 50) + } + + def rate(game: Game, print: String => Unit) { + for (material <- Material.all) { + print("A price of " + material + " to buy from the bank: " + game.alienTrade.buyPriceOf(material)) + print("A price of " + material + " to sell to the bank: " + game.alienTrade.sellPriceOf(material)) + } + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/runner/InitialRunner.scala b/src/main/scala/net/javachallenge/runner/InitialRunner.scala new file mode 100644 index 0000000..8d89247 --- /dev/null +++ b/src/main/scala/net/javachallenge/runner/InitialRunner.scala @@ -0,0 +1,29 @@ +package net.javachallenge.runner + +import net.javachallenge.api.ComputerPlayer +import net.javachallenge.api.command.Command +import jp.ac.waseda.cs.washi.gameaiarena.runner.AbstractRunner +import java.util.Collections +import net.javachallenge.api.TrianglePoint +import net.javachallenge.api.Game + +class InitialRunner(com: ComputerPlayer) extends AbstractRunner[Game, TrianglePoint, ComputerPlayer] { + private var _game: Game = null + private var _location: TrianglePoint = null + + def getComputerPlayer() = com + + def runPreProcessing(game: Game) { + _game = game + _location = null + com.saveTemporalVeinLocation(null) + } + + def runProcessing() { + _location = com.selectVein(_game) + } + + def runPostProcessing() = { + if (_location != null) _location else com.getTemporalVeinLocation() + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/runner/MainRunner.scala b/src/main/scala/net/javachallenge/runner/MainRunner.scala new file mode 100644 index 0000000..54bf33b --- /dev/null +++ b/src/main/scala/net/javachallenge/runner/MainRunner.scala @@ -0,0 +1,31 @@ +package net.javachallenge.runner + +import net.javachallenge.api.ComputerPlayer +import net.javachallenge.api.command.Command +import jp.ac.waseda.cs.washi.gameaiarena.runner.AbstractRunner +import java.util.Collections +import net.javachallenge.api.Game + +class MainRunner(com: ComputerPlayer) extends AbstractRunner[Game, Array[String], ComputerPlayer] { + private var _game: Game = null + private var _commands: Array[Command] = null + + def getComputerPlayer() = com + + def runPreProcessing(game: Game) { + _game = game + _commands = null + com.saveTemporalCommands(null) + } + + def runProcessing() { + val commands = com.selectActions(_game) + _commands = if (commands != null) commands.toArray(Array()) else null + } + + def runPostProcessing() = { + val nullableCommands = if (_commands != null) _commands else com.getTemporalCommands() + val commands = if (nullableCommands != null) nullableCommands else Array() + commands.map(cmd => cmd.toString).take(100).toArray + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/AbstractScene.scala b/src/main/scala/net/javachallenge/scene/AbstractScene.scala new file mode 100644 index 0000000..287eccd --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/AbstractScene.scala @@ -0,0 +1,53 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import scala.io.Source +import java.util.Scanner +import net.javachallenge.entity.Game +import java.io.File +import com.google.common.io.Files +import net.javachallenge.util.misc.DateUtils +import java.nio.charset.Charset +import net.javachallenge.util.settings.Defaults + +abstract class AbstractScene extends Scene[GameEnvironment] { + + new File("log").mkdir() + val writer = Files.newWriter(new File("log/log_" + DateUtils.dateStringForFileName + ".txt"), Charset.defaultCharset) + + def env = getEnvironment + def renderer = env.getRenderer + def inputer = env.getInputer + + def game = env.game + def game_=(game: Game) = { + getEnvironment.game = game + game + } + + def execute(): (Game, Scene[GameEnvironment]) + + final override def run() = { + val (newGame, scene) = execute() + game = newGame + scene + } + + protected def displayCore(text: String) + + final def display(text: String) { + displayCore(text) + writer.write(text) + writer.flush() + } + + final def displayLine(text: String) { + display(text + Defaults.NEW_LINE) + } + + final def describe(sceneDescription: String) { + val dashes = "-" * 20 + displayLine(dashes + sceneDescription + dashes) + } +} diff --git a/src/main/scala/net/javachallenge/scene/CommandBaseScene.scala b/src/main/scala/net/javachallenge/scene/CommandBaseScene.scala new file mode 100644 index 0000000..ea1f497 --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/CommandBaseScene.scala @@ -0,0 +1,27 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.Game +import net.javachallenge.Main +import net.javachallenge.util.settings.Defaults +import com.google.common.io.Files +import java.io.File +import java.nio.charset.Charset +import net.javachallenge.util.misc.DateUtils + +abstract class CommandBaseScene extends AbstractScene { + override def execute() = { + nextCommand match { + case Some(command) => + displayLine("> " + command.mkString(" ")) + if (command.length > 0) execute(command) else (game, this) + case None => + (game, this) + } + } + + def nextCommand: Option[List[String]] + + def execute(words: List[String]): (Game, Scene[GameEnvironment]) +} diff --git a/src/main/scala/net/javachallenge/scene/EmptyScene.scala b/src/main/scala/net/javachallenge/scene/EmptyScene.scala new file mode 100644 index 0000000..a1f94f5 --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/EmptyScene.scala @@ -0,0 +1,15 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.Game + +abstract class EmptyScene(val nextScene: Scene[GameEnvironment]) extends CommandBaseScene { + override def execute() = { + (game, nextScene) + } + + override def nextCommand: Option[List[String]] = throw new Exception() + + override def execute(words: List[String]): (Game, Scene[GameEnvironment]) = throw new Exception() +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/InitialRunnerScene.scala b/src/main/scala/net/javachallenge/scene/InitialRunnerScene.scala new file mode 100644 index 0000000..0a92c7b --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/InitialRunnerScene.scala @@ -0,0 +1,24 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.runner.AbstractRunner +import net.javachallenge.api.Game +import net.javachallenge.api.ComputerPlayer +import net.javachallenge.entity.TrianglePoint +import scala.util.Random +import net.javachallenge.entity.Player +import net.javachallenge.entity.ApiConversion._ + +trait InitialRunnerScene extends CommandBaseScene { + var runners: IndexedSeq[AbstractRunner[Game, net.javachallenge.api.TrianglePoint, ComputerPlayer]] = null + + override def nextCommand = { + val location: TrianglePoint = runners(game.currentPlayerId).run(game) + val isValid = (p: TrianglePoint) => game.field.veins.contains(p) && + game.field.veins(p).ownerId == Player.neutralPlayer.id + val validLocation = if (isValid(location)) + location + else + game.field.validCoords.filter(isValid).head + Some(List(validLocation.cmdStr)) + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/MainRunnerScene.scala b/src/main/scala/net/javachallenge/scene/MainRunnerScene.scala new file mode 100644 index 0000000..b62b4ed --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/MainRunnerScene.scala @@ -0,0 +1,33 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.runner.AbstractRunner +import net.javachallenge.api.Game +import net.javachallenge.api.ComputerPlayer +import net.javachallenge.entity.TrianglePoint +import scala.util.Random +import scala.collection.immutable +import scala.collection.mutable +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment + +trait MainRunnerScene extends CommandBaseScene { + var runners: IndexedSeq[AbstractRunner[Game, Array[String], ComputerPlayer]] = null + val queue: mutable.Queue[List[String]] = mutable.Queue() + + override def execute() = { + var newScene: Scene[GameEnvironment] = this + val cmdStrs = runners(game.currentPlayerId).run(game) ++ Array("finish") + for (cmdStr <- cmdStrs) { + val command = cmdStr.split(" ").filter(_.length > 0).toList + displayLine("> " + command.mkString(" ")) + val (nextGame, nextScene) = if (command.length > 0) execute(command) else (game, this) + game = nextGame + newScene = nextScene + } + (game, newScene) + } + + override def nextCommand = { + throw new Exception() + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/MainScene.scala b/src/main/scala/net/javachallenge/scene/MainScene.scala new file mode 100644 index 0000000..5ccc517 --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/MainScene.scala @@ -0,0 +1,236 @@ +package net.javachallenge.scene + +import net.javachallenge.entity.Game +import net.javachallenge.printer.Printer +import net.javachallenge.util.internationalization.I18n +import net.javachallenge.entity.Material +import net.javachallenge.util.misc.IntStr +import net.javachallenge.util.misc.IndexStr +import net.javachallenge.util.misc.TrianglePointStr +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.Offer +import net.javachallenge.entity.Demand +import net.javachallenge.entity.InvalidCommandException + +abstract class MainScene(val nextScene: Scene[GameEnvironment]) extends CommandBaseScene { + val commands: Map[String, List[String] => Game] = + Map("h" -> help _, "st" -> serialize _, + "p" -> players _, "v" -> veins _, "t" -> trades _, + "sq" -> squads _, + "bu" -> buy _, "se" -> sell _, + "o" -> offer _, "d" -> demand _, "ba" -> bank _, + "l" -> launch _, "u" -> upgrade _, "r" -> rate _, + "f" -> finish _) + + override def initialize() { + describe("The game starts!") + outputTurnInfo(game) + } + + def outputTurnInfo(game: Game) { + displayLine("Round = %d, Player = %s".format(game.round, game.currentPlayer)) + displayLine("Please enter a command name and arguments, or just enter 'help'.") + } + + def help(args: List[String]) = { + displayLine("You can use the following commands:") + displayLine("%45s : %s".format("st[ring]", "Show the serialized game instance")) + displayLine("%45s : %s".format("p[layers]", "Show the player list")) + displayLine("%45s : %s".format("v[eins]", "Show the vein list")) + displayLine("%45s : %s".format("sq[uads]", "Show the squad list")) + displayLine("%45s : %s".format("t[rades]", "Show the offer/demand list")) + displayLine("%45s : %s".format("r[ate]", "Show the rate of trade with the bank")) + displayLine("%45s : %s".format("bu[y] ", "Buy materials for the given offer")) + displayLine("%45s : %s".format("se[ll] ", "Sell materials for the given demand")) + displayLine("%45s : %s".format("o[ffer] ", "Make offer for selling the given materials and price of a material")) + displayLine("%45s : %s".format("d[emand] ", "Make demand for buying the given materials and price of a material")) + displayLine("%45s : %s".format("ba[nk] <'buy' | 'sell'> ", "Buy materials for the given offer")) + displayLine("%45s : %s".format("l[aunch] ", "Send a squad for occupying another vein")) + displayLine("%45s : %s".format("u[pgrade] ", "Upgrade material or power rank of the given vein")) + displayLine("%45s : %s".format("f[inish]", "Finish your turn and advance to the next turn")) + displayLine("You shoud specify location without any spaces such as 10,-5.") + displayLine("Mouse left click: Show the information of the clicked vein or/and squad.") + displayLine("Mouse right click: Enter the clicked location in the command textbox.") + game + } + + def execute(commandAndArgs: List[String]) = { + val command = commandAndArgs.head + val args = commandAndArgs.tail + + val c1 = command.substring(0, 1) + val c2 = (command + " ").substring(0, 2) + val cmd = if (commands.contains(c1)) c1 else c2 + + val newGame = commands.get(cmd) match { + case Some(cmdFunc) => { + try { + cmdFunc(args) + } catch { + case e: InvalidCommandException => + displayLine("Error: " + e.getMessage) + game + case e: MatchError => + displayLine("Input command is wrong format.") + game + } + } + case _ => { + displayLine("error: %s".format(I18n.get("unknown_error") + "(" + command + ")")) + game + } + } + (newGame, if (!newGame.isEnded) this else nextScene) + } + + /** + * Displays the serialized game instance. + * + * @param args arguments for this command + */ + def serialize(args: List[String]) = { + displayLine(game.toString) + game + } + + /** + * Displays all the squads. + * + * @param args arguments for this command + */ + def squads(args: List[String]) = { + Printer.squads(game, displayLine) + game + } + + /** + * Displays all the players. + * + * @param args arguments for this command + */ + def players(args: List[String]) = { + Printer.players(game, displayLine) + game + } + + /** + * Displays all veins. + * + * @param args arguments for this command + */ + def veins(args: List[String]) = { + Printer.veins(game, displayLine) + game + } + + /** + * Displays all the available trades. + */ + def trades(args: List[String]) = { + Printer.trades(game, displayLine) + game + } + + /** + * Buys materials from an existing offer. + */ + def buy(args: List[String]) = buyOrSell(args, true) + + /** + * Sells materials to an existing demand. + */ + def sell(args: List[String]) = buyOrSell(args, false) + + private def buyOrSell(args: List[String], isBuy: Boolean) = { + val (IntStr(publisherId) :: Material(material) :: IntStr(amount) :: Nil) = args + game.auctionHall.trades.get((publisherId, material)) match { + case Some(offer: Offer) if isBuy => game.buy(offer, amount) + case Some(demand: Demand) if !isBuy => game.sell(demand, amount) + case _ => throw new IllegalArgumentException("There is no specified trade") + } + } + + /** + * Makes an offer to sell material. + */ + def offer(args: List[String]) = offerOrDemand(args, true) + + /** + * Makes a demand to buy material. + */ + def demand(args: List[String]) = offerOrDemand(args, false) + + def offerOrDemand(args: List[String], isOffer: Boolean) = { + val (Material(material) :: IntStr(amount) :: IntStr(price) :: Nil) = args + if (isOffer) + game.offer(material, amount, price) + else + game.demand(material, amount, price) + } + + /** + * Sends a squad to the specified vein with given robots. + * + * @param args arguments for this command + */ + def launch(args: List[String]) = { + val (IntStr(robot) :: TrianglePointStr(from) :: TrianglePointStr(to) :: rest) = args + if (rest.isEmpty) { + game.sendSquad(robot, from, to) + } else { + game.sendSquad(robot, from :: to :: rest.map { case TrianglePointStr(p) => p }) + } + } + + def bank(args: List[String]) = { + val (cmd :: Material(material) :: IntStr(amount) :: Nil) = args + cmd match { + case "buy" => { + game.tradeWithAlien(material, amount, true) + } + case "sell" => { + game.tradeWithAlien(material, amount, false) + } + } + } + + /** + * Invests a material or robot technology for enhancing the rank. + * + * @param args arguments for this command + */ + def upgrade(args: List[String]) = { + val (kindStr :: TrianglePointStr(location) :: _) = args + if (!game.field.veins.contains(location)) { + throw new IllegalArgumentException("Wrong vein location.") + } + val vein = game.field.veins(location) + kindStr match { + case "material" => { + game.upgrade(location, vein, true) + } + case "robot" => { + game.upgrade(location, vein, false) + } + } + } + + def rate(args: List[String]) = { + Printer.rate(game, displayLine) + game + } + + /** + * Advances this turn to activate the next player. + * + * @param args arguments for this command + */ + def finish(args: List[String]) = { + val newGame = game.advanceTurn() + if (!newGame.isEnded) { + outputTurnInfo(newGame) + } + newGame + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/PlayerScene.scala b/src/main/scala/net/javachallenge/scene/PlayerScene.scala new file mode 100644 index 0000000..a3bba9c --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/PlayerScene.scala @@ -0,0 +1,26 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.entity.Field +import net.javachallenge.entity.Game +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.GameSetting +import java.util.Random + +abstract class PlayerScene(nextScene: Scene[GameEnvironment], setting: GameSetting = GameSetting()) + extends CommandBaseScene { + override def initialize() { + describe("Enter player names") + displayLine("Please enter player names with space delimiters.") + } + + override def execute(names: List[String]) = { + if (names.size <= 1) { + displayLine("Please enter two or more names.") + (game, this) + } else { + displayLine(names.size + " players have joined the game. (" + names.mkString(", ") + ")") + (Game(names, setting, Field(setting, new Random())), nextScene) + } + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/ResultScene.scala b/src/main/scala/net/javachallenge/scene/ResultScene.scala new file mode 100644 index 0000000..b6cbdea --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/ResultScene.scala @@ -0,0 +1,70 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.Player +import scala.util.Sorting +import net.javachallenge.util.misc.ImageLoader +import java.awt.Color +import java.awt.Font +import java.awt.Graphics2D +import java.awt.RenderingHints + +trait ResultScene extends CommandBaseScene { + + var sortedPlayers: Array[Player] = null + + override def initialize() = { + game = game.clearAuctionHall() + displayLine("The game ends!") + val veinCounts = game.field.veinCounts + displayLine("time to live: " + game.players.map(p => p.name + ":" + p.timeToLive).mkString(", ")) + displayLine("veins: " + game.players.map(p => p.name + ":" + veinCounts(p.id)).mkString(", ")) + displayLine("robots: " + game.players.map(p => p.name + ":" + game.field.sumRobots(p.id)).mkString(", ")) + displayLine("money: " + game.players.map(p => p.name + ":" + game.getTotalMoneyWhenSellingAllMaterials(p.id)).mkString(", ")) + sortedPlayers = Sorting.stableSort(game.players, (p1: Player, p2: Player) => { + if (p1.timeToLive != p2.timeToLive) + p1.timeToLive > p2.timeToLive + else if (veinCounts(p1.id) != veinCounts(p2.id)) + veinCounts(p1.id) > veinCounts(p2.id) + else if (game.field.sumRobots(p1.id) != game.field.sumRobots(p2.id)) + game.field.sumRobots(p1.id) > game.field.sumRobots(p2.id) + else if (game.getTotalMoneyWhenSellingAllMaterials(p1.id) != game.getTotalMoneyWhenSellingAllMaterials(p2.id)) + game.getTotalMoneyWhenSellingAllMaterials(p1.id) > game.getTotalMoneyWhenSellingAllMaterials(p2.id) + else + p1.id < p2.id + }) + } + + override def draw() { + val backImg = ImageLoader.loadResultBackground(renderer) + val numbers = ImageLoader.loadResultNumber(renderer) + val lamp = ImageLoader.loadLamp(renderer) + + val fontColor = new Color(255, 190, 104) + + val font = new Font(Font.SERIF, Font.BOLD, 24); + val graphics = renderer.getPanel().getGraphics().asInstanceOf[Graphics2D] + graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); + + def drawNumber(value: Int, right: Int, top: Int, size: Int) { + var x = right; + value.toString.reverse.foreach { d => + x -= size / 2 + + renderer.drawImage(numbers(size)(d.toInt - '0'.toInt), x, top) + } + } + + renderer.drawImage(backImg, 0, 0) + for ((player, index) <- sortedPlayers.zipWithIndex) { + renderer.drawImage(lamp(player.id), 72, 56 + 72 * index) + renderer.drawString(player.name, 136, 104 + 72 * index, fontColor, font) + drawNumber(player.timeToLive, 392 + 96, 56 + 72 * index, 64) + drawNumber(game.field.veinCounts(player.id), 544 + 64, 56 + 72 * index, 64) + drawNumber(game.field.sumRobots(player.id), 680 + 144, 72 + 72 * index, 48) + drawNumber(game.getTotalMoneyWhenSellingAllMaterials(player.id), 880 + 144, 72 + 72 * index, 48) + } + } +} diff --git a/src/main/scala/net/javachallenge/scene/VeinScene.scala b/src/main/scala/net/javachallenge/scene/VeinScene.scala new file mode 100644 index 0000000..0bec382 --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/VeinScene.scala @@ -0,0 +1,65 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.util.misc.IndexStr +import net.javachallenge.entity.Player +import net.javachallenge.entity.Game +import net.javachallenge.printer.Printer +import net.javachallenge.util.misc.TrianglePointStr + +abstract class VeinScene(val nextScene: Scene[GameEnvironment]) + extends CommandBaseScene { + var selectCount = 0 + + def veins = game.field.veins.toStream + + override def initialize() { + describe("Generated veins") + Printer.veins(game, displayLine) + describe("Select own veins") + startSelect(game) + } + + def startSelect(game: Game) { + displayLine("%s: Choose your vein by number".format(game.currentPlayer.name)) + } + + override def execute(words: List[String]) = { + try { + val TrianglePointStr(location) :: Nil = words + if (!game.field.veins.contains(location)) { + throw new IllegalArgumentException("Wrong vein location, please try again.") + } + + val vein = game.field.veins(location) + if (vein.ownerId != Player.neutralPlayer.id) { + throw new IllegalArgumentException("The specified vein has been already taken.") + } + display("Your choice: ") + Printer.vein(location, vein, displayLine) + val (newSelectCount, newGame) = game.occupy(selectCount, location) + selectCount = newSelectCount + if (isFinished) { + (newGame, nextScene) + } else { + startSelect(newGame) + (newGame, this) + } + } catch { + case e: MatchError => { + displayLine("Wrong format, please try again.") + (game, this) + } + case e: IllegalArgumentException => { + displayLine(e.getMessage) + (game, this) + } + } + } + + def isFinished = { + val nPlayer = game.playerCount + (selectCount >= nPlayer * 2) + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/WaitingScene.scala b/src/main/scala/net/javachallenge/scene/WaitingScene.scala new file mode 100644 index 0000000..9f65e2b --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/WaitingScene.scala @@ -0,0 +1,18 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.Game + +abstract class WaitingScene(val nextScene: Scene[GameEnvironment]) extends CommandBaseScene { + override def execute() = { + if (getInputer().isPush(0)) + (game, nextScene) + else + (game, this) + } + + override def nextCommand: Option[List[String]] = throw new Exception() + + override def execute(words: List[String]): (Game, Scene[GameEnvironment]) = throw new Exception() +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/console/ConsoleScene.scala b/src/main/scala/net/javachallenge/scene/console/ConsoleScene.scala new file mode 100644 index 0000000..94939ac --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/console/ConsoleScene.scala @@ -0,0 +1,18 @@ +package net.javachallenge.scene.console + +import java.util.Scanner +import net.javachallenge.scene.CommandBaseScene + +trait ConsoleScene extends CommandBaseScene { + override def nextCommand = ConsoleScene.nextCommand + + override def displayCore(text: String) = print(text) +} + +object ConsoleScene { + var scanner = new Scanner(System.in) + + def nextCommand = { + Some(scanner.nextLine().split(" ").filter(_.length > 0).toList) + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/graphic/CompositeGraphicalScene.scala b/src/main/scala/net/javachallenge/scene/graphic/CompositeGraphicalScene.scala new file mode 100644 index 0000000..4e3c06c --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/graphic/CompositeGraphicalScene.scala @@ -0,0 +1,18 @@ +package net.javachallenge.scene.graphic + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.util.misc.ImageLoader + +trait CompositeGraphicalScene extends Scene[GameEnvironment] { + var sceneList: List[Scene[GameEnvironment]] = List(); + override def draw() = { + for (scene <- sceneList) { + scene.draw() + } + } + + def addScene(scene: Scene[GameEnvironment]) = { + sceneList = scene :: sceneList + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/graphic/GraphicalScene.scala b/src/main/scala/net/javachallenge/scene/graphic/GraphicalScene.scala new file mode 100644 index 0000000..07c25f2 --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/graphic/GraphicalScene.scala @@ -0,0 +1,373 @@ +package net.javachallenge.scene.graphic + +import scala.math.abs +import scala.collection.mutable +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import jp.ac.waseda.cs.washi.gameaiarena.api.Point2 +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.Game +import net.javachallenge.scene.AbstractScene +import net.javachallenge.util.misc.ImageLoader +import net.javachallenge.entity.Material +import net.javachallenge.scene.CommandBaseScene +import java.awt.Color +import java.awt.Font + +trait GraphicalScene extends AbstractScene { + /* Image sizes (width, height) */ + val materialImageSize = Size(16, 16) + val veinImageSize = Size(38, 25) + val plusImageSize = Size(5, 5) + val robotImageSize = Size(10, 9) + val numberImageSize = Size(6, 9) + val roundTitleImageSize = Size(128, 32) + val roundSlashImageSize = Size(32, 32) + val roundNumberImageSize = Size(20, 32) + val veinTopMargin = veinImageSize.x - veinImageSize.y + val font = new Font(Font.MONOSPACED, Font.PLAIN, 12); + + class Size(val x: Int, val y: Int) + object Size { + def apply(x: Int, y: Int) = new Size(x, y) + } + + def drawTile() { + val tileImg = ImageLoader.loadTiles(renderer)(env.tileSize) + for (y <- -(game.setting.mapSize - 1) to (game.setting.mapSize - 1)) { + for (x <- -(game.setting.mapSize - 1) to (game.setting.mapSize - 1) - abs(y)) { + val (px, py) = env.toHexPoint(x, y) + renderer.drawImage(tileImg, px + env.offsetX, py + env.offsetY) + } + } + } + + def drawRound() { + // val roundTitle = ImageLoader.loadRoundTitle(renderer) + val roundSlash = ImageLoader.loadRoundSlash(renderer) + val roundPosition = new Point2(10, 10) + + // renderer.drawImage(roundTitle, roundPosition.x, roundPosition.y) + renderer.drawImage(roundSlash, roundPosition.x + roundTitleImageSize.x / 2 - roundSlashImageSize.x / 2 + 2, + roundPosition.y) + + def drawRoundNumber(value: Int, right: Int, top: Int) { + val numberImages = ImageLoader.loadRoundNumber(renderer) + var x = right; + value.toString.reverse.foreach { d => + x -= roundNumberImageSize.x + + renderer.drawImage(numberImages(d.toInt - '0'.toInt), x, top) + } + } + drawRoundNumber(math.min(game.round, game.setting.maxRound), 60, roundPosition.y) + drawRoundNumber(game.setting.maxRound, roundPosition.x + roundTitleImageSize.x / 2 + roundSlashImageSize.x / 2 + roundNumberImageSize.x * 3, roundPosition.y) + + } + + /** + * NOTE align: right + */ + def drawNumber(format: String, number: Int, x: Int, y: Int, ownerId: Int = -1) { + val numberImages = ImageLoader.loadNumbers(renderer) + var drawX = x; + format.format(number).reverse.foreach { d => + drawX -= numberImageSize.x + renderer.drawImage(numberImages(ownerId, d.toString.toInt), drawX, y) + } + } + + def drawVeins() { + val materialImages = ImageLoader.loadMaterials(renderer) + val veinImages = ImageLoader.loadVeins(renderer) + val plusImages = ImageLoader.loadPlusMarks(renderer) + val numberImages = ImageLoader.loadNumbers(renderer) + + + for ((p, v) <- game.field.veins) { + val px = env.trianglePointToPixelPoint(p) + val veinLeft = px.x - veinImageSize.x / 2 + val veinTop = px.y - (veinImageSize.y + veinTopMargin) / 2 + val veinRight = px.x + veinImageSize.x / 2 + val veinBottom = px.y + (veinImageSize.y + veinTopMargin) / 2 + + renderer.drawImage(materialImages(v.material), px.x - materialImageSize.x / 2, px.y - materialImageSize.y / 2) + renderer.drawImage(veinImages(v.ownerId), veinLeft, veinTop + veinTopMargin) + + /* Draw plus images according to ranks */ + for (i <- 1 to 3) { + renderer.drawImage(plusImages(if (v.materialRank >= i) v.ownerId else -1), veinLeft + (i - 2) * plusImageSize.x, veinTop + veinTopMargin) + renderer.drawImage(plusImages(if (v.robotRank >= i) v.ownerId else -1), veinRight + (i - 3) * plusImageSize.x, veinTop + veinTopMargin) + } + drawNumber("%02d", v.materialIncome, + veinLeft - 3 + plusImageSize.x * 2, + veinTop + veinTopMargin + plusImageSize.y + 1, + v.ownerId) + drawNumber("%02d", v.robotIncome, + veinRight - 8 + plusImageSize.x * 2, + veinTop + veinTopMargin + plusImageSize.y + 1, + v.ownerId) + + drawNumber("%03d", v.robot, + px.x - numberImageSize.x / 2 * 3 + 1 + plusImageSize.x * 3, + veinBottom - numberImageSize.y, + v.ownerId) + } + } + + def drawPlayers() = { + val numberImages = ImageLoader.loadNumbers(renderer) + val infoImages = ImageLoader.loadPlayerInformationBackgrounds(renderer) + val highlightImage = ImageLoader.loadPlayerHighlightFrame(renderer) + val activeStarImage = ImageLoader.loadActivePlayerStar(renderer) + + val buyIconImage = ImageLoader.loadBuyIcon(renderer) + val sellIconImage = ImageLoader.loadSellIcon(renderer) + + val infoImageSize = Size(176, 68) + val infoMargin = 0 + val infoAreaX = if (env.tileSize == 32) 672 else 992 + val infoAreaY = 0 + + val pid2Material2Income = game.field.totalMaterialIncomes + val pid2VeinRobotAndIncome = game.field.totalVeinRobotAndIncomes + val pid2SquadRobot = game.field.totalSquadRobots + + for (player <- game.players) { + // playerArea: area for individual players + val playerAreaTop = infoAreaY + (infoImageSize.y + infoMargin) * player.id + renderer.drawImage(infoImages(player.id), + infoAreaX, + playerAreaTop) + + /* Highlight active player */ + if (player == game.currentPlayer) { + renderer.drawImage(highlightImage, + infoAreaX, + playerAreaTop) + renderer.drawImage(activeStarImage, + infoAreaX - 32, + playerAreaTop) + + } + + /* Name */ + val g = renderer.getPanel().getGraphics() + renderer.drawString(player.name, + infoAreaX + 5, + playerAreaTop + 14, // 14 -> font size + Color.WHITE, font) + + /* Status */ + + /* vein Count */ + { + + val x = infoAreaX + infoImageSize.x + 106 + var numX = x + val y = playerAreaTop + 5 + + game.field.countVeins(player.id).toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX, y) + numX -= numberImageSize.x + } + } + + /* money */ + { + val x = infoAreaX + 186 + var numX = x + val y = playerAreaTop + 19 + + player.getMoney.toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX, y) + numX -= numberImageSize.x + } + } + + /* totalmoney */ + { + val x = infoAreaX + 282 + var numX = x + val y = playerAreaTop + 19 + + game.getTotalMoneyWhenSellingAllMaterials(player.id).toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX, y) + numX -= numberImageSize.x + } + } + + val offsetX = 0 + val offsetY = 5 + for (i <- 0 to 2) { + val y = playerAreaTop + (infoImageSize.y / 5) * (i + 2) - 1 * i // -1*i because of higher top area + + /* material info (number of materials, number of veins for that material) */ + val materialInfo = List( + player.materials.getOrElse(Material.all(i), 0), + pid2Material2Income(player.id)(Material.all(i))) + + for (j <- 0 to 1) { + val x = infoAreaX + 55 + 48 * j - numberImageSize.x + + var numX = x + materialInfo(j).toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX + offsetX, y + offsetY) + numX -= numberImageSize.x + } + } + } + + /* Total robots and veins */ + val totalrobot = pid2VeinRobotAndIncome(player.id)._1 + pid2SquadRobot(player.id) + val numbers = List(totalrobot, pid2VeinRobotAndIncome(player.id)._2) + for (j <- 0 to 1) { + val y = playerAreaTop + (infoImageSize.y / 5) + 1 + val x = infoAreaX + 55 + 48 * j - numberImageSize.x + + var numX = x + numbers(j).toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX + offsetX, y + offsetY) + numX -= numberImageSize.x + } + } + + /* Trade */ + + for (i <- 0 to 2) { + val y = playerAreaTop + (infoImageSize.y / 5) * (i + 2) - 1 * i // -1*i because of higher top area + + /* material info (number of materials, number of veins for that material) */ + val materialInfo = List( + player.materials.getOrElse(Material.all(i), 0), + pid2Material2Income(player.id)(Material.all(i))) + + /* offer */ + for (j <- 0 to 1) { + val trade = if (j == 0) game.getOffer(player.id, Material.all(i)) else game.getDemand(player.id, Material.all(i)) + if (trade != null) { + + if (j == 0) { + renderer.drawImage(sellIconImage, infoAreaX + 110, y + 5) + } else { + renderer.drawImage(buyIconImage, infoAreaX + 110, y + 5) + } + + { + val x = infoAreaX + 169 + var numX = x + + trade.amount.toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX + offsetX, y + offsetY) + numX -= numberImageSize.x + } + } + { + val x = infoAreaX + 212 + var numX = x + + trade.price.toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX + offsetX, y + offsetY) + numX -= numberImageSize.x + } + } + { + val x = infoAreaX + 279 + var numX = x + + (trade.amount * trade.price).toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX + offsetX, y + offsetY) + numX -= numberImageSize.x + } + } + } + } + + } + + } + + (infoAreaX, infoAreaY + (infoImageSize.y + infoMargin) * game.players.size) + } + + def drawSquads() { + val robotImages = ImageLoader.loadRobots(renderer) + val numberImages = ImageLoader.loadNumbers(renderer) + + for (s <- game.field.squads) { + val px = env.trianglePointToPixelPoint(s.current) + + val squadImageWidth = robotImageSize.x + numberImageSize.x * 3 + 1 + val squadImageX = px.x - squadImageWidth / 2 + val squadImageY = if (game.field.veins.toMap.contains(s.current)) + px.y - (veinImageSize.y + veinTopMargin) / 2 + else + px.y - robotImageSize.y / 2 + + renderer.drawImage(robotImages(s.ownerId), squadImageX, squadImageY) + /* Draw robots */ + var numberX = squadImageX + robotImageSize.x + 1 + var numberY = squadImageY + "%03d".format(s.robot).foreach { d => + renderer.drawImage(numberImages(s.ownerId, d.toString.toInt), numberX, numberY) + numberX += plusImageSize.x + } + } + } + + def drawBank(x: Int, y: Int) { + val infoImage = ImageLoader.loadBankInformationBackground(renderer) + val numberImages = ImageLoader.loadNumbers(renderer) + + renderer.drawImage(infoImage, x, y) + // TODO: + + for (i <- 0 to 2) { + + val numY = y + 12 * i + 35 + + { + var numX = x + 212 + + game.alienTrade.sellPriceOf(Material.all(i)).toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX, numY) + numX -= numberImageSize.x + } + } + + { + var numX = x + 280 + + game.alienTrade.buyPriceOf(Material.all(i)).toString.reverse.foreach { n => + renderer.drawImage(numberImages(-1, n.toString.toInt), numX, numY) + numX -= numberImageSize.x + } + } + + } + + //game.bank.buyPriceOf(material) + //game.bank.sellPriceOf(game, material) + } + + override def draw() { + val backImg = ImageLoader.loadBackgrounds(renderer)(env.tileSize) + renderer.drawImage(backImg, 0, 0) + + if (game == null) return + + val veinSample = ImageLoader.loadVeinSample(renderer) + renderer.drawImage(veinSample, 0, 425) + + drawTile() + + drawRound() + + drawVeins() + + drawSquads() + + val (nextX, nextY) = drawPlayers() + + drawBank(nextX, nextY) + } +} diff --git a/src/main/scala/net/javachallenge/scene/graphic/TextBoxScene.scala b/src/main/scala/net/javachallenge/scene/graphic/TextBoxScene.scala new file mode 100644 index 0000000..f80b0b9 --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/graphic/TextBoxScene.scala @@ -0,0 +1,25 @@ +package net.javachallenge.scene.graphic + +import net.javachallenge.scene.CommandBaseScene +import scala.collection.mutable.Queue + +trait TextBoxScene extends CommandBaseScene { + override def nextCommand = TextBoxScene.nextCommand + + override def displayCore(text: String) = TextBoxScene.display(text) +} + +object TextBoxScene { + var commands = Queue[String]() + + var display: (String => Unit) = null + + def addCommand(command: String) = commands.enqueue(command) + + def nextCommand = { + if (commands.size > 0) + Some(commands.dequeue.split(" ").filter(_.length > 0).toList) + else + None + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/scene/graphic/TitleScene.scala b/src/main/scala/net/javachallenge/scene/graphic/TitleScene.scala new file mode 100644 index 0000000..53f84f7 --- /dev/null +++ b/src/main/scala/net/javachallenge/scene/graphic/TitleScene.scala @@ -0,0 +1,14 @@ +package net.javachallenge.scene + +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.GameEnvironment +import net.javachallenge.util.misc.ImageLoader + +trait TitleScene extends Scene[GameEnvironment] { + override def draw() = { + val renderer = getRenderer() + ImageLoader.prefetch(renderer) + val title = ImageLoader.loadTitle(renderer) + renderer.drawImage(title, 0, 0) + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/internationalization/I18n.scala b/src/main/scala/net/javachallenge/util/internationalization/I18n.scala new file mode 100644 index 0000000..fecb141 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/internationalization/I18n.scala @@ -0,0 +1,42 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.internationalization + +/** + * An internationalized word + * + * @constructor creates a word that can be translated + * @param string representing the key of the word + */ +class I18n(val key: String) { + /** + * Translates the word or returns it if no translation is found. + * + * @return the translated word + */ + def translate = Locale.current match { + case None => key + case Some(locale) => locale.translate(key) + } +} + +/** + * Implicit conversion and helpers for + * [[net.javachallenge.util.internationalization.I18n]] instances. + */ +object I18n { + def apply(key: String) = new I18n(key) + + /** + * Translates the given word with the current locale. + * + * @param word the word to translate + * @return the translated word + */ + def get(word: String) = I18n(word).translate + + implicit def stringToI18n(str: String) = I18n(str) + implicit def i18nToStr(word: I18n) = word.translate +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/internationalization/Locale.scala b/src/main/scala/net/javachallenge/util/internationalization/Locale.scala new file mode 100644 index 0000000..2ac4327 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/internationalization/Locale.scala @@ -0,0 +1,145 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.internationalization + +import scala.collection.mutable + +/** + * A locale used in the application + * + * @constructor creates a locale with a language and a country + * @param language the name of the locale's language (eg. 'ja') + * @param country the name of the locale's country (eg. 'JP') + */ +class Locale(val language: String, val country: String) { + /** + * Registers the new locale when created + */ + Locale.registerLocale(this) + + /** + * A container for the translations in the locale's language + */ + val map: mutable.Map[String, String] = mutable.Map() + + /** + * Returns a readable name for the locale. + * + * @return the locale name (eg. 'ja_JP') + */ + override def toString: String = (language + "_" + country) + + /** + * Adds a word for translation in the locale's language. + * + * @param key the key of the word (eg. 'hello_world') + * @param word the translation for the key (eg. 'こんにちは世界。') + */ + def addWord(key: String, word: String) = (map += (key -> word)) + + /** + * Translates the given key in the locale's language or + * returns the key if the locale is not found. + * + * @param key the key to translate + * @return the translation for the key + */ + def translate(key: String): String = map get (key) match { + case None => Locale.fallback match { + case Some(locale) if locale != this => locale.translate(key) + case _ => key + } + case Some(string) => string + } +} + +/** + * Helpers for [[net.javachallenge.util.internationalization.Locale]] instances + */ +object Locale { + /** + * The current locale for the application. + */ + var current: Option[Locale] = None + + /** + * The fallback to use when key not found in current locale. + */ + var fallback: Option[Locale] = None + + /** + * A container for the available locales + * The key used is the language of the locale (eg. 'ja') + */ + var locales: mutable.Map[String, Locale] = mutable.Map() + + /** + * Constructs a locale with the given language if it does not already exists. + * Set the locale's country to the language name in uppercase. + * + * @param language the locale's language + * @return the new locale + */ + def apply(language: String): Locale = getOrCreate(language, language toUpperCase) + + /** + * Constructs a locale with the given language if it does not already exists. + * + * @param language the locale's language + * @return the new locale + */ + def apply(name: String, country: String): Locale = getOrCreate(name, country) + + /** + * Returns the locale if it exists or create a new one if not. + * + * @param language the name of the locale's language (eg. 'ja') + * @param country the name of the locale's country (eg. 'JP') + * @return the existent or newly created locale + */ + protected def getOrCreate(language: String, country: String): Locale = { + locales.get(language) match { + case Some(locale) => locale + case None => new Locale(language, country) + } + } + + /** + * Clear all the infos of the locales + */ + def clear: Unit = { + locales.clear + current = None + fallback = None + } + + /** + * Adds a new locale to the available locales. + * + * @param locale the language name for the locale (eg. 'ja') + */ + def registerLocale(locale: Locale): Unit = (locales += (locale.language -> locale)) + + /** + * Set the current locale for the application. + * + * @param locale the language name for the locale (eg. 'ja') + */ + def set(locale: String): Unit = (current = locales.get(locale)) + + /** + * Set the current fallback for the application. + * + * @param locale the language name for the locale (eg. 'ja') + */ + def setFallback(locale: String): Unit = (fallback = locales.get(locale)) + + /** + * Checks if the locale exists or not. + * + * @param locale the language name for the locale (eg. 'ja') + */ + def has(locale: String): Boolean = locales.isDefinedAt(locale) +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/internationalization/XMLTranslationLoader.scala b/src/main/scala/net/javachallenge/util/internationalization/XMLTranslationLoader.scala new file mode 100644 index 0000000..f765755 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/internationalization/XMLTranslationLoader.scala @@ -0,0 +1,74 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.internationalization + +import java.io.File +import scala.xml._ + +/** + * Loader for the translations file. + * + * Contains method to load all the translations in the given folder. + * + * @constructor creates a loader with the given path. + * @param translationsFolderPath the path of the folder containtng translation + * files + */ +class XMLTranslationLoader(translationsFolderPath: String) { + /** + * Searches in the folder for translation files and generates the locales and + * their translation. + * + * @throws NullPointerException if the folder does not exist + */ + def makeLocales: Unit = { + val folder = new File(translationsFolderPath) + folder.listFiles.foreach({ + file => + { + val fileName = file toString + val extension = fileName substring (fileName.lastIndexOf(".") + 1) + if (extension == "xml") { + val node = XML.loadFile(file) + node.attribute("language") match { + case Some(lang) => generateLocale(lang toString, node) + case None => + } + } + } + }) + } + + /** + * Creates the locale with the given name and parses the XML + * tree to register all the translations. + * + * @param lang the name of the language for the locale + * @param node the XML tree to parse + */ + protected def generateLocale(lang: String, node: Elem) = { + val country: String = node.attribute("country") match { + case Some(c) => c.toString + case None => lang.toUpperCase + } + val locale = Locale(lang, country) + node.child.foreach({ + entry => + entry.attribute("key") match { + case Some(key) => locale.addWord(key toString, entry text) + case None => + } + }) + } +} + +/** + * Contains helper method to generate locales + */ +object XMLTranslationLoader { + def generateLocales(translationsFolderPath: String): Unit = { + new XMLTranslationLoader(translationsFolderPath).makeLocales + } +} diff --git a/src/main/scala/net/javachallenge/util/misc/DateUtils.scala b/src/main/scala/net/javachallenge/util/misc/DateUtils.scala new file mode 100644 index 0000000..f70b54e --- /dev/null +++ b/src/main/scala/net/javachallenge/util/misc/DateUtils.scala @@ -0,0 +1,16 @@ +package net.javachallenge.util.misc + +import java.util.Calendar + +object DateUtils { + def dateStringForFileName = { + val calendar = Calendar.getInstance(); + val year = calendar.get(Calendar.YEAR) + val month = calendar.get(Calendar.MONTH) + 1 + val day = calendar.get(Calendar.DAY_OF_MONTH) + val hour = calendar.get(Calendar.HOUR_OF_DAY) + val minute = calendar.get(Calendar.MINUTE) + val second = calendar.get(Calendar.SECOND) + List(year, month, day, hour, minute, second).mkString("_") + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/misc/ImageLoader.scala b/src/main/scala/net/javachallenge/util/misc/ImageLoader.scala new file mode 100644 index 0000000..3c8961b --- /dev/null +++ b/src/main/scala/net/javachallenge/util/misc/ImageLoader.scala @@ -0,0 +1,255 @@ +package net.javachallenge.util.misc; + +import java.awt.Image +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import jp.ac.waseda.cs.washi.gameaiarena.gui.Renderer +import net.javachallenge.entity.Gas +import net.javachallenge.entity.Stone +import net.javachallenge.entity.Metal +import net.javachallenge.entity.Material +import scala.collection.mutable +import scala.collection.Seq + +/** + * 画像の読み込み処理を行うクラスです。 + */ +object ImageLoader { + /** + * 全ての画像を読み込みます。 + * + * @param render {@link Renderer} + */ + def prefetch(render: Renderer) = { + val fetchedImages = mutable.ListBuffer[Image]() + for (val method <- ImageLoader.getClass().getMethods()) { + if (method.getName().startsWith("load")) { + fetchedImages ++= dig(method.invoke(this, render)) + } + } + fetchedImages.toList + } + + def dig(obj: Any): Seq[Image] = { + obj match { + case m: Map[_, _] => m.values.toSeq.flatten(dig(_)) + case s: Seq[_] => s.flatten(dig(_)) + case i: Image => Seq(i) + } + } + + private var _backgrounds: Map[Int, Image] = null + + def loadBackgrounds(render: Renderer) = { + if (_backgrounds == null) { + _backgrounds = Map( + 32 -> render.loadImage("img/map32.png"), + 48 -> render.loadImage("img/map48.png")) + } + _backgrounds + } + + private var _tiles: Map[Int, Image] = null + + def loadTiles(render: Renderer) = { + if (_tiles == null) { + _tiles = Map( + 32 -> render.loadImage("img/hex32.png"), + 48 -> render.loadImage("img/hex48.png")) + } + _tiles + } + + private var _roundTitle: Image = null + + def loadRoundTitle(renderer: Renderer) = { + if (_roundTitle == null) { + _roundTitle = renderer.loadImage("img/round/round.png") + } + _roundTitle + } + + private var _roundSlash: Image = null + + def loadRoundSlash(renderer: Renderer) = { + if (_roundSlash == null) { + _roundSlash = renderer.loadImage("img/round/rnslash.png") + } + _roundSlash + } + + private var _roundNumber: Map[Int, Image] = null + + def loadRoundNumber(render: Renderer) = { + if (_roundNumber == null) { + _roundNumber = Range(0, 10).map(num => + (num, render.loadImage( + "img/round/rn" + num.toString + ".png"))).toMap + } + _roundNumber + } + + private var _materials: Map[Material, Image] = null + + def loadMaterials(render: Renderer) = { + if (_materials == null) { + _materials = Map(Gas -> render.loadImage("img/mat01.png"), + Stone -> render.loadImage("img/mat02.png"), + Metal -> render.loadImage("img/mat03.png")) + } + _materials + } + + private var _buyIcon: Image = null + def loadBuyIcon(render: Renderer) = { + if (_buyIcon == null) { + _buyIcon = render.loadImage("img/playerInfo/buy.png") + } + _buyIcon + } + + private var _sellIcon: Image = null + def loadSellIcon(render: Renderer) = { + if (_sellIcon == null) { + _sellIcon = render.loadImage("img/playerInfo/sell.png") + } + _sellIcon + } + + /** プレイヤーインデックス(中立は-1) => 画像の辞書 */ + private val _playerIndices = (-1 to 5) + private var _veins: Map[Int, Image] = null + + def loadVeins(render: Renderer) = { + if (_veins == null) { + _veins = + _playerIndices + .map { t => (t, render.loadImage("img/vein" + t + ".png")) } + .toMap + } + _veins + } + + private var _plusMarks: Map[Int, Image] = null + def loadPlusMarks(render: Renderer) = { + if (_plusMarks == null) { + _plusMarks = + _playerIndices + .map { t => (t, render.loadImage("img/plus" + t + ".png")) } + .toMap + } + _plusMarks + } + + private var _robots: Map[Int, Image] = null + def loadRobots(render: Renderer) = { + if (_robots == null) { + _robots = + _playerIndices + .filter { _ >= 0 } + .map { t => (t, render.loadImage("img/robot" + t + ".png")) } + .toMap + } + _robots + } + + private var _numbers: Map[(Int, Int), Image] = null + def loadNumbers(render: Renderer) = { + if (_numbers == null) { + _numbers = + _playerIndices + .flatMap { + p => + (0 to 9).map { + n => ((p, n), render.loadImage("img/number/num" + p + n + ".png")) + } + } + .toMap + } + _numbers + } + + private var _playerInformationBackgrounds: Map[Int, Image] = null + def loadPlayerInformationBackgrounds(render: Renderer) = { + if (_playerInformationBackgrounds == null) { + _playerInformationBackgrounds = + _playerIndices + .filter { _ >= 0 } + .map { t => (t, render.loadImage("img/playerInfo/p" + t + ".png")) } + .toMap + } + _playerInformationBackgrounds + } + + private var _bankInformationBackground: Image = null + def loadBankInformationBackground(render: Renderer) = { + if (_bankInformationBackground == null) { + _bankInformationBackground = render.loadImage("img/bank.png") + } + _bankInformationBackground + } + + private var _playerHighlightFrame: Image = null + def loadPlayerHighlightFrame(render: Renderer) = { + if (_playerHighlightFrame == null) { + _playerHighlightFrame = render.loadImage("img/pactive.png") + } + _playerHighlightFrame + } + + private var _activePlayerStar: Image = null + def loadActivePlayerStar(render: Renderer) = { + if (_activePlayerStar == null) { + _activePlayerStar = render.loadImage("img/star.png") + } + _activePlayerStar + } + + private var _veinSample: Image = null + def loadVeinSample(render: Renderer) = { + if (_veinSample == null) { + _veinSample = render.loadImage("img/veinSample.png") + } + _veinSample + } + + private var _resultBackground: Image = null + def loadResultBackground(render: Renderer) = { + if (_resultBackground == null) { + _resultBackground = render.loadImage("img/result/result_BG.png") + } + _resultBackground + } + + private var _resultNumber: Map[Int, Map[Int, Image]] = null + def loadResultNumber(render: Renderer) = { + if (_resultNumber == null) { + _resultNumber = Map( + 48 -> + Range(0, 10).map(num => + (num, render.loadImage("img/result/48_" + num.toString + ".png"))).toMap, + 64 -> + Range(0, 10).map(num => + (num, render.loadImage("img/result/64_" + num.toString + ".png"))).toMap) + } + _resultNumber + } + + private var _lamp: Map[Int, Image] = null + def loadLamp(render: Renderer) = { + if (_lamp == null) { + _lamp = Range(0, 6).map(num => + (num, render.loadImage("img/result/lamp_" + num.toString + ".png"))).toMap + } + _lamp + } + + private var _title: Image = null + def loadTitle(render: Renderer) = { + if (_title == null) { + _title = render.loadImage("img/start/title.png") + } + _title + } + +} diff --git a/src/main/scala/net/javachallenge/util/misc/Loan.scala b/src/main/scala/net/javachallenge/util/misc/Loan.scala new file mode 100644 index 0000000..059b28a --- /dev/null +++ b/src/main/scala/net/javachallenge/util/misc/Loan.scala @@ -0,0 +1,38 @@ +package net.javachallenge.util.misc + +// from http://d.hatena.ne.jp/gakuzo/20110701/1309522420 + +trait Closer[-A] { + def close(value: A) +} + +class Loan[A] private (value: A, closer: Closer[A]) { + + def foreach[B](f: A => B): B = try { + f(value) + } finally { + closer.close(value) + } + +} + +object Loan { + + def apply[A](value: A)(implicit closer: Closer[A]) = new Loan(value, closer) + + type Closeable = { def close() } + implicit val closeable = new Closer[Closeable] { + def close(value: Closeable) = value.close() + } + + type Releasable = { def release() } + implicit val destroyable = new Closer[Releasable] { + def close(value: Releasable) = value.release() + } + + type Disposable = { def dispose() } + implicit val disposable = new Closer[Disposable] { + def close(value: Disposable) = value.dispose() + } + +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/misc/NotImplementedException.scala b/src/main/scala/net/javachallenge/util/misc/NotImplementedException.scala new file mode 100644 index 0000000..0eee85c --- /dev/null +++ b/src/main/scala/net/javachallenge/util/misc/NotImplementedException.scala @@ -0,0 +1,6 @@ +package net.javachallenge.util.misc + +case class NotImplementedException(message: String) extends Exception(message) { + def this() = this("") + +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/misc/StringConverter.scala b/src/main/scala/net/javachallenge/util/misc/StringConverter.scala new file mode 100644 index 0000000..1186a99 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/misc/StringConverter.scala @@ -0,0 +1,26 @@ +package net.javachallenge.util.misc + +import scala.util.control.Exception._ +import net.javachallenge.entity.Material +import jp.ac.waseda.cs.washi.gameaiarena.api.Point2 +import net.javachallenge.entity.TrianglePoint + +object IndexStr { + def apply(i: Int) = (i + 1).toString + def unapply(s: String) = catching(classOf[NumberFormatException]).opt(s.toInt - 1) +} + +object IntStr { + def apply(i: Int) = i.toString + def unapply(s: String) = catching(classOf[NumberFormatException]).opt(s.toInt) +} + +object TrianglePointStr { + def apply(p: TrianglePoint) = p.toString + def unapply(s: String) = { + catching(classOf[Exception]).opt(Point2.parse(s)) match { + case Some(p) => Some(TrianglePoint(p.x, p.y)) + case _ => None + } + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/settings/Defaults.scala b/src/main/scala/net/javachallenge/util/settings/Defaults.scala new file mode 100644 index 0000000..510b8ac --- /dev/null +++ b/src/main/scala/net/javachallenge/util/settings/Defaults.scala @@ -0,0 +1,41 @@ +package net.javachallenge.util.settings + +/** + * Object containing default values for the application. + */ +object Defaults { + /** + * The default environment for the application. + */ + val ENV: Environment = Production + + /** + * The default XML parser for settings. + */ + val XML_SETTINGS_PARSER_CLASS_NAME = "net.javachallenge.util.settings.XMLSettingsParser" + + /** + * Returns the default path of the configuration file's folder. + */ + def SETTINGS_PATH = "src/%s/config".format(Environment.current.folder) + + /** + * The default file format for settings. + */ + val SETTINGS_FORMAT = "xml" + + /** + * The default locale for the application if not present in configuration file. + */ + val LOCALE = "ja" + + /** + * The default fallback for the application if not present in configuration file. + */ + val FALLBACK = "ja" + + /** + * The default character of new line. + */ + val NEW_LINE = System.getProperty("line.separator") +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/settings/EffectiveSettings.scala b/src/main/scala/net/javachallenge/util/settings/EffectiveSettings.scala new file mode 100644 index 0000000..e2d0a65 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/settings/EffectiveSettings.scala @@ -0,0 +1,21 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +/** + * Contain general settings for the application + */ +object EffectiveSettings { + + /** + * The XML settings parser class name in use + */ + var xmlSettingsParserClassName = Defaults.XML_SETTINGS_PARSER_CLASS_NAME + + /** + * The setting file format in use + */ + var settingsFormat = Defaults.SETTINGS_FORMAT +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/settings/Environment.scala b/src/main/scala/net/javachallenge/util/settings/Environment.scala new file mode 100644 index 0000000..fd6483f --- /dev/null +++ b/src/main/scala/net/javachallenge/util/settings/Environment.scala @@ -0,0 +1,58 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +/** + * Parent class to represent an environment + */ +abstract class Environment { + /** + * The folder of the sources for the given environment + */ + val folder: String +} + +/** + * The production environment + */ +case object Production extends Environment { + /** + * {@inheritDoc} + */ + val folder = "main" +} + +/** + * The development/debug environment + */ +case object Development extends Environment { + /** + * {@inheritDoc} + */ + val folder = "main" +} + +/** + * The test environment + */ +case object Test extends Environment { + /** + * {@inheritDoc} + */ + val folder = "test" +} + +/** + * Companion object with current environment and its setter + */ +object Environment { + var current: Environment = Defaults.ENV + + /** + * Set the current environment + * @param env the environment to set + */ + def setEnvironment(env: Environment) = (current = env) +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/settings/IllegalSettingsException.scala b/src/main/scala/net/javachallenge/util/settings/IllegalSettingsException.scala new file mode 100644 index 0000000..f50d134 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/settings/IllegalSettingsException.scala @@ -0,0 +1,10 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +/** + * An exception to throw when the settings cannot be parsed correctly + */ +case class IllegalSettingsException(text: String) extends Exception(text) \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/settings/SettingsLoader.scala b/src/main/scala/net/javachallenge/util/settings/SettingsLoader.scala new file mode 100644 index 0000000..41b799b --- /dev/null +++ b/src/main/scala/net/javachallenge/util/settings/SettingsLoader.scala @@ -0,0 +1,37 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +/** + * A general wrapper to load settings from files + */ +object SettingsLoader { + + /** + * Gets the loader class name depending on the format and current settings + */ + def loadSettings: Unit = EffectiveSettings.settingsFormat match { + case "xml" => load(EffectiveSettings.xmlSettingsParserClassName) + case x => throw new IllegalSettingsException("No parser found " + + "for format %s.".format(x)) + } + + /** + * Loads the settings using the format and file defined in + * [[net.javachallenge.util.settings.Defaults]] and + * [[net.javachallenge.util.settings.EffectiveSettings]] with + * the given loader + * + * @param parserClass the name of the class used to load the settings + */ + def load(parserClass: String): Unit = { + val file = "%s/config.%s".format(Defaults.SETTINGS_PATH, + EffectiveSettings.settingsFormat) + + val parser = Class.forName(parserClass).newInstance.asInstanceOf[SettingsParser] + + parser.loadSettings(file) + } +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/settings/SettingsParser.scala b/src/main/scala/net/javachallenge/util/settings/SettingsParser.scala new file mode 100644 index 0000000..443a673 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/settings/SettingsParser.scala @@ -0,0 +1,18 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +/** + * A trait used as an interface to have a general settings parser + * not dependant on the file format + */ +trait SettingsParser { + /** + * Loads the settings in the given file + * + * @param filePath the path of the file containing the settings + */ + def loadSettings(filePath: String): Unit +} \ No newline at end of file diff --git a/src/main/scala/net/javachallenge/util/settings/XMLSettingsParser.scala b/src/main/scala/net/javachallenge/util/settings/XMLSettingsParser.scala new file mode 100644 index 0000000..34456d6 --- /dev/null +++ b/src/main/scala/net/javachallenge/util/settings/XMLSettingsParser.scala @@ -0,0 +1,85 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +import scala.xml._ +import net.javachallenge.util.internationalization._ + +/** + * A parser to load settings saved in an XML file + */ +class XMLSettingsParser extends SettingsParser { + + /** + * The facade function to load the settings. + * + * @param filePath the pah of the XML file + */ + def loadSettings(filePath: String): Unit = { + val xml: Elem = XML.load(filePath) + + //The settings in the XML file should be in tag. + if (xml.label != "application") { + throw new IllegalSettingsException("Settings must be in tag" + + ".") + } + + xml.child.filterNot(isEmptyNode(_)) foreach { child => loadSetting(child label, child) } + } + + /** + * Checks if the node is empty. + * A child is empty if it 'does not have any text', + * 'does not have any child' and 'does not have any attribute' + * + * @param node the node to evaluate + * @return if the node is empty or not + */ + def isEmptyNode(node: Node): Boolean = { + node.text.trim.isEmpty && + node.attributes.isEmpty && + node.child.isEmpty + } + + /** + * Loads a single setting. + * TODO Add parsable settings by adding cases in the pattern matching + * + * @param settingName the name of the setting + * @param setting the XML node containing the settings + */ + def loadSetting(settingName: String, setting: Node): Unit = settingName match { + case "translator" => loadTranslatorSettings(setting) + case x => throw new IllegalSettingsException("Unknown setting '%s'".format(x)) + } + + /** + * Loads the settings for the translator. + * + * @param node the XML node containing the settings for the translator + */ + def loadTranslatorSettings(node: Node): Unit = { + + //Loads the translation files in the tags + (node \ "resources").headOption match { + case Some(resources) => (resources \\ "folder") foreach { + folder => XMLTranslationLoader.generateLocales(folder.text toString) + } + case None => + } + + //Looks for a default locale or loads the one present in [[net.javachallenge.util.Defaults]] object if not existent + node attribute ("default-locale") match { + case Some(language) => Locale.set(language toString) + case None => Locale.set(Defaults.LOCALE) + } + + //Looks for a fallback or loads the one present in [[net.javachallenge.util.Defaults]] object if not existent + node attribute ("fallback") match { + case Some(fallback) => Locale.setFallback(fallback toString) + case None => Locale.setFallback(Defaults.FALLBACK) + } + } +} diff --git a/src/test/config/config.xml b/src/test/config/config.xml new file mode 100644 index 0000000..0a6caae --- /dev/null +++ b/src/test/config/config.xml @@ -0,0 +1,9 @@ + + + + + + src/test/resources/translations + + + \ No newline at end of file diff --git a/src/test/java/DummyTest.java b/src/test/java/DummyTest.java new file mode 100644 index 0000000..d629e62 --- /dev/null +++ b/src/test/java/DummyTest.java @@ -0,0 +1,6 @@ +import org.junit.Test; + +public class DummyTest { + @Test + public void dummy() {} +} diff --git a/src/test/resources/translations/messages_ja.xml b/src/test/resources/translations/messages_ja.xml new file mode 100644 index 0000000..8000692 --- /dev/null +++ b/src/test/resources/translations/messages_ja.xml @@ -0,0 +1,5 @@ + + + + こんにちは世界。 + \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/entity/GameScenarioSpecTest.scala b/src/test/scala/net/javachallenge/entity/GameScenarioSpecTest.scala new file mode 100644 index 0000000..8fdeecf --- /dev/null +++ b/src/test/scala/net/javachallenge/entity/GameScenarioSpecTest.scala @@ -0,0 +1,200 @@ +package net.javachallenge.entity + +import java.io.StringReader +import java.util.Scanner +import net.javachallenge.GameEnvironment +import net.javachallenge.scene.console._ +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.mutable.MutableList +import scala.collection.mutable.Queue +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.scene.CommandBaseScene +import net.javachallenge.scene.VeinScene +import net.javachallenge.scene.MainScene +import scala.collection.immutable.TreeMap + +@RunWith(classOf[JUnitSuiteRunner]) +class GameScenarioSpecTest extends Specification with JUnit { + + "Game" should { + + val names = List("A", "B", "C") + val p11 = TrianglePoint(-5, 3) + val p12 = TrianglePoint(6, 0) + val p21 = TrianglePoint(-3, 3) + val p22 = TrianglePoint(0, -1) + val p31 = TrianglePoint(4, 0) + val p32 = TrianglePoint(2, 0) + val p4 = TrianglePoint(7, 3) + val p5 = TrianglePoint(5, -2) + val p6 = TrianglePoint(6, 2) + + val veins = TreeMap(p11 -> Vein(p11, Gas, 10, 10, 1), + p12 -> Vein(p12, Stone, 10, 10, 1), + p21 -> Vein(p21, Stone, 10, 10, 1), + p22 -> Vein(p22, Gas, 10, 10, 1), + p31 -> Vein(p31, Metal, 10, 10, 1), + p32 -> Vein(p32, Metal, 10, 10, 1), + p4 -> Vein(p4, Metal, 10, 10, 1), + p5 -> Vein(p5, Metal, 10, 10, 1), + p6 -> Vein(p6, Stone, 10, 10, 1)) + + val settings = GameSetting(veinCount = 10, moveTurn = 1, initialMoney = 100) + val env: GameEnvironment = GameEnvironment(game = Game(names, settings, Field(settings, veins))) + val man = env.getSceneManager().setFps(1000); + def game = env.game + + val mainScene = new MainScene(null) with TestScene + val veinScene = new VeinScene(mainScene) with TestScene + man.initialize(env, veinScene) + + def run(scene: Scene[GameEnvironment], command: String) = { + TestScene.push(command.split(" ").toList) + man.runOneStep(env, scene) + } + + def runMain(command: String) = { + run(mainScene, command) + } + + run(veinScene, p11.cmdStr) + run(veinScene, p21.cmdStr) + run(veinScene, p31.cmdStr) + run(veinScene, p32.cmdStr) + run(veinScene, p22.cmdStr) + run(veinScene, p12.cmdStr) + + runMain("finish") + runMain("finish") + runMain("finish") + + "advance turns for increasing materials" in { + val ps = game.players + ps(0).materials must_== mutable.Map(Metal -> 0, Gas -> 10, Stone -> 10) + game.field.veins(p11).robot must_== 11 + game.field.veins(p12).robot must_== 11 + ps(1).materials must_== mutable.Map(Metal -> 0, Gas -> 10, Stone -> 10) + game.field.veins(p21).robot must_== 11 + game.field.veins(p22).robot must_== 11 + ps(2).materials must_== mutable.Map(Metal -> 20, Gas -> 0, Stone -> 0) + game.field.veins(p31).robot must_== 11 + game.field.veins(p32).robot must_== 11 + } + + "advance turns changing current player" in { + game.currentPlayer must_== game.players(0) + runMain("finish") + game.currentPlayer must_== game.players(1) + runMain("finish") + game.currentPlayer must_== game.players(2) + runMain("finish") + game.currentPlayer must_== game.players(0) + } + + "get initial money" in { + game.players(0).money must_== game.setting.initialMoney + } + + "trade materials" in { + runMain("demand gas 10 1") + runMain("finish") + game.players(0).materials must_== mutable.Map(Metal -> 0, Gas -> 20, Stone -> 20) + game.players(0).money must_== settings.initialMoney - 10 + runMain("sell 0 gas 10") + runMain("finish") + runMain("offer metal 20 1") + runMain("finish") + game.players(2).materials must_== mutable.Map(Metal -> 20, Gas -> 0, Stone -> 0) + game.players(2).money must_== settings.initialMoney + runMain("buy 2 metal 10") + runMain("finish") + runMain("buy 2 metal 10") + runMain("finish") + val ps = game.players + ps(0).materials must_== mutable.Map(Metal -> 10, Gas -> 40, Stone -> 30) + ps(0).money must_== settings.initialMoney - 20 + ps(1).materials must_== mutable.Map(Metal -> 10, Gas -> 20, Stone -> 30) + ps(1).money must_== settings.initialMoney + ps(2).materials must_== mutable.Map(Metal -> 20, Gas -> 0, Stone -> 0) + ps(2).money must_== settings.initialMoney + 20 + } + + "launch squads to a own vein" in { + runMain("finish") + runMain("finish") + + game.field.squads.size must_== 0 + runMain("launch 10 " + p31.cmdStr + " " + p32.cmdStr) + game.field.squads.size must_== 1 + game.field.veins(p31).robot must_== 1 + game.field.veins(p31).ownerId must_== 2 + game.field.veins(p32).robot must_== 11 + game.field.veins(p32).ownerId must_== 2 + + for (i <- 0 until 3) runMain("finish") + + game.field.squads.size must_== 1 + game.field.veins(p31).robot must_== 2 + game.field.veins(p31).ownerId must_== 2 + game.field.veins(p32).robot must_== 12 + game.field.veins(p32).ownerId must_== 2 + + for (i <- 0 until 3) runMain("finish") + + game.field.squads.size must_== 0 + game.field.veins(p31).robot must_== 3 + game.field.veins(p31).ownerId must_== 2 + game.field.veins(p32).robot must_== 23 + game.field.veins(p32).ownerId must_== 2 + } + + "launch squads to a neutral vein" in { + runMain("launch 10 " + p12.cmdStr + " " + p6.cmdStr) + for (i <- 0 until 3) runMain("finish") + + runMain("launch 1 " + p12.cmdStr + " " + p6.cmdStr) + for (i <- 0 until 3) runMain("finish") + + for (i <- 0 until 2) { + game.field.squads.size must_== 2 + game.field.veins(p6).robot must_== 10 + for (i <- 0 until 3) runMain("finish") + } + + game.field.squads.size must_== 1 + game.field.veins(p6).robot must_== 0 + game.field.veins(p6).ownerId must_== -1 + + for (i <- 0 until 3) runMain("finish") + + game.field.squads.size must_== 0 + game.field.veins(p6).robot must_== 1 + game.field.veins(p6).ownerId must_== 0 + } + + "launch squads to not neutral veins" in { + runMain("launch 10 " + p11.cmdStr + " " + p21.cmdStr) + runMain("finish") + runMain("launch 10 " + p21.cmdStr + " " + p4.cmdStr) + runMain("launch 1 " + p21.cmdStr + " " + p11.cmdStr) + + game.field.squads.size must_== 3 + runMain("finish") + game.field.squads.size must_== 2 + runMain("finish") + game.field.veins(p11).robot must_== 2 + game.field.veins(p21).robot must_== 1 + + for (i <- 0 until 3) runMain("finish") + game.field.veins(p11).ownerId must_== 0 + game.field.veins(p11).robot must_== 3 + game.field.veins(p21).ownerId must_== 0 + game.field.veins(p21).robot must_== 8 + } + } +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/entity/GameSetSpecTest.scala b/src/test/scala/net/javachallenge/entity/GameSetSpecTest.scala new file mode 100644 index 0000000..249684d --- /dev/null +++ b/src/test/scala/net/javachallenge/entity/GameSetSpecTest.scala @@ -0,0 +1,79 @@ +package net.javachallenge.entity + +import net.javachallenge.GameEnvironment +import net.javachallenge.scene.console._ +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.mutable.Queue +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.scene.CommandBaseScene +import net.javachallenge.scene.VeinScene +import net.javachallenge.scene.MainScene +import scala.collection.immutable.TreeMap + +@RunWith(classOf[JUnitSuiteRunner]) +class GameSetSpecTest extends Specification with JUnit { + "GameScenario" should { + + val names = List("hoge", "piyo") + val p11 = TrianglePoint(0, 0) + val p12 = TrianglePoint(1, 0) + val p21 = TrianglePoint(-1, 0) + val p22 = TrianglePoint(2, 0) + + val veins = TreeMap( + p11 -> Vein(p11, Gas, 1000, 1, 10), + p12 -> Vein(p12, Stone, 1000, 1, 10), + p21 -> Vein(p21, Stone, 10, 1, 10), + p22 -> Vein(p22, Gas, 10, 1, 10)) + + val settings = GameSetting(veinCount = 4, moveTurn = 1) + val env: GameEnvironment = GameEnvironment(game = Game(names, settings, Field(settings, veins))) + val man = env.getSceneManager().setFps(1000); + def game = env.game + + val mainScene = new MainScene(null) with TestScene + val veinScene = new VeinScene(mainScene) with TestScene + man.initialize(env, veinScene) + + def run(scene: Scene[GameEnvironment], command: String) = { + TestScene.push(command.split(" ").toList) + man.runOneStep(env, scene) + } + + def runMain(command: String) = { + run(mainScene, command) + } + + run(veinScene, p11.cmdStr) + run(veinScene, p21.cmdStr) + run(veinScene, p22.cmdStr) + run(veinScene, p12.cmdStr) + + "game set when conquested" in { + runMain("launch 1000 " + p11.cmdStr + " " + p21.cmdStr) + runMain("launch 1000 " + p12.cmdStr + " " + p22.cmdStr) + val nextScene = runMain("finish") + + game.field.veins(p21).ownerId must_== 0 + game.field.veins(p22).ownerId must_== 0 + game.currentPlayerId must_== 0 + + nextScene must_!= mainScene + } + + "game set when time over" in { + for (n <- (0 until game.setting.maxRound * 2 - 1)) { + runMain("finish") + } + + game.currentPlayerId must_== 1 + + runMain("finish") must_!= mainScene + } + } +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/entity/SellSpecTest.scala b/src/test/scala/net/javachallenge/entity/SellSpecTest.scala new file mode 100644 index 0000000..b639242 --- /dev/null +++ b/src/test/scala/net/javachallenge/entity/SellSpecTest.scala @@ -0,0 +1,75 @@ +package net.javachallenge.entity + +import net.javachallenge.GameEnvironment +import net.javachallenge.scene.console._ +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.mutable.Queue +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.scene.CommandBaseScene +import net.javachallenge.scene.VeinScene +import net.javachallenge.scene.MainScene +import scala.collection.immutable.TreeMap + +@RunWith(classOf[JUnitSuiteRunner]) +class SellSpecTest extends Specification with JUnit { + "GameScenario" should { + val names = List("hoge", "foo", "bar") + val p11 = TrianglePoint(-5, 3) + val p12 = TrianglePoint(6, 0) + val p21 = TrianglePoint(-3, 3) + val p22 = TrianglePoint(0, -1) + val p31 = TrianglePoint(-7, 0) + val p32 = TrianglePoint(4, -2) + val p4 = TrianglePoint(7, 2) + val p5 = TrianglePoint(5, -2) + val p6 = TrianglePoint(6, 2) + + val veins = TreeMap(p11 -> Vein(p11, Gas, 10, 10, 1), + p12 -> Vein(p12, Stone, 10, 10, 1), + p21 -> Vein(p21, Stone, 10, 10, 1), + p22 -> Vein(p22, Gas, 10, 10, 1), + p31 -> Vein(p31, Metal, 10, 10, 1), + p32 -> Vein(p32, Metal, 10, 10, 1), + p4 -> Vein(p4, Metal, 10, 10, 1), + p5 -> Vein(p5, Metal, 10, 10, 1), + p6 -> Vein(p6, Stone, 10, 10, 1)) + + val settings = GameSetting(veinCount = 10, moveTurn = 1) + val env: GameEnvironment = GameEnvironment(game = Game(names, settings, Field(settings, veins))) + val man = env.getSceneManager().setFps(1000); + def game = env.game + + val mainScene = new MainScene(null) with TestScene + val veinScene = new VeinScene(mainScene) with TestScene + man.initialize(env, veinScene) + + def run(scene: Scene[GameEnvironment], command: String) = { + TestScene.push(command.split(" ").toList) + man.runOneStep(env, scene) + } + + def runMain(command: String) = { + run(mainScene, command) + } + + run(veinScene, p11.cmdStr) + run(veinScene, p21.cmdStr) + run(veinScene, p31.cmdStr) + run(veinScene, p32.cmdStr) + run(veinScene, p22.cmdStr) + run(veinScene, p12.cmdStr) + + "sell material when no demand" in { + game.currentPlayer must_== game.players(0) + runMain("sell 1 10") + runMain("finish") + game.currentPlayer must_== game.players(1) + } + + } +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/entity/SquadSpecTest.scala b/src/test/scala/net/javachallenge/entity/SquadSpecTest.scala new file mode 100644 index 0000000..b15d578 --- /dev/null +++ b/src/test/scala/net/javachallenge/entity/SquadSpecTest.scala @@ -0,0 +1,125 @@ +package net.javachallenge.entity + +import net.javachallenge.GameEnvironment +import net.javachallenge.scene.console._ +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.mutable.MutableList +import scala.collection.mutable.Queue +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.scene.CommandBaseScene +import net.javachallenge.scene.VeinScene +import net.javachallenge.scene.MainScene +import scala.collection.immutable.TreeMap + +@RunWith(classOf[JUnitSuiteRunner]) +class SquadSpecTest extends Specification with JUnit { + + "GameScenario" should { + val names = List("A", "B", "C") + val p11 = TrianglePoint(-5, 3) + val p12 = TrianglePoint(-1, 3) + val p21 = TrianglePoint(-3, 3) + val p22 = TrianglePoint(0, -1) + val p31 = TrianglePoint(-7, 0) + val p32 = TrianglePoint(4, -2) + val p4 = TrianglePoint(7, 2) + val p5 = TrianglePoint(5, -2) + val p6 = TrianglePoint(-7, 3) + + val veins = TreeMap(p11 -> Vein(p11, Gas, 10, 10, 1), + p12 -> Vein(p12, Stone, 10, 10, 1), + p21 -> Vein(p21, Stone, 9, 10, 1), + p22 -> Vein(p22, Gas, 10, 10, 1), + p31 -> Vein(p31, Metal, 10, 10, 1), + p32 -> Vein(p32, Metal, 10, 10, 1), + p4 -> Vein(p4, Metal, 10, 10, 1), + p5 -> Vein(p5, Metal, 10, 10, 1), + p6 -> Vein(p6, Stone, 100, 10, 1)) + + val settings = GameSetting(veinCount = 10, moveTurn = 1) + val env: GameEnvironment = GameEnvironment(game = Game(names, settings, Field(settings, veins))) + val man = env.getSceneManager().setFps(1000); + def game = env.game + + val mainScene = new MainScene(null) with TestScene + val veinScene = new VeinScene(mainScene) with TestScene + man.initialize(env, veinScene) + + def run(scene: Scene[GameEnvironment], command: String) = { + TestScene.push(command.split(" ").toList) + man.runOneStep(env, scene) + } + + def runMain(command: String) = { + run(mainScene, command) + } + + run(veinScene, p11.cmdStr) + run(veinScene, p21.cmdStr) + run(veinScene, p31.cmdStr) + run(veinScene, p32.cmdStr) + run(veinScene, p22.cmdStr) + run(veinScene, p12.cmdStr) + + runMain("finish") + runMain("finish") + runMain("finish") + + "launch robots and decrease robot" in { + game.field.veins(p11).robot must_== 11 + game.field.veins(p6).robot must_== 100 + + runMain("launch 11 " + p11.cmdStr + " " + p6.cmdStr) + game.field.veins(p11).robot must_== 0 + + for (i <- 0 until 6) { + runMain("finish") + } + + game.field.veins(p11).robot must_== 2 + game.field.veins(p6).robot must_== 89 + } + + "launch robots and not conquest" in { + game.field.veins(p11).robot must_== 11 + game.field.veins(p21).robot must_== 10 + + runMain("launch 10 " + p11.cmdStr + " " + p21.cmdStr) + runMain("launch 1 " + p11.cmdStr + " " + p21.cmdStr) + game.field.veins(p11).robot must_== 0 + + for (i <- 0 until 6) { + runMain("finish") + } + + game.field.veins(p11).robot must_== 2 + game.field.veins(p21).robot must_== 1 + game.field.veins(p21).ownerId must_== 1 + } + + "launch robots and conquest" in { + game.field.veins(p11).robot must_== 11 + game.field.veins(p12).robot must_== 11 + game.field.veins(p21).robot must_== 10 + + runMain("launch 6 " + p11.cmdStr + " " + p21.cmdStr) + runMain("launch 6 " + p12.cmdStr + " " + p21.cmdStr) + game.field.veins(p11).robot must_== 5 + game.field.veins(p12).robot must_== 5 + + for (i <- 0 until 6) { + runMain("finish") + } + + game.field.veins(p11).robot must_== 7 + game.field.veins(p12).robot must_== 7 + game.field.veins(p21).robot must_== 1 + game.field.veins(p21).ownerId must_== 0 + } + } +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/entity/TestScene.scala b/src/test/scala/net/javachallenge/entity/TestScene.scala new file mode 100644 index 0000000..19836cd --- /dev/null +++ b/src/test/scala/net/javachallenge/entity/TestScene.scala @@ -0,0 +1,17 @@ +package net.javachallenge.entity + +import scala.collection.mutable +import net.javachallenge.scene.CommandBaseScene + +trait TestScene extends CommandBaseScene { + def nextCommand = TestScene.pop + + def displayCore(text: String) = print(text) +} + +object TestScene { + private val commands: mutable.Queue[List[String]] = mutable.Queue() + def pop() = Some(commands.dequeue) + def push(command: List[String]) = commands.enqueue(command) + def push(command: String*) = commands.enqueue(command.toList) +} diff --git a/src/test/scala/net/javachallenge/entity/TrianglePointSpecTest.scala b/src/test/scala/net/javachallenge/entity/TrianglePointSpecTest.scala new file mode 100644 index 0000000..d914587 --- /dev/null +++ b/src/test/scala/net/javachallenge/entity/TrianglePointSpecTest.scala @@ -0,0 +1,37 @@ +package net.javachallenge.entity + +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import org.junit.runner.RunWith +import org.specs.runner.JUnitSuiteRunner + +@RunWith(classOf[JUnitSuiteRunner]) +class TrianglePointSpecTest extends Specification with JUnit { + + "TrianglePoint" should { + "judge whether two points are connected" in { + TrianglePoint(0, 0).isConnected(TrianglePoint(0, 1)) must_== true + TrianglePoint(0, 0).isConnected(TrianglePoint(1, 0)) must_== true + TrianglePoint(0, 0).isConnected(TrianglePoint(-1, 0)) must_== true + TrianglePoint(0, 0).isConnected(TrianglePoint(0, -1)) must_== false + + TrianglePoint(0, 1).isConnected(TrianglePoint(0, 0)) must_== true + TrianglePoint(0, 1).isConnected(TrianglePoint(1, 1)) must_== true + TrianglePoint(0, 1).isConnected(TrianglePoint(-1, 1)) must_== true + TrianglePoint(0, 1).isConnected(TrianglePoint(0, 2)) must_== false + } + + "caluculate shortest path between two points" in { + TrianglePoint(0, 0).shortestPath(TrianglePoint(0, 1)) must_== TrianglePoint(0, 0) :: TrianglePoint(0, 1) :: Nil + TrianglePoint(0, 0).shortestPath(TrianglePoint(0, -1)) must_== TrianglePoint(0, 0) :: TrianglePoint(-1, 0) :: TrianglePoint(-1, -1) :: TrianglePoint(0, -1) :: Nil + } + + "caluculate shortest path between same point" in { + TrianglePoint(0, 0).shortestPath(TrianglePoint(0, 0)) must_== TrianglePoint(0, 0) :: Nil + TrianglePoint(0, 0).getDistance(TrianglePoint(0, 0)) must_== 0 + } + } + +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/runner/ReplaySpecTest.scala b/src/test/scala/net/javachallenge/runner/ReplaySpecTest.scala new file mode 100644 index 0000000..65d27a8 --- /dev/null +++ b/src/test/scala/net/javachallenge/runner/ReplaySpecTest.scala @@ -0,0 +1,58 @@ +package net.javachallenge.runner + +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import org.junit.runner.RunWith +import net.javachallenge.scene.MainScene +import net.javachallenge.scene.VeinScene +import net.javachallenge.scene.console.ConsoleScene +import net.javachallenge.scene.MainRunnerScene +import net.javachallenge.scene.InitialRunnerScene +import net.javachallenge.GameEnvironment +import net.javachallenge.entity.Game +import net.javachallenge.RunnerInitializer +import net.javachallenge.entity.Field +import net.javachallenge.scene.ResultScene +import net.javachallenge.scene.ResultScene +import net.javachallenge.scene.EmptyScene + +@RunWith(classOf[JUnitSuiteRunner]) +class ReplaySpecTest extends Specification with JUnit { + val env = GameEnvironment() + "Replay runner" should { + "read a replay file" in { + val fileName = "replay/2012_11_16_16_32__tokoharuAI_Sabateur_JoeJack_near_player_Wand_Player_Myu.rep" + val (irs, mrs, names, settings, random) = RunnerInitializer.initializeReplay(fileName) + env.game = Game(names, settings, Field(settings, random)) + val man = env.getSceneManager.setFps(9999) + val endScene = new EmptyScene(null) with ResultScene with ConsoleScene + val mainScene = new MainScene(endScene) with ConsoleScene with MainRunnerScene + val veinScene = new VeinScene(mainScene) with ConsoleScene with InitialRunnerScene + mainScene.runners = Vector(mrs: _*) + veinScene.runners = Vector(irs: _*) + + man.initialize(env, veinScene) + while (man.runOneStep(env, veinScene) == veinScene) {} + while (man.runOneStep(env, mainScene) == mainScene) {} + mainScene.game.field.countVeins(0) must_== 10 + mainScene.game.field.countVeins(1) must_== 30 + mainScene.game.field.countVeins(2) must_== 0 + mainScene.game.field.countVeins(3) must_== 0 + mainScene.game.field.countVeins(4) must_== 0 + mainScene.game.field.countVeins(5) must_== 0 + mainScene.game.field.sumRobots(0) must_== 5643 + mainScene.game.field.sumRobots(1) must_== 15466 + mainScene.game.field.sumRobots(2) must_== 0 + mainScene.game.field.sumRobots(3) must_== 0 + mainScene.game.field.sumRobots(4) must_== 0 + mainScene.game.field.sumRobots(5) must_== 0 + mainScene.game.getTotalMoneyWhenSellingAllMaterials(0) must_== 383037 + mainScene.game.getTotalMoneyWhenSellingAllMaterials(1) must_== 207339 + mainScene.game.getTotalMoneyWhenSellingAllMaterials(2) must_== 74113 + mainScene.game.getTotalMoneyWhenSellingAllMaterials(3) must_== 19087 + mainScene.game.getTotalMoneyWhenSellingAllMaterials(4) must_== 59035 + mainScene.game.getTotalMoneyWhenSellingAllMaterials(5) must_== 14118 + } + } +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/runner/RunnerSpecTest.scala b/src/test/scala/net/javachallenge/runner/RunnerSpecTest.scala new file mode 100644 index 0000000..fd86c18 --- /dev/null +++ b/src/test/scala/net/javachallenge/runner/RunnerSpecTest.scala @@ -0,0 +1,246 @@ +package net.javachallenge.entity + +import java.io.StringReader +import java.util.Scanner +import net.javachallenge.GameEnvironment +import net.javachallenge.scene.console._ +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import scala.collection.mutable +import scala.collection.immutable +import scala.collection.mutable.MutableList +import scala.collection.mutable.Queue +import jp.ac.waseda.cs.washi.gameaiarena.gui.Scene +import net.javachallenge.scene.CommandBaseScene +import net.javachallenge.scene.VeinScene +import net.javachallenge.scene.MainScene +import scala.collection.immutable.TreeMap +import net.javachallenge.runner.MainRunner +import net.javachallenge.scene.MainRunnerScene +import net.javachallenge.scene.InitialRunnerScene +import net.javachallenge.api.MockPlayer +import jp.ac.waseda.cs.washi.gameaiarena.runner.AbstractRunner +import net.javachallenge.api.ComputerPlayer +import net.javachallenge.runner.InitialRunner + +@RunWith(classOf[JUnitSuiteRunner]) +class RunnerSpecTest extends Specification with JUnit { + + "Game with runners" should { + val names = List("A", "B", "C") + val p11 = TrianglePoint(-5, 3) + val p12 = TrianglePoint(6, 0) + val p21 = TrianglePoint(-3, 3) + val p22 = TrianglePoint(0, -1) + val p31 = TrianglePoint(4, 0) + val p32 = TrianglePoint(2, 0) + val p4 = TrianglePoint(7, 3) + val p5 = TrianglePoint(5, -2) + val p6 = TrianglePoint(6, 2) + + class Player1 extends MockPlayer { + var selectCount: Int = 0; + val ps: Vector[TrianglePoint] = Vector(p11, p12) + override def selectVein(game: net.javachallenge.api.Game): net.javachallenge.api.TrianglePoint = { + selectCount += 1 + ps(selectCount - 1) + } + } + + class Player2 extends MockPlayer { + var selectCount: Int = 0; + val ps: Vector[TrianglePoint] = Vector(p21, p22) + override def selectVein(game: net.javachallenge.api.Game): net.javachallenge.api.TrianglePoint = { + selectCount += 1 + ps(selectCount - 1) + } + } + + class Player3 extends MockPlayer { + var selectCount: Int = 0; + val ps: Vector[TrianglePoint] = Vector(p31, p32) + override def selectVein(game: net.javachallenge.api.Game): net.javachallenge.api.TrianglePoint = { + selectCount += 1 + ps(selectCount - 1) + } + } + + val cmps = List(new Player1(), new Player2(), new Player3()) + + val veins = TreeMap(p11 -> Vein(p11, Gas, 10, 10, 1), + p12 -> Vein(p12, Stone, 10, 10, 1), + p21 -> Vein(p21, Stone, 10, 10, 1), + p22 -> Vein(p22, Gas, 10, 10, 1), + p31 -> Vein(p31, Metal, 10, 10, 1), + p32 -> Vein(p32, Metal, 10, 10, 1), + p4 -> Vein(p4, Metal, 10, 10, 1), + p5 -> Vein(p5, Metal, 10, 10, 1), + p6 -> Vein(p6, Stone, 10, 10, 1)) + + val settings = GameSetting(veinCount = 10, moveTurn = 1) + val env: GameEnvironment = GameEnvironment(game = Game(names, settings, Field(settings, veins))) + val man = env.getSceneManager().setFps(1000); + def game = env.game + + val mainScene = new MainScene(null) with ConsoleScene with MainRunnerScene + val veinScene = new VeinScene(mainScene) with ConsoleScene with InitialRunnerScene + veinScene.runners = Vector(cmps.map(cmp => new InitialRunner(cmp)): _*) + mainScene.runners = Vector(cmps.map(cmp => new MainRunner(cmp)): _*) + man.initialize(env, veinScene) + + def run(scene: Scene[GameEnvironment]) = { + man.runOneStep(env, scene) + } + + run(veinScene) + run(veinScene) + run(veinScene) + run(veinScene) + run(veinScene) + run(veinScene) + + run(mainScene) + run(mainScene) + run(mainScene) + + // "start game with mocks" in { + // val veinScene = new VeinScene(null) with ConsoleScene with InitialRunnerScene + // val cmps = List(new MockPlayer(), new MockPlayer(), new MockPlayer()) + // veinScene.runners = Vector(cmps.map(cmp => new InitialRunner(cmp)): _*) + // man.initialize(env, veinScene) + // + // run(veinScene) + // run(veinScene) + // run(veinScene) + // run(veinScene) + // run(veinScene) + // run(veinScene) must_!= mainScene + // } + + "advance turns for increasing materials" in { + val ps = game.players + ps(0).materials must_== mutable.Map(Metal -> 0, Gas -> 10, Stone -> 10) + game.field.veins(p11).robot must_== 11 + game.field.veins(p12).robot must_== 11 + ps(1).materials must_== mutable.Map(Metal -> 0, Gas -> 10, Stone -> 10) + game.field.veins(p21).robot must_== 11 + game.field.veins(p22).robot must_== 11 + ps(2).materials must_== mutable.Map(Metal -> 20, Gas -> 0, Stone -> 0) + game.field.veins(p31).robot must_== 11 + game.field.veins(p32).robot must_== 11 + } + + // "advance turns changing current player" in { + // game.currentPlayer must_== game.players(0) + // run(mainScene) + // game.currentPlayer must_== game.players(1) + // run(mainScene) + // game.currentPlayer must_== game.players(2) + // run(mainScene) + // game.currentPlayer must_== game.players(0) + // } + // + // "get initial money" in { + // game.players(0).money must_== game.settings.initialMoney + // } + // + // "trade materials" in { + // run(mainScene) + // run(mainScene) + // game.players(0).materials must_== mutable.Map(Metal -> 0, Gas -> 20, Stone -> 20) + // game.players(0).money must_== settings.initialMoney - 10 + // run(mainScene) + // run(mainScene) + // run(mainScene) + // run(mainScene) + // game.players(2).materials must_== mutable.Map(Metal -> 20, Gas -> 0, Stone -> 0) + // game.players(2).money must_== settings.initialMoney + // run(mainScene) + // run(mainScene) + // run(mainScene) + // run(mainScene) + // val ps = game.players + // ps(0).materials must_== mutable.Map(Metal -> 10, Gas -> 40, Stone -> 30) + // ps(0).money must_== settings.initialMoney - 20 + // ps(1).materials must_== mutable.Map(Metal -> 10, Gas -> 20, Stone -> 30) + // ps(1).money must_== settings.initialMoney + // ps(2).materials must_== mutable.Map(Metal -> 20, Gas -> 0, Stone -> 0) + // ps(2).money must_== settings.initialMoney + 20 + // } + // + // "launch squads to a own vein" in { + // run(mainScene) + // run(mainScene) + // + // game.field.squads.size must_== 0 + // run(mainScene) + // game.field.squads.size must_== 1 + // game.field.veins(p31).robot must_== 1 + // game.field.veins(p31).ownerId must_== 2 + // game.field.veins(p32).robot must_== 11 + // game.field.veins(p32).ownerId must_== 2 + // + // for (i <- 0 until 3) run(mainScene) + // + // game.field.squads.size must_== 1 + // game.field.veins(p31).robot must_== 2 + // game.field.veins(p31).ownerId must_== 2 + // game.field.veins(p32).robot must_== 12 + // game.field.veins(p32).ownerId must_== 2 + // + // for (i <- 0 until 3) run(mainScene) + // + // game.field.squads.size must_== 0 + // game.field.veins(p31).robot must_== 3 + // game.field.veins(p31).ownerId must_== 2 + // game.field.veins(p32).robot must_== 23 + // game.field.veins(p32).ownerId must_== 2 + // } + // + // "launch squads to a neutral vein" in { + // run(mainScene) + // for (i <- 0 until 3) run(mainScene) + // + // run(mainScene) + // for (i <- 0 until 3) run(mainScene) + // + // for (i <- 0 until 2) { + // game.field.squads.size must_== 2 + // game.field.veins(p6).robot must_== 10 + // for (i <- 0 until 3) run(mainScene) + // } + // + // game.field.squads.size must_== 1 + // game.field.veins(p6).robot must_== 0 + // game.field.veins(p6).ownerId must_== -1 + // + // for (i <- 0 until 3) run(mainScene) + // + // game.field.squads.size must_== 0 + // game.field.veins(p6).robot must_== 1 + // game.field.veins(p6).ownerId must_== 0 + // } + // + // "launch squads to not neutral veins" in { + // run(mainScene) + // run(mainScene) + // run(mainScene) + // run(mainScene) + // + // game.field.squads.size must_== 3 + // run(mainScene) + // game.field.squads.size must_== 2 + // run(mainScene) + // game.field.veins(p11).robot must_== 2 + // game.field.veins(p21).robot must_== 1 + // + // for (i <- 0 until 3) run(mainScene) + // game.field.veins(p11).ownerId must_== 0 + // game.field.veins(p11).robot must_== 3 + // game.field.veins(p21).ownerId must_== 0 + // game.field.veins(p21).robot must_== 8 + // } + } +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/util/internationalization/I18nSpecTest.scala b/src/test/scala/net/javachallenge/util/internationalization/I18nSpecTest.scala new file mode 100644 index 0000000..77b144d --- /dev/null +++ b/src/test/scala/net/javachallenge/util/internationalization/I18nSpecTest.scala @@ -0,0 +1,53 @@ +package net.javachallenge.util.internationalization + +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } + +@RunWith(classOf[JUnitSuiteRunner]) +class I18nSpecTest extends Specification with JUnit { + "I18n" should { + doFirst { + val helloworld = "hello_world" + val en = Locale("en", "US") + val fr = Locale("fr") + val es = Locale("es") + en.addWord(helloworld, "Hello World!") + en.addWord("hi", "Hey!") + fr.addWord(helloworld, "Bonjour le Monde !") + es.addWord(helloworld, "¡Hola Mundo!") + } + + "not respond to unregistered locales" in { + Locale.has("ja") must beFalse + Locale.has("it") must beFalse + } + + "respond to registered locales" in { + Locale.has("en") must beTrue + Locale.has("fr") must beTrue + } + + "return the key when locale is not found" in { + Locale.set("ja") + I18n("hello_world").translate must_== "hello_world" + } + + "return the key when word is not found" in { + Locale.set("en") + I18n("foo").translate must_== "foo" + } + + "translate the word when it is found" in { + Locale.set("fr") + I18n("hello_world").translate must_== "Bonjour le Monde !" + } + + "translate word with fallback if not found in current locale" in { + Locale.setFallback("en") + I18n("hi").translate must_== "Hey!" + } + } + +} diff --git a/src/test/scala/net/javachallenge/util/internationalization/TranslationLoaderSpecTest.scala b/src/test/scala/net/javachallenge/util/internationalization/TranslationLoaderSpecTest.scala new file mode 100644 index 0000000..00d9c20 --- /dev/null +++ b/src/test/scala/net/javachallenge/util/internationalization/TranslationLoaderSpecTest.scala @@ -0,0 +1,34 @@ +package net.javachallenge.util.internationalization + +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } + +@RunWith(classOf[JUnitSuiteRunner]) +class TranslationLoaderSpecTest extends Specification with JUnit { + + "Translation loader" should { + doFirst { + XMLTranslationLoader.generateLocales("src/test/resources/translations") + Locale.set("ja") + } + + "not have inexistent locales" in { + Locale.has("it") must beFalse + } + + "have existent locales" in { + Locale.has("ja") must beTrue + } + + "not translate inexistent keys" in { + I18n.get("foo") must_== "foo" + } + + "translate existent keys" in { + I18n.get("hello_world") must_== "こんにちは世界。" + } + } + +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/util/settings/EnvironmentSpecTest.scala b/src/test/scala/net/javachallenge/util/settings/EnvironmentSpecTest.scala new file mode 100644 index 0000000..338631c --- /dev/null +++ b/src/test/scala/net/javachallenge/util/settings/EnvironmentSpecTest.scala @@ -0,0 +1,33 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import net.javachallenge.util.internationalization.Locale + +@RunWith(classOf[JUnitSuiteRunner]) +class EnvironmentSpecTest extends Specification with JUnit { + + "Environment" should { + + doLast { + Environment.setEnvironment(Defaults.ENV) + } + + "initialize to default" in { + Environment.current must_== Defaults.ENV + } + + "yield proper folder path" in { + Environment.setEnvironment(Production) + Environment.current.folder must_== "main" + Environment.setEnvironment(Test) + Environment.current.folder must_== "test" + } + } +} \ No newline at end of file diff --git a/src/test/scala/net/javachallenge/util/settings/XMLSettingsParserSpecTest.scala b/src/test/scala/net/javachallenge/util/settings/XMLSettingsParserSpecTest.scala new file mode 100644 index 0000000..905f6df --- /dev/null +++ b/src/test/scala/net/javachallenge/util/settings/XMLSettingsParserSpecTest.scala @@ -0,0 +1,35 @@ +/** + * @author Daniel Perez + */ + +package net.javachallenge.util.settings + +import org.junit.runner.RunWith +import org.specs._ +import org.specs.matcher._ +import org.specs.runner.{ JUnitSuiteRunner, JUnit } +import net.javachallenge.util.internationalization.Locale +import net.javachallenge.util.internationalization.I18n + +@RunWith(classOf[JUnitSuiteRunner]) +class XMLSettingsParserSpecTest extends Specification with JUnit { + + "XMLSettingsParser" should { + + doFirst { + Environment.setEnvironment(Test) + Locale.clear + SettingsLoader.loadSettings + } + + doLast { + Environment.setEnvironment(Defaults.ENV) + Locale.clear + } + + "load translation settings" in { + Locale.has("ja") must beTrue + I18n.get("hello_world") must_== "こんにちは世界。" + } + } +} \ No newline at end of file