/****************************************************************
 * RPSBot.c -- Rock Paper Scissors playing bot			*
 *							        *
 * Author: stderr stderr.dev@gmail.com			        *
 *							        *
 * Usage: Run program, and it will connect to the defined host  *
 *		and run as a rock paper scissors playing robot.	*
 *								*
 * Purpose: Demonstrate a simple bot.				*
 *								*
 * Protocol: The robot runs on an ICS (Internet Chess Server)   *
 *     For more information on the protocol you can visit the   *
 *     following links. 					*
 *     http://www.freechess.org				        *
 *     http://chessd.sf.net				        *
 *								*
 * File Format: The statistics file format is as follows...     *
 *     The stat file for a given players is saved to a file 	*
 *     with the same name as the persons handle. So if KSM 	*
 *     plays a game with RPSBot his statistic file will be ksm  *
 *     The statistics are saved in the following format:       	*
 *     Win Lose Tie		      				*
 *     3 integers that contain the win number, the lose number  *
 *     and the tie number. So if KSM has played 5 games and 	*
 *     won 3, lost 1 and tied 1 his file would look like this   *
 *								*
 *		[Open file KSM]					*
 *		3 1 1						*
 *		[EOF]						*
 *								*
 *     The file parser need only to read the first line to get  *
 *     the stats of a given player.			        *
 *								*
 * Configuration: To configure the bot to play on another chess *
 *     server, you need to change the following #defines...	*
 *     HOST PORT LOGIN PASSW					*
 *								*
 * NOTE: This program will run on linux based machines or on    *
 *     a Windows box running cygwin. You could change the     	*
 *     sockets to work with windows aswell. If you decide to    *
 *     convert this code to run on windows, please email me 	*
 *     the changed source code.		      			*
 *							        *
 * NOTE: When running this program on an ICS, and the bot's     *
 *     handle isn't on the TD list change TELLCOMMAND define to *
 *     "tell" instead of "qtell".                               *
 *                                                              *
 * This bot is released under the GNU Public liscense, and may  *
 * be distributed freely. I only request that you email me      *
 * any updates you put in the code.	      			*
 ****************************************************************/

#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

/* defines used in the switch statements */
#define ROCK 		1
#define PAPER 		2
#define SCISSORS	3

/* host information */
#define HOST "127.0.0.1"
#define PORT 5000

/* handle and password data */
#define LOGIN "RPSBot\n"
#define PASSW "passgoeshere\n"

/* used for communicating with players */
/* if you change qtell to xtell or tell, you will need to edit
 * the tell strings in functions rock() paper() scissors()
 * help() and stats(). If you don't change the commands, it
 * won't send all of the responses correctly.
 */
#define TELLCOMMAND "qtell"

/* function prototypes */
void rock();
void paper();
void scissors();
void help();
void stats(char *name);

/* function to be called by rock(), paper(), scissors() functions */
/* returns the random number that was generated as an integer. */
int random_gen();

/* string to lower case function */
char *stolower(char *str);

/* global variable declaration */
char handle[18];	/* used to keep the handle of the teller */
char stat_handle[51];   /* used to store handle to get stats of */
char command[51];	/* used to see what command the person sent */

/* file of the open socket */
int fd;

