IT Duel 2017: "Battle of the Bots - Hexagon" creation of the game - Part 1 - The Rules

IT duel 2017, Minsk

So, the event passed successfully - with drive, joy of victories, bitterness of defeats and cheerful after-party! This competition was another challenge for Anadea - a year ago we learned how to organize isolated tournaments in separate cities, this time a tournament was held in three cities simultaneously with a general final for all cities. I dare to think that the challenge was accepted with dignity :)

About 30 employees of Anadea participated in the preparation and conduct of the event, dozens of representatives of our partners and just good friends provided information support. In this sense, a huge work was done and we are grateful to all involved! For our company, this competition has become a great way to maintain a friendly atmosphere in the team, and also allowed us to meet some young and talented programmers.

Do you like programming? Want to work on interesting projects in a friendly team? Contact us at jobs@anadea.info!

I, being the ideologist of the tournament as a whole, as well as the architect and the coder of the game engine - will try to describe the technical component of the tournament in a series of articles. I will not give you a ready-to-use solution, instead I will provide a detailed write-up of key tools and neat tricks.

In fact, the game engine architecture was written from scratch a year ago, during the preparation of Minsk IT Duel 2016. For the current event, as part of the intensive web developer course, our interns implemented a new game mechanics. I just added support of several sandboxes with advancing to the final, an opportunity to play with your own bot online, updated dependencies (Dokku, templates of bots), updated the text of the rules, launched and tested sandboxes, did some other stuff.

Compared to the previous year, the technical preparation of the 2017 tournament was quite monotonous, without busting my guts - plenty of time, a modest amount of work, active assistance of interns and employees - there is nothing special to describe. Another thing was …

Creating the game engine a year ago!

As it happens, everything started with the problem statement:

Let's run a kind of hackathon... By the way, the venue for the event has already been booked, in a month…

Well, let's see what can be done by one engineer (me) within a month, with occasional involvement of frontend / QA / DevOps specialists. The problem was solved in the usual mode "die, but do it!" When holding a public event, the reputation of the company is at stake and there is no room for error.

Frequency of commits

In the first article of the series, we will describe the rules of the game, the technical requirements for gaming sandboxes and the game interface. It is worth mentioning that, in order to save the precious time, we have skipped the preliminary elaboration of the technical specifications as well as standard activities on estimating and planning of sub-tasks. Instead, in just a day and a half, I created a prototype (playing boards generator, training bot, game logic, online game with the bot). Further functionality was added according to the principle "there is still time, what else can I do?". As a result of the monthly sprint, we got:

Rules of the game

The playing board consists of cells where each cell constitutes a regular hexagon. At the beginning of the game, tree chips from each player (blue and red) are placed on the board. There are also several random "stones" (white) which are not allowed to be occupied. Yellow cells are "empty".

Start of the game

Two types of moves are available.

Let's call the first type a "multiplication" - you can make a move from any of your cells to an empty adjacent cell. By this move, a new chip of a player is placed in the target cell, and the old one remains in its cell.

Types of moves - multiplication

The second type of move is a "jump" - any of your chips can jump over one cell to an empty one. A new chip of a player is placed in the target cell, the old one disappears. You can jump over a cell of any type, including "stones".

Types of moves - jump

The number of available jumps is limited in order to prevent the endless dragging out of a round by players. For every two "multiplication" moves another "jump" becomes available. At the beginning of a round, three chips of a team are considered as placed by "multiplication", i.e. the player has one available "jump" at once, and will get another one after the first "multiplication". The game server provides teams with a number of available jumps at each point in time.

The number of available jumps

For any type of move, all opponent's chips co-located with the target chip of a move are replaced with player's chips.

Сhip replacement

Players move chips by turns. If a player can not make a move in his/her turn without breaking the rules of the game - he loses a turn. The game is over when none of the players can make a move according to the rules. The player who has more chips on the board wins.

Winner

To taste a bit of a game, try to play online. In online mode, you can make moves by dragging and dropping your chip to the target cell with the mouse.

How the competition is organized

Players are expected to make a bot that can play (and win) the game described above. The bot is a JSON API web service. The game server generates maps, brings bots together in battles, monitors the observance of the game rules during the battle, and grants bonus points.

The tournament is divided into rounds. Within a round, each team plays one match with each team of its sandbox.

To ensure equal chances, a pair of bots plays on the same board twice within a match - so that each of them can make the first move. The result of the match is deduced from the number of wins in these two battles. An example of victory: 2:0, 1:0. An example of draw: 1:1, 0:0.

The number of points a bot earns at the end of a match depends on the position of the opponent in the ladder (the summarized rating of bots):

  • 4 points for a victory over the opponent higher in the ladder,
  • 2 points for a victory over the opponent lower in the ladder,
  • 1 point for a draw with the opponent higher in ladder.

Then, these points are multiplied by the factor that depends on the size of the board - the bigger the board, the greater the factor.

As you can see, the overtaking team gets points twice as fast as the team that pulls away.

