<commit_before>package org.ggp.base.util.match;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.ggp.base.util.crypto.SignableJSON;
import org.ggp.base.util.crypto.BaseCryptography.EncodedKeyPair;
import org.ggp.base.util.game.Game;
import org.ggp.base.util.game.RemoteGameRepository;
import org.ggp.base.util.gdl.factory.GdlFactory;
import org.ggp.base.util.gdl.factory.exceptions.GdlFormatException;
import org.ggp.base.util.gdl.grammar.Gdl;
import org.ggp.base.util.gdl.grammar.GdlConstant;
import org.ggp.base.util.gdl.grammar.GdlFunction;
import org.ggp.base.util.gdl.grammar.GdlRelation;
import org.ggp.base.util.gdl.grammar.GdlSentence;
import org.ggp.base.util.gdl.grammar.GdlTerm;
import org.ggp.base.util.gdl.scrambler.GdlScrambler;
import org.ggp.base.util.gdl.scrambler.MappingGdlScrambler;
import org.ggp.base.util.gdl.scrambler.NoOpGdlScrambler;
import org.ggp.base.util.statemachine.Move;
import org.ggp.base.util.statemachine.Role;
import org.ggp.base.util.symbol.factory.SymbolFactory;
import org.ggp.base.util.symbol.factory.exceptions.SymbolFormatException;
import org.ggp.base.util.symbol.grammar.SymbolList;

import external.JSON.JSONArray;
import external.JSON.JSONException;
import external.JSON.JSONObject;

/**
 * Match encapsulates all of the information relating to a single match.
 * A match is a single play through a game, with a complete history that
 * lists what move each player made at each step through the match. This
 * also includes other relevant metadata about the match, including some
 * unique identifiers, configuration information, and so on.
 * 
 * NOTE: Match objects created by a player, representing state read from
 * a server, are not completely filled out. For example, they only get an
 * ephemeral Game object, which has a rulesheet but no key or metadata.
 * Gamers which do not derive from StateMachineGamer also do not keep any
 * information on what states have been observed, because (somehow) they
 * are representing games without using state machines. In general, these
 * player-created Match objects shouldn't be sent out into the ecosystem.
 * 
 * @author Sam
 */
public final class Match
{
    private final String matchId;
    private final String randomToken;
    private final String spectatorAuthToken;
    private final int playClock;
    private final int startClock;
    private final int previewClock;
    private final Date startTime;
	private final Game theGame;
	private final List<List<GdlTerm>> moveHistory;
	private final List<Set<GdlSentence>> stateHistory;
	private final List<List<String>> errorHistory;
	private final List<Date> stateTimeHistory;
	private boolean isCompleted;
	private boolean isAborted;
	private final List<Integer> goalValues;
	private final int numRoles;
	
	private EncodedKeyPair theCryptographicKeys;
	private List<String> thePlayerNamesFromHost;
	private List<Boolean> isPlayerHuman;
	
	private GdlScrambler theGdlScrambler = new NoOpGdlScrambler();

	public Match(String matchId, int previewClock, int startClock, int playClock, Game theGame)
	{
		this.matchId = matchId;
		this.previewClock = previewClock;
		this.startClock = startClock;
		this.playClock = playClock;
		this.theGame = theGame;
		
		this.startTime = new Date();
		this.randomToken = getRandomString(32);
		this.spectatorAuthToken = getRandomString(12);
		this.isCompleted = false;
		this.isAborted = false;
		
		this.numRoles = Role.computeRoles(theGame.getRules()).size();
		
		this.moveHistory = new ArrayList<List<GdlTerm>>();
		this.stateHistory = new ArrayList<Set<GdlSentence>>();
		this.stateTimeHistory = new ArrayList<Date>();
		this.errorHistory = new ArrayList<List<String>>();
		
		this.goalValues = new ArrayList<Integer>();
	}
	