int main(void)
{
	char str[256]; /* recieves the text from the server */
	char *ptr;     /* used to chop off the tags on the handle */

	int x; /* loop counter */

	/* structure to hold information about the host */
	struct sockaddr_in client;

	/* open a socket */
	if ((fd = socket(AF_INET,SOCK_STREAM,0)) == -1) {
		printf("socket() error\n");
		exit(8);
	}

	/* set the appropriate settings for the socket */
	memset(&(client),'\0',sizeof(client));
	client.sin_family = AF_INET;
	client.sin_port = htons(PORT);
	inet_aton(HOST,&client.sin_addr);

	/* try connecting to the host */
	if (connect(fd,(struct sockaddr*)&client,sizeof(struct sockaddr)) == -1) {
		printf("connect() error\n");
		exit(8);
	}

	/* send login information and report to owner */
	send(fd,LOGIN,sizeof(LOGIN),0);
	send(fd,PASSW,sizeof(PASSW),0);
	send(fd,"set interface RPSBot v2.0\n",26,0);
	send(fd,"set open 0\n",11,0);
	send(fd,"set shout 0\n",12,0);
	send(fd,"set cshout 0\n",13,0);

	while(1) {
		/* set the appropriate strings to 0 */
		memset(&(str),'\0',sizeof(str));
		memset(&(command),'\0',sizeof(command));
		memset(&(stat_handle),'\0',sizeof(stat_handle));

		/* get text from server */
		/* make sure the connection is still alive */
		if (recv(fd,str,sizeof(str),0) == 0) {
		  break;
		}

		/* display text on screen */
		printf("%s",str);
		fflush(stdout);

		/* parse to see if a tell was recieved */
		sscanf(str,"%s tells you: %s %s",&handle,&command,&stat_handle);

		strcpy(command,stolower(command));
		strcpy(stat_handle,stolower(stat_handle));
		strcpy(handle,stolower(handle));

		/* chop off the title on a player */
		/* KSM(*) becomes KSM */
		ptr = strstr(handle,"(");
		if (ptr != 0)
			*ptr = '\0';

		/* check to see what command the user sent */
		if (!(strcmp(command,"rock")))
			rock();
		if (!(strcmp(command,"paper")))
			paper();
		if (!(strcmp(command,"scissors")))
			scissors();
		if (!(strcmp(command,"help")))
			help();
		/* check if the user wants to see his stats or someone else's */
		if ((!(strcmp(command,"stats"))) && (strstr(stat_handle,"%") != 0))
		        stats(handle);
		if ((!(strcmp(command,"stats"))) && (strstr(stat_handle,"%") == 0))
		        stats(stat_handle);
	}

	/* close the socket */
	close(fd);
	return 0;
}

/* handle rock tells */
void rock()
{
	int comp_choice; 	/* will recieve the value from random_gen() */
	int win, lose, tie;     /* variables for stats */

	char tell[100];		/* holds qtell information going to the server */
	char line[100];		/* used to read from the open file for stats */
	char dir[51];           /* directory of the stat files */

	FILE *in_file;		/* read the stats */
	FILE *out_file;		/* write the stats */

	/* set dir to the stats directory + handle */
	sprintf(dir,"stats/%s",handle);

	/* open up the handles stats file */
	in_file = fopen(dir,"r");
	/* if it couldn't open, then create a stats file */
	if (in_file == NULL) {
		/* open and write to the stats file */
		out_file = fopen(dir,"w");
		fprintf(out_file,"%d %d %d\n",0,0,0);
		fclose(out_file);

		/* reopen the file for reading */
		in_file = fopen(dir,"r");
	}

	/* get the first line of the text file */
	fgets(line,sizeof(line),in_file);
	/* parse into 3 distinct integers */
	sscanf(line,"%d %d %d",&win,&lose,&tie);

	/* close the file */
	fclose(in_file);

	/* open the file again but this time for writing */
	out_file = fopen(dir,"w");

	/* set tell array */
	sprintf(tell,"%s %s Computer chose: ",TELLCOMMAND, handle);

	comp_choice = random_gen(); /* put the value of the computers choice into comp_choice */

	/*
	 * switch statement to handle the computers choice
	 */
	switch(comp_choice)
	{
		case ROCK: /* if computer chose rock, then */
			strcat(tell,"Rock\\n"); /* note the \\n is to format the qtell */
			strcat(tell,"Result: You Tie!\n");
			/* write the new results to the stats file */
			fprintf(out_file,"%d %d %d\n",win,lose,tie+1);
			break;
		case PAPER: /* if computer chose paper, then */
			strcat(tell,"Paper\\n");
			strcat(tell,"Result: You Lose!\n");
			/* write the new results to the stats file */
			fprintf(out_file,"%d %d %d\n",win,lose+1,tie);
			break;
		case SCISSORS: /* if computer chose scissors, then */
			strcat(tell,"Scissors\\n");
			strcat(tell,"Result: You Win!\n");
			/* write the new results to the stats file */
			fprintf(out_file,"%d %d %d\n",win+1,lose,tie);
			break;
		default:
			break;
	}

	/* send the command to the server */
	send(fd,tell,strlen(tell),0);

	/* close the writing file */
	fclose(out_file);
}

