Tic-Tac-Toe

An introduction to our Java SDK

Motivation and Set Up

Why Tic-Tac-Toe? Well aside from being more fun than your average CRM system, we want this tutorial to focus on the use of the API itself and not the architectural issues surrounding integrating your system with ours, which will be different depending on your needs and setup. Thus, we wanted an example that was easy to relate to, but would not confuse the forest with the trees.

This tutorial assumes the reader has a firm understanding of Java programming.

We are going to use TrackVia as a persistent data store for the state of our Tic-Tac-Toe game. To do this we wil need to setup a table that can store the state of all nine squares in the game, who's turn it is, and the name of our game. For the name we will use a short answer field. For the nine squares and the turn indicator, we know these values will either be X, O or null, so a drop down field will work for all of them.

To allow us to reference the fields for the game board they will be named "0,0" through "2,2" so we can iterate over them using a zero indexed for-loop. The name field will be called "Name" and the turn field will be called "Turn." Our game table will look like this:

TrackVia table setup

Introduction to the Code

All of the code for this tutorial can be found in our Java SDK under src/test/java/trackvia/client/examples/tictactoe

Here's a brief run down of the files in our Tic-Tac-Toe program:

  • trackvia.config.template - This file stores the configuration information for authenticating with TrackVia. This includes the user's information and your user key. Make a copy of this file called trackvia.config and change the values to match those of your account and credentials. If you do not know what a certain field should be, the default value should work.
  • GameController.java - This is the main class of our game. It controls whose turn it is and prompts the user for input. Execute this class to run the game.
  • GameState.java - This class keeps track of the state of the game, the position of the pieces, if there's a winner or a tie, and renders the game to our UI.
  • Piece.java - A simple enum for our X's, O's, and empty spots. This class also differentiates between values for TrackVia and values to display to the user. For example, an empty in TrackVia is a null but a " " in our simple UI.
  • TrackViaConnector.java - This class interfaces between the TrackVia SDK and our game. It fetches the list of games, creates new ones, and updates the game state as the game is played. We will focus on this class for the rest of this tutorial.

Authenticating

Here we look at the setupClient method in the GameController class. This method authenticates the user with TrackVia and gets the access and refresh tokens that are used on all subsequent calls to the API:



	public void setupClient(String configPath) throws IOException {
		String email;
		String userKey;
		String password;
		String scheme;
		String hostName;
		int port;
		String path;
		
		Properties config = new Properties();
		FileInputStream inputStream = null;
		try {
			inputStream = new FileInputStream(configPath);
		} catch (FileNotFoundException e) {
			throw new FileNotFoundException("Config file ("+ configPath + ") not found. " + 
					"Did you remember to make a copy from the template file?");
		}
		config.load(inputStream);
		email = config.getProperty("email");
		userKey = config.getProperty("user_key");
		password = config.getProperty("password");
		scheme = config.getProperty("scheme");
		hostName = config.getProperty("hostname");
		port = Integer.parseInt(config.getProperty("port"));
		path = config.getProperty("path");
		
		TrackviaClient client = TrackviaClient.create(path, scheme, hostName, 
				port, email, password, userKey);
		connector = new TrackViaConnector(client);
	}

Here all the configuration options are read from the config file used to create our TrackViaClient. Most users will not need to use things like port and path, but if you are behind a firewall these options could be useful.

The TrackViaClient class will handle token expiration and obtain a new token using the refresh token. So once you authenticate you do not have to worry about authenticating again.

Listing View

To play our game we will need to pick which view represents the table that stores our game state. We could set this using our config file, but since views are an important part of our API, we wanted to show how to list them. In theory you could have multiple views which all pull data from the same table, but filter out games where it is X's turn or where the game has been finished.

The getListOfViews method in TrackViaConnector gets a list of views, pulls out the name and ID, and converts it to a list of strings for the UI to display:


	public List getListOfViews(){
		List views = trackVia.getViews();
		List retVal = new ArrayList<>(views.size()); 
		for(View view : views) {
			retVal.add(view.getId() + " - " + view.getName());
		}
		return retVal;
	}

The getViews method on the TrackViaClient class returns a list of class View. Because the API uses the numeric ID to identify all resources in TrackVia we link it to the name of the view, so the user can easily tell our game which view to use.

List Records