	public Match(String theJSON, Game theGame, String authToken) throws JSONException, SymbolFormatException, GdlFormatException {
        JSONObject theMatchObject = new JSONObject(theJSON);

        this.matchId = theMatchObject.getString("matchId");
        this.startClock = theMatchObject.getInt("startClock");
        this.playClock = theMatchObject.getInt("playClock");
        if (theGame == null) {
            this.theGame = RemoteGameRepository.loadSingleGame(theMatchObject.getString("gameMetaURL"));
            if (this.theGame == null) {
                throw new RuntimeException("Could not find metadata for game referenced in Match object: " + theMatchObject.getString("gameMetaURL"));
            }
        } else {
            this.theGame = theGame;
        }
        
        if (theMatchObject.has("previewClock")) {
        	this.previewClock = theMatchObject.getInt("previewClock");
        } else {
        	this.previewClock = -1;
        }
        
        this.startTime = new Date(theMatchObject.getLong("startTime"));
        this.randomToken = theMatchObject.getString("randomToken");
        this.spectatorAuthToken = authToken;
        this.isCompleted = theMatchObject.getBoolean("isCompleted");
        if (theMatchObject.has("isAborted")) {
        	this.isAborted = theMatchObject.getBoolean("isAborted");
        } else {
        	this.isAborted = false;
        }

        this.numRoles = Role.computeRoles(theGame.getRules()).size();
        
        this.moveHistory = new ArrayList<List<GdlTerm>>();<commit_msg>Fix the way roles are computed when the game is loaded from a stored match based on gameMetaURL.<commit_after>package org.ggp.base.util.match;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.ggp.base.util.crypto.SignableJSON;
import org.ggp.base.util.crypto.BaseCryptography.EncodedKeyPair;
import org.ggp.base.util.game.Game;
import org.ggp.base.util.game.RemoteGameRepository;
import org.ggp.base.util.gdl.factory.GdlFactory;
import org.ggp.base.util.gdl.factory.exceptions.GdlFormatException;
import org.ggp.base.util.gdl.grammar.Gdl;
import org.ggp.base.util.gdl.grammar.GdlConstant;
import org.ggp.base.util.gdl.grammar.GdlFunction;
import org.ggp.base.util.gdl.grammar.GdlRelation;
import org.ggp.base.util.gdl.grammar.GdlSentence;
import org.ggp.base.util.gdl.grammar.GdlTerm;
import org.ggp.base.util.gdl.scrambler.GdlScrambler;
import org.ggp.base.util.gdl.scrambler.MappingGdlScrambler;
import org.ggp.base.util.gdl.scrambler.NoOpGdlScrambler;
import org.ggp.base.util.statemachine.Move;
import org.ggp.base.util.statemachine.Role;
import org.ggp.base.util.symbol.factory.SymbolFactory;
import org.ggp.base.util.symbol.factory.exceptions.SymbolFormatException;
import org.ggp.base.util.symbol.grammar.SymbolList;

import external.JSON.JSONArray;
import external.JSON.JSONException;
import external.JSON.JSONObject;

/**
 * Match encapsulates all of the information relating to a single match.
 * A match is a single play through a game, with a complete history that
 * lists what move each player made at each step through the match. This
 * also includes other relevant metadata about the match, including some
 * unique identifiers, configuration information, and so on.
 * 
 * NOTE: Match objects created by a player, representing state read from
 * a server, are not completely filled out. For example, they only get an
 * ephemeral Game object, which has a rulesheet but no key or metadata.
 * Gamers which do not derive from StateMachineGamer also do not keep any
 * information on what states have been observed, because (somehow) they
 * are representing games without using state machines. In general, these
 * player-created Match objects shouldn't be sent out into the ecosystem.
 * 
 * @author Sam
 */
public final class Match
{
    private final String matchId;
    private final String randomToken;
    private final String spectatorAuthToken;
    private final int playClock;
    private final int startClock;
    private final int previewClock;
    private final Date startTime;
	private final Game theGame;
	private final List<List<GdlTerm>> moveHistory;
	private final List<Set<GdlSentence>> stateHistory;
	private final List<List<String>> errorHistory;
	private final List<Date> stateTimeHistory;
	private boolean isCompleted;
	private boolean isAborted;
	private final List<Integer> goalValues;
	private final int numRoles;
	
	private EncodedKeyPair theCryptographicKeys;
	private List<String> thePlayerNamesFromHost;
	private List<Boolean> isPlayerHuman;
	
	private GdlScrambler theGdlScrambler = new NoOpGdlScrambler();

	public Match(String matchId, int previewClock, int startClock, int playClock, Game theGame)
	{
		this.matchId = matchId;
		this.previewClock = previewClock;
		this.startClock = startClock;
		this.playClock = playClock;
		this.theGame = theGame;
		
		this.startTime = new Date();
		this.randomToken = getRandomString(32);
		this.spectatorAuthToken = getRandomString(12);
		this.isCompleted = false;
		this.isAborted = false;
		
		this.numRoles = Role.computeRoles(theGame.getRules()).size();
		
		this.moveHistory = new ArrayList<List<GdlTerm>>();
		this.stateHistory = new ArrayList<Set<GdlSentence>>();
		this.stateTimeHistory = new ArrayList<Date>();
		this.errorHistory = new ArrayList<List<String>>();
		
		this.goalValues = new ArrayList<Integer>();
	}
	
	public Match(String theJSON, Game theGame, String authToken) throws JSONException, SymbolFormatException, GdlFormatException {
        JSONObject theMatchObject = new JSONObject(theJSON);

        this.matchId = theMatchObject.getString("matchId");
        this.startClock = theMatchObject.getInt("startClock");
        this.playClock = theMatchObject.getInt("playClock");
        if (theGame == null) {
            this.theGame = RemoteGameRepository.loadSingleGame(theMatchObject.getString("gameMetaURL"));
            if (this.theGame == null) {
                throw new RuntimeException("Could not find metadata for game referenced in Match object: " + theMatchObject.getString("gameMetaURL"));
            }
        } else {
            this.theGame = theGame;
        }
        
        if (theMatchObject.has("previewClock")) {
        	this.previewClock = theMatchObject.getInt("previewClock");
        } else {
        	this.previewClock = -1;
        }
        
        this.startTime = new Date(theMatchObject.getLong("startTime"));
        this.randomToken = theMatchObject.getString("randomToken");
        this.spectatorAuthToken = authToken;
        this.isCompleted = theMatchObject.getBoolean("isCompleted");
        if (theMatchObject.has("isAborted")) {
        	this.isAborted = theMatchObject.getBoolean("isAborted");
        } else {
        	this.isAborted = false;
        }

        this.numRoles = Role.computeRoles(this.theGame.getRules()).size();
        
        this.moveHistory = new ArrayList<List<GdlTerm>>();
{'fraction_non_alphanumeric': 0.09730925175082934, 'fraction_numerical': 0.0009214891264283082, 'mean_word_length': 4.033858998144712, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4547293', 'n_tokens_mistral': 3276, 'n_tokens_neox': 3191, 'n_words': 810}