Getting Started
Creating an Account
The first step is to create an account. We use GitHub for authentication, so if you alraedy have a GitHub account, simply login with it, otherwise you can make an account here
Creating an Algoritm
Now that you have an account, you will want to create a competition winning algorithm! This section will show how to create a simple balancing algorithm in python, and show how interact with the API in the process.
HTTP Requests
All comunication the and from the API is done through HTTP requests. This can be done in many ways, but for our python program will will use the Requests library, which can be installed through pip:
python -m pip install requests
Through Requests, we can create wrapper functions for all the API calls required to make our simple algorithm. The first API call we will need is Submit Run, which will allow us to test our algorithms performance.
Submit Run
The way we can alter the balance of a game in TAG is through changing the game parameters used. These parameters are represented as a JSON object. The Games page outlines all the parameters that can be tuned per game. In our case, we will be balancing Dominion, and will test the below parameters:
{
"HAND_SIZE": 3,
"COPPER_SUPPLY: 10
}
Now we need to wrap the API call in a python function, the Documentation details everything required to make a sucessful request. Looking at Submit Run, we can see it is a POST request with a JSON body, and that it requires three parameters, game, params, and api_key.
# Import libraries required for the program
import requests
import sys
import time
# Constant values
game = "Dominion"
params = {
"HAND_SIZE": 3,
"COPPER_SUPPLY": 10
}
url_base = "balance-competition.tabletopgames.ai"
def submit_run(game: str, params:dict, api_key:str) -> dict:
url = url_base + "submit_run/"
# Lets the server know the request has a JSON body
headers = {
"Content-Type": "application/json"
}
# The body of the request, contains the required parameters
data = {
"game": game,
"params": params,
"api_key": api_key
}
response = requests.post(url, json=data, headers=headers)
return response.json()
This function will allow us to submit a Dominion run with the params specified in the variable. Note that the API key is not defined here. Leaving your API key in code commited to a public repository can result in it being leaked. We advise to keep your API key in a .env file, but for the purpose of this example, it will be passed as a command line argument.
Now that we have a way to submit a run, we need to be able to query its status (sadly simulating games is not an instantaneous task…), for this we will create another wrapper function for the Query Run call.
Query Run
Query Run is a GET request (it doesn’t modify any data), therefore this function will be a lot simpler, all of the request is contained in the URL, which is defined as seen below.
def query_run(run_id: str) -> dict:
url = url = url_base + "query_run/"
# the params to pass in the URL
params = {
"id": run_id,
}
response = requests.get(url, params=params)
return response.json()
Unlike the other API calls, this one does not require an API key, that is because it is not modifiying not retrieving any sensitive data. Now that we have a way to query run’s status, we need to wrap the final call: Retrieve Result.
Retrieve Result
The structure of this call is very similar to Query Run, this is due to the fact that they are both GET requests. However, this call requires the API key, which is simply another entry in the params dictionary.
def retrieve_result(run_id: str, api_key:str) -> dict:
url = url = url_base + "retrieve_result/"
params = {
"id": run_id,
"api_key": api_key
}
response = requests.get(url, params=params)
return response.json()
Now that we have function wrappers for the API calls we need, we can work on making our simple program.
Main Function
The main function has a simple program flow:
- Retrieve the API key from the command line arguments
- Submit a run with the wrapper function
- Record the returned run ID
- Perodically query the run (every 10 seconds)
- Once the status shows as ‘complete’ retrieve and output the result
if __name__ == "__main__":
# Retrieve the API key from the command line arguments
if len(sys.argv) > 1:
api_key = sys.argv[1]
else:
print("Please provide the API key as a command line argument.")
sys.exit(1)
# Submit the run
response = submit_run(game, params, api_key)
print("Submit Run Response:", response)
# Query the run status
run_id = response.get("runID")
status = ""
while status != "complete":
time.sleep(10)
print("Checking status...")
status_response = query_run(run_id)
print("Query Run Status Response:", status_response)
status = status_response.get("run_status")
# Retrieve the result
result_response = retrieve_result(run_id, api_key)
print("Retrieve Result Response:", result_response)
With all these code blocks stored in one file (for example main.py), we can run our program with our API key (find yours in user settings), and get a result!
python main.py 324f653f-fa0a-4548-9ea2-4748898132e5
This will give us the output:
Submit Run Response: {'runID': 10}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'running'}
Checking status...
Query Run Status Response: {'run_status': 'results_errored'}
Checking status...
Query Run Status Response: {'run_status': 'complete'}
Retrieve Result Response: {'score': 857.1111085679796, 'runID': 10}
We can see that our simple program got a score of 857, not bad!
Next Steps
Now this program clearly won’t win any awards, but yours could! While we want your creativity and problem solving to be unconstrained, if you’re feeling overwhelmed we can suggest the DEAP library for Python as a start.
Once you’re algorithm has found a set of parameters you’re happy with, make sure to enter them in the Submit Entry page, where your parameters will be scored, and you will be entered on our leaderboard!