Each of the cities (Grodno, Dnepr, Minsk) will compete in its own game sandbox. The strongest three teams in each city will get prizes.

Besides, at the end of the tournament top 3 teams from the cities will be taken to the final sandbox to compete for the super prize.

During the tournament, various events will occur in the game world. For example, new types of the game fields will appear.

How to create a bot

As already mentioned, the right bot is a web service that responds to a certain set of HTTP requests in the correct way. In other words, your bot should comply with the following API documentation.

In POST, PUT and DELETE requests, in addition to the id in the URL, JSON will be sent in the body of the request.

The response should also be JSON with header Content-Type: application/json.

POST /games - game start

The request body will contain JSON with the keys:

  • id [string] - the identifier of the current game, for instance, "asdyhjk43566h".

  • first_turn [boolean] - indicates if the bot takes the first turn.

  • training [boolean] - comes with the true value if you are playing with your bot online.

  • jumps [object] - the number of available jumps. For example,

      {
        "1": 4,
        "2": 5
      }
    

    means that the team of color 1 can jump 4 times, the team of color 2 - 5 times.

  • board [object] - actually, the game board. For example,

      {
        "size": 3,
        "cells": [
          [-1, 1, 0, 2, -1],
          [0, -1, 0, 0, -1],
          [2, 0, 0, 0, 1],
          [0, 0, 0, -1, -1],
          [-1, 1, 0, 2, -1]
        ]
        }
    

    This corresponds to the following board:

Sample game board

The elements of the cells array correspond to "lines" of the game board. Note that every second line of the game board is shifted to the right by half a hex. Besides, hereinafter indexing of arrays starts with 0.

cells[i][j]== N means that the cell of the i-row of the j-column:

  • is a stone when N == -1,
  • is empty when N == 0,
  • contains a chip of color 1 when N == 1,
  • contains a chip of color 2 when N == 2.

Color 1 always makes the first move.

The response should be {"status":"ok"}.

An example of a request:

$ curl -H "Content-Type: application/json" -X POST -d '{"id":"asdyhjk43566h","first_turn":"true","training":"true","jumps":{"1":1,"2":1},"board":{"size":3,"cells":[[-1,1,0,2,-1],[0,-1,0,0,-1],[2,0,0,0,1],[0,0,0,-1,-1],[-1,1,0,2,-1]]}}' http://localhost:3000/games

GET /games/:id - request for your move

In addition to the id in the URL, the following will come as a GET parameter:

  • color - the color which you are offered to make a move with.

The server will automatically skip your move if there are no legal moves for you (in such a case you will not receive GET /games/:id). Thus, if you have received this request, then there is at least one available move with the current color and you ought to make a move.

Respond with {"status":"ok", "move_from":[i1,j1], "move_to":[i2,j2]}, where i1 and j1 are the coordinates of the cell that you want to make a move from, i2 and j2 - the coordinates of the cell that you want to make a move to. Remember that indexing of arrays starts with 0.

You should not consider that your move is accepted only based on the fact that you have responded to this request. That is, you shouldn't change the state of the game board inside the given endpoint.

An example of request:

$ curl -X GET http://localhost:3000/games/asdyhjk43566h?color=1

PUT /games/:id - notification of a change in the game board status resulting from your move or move of your opponent

In addition to the id in the URL, JSON with the keys will be sent in the body of the request:

  • jumps [object] - the updated number of available jumps in the same format as in the POST request.
  • changes [array] - the list of changes in the game board. For example,
      [
        [i1, j1, old_color1, new_color1],
        [i2, j2, old_color2, new_color2]
      ]
    
    means that cell [i1][j1] has changed its color from old_color1 to new_color1, the cell [i2][j2] changed its color from old_color2 to new_color2, and so on.

Respond with {"status":"ok"}.

An example of request:

$ curl -H "Content-Type: application/json" -X PUT -d '{"jumps":{"1":2,"2":3},"changes":[[0,5,2,0],[0,3,0,2]]}' http://localhost:3000/games/asdyhjk43566h

DELETE /games/:id - game over

Respond with {"status":"ok"}.

An example of a request:

$ curl -H "Content-Type: application/json" -X DELETE http://localhost:3000/games/asdyhjk43566h

An example of requests sequence from the side of the game server

Let's say the game server brings together teams Team1 and Team2 in a match. In this moment, a random board is generated and a random team is chosen to make the first move. Suppose Team1 takes the move first. The sequence of requests from the game server will be as follows:

  • POST -> Team1
  • POST -> Team2
  • GET -> Team1
  • PUT -> Team1
  • PUT -> Team2
  • GET -> Team2
  • PUT -> Team2
  • PUT -> Team1
  • ...
  • DELETE -> Team1
  • DELETE -> Team2

Then, the game server calculates the game on the same board, but with the first move of Team2:

  • POST -> Team2
  • POST -> Team1
  • GET -> Team2
  • PUT -> Team2
  • PUT -> Team1
  • GET -> Team1
  • PUT -> Team1
  • PUT -> Team2
  • ...
  • DELETE -> Team2
  • DELETE -> Team1