/* handle paper tells */
void paper()
{
	int comp_choice; 	/* will recieve the value from random_gen() */
	int win, lose, tie;     /* holds the stats from the file */

	char tell[100];		/* holds the qtell information */
	char line[100];		/* used to read from the stats file */
	char dir[51];           /* hold directory of stat files */

	FILE *in_file;		/* read file for stats */
	FILE *out_file;		/* write file for stats */

	/* set dir to the stats directory + handle */
	sprintf(dir,"stats/%s",handle);

	/* open the stats for reading of variables */
	in_file = fopen(dir,"r");
	/* if file doesn't exist then create a file, and reopen for reading */
	if (in_file == NULL) {
		/* open and write results to the new file */
		out_file = fopen(dir,"w");
		fprintf(out_file,"%d %d %d\n",0,0,0);
		fclose(out_file);

		/* reopen the file for reading */
		in_file = fopen(dir,"r");
	}

	/* get the first line from the stats file */
	fgets(line,sizeof(line),in_file);
	/* parse into the win, lose, and tie variables */
	sscanf(line,"%d %d %d",&win,&lose,&tie);

	/* close the reading file */
	fclose(in_file);

	/* open file for writing to the stats file */
	out_file = fopen(dir,"w");

	/* the tell variable will be = "qtell <handle> Computer chose: " */
	sprintf(tell,"%s %s Computer chose: ",TELLCOMMAND,handle);

	comp_choice = random_gen(); /* put the value of the computers choice into comp_choice */

	/*
	 * switch statement to handle the computers choice
	 */
	 switch(comp_choice)
	 {
		 case ROCK: /* if computer chose rock */
			 strcat(tell,"Rock\\n");
			 strcat(tell,"Result: You Win!\n");
			 /* write the new stats to the file */
			 fprintf(out_file,"%d %d %d\n",win+1,lose,tie);
			 break;
		 case PAPER: /* if computer chose paper */
			 strcat(tell,"Paper\\n");
			 strcat(tell,"Result: You Tie!\n");
			 /* write the new stats to the file */
			 fprintf(out_file,"%d %d %d\n",win,lose,tie+1);
			 break;
		 case SCISSORS: /* if computer chose scissors */
			 strcat(tell,"Scissors\\n");
			 strcat(tell,"Result: You Lose!\n");
			 /* write the new stats to the file */
			 fprintf(out_file,"%d %d %d\n",win,lose+1,tie);
			 break;
		 default:
		     break;
	 }

	 /* send the qtell to the server */
	 send(fd,tell,strlen(tell),0);
	 /* close file */
	 fclose(out_file);
}

void scissors()
{
	int comp_choice; 	/* will recieve the value from random_gen() */
	int win, lose, tie;     /* statistic variables */

	char tell[100];		/* tell command variable */
	char line[100];		/* used to read information from the stats file */
	char dir[51];           /* hold directory of stat files */

	FILE *in_file;		/* read stats file */
	FILE *out_file;		/* write stats file */

	/* set dir to the stats directory + handle */
	sprintf(dir,"stats/%s",handle);

	/* open stat file for reading */
	in_file = fopen(dir,"r");
	/* if file doesn't exist, create a new rating file */
	if (in_file == NULL) {
		out_file = fopen(dir,"w");
		fprintf(out_file,"%d %d %d\n",0,0,0);
		fclose(out_file);

		/* reopen the file for reading */
		in_file = fopen(dir,"r");
	}

	/* read the first line of the stats file */
	fgets(line,sizeof(line),in_file);
	/* parse the line into 3 integers */
	sscanf(line,"%d %d %d",&win,&lose,&tie);

	/* close reading file */
	fclose(in_file);

	/* open write file */
	out_file = fopen(dir,"w");

	/* set tell to "qtell <handle> Computer chose: " */
	sprintf(tell,"%s %s Computer chose: ",TELLCOMMAND,handle);

	comp_choice = random_gen(); /* put the value of the computers choice into comp_choice */

	/*
	 * switch statement to handle the computers choice
	 */
	 switch(comp_choice)
	 {
		 case ROCK: /* if computer chose rock */
			 strcat(tell,"Rock\\n");
			 strcat(tell,"Result: You Lose!\n");
			 /* save new stats to file */
			 fprintf(out_file,"%d %d %d\n",win,lose+1,tie);
			 break;
		 case PAPER: /* if computer chose paper */
			 strcat(tell,"Paper\\n");
			 strcat(tell,"Result: You Win!\n");
			 /* save new stats to file */
			 fprintf(out_file,"%d %d %d\n",win+1,lose,tie);
			 break;
		 case SCISSORS: /* if computer chose scissors */
			 strcat(tell,"Scissors\\n");
			 strcat(tell,"Result: You Tie!\n");
			 /* save new stats to file */
			 fprintf(out_file,"%d %d %d\n",win,lose,tie+1);
			 break;
		 default:
		     break;
	 }

	 /* send tell command */
	 send(fd,tell,strlen(tell),0);
	 /* close write file */
	 fclose(out_file);
}

