The objective of this homework was to create a RESTful web service that allows anyone to play chess with an AI opponent.
The project was developed using the following environment:
- OS: Windows 10, Ubuntu 18.04 LTS running in VMware
- IDE: IntelliJ IDEA Ultimate 2018.3
- Hypervisor: VMware Workstation 15 Pro
- Build System: Gradle
- Framework: Spring Boot
- Ubuntu or any other Unix based OS capable of running KVM
- QEMU hypervisor installed on your system
- Capstan installed on your system
- Docker running on your system
A docker image for this project can be pulled from my repository on Docker Hub:
docker pull mayankrastogi/chessservice:0.0.1-SNAPSHOT
- HTTP Method:
POST
- End Point:
/chess/new
- Parameters:
playerName Your name.
playerColor The color you wish to play with (BLACK | WHITE).
opponentAILevel Level of AI you wish to play against (1 | 2).
- Sample Response:
{
"gameID": "cc809856-dfed-4cc5-8790-59708d5829d0",
"clientMove": null,
"serverMove": {
"fromSquare": "d2",
"toSquare": "d4",
"promotionPiece": null
},
"status": {
"hasGameEnded": false,
"status": "WAITING_FOR_OPPONENT",
"fen": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq d3 0 1",
"pgn": "[Event \"Game\"]\n[Date \"2019.3.30\"]\n[White \"Chess AI\"]\n[Black \"Mayank\"]\n\n1. d2-d4 ",
"winner": null,
"moves": [
{
"fromSquare": "d2",
"toSquare": "d4",
"promotionPiece": null
}
]
}
}
- HTTP Method:
PUT
- End Point:
/chess/{gameID}/move
- Parameters:
(Path parameter) gameID: The game on which the move is to be made.
(Request Body) Description of the move. The promotionPiece should be supplied when a move is made that results in a pawn reaching the opponent's end of the chess board.
- Sample Request Body:
{
"fromSquare": "e7",
"toSquare": "e8",
"promotionPiece": "QUEEN"
}
- Sample Response:
{
"gameID": "6e89bc43-672f-44e4-ab6f-843183b7a792",
"clientMove": {
"fromSquare": "e7",
"toSquare": "e6",
"promotionPiece": null
},
"serverMove": {
"fromSquare": "a2",
"toSquare": "a4",
"promotionPiece": null
},
"status": {
"hasGameEnded": false,
"status": "WAITING_FOR_OPPONENT",
"fen": "rnbqkbnr/pppp1ppp/4p3/8/P7/2N5/1PPPPPPP/R1BQKBNR b KQkq a3 0 2",
"pgn": "[Event \"Game\"]\n[Date \"2019.3.31\"]\n[White \"Chess AI\"]\n[Black \"Mayank\"]\n\n1. Nb1-c3 e7-e6 2. a2-a4 ",
"winner": null,
"moves": [
{
"fromSquare": "b1",
"toSquare": "c3",
"promotionPiece": null
},
{
"fromSquare": "e7",
"toSquare": "e6",
"promotionPiece": null
},
{
"fromSquare": "a2",
"toSquare": "a4",
"promotionPiece": null
}
]
}
}
- HTTP Method:
GET
- End Point:
/chess/{gameID}/state
- Parameters:
(Path Parameter) gameID: The ID of the chess game whose state is to be fetched.
- Sample Response:
{
"hasGameEnded": false,
"status": "WAITING_FOR_OPPONENT",
"fen": "rnbqkbnr/pppp1ppp/4p3/8/P7/2N5/1PPPPPPP/R1BQKBNR b KQkq a3 0 2",
"pgn": "[Event \"Game\"]\n[Date \"2019.3.31\"]\n[White \"Chess AI\"]\n[Black \"Mayank\"]\n\n1. Nb1-c3 e7-e6 2. a2-a4 ",
"winner": null,
"moves": [
{
"fromSquare": "b1",
"toSquare": "c3",
"promotionPiece": null
},
{
"fromSquare": "e7",
"toSquare": "e6",
"promotionPiece": null
},
{
"fromSquare": "a2",
"toSquare": "a4",
"promotionPiece": null
}
]
}
- HTTP Method:
POST
- End Point:
/play
- Parameters:
serverURL URL of the chess service.
serverPlayerAILevel AI level of the server player.
playerName The name by which this AI player should be identified.
playerColor The color to use for this AI player.
playerAILevel The AI level of this player.
maxMoves Maximum number of moves before the game is considered to be over. Default: 100.
- Sample Response:
{
"playerName": "client",
"playerColor": "BLACK",
"playerAILevel": 2,
"serverPlayerName": "Chess AI",
"serverPlayerColor": "WHITE",
"serverPlayerAILevel": 1,
"serverURL": "http://localhost:8080",
"outcome": {
"hasGameEnded": false,
"status": "WAITING_FOR_OPPONENT",
"fen": "rnbqkb1r/p1pppppp/8/1p6/5P2/3P1PPN/PPP5/RNBQKB1R b KQkq - 0 6",
"pgn": "[Event \"Game\"]\n[Date \"2019.3.31\"]\n[White \"Chess AI\"]\n[Black \"client\"]\n\n1. h2-h4 b7-b5 2. f2-f4 Ng8-h6 3. Ng1-h3 Nh6-f5 4. d2-d3 Nf5xh4 5. g2-g3 Nh4-f3+ 6. e2xf3 ",
"winner": null,
"moves": [
{
"fromSquare": "h2",
"toSquare": "h4",
"promotionPiece": null
},
{
"fromSquare": "b7",
"toSquare": "b5",
"promotionPiece": null
},
{
"fromSquare": "f2",
"toSquare": "f4",
"promotionPiece": null
},
{
"fromSquare": "g8",
"toSquare": "h6",
"promotionPiece": null
},
{
"fromSquare": "g1",
"toSquare": "h3",
"promotionPiece": null
},
{
"fromSquare": "h6",
"toSquare": "f5",
"promotionPiece": null
},
{
"fromSquare": "d2",
"toSquare": "d3",
"promotionPiece": null
},
{
"fromSquare": "f5",
"toSquare": "h4",
"promotionPiece": null
},
{
"fromSquare": "g2",
"toSquare": "g3",
"promotionPiece": null
},
{
"fromSquare": "h4",
"toSquare": "f3",
"promotionPiece": null
},
{
"fromSquare": "e2",
"toSquare": "f3",
"promotionPiece": null
}
]
}
}
-
Clone or download this repository
-
Open Command Prompt (if on Windows) or Terminal (if on Linux/Mac) and browse to the project directory
-
Run the test cases and build a fat
jar
of the application usingGradle
On Windows:
gradlew clean test bootJar
On Linux/Mac:
./gradlew clean test bootJar
-
Run the Spring Boot application using the built
jar
filejava -jar build/libs/chessservice-0.0.1-SNAPSHOT.jar
-
Open Postman or any other application which lets you send
HTTP
requests -
Make a request to start a new game by sending a
POST
request tolocalhost:8080/chess/new
. Check the API specification section to find out the set of parameters to pass -
The application can be terminated by pressing the
Ctrl
+C
hot key on the Command Prompt / Terminal
-
Log into a Linux/Mac machine that has QEMU and KVM installed and configured
-
Open terminal and browse to the project directory
-
Install Capstan if you don't have it already
curl https://raw.githubusercontent.com/cloudius-systems/capstan/master/scripts/download | bash
-
Copy
openjdk8
base images fromosv-base-images
directory by runninginstall-images.sh
script. This script copies and extracts the base images in that directory to~/.capstan/repositories/mayankrastogi/
.sudo osv-base-images/install-images.sh
If you get an error regarding
\r
, please convert the line endings in that script fromCRLF
toLF
using your favorite text editor. -
Build the unikernal image
sudo $HOME/bin/capstan build -v
-
Launch the VM instance and forward port
8080
of the VM to port8080
of the hostsudo $HOME/bin/capstan run -f 8080:8080
-
The VM should have launched now and our Spring Boot application should have started. Test it by making a request to start a new game by sending a
POST
request tolocalhost:8080/chess/new
. Check the API specification section to find out the set of parameters to pass
-
Ensure that docker is installed and running
-
Open Command Prompt or Terminal from where you can issue docker commands and browse to the project directory
-
Build the docker image
docker build -t chessservice .
-
Run the docker image and forward port
8080
of the container to port8080
of the hostdocker run -p 8080:8080 chessservice
-
Our Spring Boot application should have started. Test it by making a request to start a new game by sending a
POST
request tolocalhost:8080/chess/new
. Check the API specification section to find out the set of parameters to pass -
Stop the application by pressing
Ctrl
+C
-
Login to your docker hub account. Enter your credentials when prompted:
docker login
-
Tag the docker image
docker tag chessservice mayankrastogi/chessservice:0.0.1-SNAPSHOT
-
Push the image to the Docker Hub repository
docker push mayankrastogi/chessservice
Watch this video on YouTube for details on how to build the VAP and deploy it on AWS EC2:
-
Log in to your AWS Console
-
Search for "EC2" from the Find Services search box
-
Click on Launch Instance
-
Select Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type - ami-0cd3dfa4e37921605
-
Choose the t2.micro as your instance type and click Next: Configure Instance Details
-
Keep the default settings on this page and select Next: Add Storage
-
Keep the default settings on this page and select Next: Add Tags
-
Keep the default settings on this page and select Next: Configure Security Group
-
Create a new security group and Add Rule of type Custom TCP Rule, specify Port Range as
8080
, and click Next: Review and Launch -
Click Launch
-
Create a new key pair or use an existing one. It is important to specify a key pair that is working for you, otherwise you won't be able to login to this instance
-
Click Launch Instance to let AWS spin up a new EC2 instance
-
Now go to the Instances tab, wait until our instance's status becomes running and then clicl on Connect
-
Copy the example
ssh
command -
Open an SSH client and paste that command. Remember to change the value of
-i
option which specifies the path to your private key for the key-pair you specified while creating the instance -
Once logged in, install docker using the below commands
sudo yum update -y sudo yum install -y docker
-
Start the docker service
sudo service docker start
-
Deploy our docker container by pulling it from Docker Hub
docker run -p 8080:8080 mayankrastogi/chessservice:0.0.1-SNAPSHOT
-
Our Spring Boot application should have started now. Test this by using Postman again. You can either use the Public IP of your instance or the Public DNS to connect to the deployed service