package org.ggp.base.apps.research; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import external.JSON.JSONArray; import external.JSON.JSONException; import external.JSON.JSONObject; /** * MatchArchiveProcessor is a utility program that reads in serialized * match archives, like those available at GGP.org/researchers, and runs * some computation on every match in the archive. * * This can be used to do many useful tasks, like counting the number of * matches that satisfy certain properties, totaling up averages across the * set of all matches, and computing statistics and player ratings. * * Currently this computes three interesting example aggregations: * - the frequency of wins for the second player, broken down by game * - a histogram of how often each game is played * - the average length of a nine-board tic-tac-toe match * * @author Sam Schreiber */ public final class MatchArchiveProcessor { // Set this to the path of the downloaded match archive file. public static final File ARCHIVE_FILE = new File(new File(new File(new File(System.getProperty("user.home")), "matchArchive"), "data"), "allMatches"); private MatchArchiveProcessor() { } public static void main(String[] args) throws IOException, JSONException { AggregateData data = new AggregateData(); String line; int nCount = 0; BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(ARCHIVE_FILE), Charset.forName("UTF-8"))); while ((line = br.readLine()) != null) { JSONObject entryJSON = new JSONObject(line); String url = entryJSON.getString("url"); JSONObject matchJSON = entryJSON.getJSONObject("data"); processMatch(url, matchJSON, data); nCount++; if (nCount % 1000 == 0) { System.out.println("Processed " + nCount + " matches."); } } br.close(); System.out.println("Second player win frequency:\n" + data.secondPlayerWinFrequency.toString()); System.out.println("Game histogram:\n" + data.gameHistogram.toString()); System.out.println("average matchLengthsFor9xTTT = " + data.matchLengthsFor9xTTT.toString()); } // This class stores all of the data that needs to be aggregated between // individual matches. It will include things like histograms, counters, // weighted averages, etc. If you're adding a new aggregation, you'll likely // want to add a new data structure here to keep track of the aggregate data // between matches. static class AggregateData { public Histogram gameHistogram = new Histogram(); public WeightedAverage matchLengthsFor9xTTT = new WeightedAverage(); public FrequencyTable secondPlayerWinFrequency = new FrequencyTable(); } // This method determines how each individual match is processed. // Right now it aggregates data about how often individual games are played, // how often the second player wins a match, and how long the average match // of nine-board tic-tac-toe takes. If you want to add more aggregations, this // is the place to do it. private static void processMatch(String theURL, JSONObject matchJSON, AggregateData data) { try { String gameURL = matchJSON.getString("gameMetaURL"); // Add a data point to the histogram of how often games are used data.gameHistogram.add(gameURL); // And for completed signed matches... if (matchJSON.has("isCompleted") && matchJSON.getBoolean("isCompleted") && matchJSON.has("matchHostPK")) { // Add a data point to the average length of 9xTTT matches, if it's a 9xTTT match if (gameURL.startsWith("http://games.ggp.org/base/games/nineBoardTicTacToe/")) { data.matchLengthsFor9xTTT.addValue(matchJSON.getJSONArray("states").length()); } // Add a data point to the frequency of second player wins, if this is a match that // has a second player and recorded goal values. if (matchJSON.has("goalValues") && matchJSON.getJSONArray("goalValues").length() > 1) { boolean secondPlayerWon = true; JSONArray goalValues = matchJSON.getJSONArray("goalValues"); for (int i = 0; i < goalValues.length(); i++) { if (i == 1) continue; if (goalValues.getInt(i) >= goalValues.getInt(1)) { secondPlayerWon = false; } } data.secondPlayerWinFrequency.add(gameURL, secondPlayerWon ? 1 : 0); } } } catch (JSONException je) { je.printStackTrace(); } } }