{"id":166,"date":"2018-03-04T12:06:34","date_gmt":"2018-03-04T12:06:34","guid":{"rendered":"http:\/\/mrraow.com\/?page_id=166"},"modified":"2019-10-02T11:17:27","modified_gmt":"2019-10-02T11:17:27","slug":"adding-games-to-ai-ai-in-java-and-compatible-languages","status":"publish","type":"page","link":"http:\/\/mrraow.com\/index.php\/aiai-home\/adding-games-to-ai-ai-in-java-and-compatible-languages\/","title":{"rendered":"Adding games to Ai Ai in Java (and compatible languages)"},"content":{"rendered":"<p>I have created a (relatively) simple interface that will allow java programmers to extend Ai Ai with additional games.<br \/>\nTo do so, you&#8217;ll need:<\/p>\n<ul>\n<li>An .mgl file to tell Ai Ai where to look for your class files<\/li>\n<li>A class file implementing the ExternalGame interface<\/li>\n<\/ul>\n<p><em>Note 1: if you implement a game designed by someone else, please get permission before distributing the files.<\/em><br \/>\n<em>Note 2: as this is pure java code, please only run external games from sources you trust.<\/em><\/p>\n<p>First, download and unzip Ai Ai, if you haven&#8217;t already done so.<br \/>\nIf you look in the ai ai installation, you will find a \/dev\/ folder. In there, you will find the following:<\/p>\n<ul>\n<li>\/src\/external\/ExternalGame.java: this is the old interface<\/li>\n<li>\/src\/external\/ExternalGameV2.java: this is the newest version of the interface<\/li>\n<li>\/src\/external\/NoughtsAndCrosses.java: example game<\/li>\n<li>\/src\/external\/BreakthroughExt.java: more complex example game with heuristics<\/li>\n<li>NoughtsAndCrosses.mgl: example .mgl file telling Ai Ai how to load the game<\/li>\n<li>BreakthroughExt.mgl: example .mgl file telling Ai Ai how to load the game<\/li>\n<li>\/bin\/external\/NoughtsAndCrosses.class: class file that Ai Ai will load<\/li>\n<li>\/bin\/external\/BreakthroughExt.class: class file that Ai Ai will load<\/li>\n<\/ul>\n<p>ExternalGameV2.java<\/p>\n<pre class=\"prettyprint\">package external;\n\nimport java.util.Map;\n\n\/**\n * Provides support for games implemented outside the main project and loaded into Ai Ai at runtime.\n * Any .class file that implements this interface should be runnable.\n * \n * NOTE: Ai Ai expects to find a default constructor for any class it loads \n * Java will create this automatically, unless you expicitly create a constructor.\n * So, if you do create another constructor (e.g. a copy constructor), don't forget to create this one too\n * \n * @author Stephen Tavener\n *\/\npublic interface ExternalGameV2 \n{\n\t\/** Return LOSS if specified player has lost the game *\/\n\tpublic static final int LOSS = -1;\n\t\/** Return DRAW if specified player has drawn the game *\/\n\tpublic static final int DRAW = 0;\n\t\/** Return WIN if specified player has won the game *\/\n\tpublic static final int WIN = 1;\n\t\/** Separates [piece type]#[player]#[cell] *\/\n\tpublic static final String DRAWABLE_SEPARATOR = \"#\";\n\t\n\t\/**\n\t * Initialisation function. Will be called only once, so does not need to be very \n\t * efficient.\n\t * JSON objects will be represented as one of the following:\n\t * Map&lt;String,Object&gt;\n\t * List&lt;String&gt;\n\t * Number\n\t * null\n\t * @param json The contents of the .mgl file as a map of key-value pairs.\n\t *\/\n\tpublic void init (Map<string,object> json);\n\t\n\t\/**\n\t * \n\t * @return Number of available actions from this state.  If the game is not in a \n\t * terminal state, there must always be one action, even if it is only \"Pass\".\n\t *\/\n\tpublic int numMoves();\n\n\t\/**\n\t * Changes the current state to a new state by applying the nth \n\t * action \n\t * Note that the nth move has no external context, and may not \n\t * always refer to the same move.\n\t * @param n in the range 0..numMoves()-1\n\t *\/\n\tpublic void applyNthMove(int n);\n\t\n\t\/**\n\t * Owner is in the range 1..number of players. Number of players \n\t * defaults to 2, but can be overridden in the mgl file.\n\t * @return the owner of the current state (e.g. the player to move)\n\t *\/\n\tpublic int getOwner();\n\n\t\/**\n\t * Checks if the game is in a terminal state\n\t * @return true if this is an end state\n\t *\/\n\tpublic boolean isTerminal();\n\n\t\/**\n\t * Called to obtain the score for a specific player - note that \n\t * multiplayer games can have multiple winners, losers, etc.\n\t * If the game is not in a terminal state, behaviour is \n\t * undetermined.\n\t * @param player in range 1 to number of players\n\t * @return returns the score for this player, \n\t * -1 (loss), 0 (draw), 1 (win)\n\t *\/\n\tpublic int mcScore(int player);\n\t\n\t\/**\n\t * Produces a deep copy of this object(game class) - necessary, \n\t * because Java returns a shallow copy by default, \n\t * which can lead to corruption of the state during search.  \n\t * \n\t * Some care is required here.  If you have an object O that \n\t * is referred to from two board positions, for example, you \n\t * need to ensure that it is only copied once; otherwise \n\t * references which were formerly \n\t * referring to the same object will be pointing to different \n\t * objects\n\t * \n\t * This function will be called frequently, so should \n\t * be as fast as possible (Java copies memory very efficiently\n\t * so an array of ints for your board representation is fine)\n\t * \n\t * @return new game which is a perfect copy of this game\n\t *\/\n\tpublic ExternalGameV2 deepCopy();\t\n\n\t\/**\n\t * Returns the hash for the current position if moveIndex is -1, \n\t * hash of position after specified move otherwise\n\t * The game MUST return a hash for the current position when \n\t * requested, but may return -1 for any other position; \n\t * \n\t * Performance warning: if the game does not supply them, Ai Ai \n\t * will generate hashes for moves by: \n\t * (1) calling deep copy to clone the game, \n\t * (2) applying the move, and \n\t * (3) calling hash(-1)\n\t * \n\t * Ideally:\n\t * - a hash should uniquely identify a position (including player \n\t *   to move and any other behaviour modification flags, e.g. castling)\n\t * - a position will always have the same hash, even if it occurs \n\t *   through a different sequence of moves\n\t * - a position will always have the same hash, even if the game is \n\t *   saved and reloaded, or copied\n\t * \n\t * Failure to do so may result in a weaker AI, but something is \n\t * better than nothing!\n\t * \n\t * See https:\/\/en.wikipedia.org\/wiki\/Zobrist_hashing \n\t * \n\t * If you choose to ignore hashing, please add the following line to \n\t * your .mgl file:\n\t * \t\t\"useGuctHashing\" : false,\n\t * \n\t * @param moveIndex child index for which hash is requested, or -1 \n\t * for current position\n\t * @return the current board hash if moveIndex is -1, index of \n\t * specified move otherwise\n\t *\/\n\tpublic long hash(int moveIndex);\n\t\n\/*================================================\n *(OPTIONAL) HEURISTIC FUNCTIONS BEYOND THIS POINT\n *================================================ *\/\n\t\/**\n\t * A switch which determines if Alpha Beta can be used with \n\t * this game \n\t * If isHeuristic() returns true, then the game is expected to \n\t * implement the other functions below - if not, they can just \n\t * return null.\n\t * (NOTE: Alpha Beta search has the potential to be considerably \n\t * stronger than UCT, but only if the heuristics are good.)\n\t * \n\t * @return true if this game provides heuristic information \n\t * for use with alpha beta search \n\t *\/\n\tpublic boolean isHeuristic();\n\t\n\t\/**\n\t * Calculates relative scores for each player.\n\t * AB search will use the difference between the scores\n\t * UCT implementations will ignore this function.\n\t * @return array of size numPlayers+1 with scores for \n\t * each player in the corresponding index; \n\t * element 0 is not used \n\t *\/\n\tpublic int[] calcHeuristics();\n\t\n\t\/**\n\t * Optional\n\t * Advances the internal state (e.g. next player) without applying a \n\t * move.\n\t * Called by AIs that perform null tests (i.e. compare the score \n\t * with the score after doing nothing for a move)! Even the \n\t * Alphabeta AIs won't call this by default, because I haven't \n\t * found it useful in most games.\n\t *\/\n\tpublic void nullMove();\n\t\n\t\/**\n\t * Used with quintessence search, where the AI continues searching \n\t * until a stable position is found.\n\t * \n\t * Example; in chess stable implies no captures are possible.\n\t * NOTE: If in doubt, return true - if not, the search may not \n\t * terminate!\n\t *\n\t * @return True if the position is stable, and therefore heuristics \n\t * are likely to be reliable, false otherwise.\n\t *\/\n\tpublic boolean isStable();\n\t\n\t\/**\n\t * Implementing classes can order moves according to most likely \n\t * strength. note that there is a trade-off here between processing \n\t * time and AI strength so implement only if necessary,\n\t * \n\t * If you return null, ordering will be random (which is good in \n\t * terms of user experience)\n\t * \n\t * AlphaBeta AIs can then use this ordering during search to improve \n\t * cutoffs.\n\t * Roughly speaking:\n\t * \t\tWorst order: AB will search to depth n\n\t * \t\tRandom order: AB will search to depth 3n\/2\n\t * \t\tBest order: AB will search to depth 2n\n\t * \n\t * UCT-based AIs may also use this ordering during the exploration \n\t * phase of the turn to prioritise or avoid certain moves\n\t * \n\t * If a move is clearly bad, do not include it in the list.\n\t * \n\t * Examples: \n\t * \t\tMost placement games should order moves from centre \n\t *      outwards. \n\t * \t\tCapture games should put captures first.\n\t * \n\t * NOTE: These are recommendations from the game to the AI only.  \n\t * The AI is free to re-order this list based on extra heuristics \n\t * like killer moves, \t * and may occasionally re-introduce excluded \n\t * moves if they were found to be strong elsewhere in the search tree.\n\t * \n\t * @param searchPlayer player who is searching, 1 to numPlayers\n\t * @param maxDepth Maximum search depth for this pass, \n\t *   or -1 if called outside of a search pass\n\t * @param currentDepth Current search depth from root for this pass, or\n\t *  -1 if called outside of a search pass\n\t * @param numVisits UCT based searches only; number of times this node \n\t * has been visited; -1 for other searches\n\t * \n\t * @return list of available move indices in recommended order of \n\t * preference (best first)\n\t *\/\n\tpublic int[] recommendedMoveOrder(\n\t\t\tint searchPlayer,\n\t\t\tint maxDepth, \n\t\t\tint currentDepth, \n\t\t\tint numVisits);\n\t\n\t\/**\n\t * The drawing method will call this function to obtain a list of \n\t * things to draw\n\t * \n\t * Each string entry should be in the following format:\n\t * \t\tkey#player#cell\n\t * Example: \"stone#White#a6\" would cause the GUI to render a white \n\t * go stone at a6.\n\t * \n\t * The key and player name are as defined in the .mgl file in the \n\t * \"pieces\" and \"agents\" sections respectively. \n\t * The cell names are grid references on the board from the bottom \n\t * left corner\n\t * (letters for column, number for row).  You can view the labels \n\t * in Ai Ai using Settings\/Show inner labels.\n\t * \n\t * NOTE: this method is only called from the GUI, and does not have \n\t * to be particularly efficient.\n\t * \n\t * @return list of assets for the GUI to draw.\n\t *\/\n\tpublic String[] drawables();\n\t\n\/* ================================================\n * USER INTERFACE \/ FEELGOOD FUNCTIONS\n * ================================================ *\/\n\t\/**\n\t * Describes a move in human-readable format. Ai Ai will use the \n\t * action description for the following:\n\t * 1. To display directly to the user in move lists\n\t * 2. To save the game to file.  To restore a game position, Ai Ai \n\t * stores the initial random seed, plus the sequence of moves so far\n\t * 3. For user interface choices - in particular, start and end \n\t * positions will be inferred from the moves if possible, and \n\t * used to translate mouse clicks into moves.\n\t * \n\t * As such, each move description from a given position must be \n\t * unique.\n\t * \n\t * So, while a game is free to use any notation it chooses, I \n\t * recommend the following if you want a good user experience. \n\t * If you need to disambiguate, Ai Ai will ignore anything after \n\t * a semicolon\n\t * \n\n\n\n\n\n<table summary=\"\">\n\n\n\n\n\n<tbody>\n\n\n\n\n\n<tr>\n\n\n\n\n\n<th>Notation<\/th>\n\n\n\n\n\n\n\n\n\n\n<th>Description<\/th>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n\n\n\n\n\n<tr>\n\n\n\n\n\n<td>Pass<\/td>\n\n\n\n\n\n\n\n\n\n\n<td>Pass (user takes no action, turn order advances)<\/td>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n\n\n\n\n\n<tr>\n\n\n\n\n\n<td>Swap<\/td>\n\n\n\n\n\n\n\n\n\n\n<td>Swap sides (usually pie rule option on second move)<\/td>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n\n\n\n\n\n<tr>\n\n\n\n\n\n<td>[to]<\/td>\n\n\n\n\n\n\n\n\n\n\n<td>Add a piece to [to], e.g. \"b4\"<\/td>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n\n\n\n\n\n<tr>\n\n\n\n\n\n<td>x[from]<\/td>\n\n\n\n\n\n\n\n\n\n\n<td>Capture the piece at [from], e.g. \"xa2\"<\/td>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n\n\n\n\n\n<tr>\n\n\n\n\n\n<td>[from]-[to]<\/td>\n\n\n\n\n\n\n\n\n\n\n<td>Movement from [from] to [to], e.g. \"a2-b4\"<\/td>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n\n\n\n\n\n<tr>\n\n\n\n\n\n<td>[from]x[to]<\/td>\n\n\n\n\n\n\n\n\n\n\n<td>Piece moves from [from] to [to] capturing piece at [to], e.g. \"a2xb4\"<\/td>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n\n\n\n\n\n<tr>\n\n\n\n\n\n<td>[from]-&gt;[player]<\/td>\n\n\n\n\n\n\n\n\n\n\n<td>Change ownership of piece, e.g. \"a2-&gt;White\"<\/td>\n\n\n\n\n\n<\/tr>\n\n\n\n\n\n<\/tbody>\n\n\n\n\n\n<\/table>\n\n\n\n\n\n\n\t * \n\t * (In particular, the GUI will look for moves called \"Swap\", \"Pass\" \n\t * and add extra options if they are present in the move list.)\n\t * \n\t * @param n nth move\n\t * @return nth action in human-readable format\n\t *\/\n\tpublic String actionDescription(int n);\n\t\n\t\/**\n\t * @return description of the current state of this game, e.g. \n\t *   \"White to move\", \"Black won\", etc.\n\t *\/\n\tpublic String stateDescription();\n\n}\n<\/string,object><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I have created a (relatively) simple interface that will allow java programmers to extend Ai Ai with additional games. To do so, you&#8217;ll need: An .mgl file to tell Ai Ai where to look for your class files A class file implementing the ExternalGame interface Note 1: if you implement a game designed by someone &hellip; <a href=\"http:\/\/mrraow.com\/index.php\/aiai-home\/adding-games-to-ai-ai-in-java-and-compatible-languages\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Adding games to Ai Ai in Java (and compatible languages)<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":10,"menu_order":1,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-166","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/pages\/166","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/comments?post=166"}],"version-history":[{"count":6,"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/pages\/166\/revisions"}],"predecessor-version":[{"id":319,"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/pages\/166\/revisions\/319"}],"up":[{"embeddable":true,"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/pages\/10"}],"wp:attachment":[{"href":"http:\/\/mrraow.com\/index.php\/wp-json\/wp\/v2\/media?parent=166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}