Tutorial
Gold Retriever is played via HTTP requests. You should have a basic understanding of HTTP before moving forward.
Already know HTTP?
Feel free to skip the next section
HTTP Explained
HTTP is how your computer communicates with web servers. For example, when you visit google.com, your computer is making an HTTP GET request to Google's server. Their server processes your request and sends back a response with HTML which your browser renders in a pretty format.
Try it!
Almost all operating systems come equipped with a command-line tool named cURL which you can use to make HTTP requests. Open up Terminal (Mac OS) / Command Prompt (Windows) and run the following:
You should get back a response like:
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
In this case, google.com is redirecting us to http://www.google.com/.
If you do:
You should get back a mess of HTML and Javascript code - the same code used to generate Google's home page.
HTTP Methods
HTTP supports a few different methods (aka verbs) such as
- GET: to read data
- POST: to create data
- PUT: to modify data
- DELETE: to delete data
There are others, but these are the most common. (Gold Retriever uses GET and POST only.)
Request Components
A typical HTTP request is made up of the following components:
- An http verb (GET, POST, PUT, DELETE, ..)
- A URL (https://www.gaimbot.com/)
- with optional parameters (https://www.gaimbot.com/?foo=bar&baz=buz)
- Request headers
- Request body
The request headers usually contain high level information like the user's operating system, browser, device, etc. as well as things like cookies.
The request body usually contains the data you want to upload to the server.
Example using cURL
curl -X POST https://foo.com/blah/?page=1 <- POST request
-H "Content-Type: application/json" <- Headers
-d '{"productId": 123, "quantity": 100}' <- Body
Response
When you make an HTTP request, the server should send back a response. A typical HTTP response is made up of the following components:
- A status code (200 = OK, 301 = Permanent Redirect, 404 = Not Found, ...)
- A status reason (e.g. "You don't have permission")
- Response headers
- Response body
(If you tried the cURL example above, and you're wondering where the status code and headers were, try it again with the
verbose flag. curl --verbose google.com
.)
Manual Game Play
Before we implement our first bot to play Gold Retriever, we should get our feet wet playing the game manually. Head over to api.gaimbot.com . It should look something like this
Some of you may recognize this style of API documentation as Swagger. It's a popular, interactive documentation framework.
Set your username
Before we start playing, let's check our username.
- Click the GET Username endpoint
- Click the Try it out button
- Click the Execute button
Everyone gets a unique username by default. Your username is mapped to your IP address. Your IP address is how we keep track of the games you own and your best scores. (What's my IP? )
Important
If you travel from your house to a coffee shop, you'll look like two totally different players to our game server because your IP address will have changed! We'll fix this by implementing proper user authentication in the future, if there's demand for it.
Now let's change our username to something we prefer.
- Click the Set Username endpoint
- Click the Try it out button
- Type in your preferred username
- Click the Execute button
Start a new game
Alas, let's start playing the game. To get started, all we need to do is
- Click the New Game endpoint
- Click the Try it out button
- Click the Execute button
The server should respond with details about the game we created in the response body. The format of the response is JSON. Take note of the game id, because we'll need it soon.
Important
Games expire after 15 minutes of inactivity, so don't take too long to start digging
Dig for gold
Now that we have a game instantiated, we can start playing. In Gold Retriever, there is only one action you can take - that is, to dig. The question is, where to dig?
Recall, the plot of land we're digging on is a 30x30 grid. The cells of the grid are identified by the sequence of integers, 0, 1, 2, ... 899 in row major order.
0 1 2 ... 28 29
30 31 32 ... 58 59
.......................
.......................
.......................
870 871 872 ... 898 899
Let's try digging at cell 100 and see what happens..
- Click the Dig endpoint
- Click the Try it out button
- Punch in the game id and the cell where you want to dig
- Click the Execute button
Whaddayaknow!? We've struck gold
At this point, we can keep on digging (in the same spot, or others) until we've run out of digs. Of course, digging manually gets tiring, so perhaps it's time to implement our first bot.
Implementing a bot
Pseudocode
Let's jot down the pseudocode for a very simple bot - one that digs in random locations.
Instantiate a new game
while the game is not over:
Choose a random cell (integer) in the range [0, 899]
Dig there
New game
Here' we'll use Python and R, but you can use any programming language with an http client.
There are lots of http client libraries in python, but the most popular one is requests
.
Installation
To create a new game with the requests library, use the post()
function like this
The the result is a Response object, hopefully with a 201 status code (meaning the game was successfully instantiated).
We can use the Response.json()
method to convert response
to a Python dictionary.
game = response.json()
print(game)
# {
# 'id': '780e1e46-f1fd-4aab-a70a-137ab08463b1',
# 'grid_dims': [20, 20],
# 'digs_remaining': 100,
# 'score': 0.0,
# 'message': 'Let the digging begin! digs_remaining: 100, score: 0.0'
# }
At this point, game
has all the stuff we need to keep track of the game state. Before we start digging, let's
print the game message.
Here we'll use Hadley Wickham's httr package for making http requests with R.
Installation
To create a new game with httr, use the POST()
function like this
The the result is a response object, hopefully with a 201 status code (meaning the game was successfully instantiated).
print(response)
# Response [https://api.gaimbot.com/games/gold-retriever/new]
# Date: 2022-08-30 19:35
# Status: 201
# Content-Type: application/json
# Size: 165 B
We can use httr's content()
function to convert response
to an R list.
game <- content(response, as = "parsed")
print(game)
# $id
# [1] "54d8122c-2213-44b6-8c7e-9edf6ee6efe4"
#
# $grid_dims
# $grid_dims[[1]]
# [1] 20
#
# $grid_dims[[2]]
# [1] 20
#
# $digs_remaining
# [1] 100
#
# $score
# [1] 0
#
# $message
# [1] "Let the digging begin! digs_remaining: 100, score: 0.0"
At this point, game
has all the stuff we need to keep track of the game state. Before we start digging, let's
print the game message.
Dig for gold
Now that our game is instantiated, let's dig for gold.
We can import Python's random module to gain access to the randint()
function to
select a random cell to excavate.
We can use R's sample()
function to select a random cell to excavate.
sample(x = 900, size = 1)
returns a random integer in the range [1, 900], but we want a random integer in the range [0, 899] so we subtract 1 from the result.
In order to tell the game server this is where we want to dig, we need to
- make a POST request
- to the endpoint
https://api.gaimbot.com/games/gold-retriever/{game id}/dig
- with a query parameter
cell=<dig_loc>
For example, if our game id was abc-123
and our goal was to dig at cell 11
, the request would look like this
We can leverage the requests library for this.
import random, requests
# Instantiate a new game
response = requests.post("https://api.gaimbot.com/games/gold-retriever/new")
game = response.json()
# Print the game message
print(game['message'])
# Decide where to dig next
dig_loc = random.randint(0, 900)
# Tell the game server
response = requests.post(
url=f'https://api.gaimbot.com/games/gold-retriever/{game["id"]}/dig',
params={'cell': dig_loc}
)
And again, we can convert the response to a dictionary and print the game message.
We can leverage the httr package for this.
library(httr)
# Instantiate a new game
response <- POST("https://api.gaimbot.com/games/gold-retriever/new")
game <- content(response, as = "parsed")
# Print the game message
print(game$message)
# Decide where to dig next
dig_loc = sample(x = 900, size = 1) - 1
# Tell the game server
response = POST(
url = paste0("https://api.gaimbot.com/games/gold-retriever/", game$id, "/dig"),
query = list(cell = dig_loc)
)
And again, we can convert the response to a list and print the game message.
Game loop
Now that we know how to dig in a random spot, we just need to repeat the process until the game is over.
Important
If you make too many requests in a short amount of time, gaimbot's servers will get angry. You're allowed to make up to 2 requests per second, so make sure you place a half-second sleep timer between each request!
To complete our masterpiece, we'll implement a while loop that iterates until we're out of digs. We can use the
digs_remaining
attribute to know when to stop. We'll also import time
and use time.sleep(0.5)
to pause execution
for half a second on every iteration (to prevent exceeding gaimbot's API rate limit).
import random, requests, time
# Instantiate a new game
response = requests.post("https://api.gaimbot.com/games/gold-retriever/new")
game = response.json()
# Print the game message
print(game['message'])
# Game loop
while game['digs_remaining'] > 0:
# Sleep for a half a second
time.sleep(0.5)
# Decide where to dig next
dig_loc = random.randint(0, 900)
# Tell the game server
response = requests.post(
url=f'https://api.gaimbot.com/games/gold-retriever/{game["id"]}/dig',
params={'cell': dig_loc}
)
game = response.json()
# Print the game message
print(game['message'])
To complete our masterpiece, we'll implement a while loop that iterates until we're out of digs. We can use the
digs_remaining
attribute to know when to stop. We'll also use Sys.sleep(0.5)
to pause execution
for half a second on every iteration (to prevent exceeding gaimbot's API rate limit).
library(httr)
# Instantiate a new game
response <- POST("https://api.gaimbot.com/games/gold-retriever/new")
game <- content(response, as = "parsed")
# Print the game message
print(game$message)
# Game loop
while(game$digs_remaining > 0){
# Pause for a bit, to prevent overloading the server!
Sys.sleep(0.5)
# Decide where to dig next
dig_loc = sample(x = 900, size = 1) - 1
# Tell the game server
response = POST(
url = paste0("https://api.gaimbot.com/games/gold-retriever/", game$id, "/dig"),
query = list(cell = dig_loc)
)
game <- content(response, as = "parsed")
# Print the game message
print(game$message)
}
Game over
When you run the bot we just built, it should output a stream of messages like
Let the digging begin! digs_remaining: 100, score: 0.0
Drat, nothing! digs_remaining: 99, score: 0.0
Drat, nothing! digs_remaining: 98, score: 0.0
...
Drat, nothing! digs_remaining: 2, score: 42.74
Whoop, you struck gold! digs_remaining: 1, score: 43.79
Game over. Final score: 43.79
If you've beaten your previous best score, the final message will include
NEW BEST SCORE!
and if you've beaten everyone's best score, it'll say
NEW HIGH SCORE!
Your personal best score will be maintained on our leaderboard.