Three days ago I had an idea, and decided to put it into action as an experiment.
It's essentially a website where you build AIs for two player games. They are then automatically contested and ranked against other users' bots through the Coder's Block API.
The first game is
Tic-Tac-Toe (Noughts and Crosses), because I thought that would be a nice starting point to test the platform's stability.
You can never lose Tic-Tac-Toe if you play it perfectly, so achieving an "Intelligence Rating" of 100% is fairly easy.
http://codersblock.net/arena/viewuser.php?user_id=6
The site is currently under development. It is a bare template, and it's lacking any ranking pages. However, the database is complete, and the platform is easily extendable. So it should be fully functional, and will sky rocket in appearance during any spare moments I have.
http://codersblock.net/arena/lobby.php
http://codersblock.net/arena/tutorial.php
http://codersblock.net/arena/register.php
I would like to see as many of you guys as possible writing Tic-Tac-Toe AIs to test this out over the next week. I'm confident that many of you can write a perfect AI in an hour or two. It's even brute-forceable.
The platform uses PHP and MySQL. It can be extended to handle any number of games (not just Tic-Tac-Toe), so more complicated games will appear in the future. The database has a ranking system (although there's no page to show it yet). MySQL injections are protected against. Passwords are securely double-salted and hashed. Invalid API protocol and any attempted protocol hacking (either accidental or on purpose) is all controlled server-side, and thus strongly protected.
Once you're done writing an AI, to see how to link it to the API, read below. If you're not using C and Windows, please help me by rewriting the API for your language. All the API does is make GET requests to a PHP page.
What Is This?
It is a platform for bots to compete against each other in various games, both contrived and common, to test each programmer's ability to write algorithmic and logical code.
What Languages?
You can use absolutely any language. However, the API has currently only been written for C in Windows. Rewriting it for other languages and OS's is necessary. If you'd like to help me, please do so.
How Does It Work?
You simply write an AI that can solve a particular problem, or make an appropriate move for one of the games in the framework. Then by calling the functions in the CB API, it will automatically be contested against other AIs.
What For?
The purpose of this is to challenge yourself, practice general and specific coding skills, learn more about algorithms and AIs, and have fun.
Anything Else?
After you bind your AI to the API, you will see the results of all your matches updated real-time on this website. You will also be ranked against every other bot who's played the same game in a variety of ways. This can help you track your progress as a programmer on Coders Block, set personal goals, and compare yourself to others.
The API
The API is limited to only the necessary commands. They are all described as follows:
cb_init(do_debug)
Initiates the CB API. Accepts an integer. If the integer is true, you will see all HTTP packets printed out in the console.
Returns 0 for success. True for failure.
cb_login(name, password, game)
Establishes and urlencodes the configuration. Does not verify. Use cb_cmd("CHECK") to verify.
Returns void.
The "game" parameter is "TIC-TAC-TOE" for Tic-Tac-Toe.
cb_cmd("CHECK")
Checks whether or not your login configuration is correct.
Returns either "NOT LOGGED IN", "INVALID GAME NAME", or "OKAY". NULL pointer if client or server failed.
Note: The login configuration is set like so: cb_login(name, password, game)
cb_cmd("GETGAME")
Enters the lobby and either looks for a game to join, or hosts a new game.
This must be called every 15 seconds to reserve your spot in the lobby.
Returns either "WAIT" or the name of the bot you are up against. NULL pointer if client or server failed.
Note: As soon as you call the GETGAME command, you are dedicating yourself to a game. If someone responds within 15 seconds, or you have entered someone's game, then failing to continue the game will result in disqualification from the match. If you want to try to leave the lobby, you may call LEAVELOBBY.
cb_cmd("LEAVELOBBY")
Attempts to leave the lobby.
Returns either "OKAY" or "NO". NULL pointer if client or server failed.
If "NO" is returned, then you are currently inside a game, and must proceed, or face disqualified from the match.
cb_cmd("GETBOARD")
Gets the state of the current game.
Returns either "NOT IN GAME", "WAIT", "WIN", "LOSE", "TIE", or the state of the board. NULL pointer if client or server failed.
If "WAIT" is returned, your opponent is still making his/her move. It is suggested to wait 3-5 seconds until calling "GETBOARD" again. Taking more than 15 seconds will count as a timeout and you will be disqualified from the match.
For Tic-Tac-Toe, the state of the board is "xxxxxxxxx" where x the character "0", "1", or "2".
These characters represent the pieces from top-left to bottom-right of the 3x3 board. 0 is a blank piece. 1 is your piece. 2 is your opponent's piece.
Replying To Move
Replying with your move is as straightforward as replying with the new state of the board, in the same format that it was given to you in, through cb_cmd().
If your opponent checks the board, and it has been 15 seconds since he/she made his/her move, then he/she will assume you have timed out, and you will be disqualified from the match.
Returns either "DQ" or "OKAY", depending on whether or not your move was valid.
"DQ" means your command was unrecognizable, your move was illegal, or you timed out. This resulted in disqualification from the match.
cb_cleanup()
Cleans up the socket connection.
Returns 0 for success. True for failure.
cbapi.h
API In C For Windows (Note: Must Add "-lwsock32" To Your Linker)
Example In C For Windows
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "cbapi.h"
void delay(int sec) {
Sleep(sec*1000);
printf(".");
}
int main() {
char *reply;
cb_init(0);
cb_login("Sane", "************", "TIC-TAC-TOE");
if( strcmp(reply = cb_cmd("CHECK"), "OKAY") ) {
printf(reply);
return cb_cleanup();
}
printf("Looking For Game ...");
while( strcmp(reply = cb_cmd("GETGAME"), "WAIT") == 0 ) delay(5);
if( !reply ) return cb_cleanup()
printf("\nPlaying Against \"%s\"\n", reply);
while(1) {
printf("Waiting For Turn ...");
while( strcmp(reply = cb_cmd("GETBOARD"), "WAIT") == 0 ) delay(0);
if( !reply ) return cb_cleanup()
if( strcmp(reply, "WIN") == 0 || strcmp(reply, "LOSE") == 0 || strcmp(reply, "TIE") == 0 ) {
printf("Game Over: %s\n", reply);
return cb_cleanup();
}
/* Make Move By Updating One Of The Pieces In 'reply' */
if( strcmp(reply = cb_cmd(reply), "DQ") == 0 ) {
printf("Disqualified For Illegal Move\n");
return cb_cleanup();
}
else printf("Made Move\n");
}
return cb_cleanup();
}
And if anyone wants to translate the API to Unix and/or other languages. It should not be difficult at all.
All the API does is open up a webpage, "api.php", with a bunch of GET commands.
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <string.h>
#include <ctype.h>
#define ERR -1
#define MAX 1024
SOCKET cb_sock;
WSADATA cb_ws;
hostent *cb_he;
struct sockaddr_in cb_a;
const char cb_hostname[] = "codersblock.net";
char cb_buff[MAX], cb_reply[MAX];
char cb_user[MAX] = {0}, cb_pass[MAX] = {0}, cb_game[MAX] = {0};
int cb_debug = 0;
char *cb_error(const char *msg) {
fprintf(stderr, "%s\n", msg);
return NULL;
}
int cb_urlencode(char *dest, const char *src) {
/* urlencode all non-alphanumeric characters in the C-string 'src'
store result in the C-string 'dest'
return the length of the url encoded C-string
*/
char *d;
int i;
for(i=0, d=dest; src[i]; i++) {
if(isalnum((unsigned char)src[i])) *(d++) = src[i];
else {
sprintf(d, "%%%02X", src[i]);
d += 3;
}
}
*d = 0;
return d-dest;
}
int cb_init(int do_debug) {
cb_debug = do_debug;
if (WSAStartup(0x101, &cb_ws) == ERR) {
cb_error("Could Not Create Socket");
return 1;
}
return 0;
}
void cb_login(const char *tuser, const char *tpass, const char *tgame) {
cb_urlencode(cb_user, tuser);
cb_urlencode(cb_pass, tpass);
cb_urlencode(cb_game, tgame);
}
char *cb_cmd(const char *cmd) {
char *size, *msg;
int status, len;
char cpycmd[MAX];
cb_sock = socket(AF_INET, SOCK_STREAM, 0);
cb_he = gethostbyname(cb_hostname);
if (!cb_he) return cb_error("Could Not Resolve Host Name");
cb_a.sin_family = AF_INET;
cb_a.sin_port = htons(80);
cb_a.sin_addr.s_addr = *((unsigned long *)cb_he->h_addr);
status = connect(cb_sock, (struct sockaddr *)&cb_a , sizeof(cb_a));
if (status == ERR) return cb_error("Could Not Connect To Server");
cb_urlencode(cpycmd, cmd);
sprintf(cb_buff, "GET /arena/api.php?cmd=%s&name=%s&pass=%s&game=%s HTTP/1.1\r\nHost: %s\r\n\r\n", cmd, cb_user, cb_pass, cb_game, cb_hostname);
if (cb_debug) printf("<<< %s\n-------------\n\n", cb_buff);
status = send(cb_sock, cb_buff, strlen(cb_buff), 0);
if (status == ERR) return cb_error("Could Not Request Web Page");
status = recv(cb_sock, cb_buff, sizeof(cb_buff), 0);
if (status == ERR) return cb_error("Could Not Receive Web Page");
if (cb_debug) printf(">>> %s\n-------------\n\n", cb_buff);
size = strstr(cb_buff, "MSG: ");
if (!size) return cb_error("Malformed Server Response ID: 1");
size += 5;
msg = strchr(size, ' ');
if (!msg) return cb_error("Malformed Server Response ID: 2");
*msg = 0;
len = atoi(size);
memcpy(cb_reply, msg+1, len);
cb_reply[len] = 0;
return cb_reply;
}
int cb_cleanup() {
return WSACleanup() == ERR;
}