/* deal with help tells */
void help()
{
	char cmd[256]; /* used to contain the help file tell command */

	/*
	 * notice the \\n, this sends "\n" to the server instead of terminating
	 * the string. Using \n would kill the string and send only data up to
	 * that point to the server.
	 */
	sprintf(cmd,"%s %s ***Commands***\\n1. rock\\n2. paper\\n3. scissors\\n4. stats [handle]\\n5. help\\n[Last modified Mar 09, 2004 by KSM]\n",TELLCOMMAND,handle);

	/* send help file */
	send(fd,cmd,strlen(cmd),0);
}

/* deal with stats tells */
void stats(char *name)
{
	int win,lose,tie;	/* stats variables */

	char tell[100];		/* contains the server tell command */
	char line[100];		/* used to read stats from the file */
	char dir[51];           /* store the directory of the stat files */

	FILE *in_file;		/* reading file */

	/* set dir to the stats directory + handle */
	sprintf(dir,"stats/%s",name);

	/* check for abusing strings */
	if (strstr(name,".") != 0 || strstr(name,"/") != 0) {
	  sprintf(tell,"%s %s Illegal characters!\n",TELLCOMMAND,handle);
	  send(fd,tell,strlen(tell),0);
	  return;
	}	

	/* open the file to read the stats */
	in_file = fopen(dir,"r");
	/* check if the user file actually exists */
	if (in_file == NULL) {
	  sprintf(tell,"%s %s No stats for user!\n",TELLCOMMAND,name);
	  send(fd,tell,strlen(tell),0);
	  printf("\n%s\n",name);
	  return;
	}

	/* get the first line from the file */
	fgets(line,sizeof(line),in_file);
	/* parse the line into the 3 int variables */
	sscanf(line,"%d %d %d",&win,&lose,&tie);

	/* set tell to "qtell <handle> Statistics\\n" (\\n for server purposes) */
	sprintf(tell,"%s %s Statistics for %s\\n",TELLCOMMAND,handle,name);

	/* sprintf(tell) to append the rest of the data to the end of tell string */
	sprintf(tell,"%sWins     Losses     Ties     Total\\n",tell);
	/* add the actual stats to the qtell. %7d and %9d position the stats */
	sprintf(tell,"%s%d%9d%11d%9d\\n",tell,win,lose,tie,win+lose+tie);

	/* add the win percentage */
	sprintf(tell,"%sWin Percent: %3.2f\n",tell,100*((win+(tie*.5))/(win+lose+tie)));

	/* send the tell to the server */
	send(fd,tell,strlen(tell),0);
}

/* generate random numbers for the RPS functions */
int random_gen()
{
	int comp_choice; /* variable to hold the computers randomly generated move */

	srand((unsigned) time(NULL)); /* set the random seed */
	comp_choice = 1 + rand() % 3; /* set comp_choice to a random num between 1 and 3 */

	return comp_choice; /* return the value of the random number */
}

/* function was taken from GICS code */
char *stolower(char *str)
{
  int i;

  if (!str)
    return NULL;
  for (i = 0; str[i]; i++) {
    if (isupper(str[i])) {
      str[i] = tolower(str[i]);
    }
  }
  return str;
}