Once the user has chosen the view that contains game data, the user can either create a new game, or join an existing game. If they want to join an existing game we will show them a list of all the records in the game data view. To do this we use the getListOfExistingGames in the TrackViaConnector class:


	public List getListOfExistingGames(int viewId){
		RecordSet recordSet = trackVia.getRecords(viewId);
		List retVal = new ArrayList<>(recordSet.getData().size()); 
		for(RecordData gameData : recordSet.getData()) {
			retVal.add(gameData.getId() + " - " + gameData.get("Name"));
		}
		return retVal;
	}

The TrackViaClient method getRecords returns a RecordSet of the records in the given view. For ease of use, TrackVia includes the meta-data, things like field type, needed to interpret a record with each query to the get records end point. That meta-data and the actual data are all stored in the RecordSet class. To access the meta-data you would call getStructure.

However, in this example we just want the data, so we call getData on the RecordSet object. This gives us a list of RecordData which implements the Java Map interface. Once again we create a list of strings to link the numeric ID of the records with their name so the user can tell us which game they want to join.

Create Record

If the user wants to create a new game we will need to create a new record in our game data view to store the game. The createGame method in TrackViaConnector will do this for us:


	public long createGame(int viewId, String name) {
		//set the name in the record
		RecordData data = new RecordData();
		data.put("Name", name);
		data.put("Turn", Piece.X.toString()); //X always goes first
		
		//add the record to a record list
		List recordList = new ArrayList<>(1);
		recordList.add(data);
		RecordDataBatch rdb = new RecordDataBatch(recordList);
		
		RecordSet records = trackVia.createRecords(viewId, rdb);
		
		return records.getData().get(0).getId();
	}

Here we use the createRecords method on the TrackViaClient object to call the create records end point. We pass in the ID of the view we want to create these records in as well as a RecordDataBatch which is a wrapper for a list of records. We can create multiple records at once using the createRecords method.

We default the first turn to X and set the name of the game. TrackVia will create the ID of the record and return it in the response. The createRecord method returns a list of created records in a RecordSet object. Since we only created one record we know the first record will be the one we want. We save the ID so we can reference the record when we update the game state.

Update Record

Now that we have a record for our game we need to update it as we play. To do this we use the writeGameState method in the TrackViaConnector class:


	public void writeGameState(GameState gameState, int viewId) {
		RecordData data = new RecordData();
		for(int i = 0; i < 3; i++) {
			for(int j = 0; j < 3; j++) {
				Piece piece = gameState.board[i][j];
				data.put(i+","+j, piece.toString());
			}
		}
		data.put("Turn", gameState.getTurn().toString());
		trackVia.updateRecord(viewId, gameState.recordId, data);
	}

This method takes the 2D array of Piece objects and projects it into a one dimensional list. We then add whose turn it is. Finally we use the updateRecord method on the TrackViaClient object to update the record. Note, that unlike the createRecords method, the updateRecord only updates one record at a time. This method takes in the numeric ID of the view that our record resides in, the ID of our record, and a RecordData object that contains the updated data for our record.

Query a Given Record

Once we have played our turn, our opponent plays, we must query the game state again. We could use the methods to get all of the records for the view, then find the one that correpsonds to our game, but that would be ineffecient. We want to directly get the data for the record we want. To do that we use the readInGameState method in TrackViaConnector:


	public void readInGameState(GameState gameState, int viewId) {
		Record record = trackVia.getRecord(viewId, gameState.recordId);
		for(int i = 0; i < 3; i++) {
			for(int j = 0; j < 3; j++) {
				String pieceStr = record.getData().get(i+","+j) == null ? null : record.getData().get(i+","+j).toString();
				Piece piece = Piece.getPiece(pieceStr);
				gameState.board[i][j] = piece;
			}
		}
		String pieceStr = record.getData().get("Turn") == null ? null : record.getData().get("Turn").toString();
		Piece turn = Piece.getPiece(pieceStr);
		gameState.setTurn(turn);
	}

To get our game state record we use the getRecord method on the TrackViaClient object. This method takes in the ID of the view and the ID of the record we want. It returns a Record object. We then loop over the list of fields to convert the one dimensional list of game state to a 2D array. Finally, we update whose turn it is.

This concludes our tutorial on our Java SDK. At this point you should feel comfortable using the SDK to navigate views, create, update, and read records. We hope you found this helpful.