Additional tips

The response time is limited, the bot should respond to the request within 1 second.

It should also be taken into account that the game server calculates several games simultaneously, i.e. your bot will be requested on several games at once. In order to distribute the load on bots evenly the game server plans calculation of matches in a round randomly.

If a bot returns an invalid response to the request (illegal move, incorrect response format, exceeding timeout), it leads to the immediate defeat in the current game.

The game server does not notify the bot that its response is incorrect. Instead, it counts the bot defeat, immediately finishes the calculation of the game, and sends both opponents a DELETE request. Such behavior can be used, for instance, as an indirect sign of an illegal move. In this case, after a GET request your bot immediately gets DELETE instead of the expected PUT.

The web interface of the game

First of all, it is necessary to choose a sandbox. A team of an open tournament will find its name in one of the sandboxes: "GRODNO, DNEPR, MINSK". Teams consisting of Anadea employees will find their names in the "INTERNAL" sandbox. Top 3 teams from the open tournament will be carried over to the "FINAL" sandbox at the end of the game to compete for the super prize.

Choosing a sandbox

In the upper right corner, there is a countdown to the next game event. By clicking the counter you can see which events have already occurred.

Counter

The host will give each team a card with login tokens. Choose the sandbox your team belongs to and enter the token into the form. A new menu item "MY TEAM" will appear, it will take you to the page with instructions on how to deploy your bot. Keep your login token and the IP address of your droplet a secret in order to avoid, for instance, DDoS attacks on your bot during the tournament.

Join the team

To "taste" the game you can play online with a training bot:

Play online

If you have already entered the token of your team, you'll get the option to play online with your own bot. It can be useful for fine-tuning its behavior. In the logs, you can identify the online game bot by the parameter training: true in the POST request.

Play with your bot

In online mode, moves are taken by dragging your chip to an empty field within a 2-cells radius. Remember that "jump" moves are limited.

Jump moves are limited

On the ladder page, you can track the current position of teams in the standings.

Ladder

Clicking on the team name in the ladder will take you to the team page. Here you can track in real time scoring dynamics as well as follow match results of the current round.

Game statistics

The matches that were won - marked in green, lost - in orange, drawn - remain gray. Besides, here you can see how many points and which team will get according to the results of the match, and results of games in the match in the format winner (chips_of_team1 - chips_of_team2).

Results of the match

If the game was ended due to an incorrect response - the results of the game indicate the reason for stopping the game. The following example should be understood as the team "222" won both matches because the team "test2" in both matches tried to make a move that is unacceptable according to the rules.

Reason for stopping the game

Points are scored for all teams simultaneously, at the end of the round.

The rows of the results of the match are clickable, clicking on them takes you to the page with replays of the game. Review and thoughtful analysis of your and your opponents' replays is a way to victory!

Viewing replays

How to deploy a bot

Templates

We made several heroku-ready simple bot templates for popular languages and platforms. Feel free to take any of them from a special repository:

  $ git clone https://github.com/Anadea/hexogon-templates.git
  $ cp -r hexogon-templates/your-language your-language
  $ cd your-language
  $ git init
  $ git add .
  $ git commit -m "Initial import"

If none of the pre-built bot templates works for you - you should make an application in your language and platform heroku-ready. We will help you with the issues if you face something that Google and Stack Overflow don't provide a solution for =)

First deploy

We encourage you to try to deploy right away, before writing your bot logic. The templates give correct HTTP responses to all API calls from the server, and you will start earning points immediately at the expense of those teams that do not give any responses yet. =)

Be sure to add the public part of your ssh-key to the form on the team page:

  $ cat ~/.ssh/id_rsa.pub

Now, execute the following at the root of repository with the application:

  $ git remote add origin dokku@DROPLET_IP:app

You can safely deploy through git push origin master. Work with origin as with a regular remote repository. Among other things, if necessary, use key --force. Push to the master branch uploads a new version of your bot. You can use any other branches for co-working.

There are several useful talking commands that may come in handy:

  $ # track the logs of your bot.
  $ # To put something into the log, it's enough to send this something to STDOUT.
  $ # In the templates of bots you can see examples of putting into log.
  $ ssh -t dokku@DROPLET_IP logs app -t
  $
  $ # Run a command in the directory of your bot.
  $ ssh -t dokku@DROPLET_IP run app rake db:migrate
  $ ssh -t dokku@DROPLET_IP run app rails c
  $
  $ # Enter the container with the bot.
  $ ssh -t dokku@DROPLET_IP enter app

Probably, you will need to store data between requests. In your droplet, several storages to choose from have already been started and configured. Data for connection to the database (Postgres) is available from the environment variable DATABASE_URL, to Redis - REDIS_URL, to Memcached - MEMCACHED_URL.

In the next article read about setting up DigitalOcean droplets and Dokku.

Get in Touch