Linux signal operations

         TicTacToe

A. First Edition
This is comp444 lab2 and it is concentrated on signal, process synchronization.
B.The problem

Your task with this assignment is to create a multi player tac-tac-toe tournament

(Xs and Os). The assignment will be divided into two parts: (1) creating a twoplayer

competition and (2) creating matches for a group of players and reporting

the results. You should proceed as follows:

Part One: The basic competition (80% of grade)

1. You will write a program that serves as the main tournament process. Its

basic role will be to generate and maintain two player processes. The players

will be instances of a player program that will be separate from the

tournament program. In other words, you will fork and exec the 2 player

processes from the tournament process.

2. The player processes must communicate with one another. Since we haven¡¯t

talked about IPC or threads yet, this communication will occur with signals

and shared files.

3. To accomplish this, the parent process must open a file and then allow the

two processes to read and write to this file. Remember, files are not (by

default) closed across fork and exec calls.

4. The two player programs can be passed a number of command line

arguments (you can add whatever you need). At the very least, they will have

to be told whether they represent the X or O player.

5. The two players will then compete in a game of tic-tac-toe. Before they start,

they must know who they are competing against. In other words, they must

know the process ID of their opponent. This cannot be passed on the

command line during the exec call since this information is not available in the

child process (it is returned to the original parent). The easiest way of doing

this is to have the parent process write the two new process IDs to the shared

file so that the two child processes can read the info when they are run.

6. NOTE!!! Even though the file is shared, you cannot just allow your three

processes (parent and two children) to read/write to it at arbitrary times.

Remember, they are all sharing the same file pointer. Instead, you must use

signals to synchronize access. Specifically, you will use SIGUSR1 and

SIGUSR2. You can do this anyway you like but I would suggest that you use

SIGUSR1 to indicate to a child process that the parent has written something

to the file and SIGUSR2 to indicate that the competitor has just written

something to the file. Note that children should not need to write anything

back to the parent.

7. Once the two children know who their competitor is, the game can begin.

Note that the game is played on a 3 by 3 grid so there are 9 possible

positions. The X player will always go first by choosing a random position on

the board (this is an awful way to start the game but it ensures that all games

won¡¯t be exactly the same). You will use rand and seed functions in the C

library to pick the starting position.

8. From this point onward, the two players will proceed one move at a time,

signaling to one another once they complete their next move. Clearly, each

player will have to keep track of the other¡¯s moves. Each process should

therefore update a local version of the board. The game will terminate either

when one player has won or there is a draw (tie). In the latter case, X is

declared the victor (I don¡¯t want you to have to keep playing until someone

wins because tic-tac-toe has a lot of ties).

9. So how do you decide how to make your next move? You will create at least

two playing ¡°strategies¡±. A strategy is a function that, given an existing board,

returns the next move. Please do not waste your time coming up with

complex strategies (unless you want to). A strategy can be as simple as ¡°find

the next open square from 0 to 8¡±, or ¡°find the next open square from 8 to 0¡±.

When the game starts, each player process will randomly pick one of these

two strategies.

10. You must, of course, check at each step to see if the game has been won.

The check is simple: the game is over if there are three identical symbols in

any row, column, or diagonal (8 possibilities in total). It is also over if all

squares are filled (a draw).

Part Two: The Tournament (20%)

To have an actual tournament, there must be more than two players. Ordinarily,

a tournament would consist of a number of rounds, with the final round producing

a single winner. However, the code required to properly manage this type of

tournament would probably be excessive (it¡¯s harder than it might sound due to

the limited communication facilities). To keep things simple, we will just have a

single round that will produce a group of winners. You will have to extend Part

One as follows:

1. The main tournament program should take one command line parameter ¨C P

= the number of players in the tournament. It must be a multiple of two ¨C an

error should result otherwise.

2. Before starting the competitions, the main program must check the resource

limits for the user. Specifically, you must check that the number of processes

and open files (P/2) will not be exceeded (given the command line argument).

3. The main process will start pairs of player processes, with each pair sharing

an open file. These processes will run concurrently (i.e., at the same time). As

such, a group of P/2 files will be open in every process, although each pair of

players will only share one of these.

4. When a game finishes, the parent process will print the process ID of the

losing process. To do so, it will need to wait for the appropriate number of

losing processes.

5. For winning processes, you will want each one to print its winning board.

However, if there are multiple processes, we can¡¯t just print them to the

screen at once (the output will get mixed together). Therefore, the command

line arguments for the player program should also include a delay value D.

The winning player should wait D seconds before printing its winning board.

So if there were four matches, you would print the results after 1, 2, 3, and 4

seconds. Note that on a very busy system, it¡¯s possible that some of the

results might still get mixed together¡­but we won¡¯t worry about that here.

6. Close files and clean up. You don¡¯t need to wait for the winning processes

since everything will now shut down. (The winning processes will be inherited

by init and will be terminated properly.)

Good Luck!

C.The idea of program
¡¡
How to generate a random seed? 2005-02-24
I was originally puzzled a lot when "time()" always return the same seed for srand(). Then I realized it returns the number in seconds and my program is so fast that all child process are generated within one second. So, I cannot use time to generate different seeds for my srand function. I remember in windows there is something like get cpu_clock numbers. But I cannot find anything in Linux. Professor, do you have any suggestion for generating different seeds for child processes except forcing each child to sleep for a while to generate different seconds in time? Thank you.

¡¡
random seed 2005-02-24
Since the goal is to create a unique seed for each process that's run, a very simple solution would be to use the process ID as the seed.

Dr Eavis
get the system limits 2005-02-21
I am currently doing lab2 and we have to check for the resource limit for the opened file and processes.

We can also use getrlimit() function but this function gives a static result and not the number of resource left. In order to check if we do not reach the limit, I guess we need something to retrieve the actual number of launched processes (for instance) for the real user id.

My question is simple : how can we do that ?
limits 2005-02-22
Actually, I just wanted you to check the static resource limits. I should have made this more clear in the assignment. I'm assuming that you will be running this process as your only processes(s), so the use of getrlimit() is all I want. I just wanted you to get a chance to use this function and see how it worked. (Linux provides a function called getrusage to get current process stats but I don't believe it provides a list of currently running processes).

Dr Eavis
about requirement no5 in part2 2005-02-19
I don't see complete necessity to pass a delay value as parameter to allow synchronized displaying of winning teams. That is to say, the parenet process can know the process ID of winning team (i.e. the winner writes the results in shared files) and can choose appropriate timing to send a signal to wake up a temperarily sleeping winning team(after winning the game, the process is a bit tired and needs a bit of sleep.) to display his winning board followed with a waitpid call. This will eliminate possibility of messing up of multi-winner racing for displaying.

So, can I treat no. 5 in part 2 as a suggestion instead of requirement as long as I can display winner results properly?

Thank you sir.
Program requirements 2005-02-22
Yes, you can certainly do that if you like. I chose the parameter passing solution because it is easy to implement. If you use wait(), then you need another signal, one that is only handled by one of the two player processes. This would seem to complicate things a little. Nevertheless, if you like that solution more, feel free to implement your program this way.

Dr Eavis

¡¡

1. This assignment may not be as easy as it seems to be, at least it is true in my case. I spent a little more than
one day to finish all basic frame coding job and begin wondering why the second assignment is much easier than 
the first one. Then the wind turns against me when I spent half days to debug for even one small, stupid bug. And
you don't know how many bugs there are. The problem is simply that signal is hard debugging, forked process is 
even harder. There is some extra technique for me to trace child process. Finally I have to choose the primitive
printf method for debugging.
2. The synchronization bug is hard to find! Look at following code:
	while (!gameOver)
	   pause();
This is one typical casual coding when you want the signal handler do some job for you. The professor already 
emphasized there is a problem, but I just don't understand it until my program seems to work fine with small number
of processes and fail when large number of processes created. You see the signal can be caught in between two lines.
And you program will pause forever. The correct way is like this:
	sigprocmask(&signalset);
	while (!gameOver)
		sigsuspend(&extraSignalSet);
This simple technique is just one of questions in our midterm. I wrote whatever I read in slides without really
understand it.
3. How many critical stages are there in this little game?
My program has a "bullet-proof" synchronization logic since I tried to generate 2000 child players and it works fine. I also design as 
many as eight game strategy for finding next tictactoe position instead of the two suggested by professor even though there is no AI at 
all.

The game logic is like this:
1. game start:
a) Every child should send a notify signal, SIGUSR1, to parent when he finishes initialization.
b) Parent creates game with carefulness. That is, it creates one child and wait until receives confirmation before proceeds on.
c) Parent send signal SIGUSR1 only to player X and player X starts game and notify player O by SIGUSR2.
d) Player X and player O will never read shared file at any possible moment because they are sharing the same file offset and if their 
	reading operation interleaves, there will be a corruption of data they read. The logic is like this: parent creates all children 
	and records all pid's of all children processes in an array of game info struct. Then parent writes game info like pid of both X 
	and O into file while all children pause. Parent then notify player X only by sending signal SIGUSR1. Player X reads game info 
	from shared file and finishes first strike of game. Then player X starts the game by notifying player O. Player O wakes up and
	 check game-start flag to initialize opponent info by reading shared file and then makes its game move and notifies opponent to 
	let game continue. Therefore there is no danger of concurrent reading of shared file.
e) Using sigsuspend instead of simple pause.
	As professor already points out that there is a big problem by using pause like following:
	while (!gameOver)
		pause();
	There is a danger when signal coming between two lines and our program will pause forever. So, I block the signal SIGUSR2 at 
	beginning and use sigsuspend().
2. game play:
a) Using sigsuspend instead of simple pause.
        As professor already points out that there is a big problem by using pause like following:
        while (!gameOver)
                pause();
        There is a danger when signal coming between two lines and our program will pause forever. So, I block the signal SIGUSR2 at    
        beginning and use sigsuspend().
b) The game-play handler checks game winning condition first. If there is a winner, this player setup game-over flag and game enters 
	next mode. Only the loser quit.

3. game over:
a) The winner should pause until parent notification for printing game.
b) Parent waits children and counts number of children who quits. When number of quits reaches number of game which is half of number of 
	players, it iterates all games by sending signal SIGUSR1 to winner player. Parent waits when the winner finishes printing and 
	quits.
c) There is no need for winner to re-setup signal handler for signal SIGUSR1 since it checks game-over flag for appropriate actions.
d) The loser cannot quit game by itself because the winner won't know if it is quit or not. So, my logic is that it is winner who sends 
	out a SIGQUIT signal to loser.
The critical synchronization point:
1. Parent needs to send signal SIGUSR1 ONLY WHEN children finishes initialization, otherwise the default handler for this signal is to 
	ask child process quit! This is the reason for cautious way of creating each child one by one.
2. Player X must also be sure player O has finished initialization. This is difficult to be done between child processes. But when 
	parent process creates each child with caution, this condition is already satisfied.
3. When loser quits, parent process cannot know when the winner process is ready for printing. So, it is very important to let SIGUSR1 
	have a single handler setup at the beginning of game. The game-over flag is used for appropriate action instead re-setup signal 
	handler. This eliminates a slight window of time when handler has not been setup and signal from parent already comes.
4. Only player X will receives signal SIGUSR1 from parent at game start stage. But when game over, both player has a chance to win and 
	both might be ready to receive SIGUSR1 for printing game.

The misc points:
1. I define many constants which represent appropriate offset in shared file such as PlayerX_Pos etc.
2. When game is over, the parent sends signal to the winner of each game and waits after the winner finishes printing game board. Then 
	parent proceeds on next game.
3. In order for better debug issue, I set up a small handler when program exits by using "atexit()" to send SIGQUIT to all children 
	processes. Otherwise when debugging, I often found dozens of children processes running when I have to kill the parent.
 
¡¡
D.The major functions
¡¡
E.Further improvement
¡¡
F.File listing
1. myhead.h
2. main.c
3. player.c
4. errHandle.c
5. makefile
¡¡
¡¡
file name: myhead.h
#ifndef MYHEAD_H
#define MYHEAD_H
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>


//define three mode and it is purely like
// DFA=Deterministic Finite Automaton
#define FindNext        0
#define Comparing       1

typedef int bool;

#define TRUE  1
#define FALSE 0

//for a particular file NAME, NOT path
//const int MaxNameLength=20;
#define MaxPathLength 128

#define MaxNameLength  20 

#define MaxIntLength   20

#define BuffSize   128

#define EmptyGridNode   ' '//the empty tic-tac-toe grid

#define PlayerTypeLength 2

#define PlayerPidLength (MaxIntLength+1)

#define BoardLength 9 //the tic-tac-toe length

#define PlayerX_Pos PlayerTypeLength

#define PlayerO_Pos (PlayerTypeLength*2+PlayerPidLength)

#define BoardPos ((PlayerTypeLength+PlayerPidLength)*2+1)

#define WinnerPos (BoardPos+BoardLength+1)

#define WinnerTypeLength 1 //simply the X or O

void errHandle(char* msg);
	
#endif


¡¡
file name: main.c
#include "myhead.h"


struct GameInfo
{
	pid_t playerX;
	pid_t playerO;
	int fd;
	pid_t winner;
};

struct GameInfo* gameinfo;

//const char* tempFileName="gameXXXXXX";

char path[MaxPathLength];
const char* playerFileName="player.exe";
//const int SleepSeconds=2;
int playerNumber, gameNumber;
int gameOverNumber=0;
bool playerReady=FALSE;
//int gameReadyNumber=0;
//bool gameStart=FALSE;
//bool gameOverFlag=FALSE;

void checkResource(int resourceType, int requireNo);

void createGame(int index);

void createPlayer(int index, char* playType);

void writePid(int index);

//void gameOverCount(int no);

void gameReadyCount(int no);

void initialize(int argc, char* argv[]);

void uninitialize();

void clearGame(int index);

int main(int argc, char* argv[])
{
	int statloc;
	int index;
	pid_t pid;
	initialize(argc, argv);
	for (index=0; index<gameNumber; index++)
	{
		createGame(index);
		//printf("create game index=%d\n", index);
	}	

	for (index=0; index<gameNumber; index++)
	{
                if (kill(gameinfo[index].playerX, SIGUSR1)<0)
                {
                        errHandle("kill error when create game\n");
                }   
		//don't send signal to playerO, because playerX will notify him.
	}
	gameOverNumber=0;
	//printf("parent waiting game over\n");
	while (gameOverNumber<gameNumber)
	{
		if (wait(&statloc)<0)
		{
			errHandle("wait error\n");
		}
		gameOverNumber++;
	}

	gameOverNumber=0;
	//printf("clear game now\n");
	printf("Tournament finished and results are like following:('E' for empty grid)\n");
	//sleep(3);
	do
	{
		//printf("waiting game no.%d\n", gameOverNumber);
		clearGame(gameOverNumber);
		//printf("after clear game");
		/*
		if (wait(&statloc)<0)
		{
			errHandle("wait error\n");
		}
		*/
		gameOverNumber++;
		//pause();
	}		
	while (gameOverNumber<gameNumber);
	return 0;
}

void uninitialize()
{
	int i;
	for (i=0; i<gameNumber; i++)
	{
		if (close(gameinfo[i].fd)<0)
		{
			errHandle("close error when end game\n");
		}
	}
	if (kill(0, SIGQUIT)<0)
	{
		printf("kill error when unintialize\n");
	}
	free(gameinfo);
}


void clearGame(int index)
{
	char winnerType;
	pid_t pid, temp;
	if (lseek(gameinfo[index].fd, WinnerPos, SEEK_SET)<0)
	{
		errHandle("seek error when clear game\n");
	}
	//printf("try to read fd=%d of index=%d\n", gameinfo[index].fd, index);
	if (read(gameinfo[index].fd, &winnerType, 1)!=1)
	{
		errHandle("read error for winnertype\n");
	}
	if (winnerType=='X')
	{
		pid=gameinfo[index].playerX;
		printf("In game no.%d, player %d is loser\n", index+1, gameinfo[index].playerO);
	}
	else
	{
		if (winnerType=='O')
		{
			pid=gameinfo[index].playerO;
			printf("In game no.%d, player %d is loser\n", index+1, gameinfo[index].playerX);
		}
		else
		{
			errHandle("unexpected error of winner type\n");
		}
	}
	//printf("try to kill process %d\n", pid);
	//while (1)
	{
		//printf("calling %d\n", pid);
	        if (kill(pid, SIGUSR1)<0)
        	{
                	errHandle("kill error when end game\n");
        	}
        	if ((temp=waitpid(pid, NULL, 0))!=pid)
        	{
			printf("wait error return %d\n", temp);
			if (kill(0, SIGUSR1)<0)
			{
				errHandle("cannot kill all processes\n");
			}
	                errHandle("wait error when clear game\n");
        	}
		else
		{
			//break;
		}
	}
}
		

/*
void gameOverCount(int no)
{
	gameOverNumber++;
	//gameOverFlag=TRUE;
	printf("one game is over and gameoverNumber=%d\n", gameOverNumber);
}
*/
         
void gameReadyCount(int no)
{
	playerReady=TRUE;

	//printf("one game ready\n");
	//gameReadyNumber++;
}
	

void initialize(int argc, char* argv[])
{
	if (argc!=2)
        {
                errHandle("usage: <main.exe> <player_number>\n");
        }
        if (sscanf(argv[1], "%d", &playerNumber)==0)
        {
                errHandle("usage: <main.exe> <player_number>\n");
        }
        if (playerNumber%2!=0)
        {
                errHandle("the player number must be multiple of 2\n");
        }
        gameNumber=playerNumber/2;
        checkResource(RLIMIT_OFILE, gameNumber);  
        checkResource(RLIMIT_NPROC, playerNumber);
        if ((gameinfo=(struct GameInfo*)malloc(gameNumber*sizeof(struct GameInfo)))==NULL)
        {
                errHandle("malloc error when allocate game info\n");
        }
        if (getcwd(path, MaxPathLength)==NULL)
        {
               errHandle("getcwd error\n");
        }
        strcat(path, "/");
        strcat(path, playerFileName);
/*
	if (signal(SIGCHLD, gameOverCount)==SIG_ERR)
	{
		errHandle("signal error for game over\n");
	}
*/
	if (signal(SIGUSR1, gameReadyCount)==SIG_ERR)
	{
		errHandle("signal error\n");
	}
	if (atexit(uninitialize)!=0)
	{
		errHandle("unable setup uninitialize handler for main\n");
	}
}


void createPlayer(int index, char* playerType)
{
	pid_t pid;
	char fdstr[MaxIntLength];
	sprintf(fdstr, "%d", gameinfo[index].fd);
	playerReady=FALSE;
	if ((pid=fork())==0)
	{
		if (execl(path, playerFileName, playerType, fdstr, NULL)==-1)
		{
			errHandle("execl error\n");
		}
	}
	else
	{
		if (playerType[0]=='X')
		{
			gameinfo[index].playerX=pid;
		}
		else
		{
			gameinfo[index].playerO=pid;
		}
	}
	//here we must wait until child response
	while (!playerReady)
	{
		sleep(SleepSeconds);
	}
}

void createGame(int index)
{
	int fd, i;
	char board[BoardLength];
	char tempFile[L_tmpnam+1];
	for (i=0; i<L_tmpnam; i++)
	{
		tempFile[i]='X';
	}
	tempFile[L_tmpnam]='\0';
	if ((fd=mkstemp(tempFile))<0)
	{
		errHandle("temp file create failed\n");
	}
	//printf("fd=%d\n", fd);

        if (unlink(tempFile)<0)
        {
                errHandle("unlink error\n");
        }

	for (i=0; i<BoardLength; i++)
	{
		board[i]=EmptyGridNode;
	}
	if (lseek(fd, BoardPos, SEEK_SET)<0)
	{
		errHandle("lseek error when create game\n");
	}
	if (write(fd, board, BoardLength)!=BoardLength)
	{
		errHandle("write error for board\n");
	}
	
	gameinfo[index].fd=fd;
	createPlayer(index, "X");
	createPlayer(index, "O");
	writePid(index);
}	

void writePid(int index)
{
	int i;
	char pidstr[PlayerTypeLength+PlayerPidLength+1];
        pidstr[0]='X';
        pidstr[1]=' ';
        sprintf(pidstr+PlayerTypeLength, "%d", gameinfo[index].playerX);
        for (i=strlen(pidstr); i<PlayerTypeLength+PlayerPidLength; i++)
        {
                pidstr[i]='\0';
        }
	if (lseek(gameinfo[index].fd, 0, SEEK_SET)<0)
	{
		errHandle("error of lseek\n");
	}
	//printf("write x of %s\n", pidstr);
	if (write(gameinfo[index].fd, pidstr, PlayerTypeLength+PlayerPidLength)!=PlayerTypeLength+PlayerPidLength)
	{
		errHandle("write error for pid\n");
	}
	pidstr[0]='O';
 	pidstr[1]=' ';
	sprintf(pidstr+PlayerTypeLength, "%d", gameinfo[index].playerO);
        for (i=strlen(pidstr); i<PlayerTypeLength+PlayerPidLength; i++)
        {
                pidstr[i]='\0';
        }
        if (lseek(gameinfo[index].fd, PlayerTypeLength+PlayerPidLength, SEEK_SET)<0)
        {
                errHandle("error of lseek\n");
        }
        if (write(gameinfo[index].fd, pidstr, PlayerTypeLength+PlayerPidLength)!=PlayerTypeLength+PlayerPidLength)
        {
                errHandle("write error for pid\n");
        }

	if (fsync(gameinfo[index].fd)<0)
	{
		errHandle("fsync error\n");
	}

 }
	
void checkResource(int resourceType, int requireNo)
{
	struct rlimit resource;
        if (getrlimit(resourceType, &resource)!=0)
        {
                errHandle("get resource error\n");
        }
        if (requireNo>resource.rlim_max)
        {
                errHandle("require number exceeds resource limit\n");
        }
        else
        {
                if (requireNo>resource.rlim_cur)
                {
			//debug
			errHandle("require number exceeds current resource limit\n");
			/*
                        resource.rlim_cur=requireNo;
                        if (setrlimit(resourceType, &resource)!=0)
                        {
                                errHandle("cannot set resource limit to match required number\n");
                        }
			*/
                }
        }
}
	
¡¡
¡¡
file name: player.c
#include "myhead.h"

//const int StrategyCount=2;
#define StrategyCount 8
typedef int (*Strategy)();

int strategy1();
int strategy2();
int strategy3();
int strategy4();
int strategy5();
int strategy6();
int strategy7();
int strategy8();

Strategy strategy[StrategyCount]=
{
	strategy1, strategy2, strategy3, strategy4, 
	strategy5, strategy6, strategy7, strategy8
};

pid_t opponent=0;
int fd;
char playerType;
char winnerPlayer;
sigset_t mask;

char board[BoardLength];
bool gameOver=FALSE;
bool gameStart=FALSE;
bool opponentStart=FALSE;
void playGame();
void tictactoe(int no);
void initialize();
void beginPlay(int no);
void readBoard();
void writeBoard(int index);
void notify();
void endGame();
void printBoard(int no);

//all pause call are in main, except playGame function
int main(int argc, char* argv[])
{
	if (argc!=3)
	{
		errHandle("This program is invoked within main.exe. usage: <player.exe><playerType><filedescriptor>\n");
	}
	//the early the better. otherwise it will be killed by parent's signal
        fd=atoi(argv[2]);
        playerType=argv[1][0];
	initialize();
	//printf("before initialize and fd is %d and playertype=%c\n", fd, playerType);  

	//every one should notify parent when he is ready
	if (kill(getppid(), SIGUSR1)<0)
	{
		errHandle("kill error when notify parent\n");
	}
	//printf("before play the board is\n");
	//readBoard(0);
	//printBoard(0);
	pause();
	//before start playerO must check if playerX already send signal
	//printf("player %c ready play game\n", playerType);
	playGame();
	//at this stage only winner survives
	//printf("game over and waiting for parent\n");

	pause();//wait until parent ask you to print

	return 0;
}

void gameHandler(int no)
{
	if (!gameOver)
	{
		beginPlay(no);
	}
	else
	{
		printBoard(no);
	}
}


void printBoard(int no)
{
	int i;
	printf("I am player %c and my pid=%d\n", playerType, getpid());
	for (i=0; i<BoardLength; i++)
	{
		printf("%c", board[i]);
		if (i%3==2)
		{
			printf("\n");
		}
	}
	printf("\n");
}

int strategy1()
{
	int i;
	for (i=0; i<BoardLength; i++)
	{
		if (board[i]==EmptyGridNode)
		{
			break;
		}
	}
	return i;

}

int strategy2()
{
	int i;
	for (i=BoardLength-1; i>=0; i--)
	{
		if (board[i]==EmptyGridNode)
		{
			break;
		}
	}
	return i;
}

int strategy3()
{
	int i,j;
	for(i=0; i<3; i++)
	{
		for (j=0; j<BoardLength; j+=3)
		{
			if (board[i+j]==EmptyGridNode)
			{
				return i+j;
			}
		}
	}
}

int strategy4()
{
	int i,j;
	for(i=1; i<=3; i++)
	{
		for (j=0; j<BoardLength; j+=3)
		{
			if (board[BoardLength-i-j]==EmptyGridNode)
			{
				return BoardLength-i-j;
			}
		}
	}
}

int strategy5()
{
	int i,j;
	for (i=3; i>0; i--)
	{
		for (j=0; j<BoardLength; j+=3)
		{
			if (board[BoardLength-i-j]==EmptyGridNode)
			{
				return BoardLength-i-j;
			}
		}
	}
}
					
int strategy6()
{
	int i,j;
	for (i=2; i>=0; i--)
	{
		for (j=0; j<BoardLength; j+=3)
		{
			if (board[i+j]==EmptyGridNode)
			{
				return i+j;
			}
		}
	}
}


int strategy7()
{
	int i,j;
	for (i=0; i<BoardLength; i+=3)
	{
		for (j=0; j<3; j++)
		{
			if (board[i+j]==EmptyGridNode)
			{
				return i+j;
			}
		}
	}
}

int strategy8()
{
	int i,j;
	for (i=0; i<BoardLength; i+=3)
	{
		for (j=1; j<=3; j++)
		{
			if (board[BoardLength-i-j]==EmptyGridNode)
			{
				return BoardLength-i-j;
			}
		}
	}
}

void endGame()
{
        //everyone can write winner
        if (lseek(fd, WinnerPos, SEEK_SET)<0)
        {
                errHandle("lseek error when write winner\n");
        }
        if (write(fd, &winnerPlayer, 1)!=1)
        {
                errHandle("write error\n");
        }
        //printf("write to fd=%d of %c is winner\n", fd, winnerPlayer);

        if (fsync(fd)<0)
        {
                errHandle("fsync error\n");
        }

}                 

void tictactoe(int no)
{
	int index, id;
	if (playerType=='O')
	{
		//when player O executes this routine the very first time
		if (!gameStart)
		{
			//beginPlay(0);
			gameStart=TRUE;
			//here nobody will send signal unless you notify others
			//here is a dangerous zone, since signal from parent can come any moment
			//it has the disable interrupt flavour
			//maskSignal(SIGUSR1);
			beginPlay(0);
			//unmaskSignal(SIGUSR1);
			//return;
		}
	}
	readBoard();//must read first
	//printf("before my move the board is\n");
	//printBoard(0);

	if (checkGame())//winner?
	{
		if (winnerPlayer!=playerType)//I am loser
		{
			//printf("player %d is loser and quit now\n", getpid());
			gameOver=FALSE;//I cannot quit NOW!!!!
			notify();//still need to notify!!!!
			//exit(0);
		}
		else
		{
			//the winner kills loser otherwise it might notify again when winner is pausing for parent
			endGame();
			if (kill(opponent, SIGQUIT)<0)
			{
				errHandle("kill error when winner kill loser\n");
			}
		}
	}
	else
	{
		//printf("still tictactoe\n");
		//strategy should be a function pointer array
		id=rand()%StrategyCount;
		index=strategy[id]();
		if (index<0)
		{
			printf("id=%d\n", id);
			//printBoard(0);
		}
		writeBoard(index);
		notify();
	}
}
		
	
void notify()
{
	//printf("player %d type=%c notify %d \n", getpid(), playerType, opponent);
	if (kill(opponent, SIGUSR2)<0)
	{
		printf("kill error when %d notify %d", getpid(), opponent);
		errHandle("notify error\n");
	}
}
		
bool checkGame()
{
	int i;
	if (board[0]!=EmptyGridNode)
	{
		if ((board[0]==board[1]&&board[0]==board[2])||(board[0]==board[3]&&board[0]==board[6]))
		{
			winnerPlayer=board[0];
			gameOver=TRUE;
			return TRUE;
		}
	}
	if (board[8]!=EmptyGridNode)
	{
		if ((board[8]==board[7]&&board[8]==board[6])||(board[8]==board[5]&&board[8]==board[2]))
		{
			winnerPlayer=board[8];
			gameOver=TRUE;
			return TRUE;
		}
	}
	if (board[4]!=EmptyGridNode)
	{
		if ((board[4]==board[1]&&board[4]==board[7])||(board[4]==board[3]&&board[4]==board[5])	
		||(board[4]==board[0]&&board[4]==board[8])||(board[4]==board[2]&&board[4]==board[6]))
		{
			winnerPlayer=board[4];
			gameOver=TRUE;
			return TRUE;
		}
	}
	for (i=0; i<BoardLength; i++)
	{
		if (board[i]==EmptyGridNode)
		{
			return FALSE;
		}
	}
	winnerPlayer='X';
	gameOver=TRUE;
	return TRUE;
}	
						
	
void playGame()
{
	sigset_t mask;
	sigemptyset(&mask);
	sigaddset(&mask, SIGUSR2);
	if (sigprocmask(SIG_BLOCK, &mask, NULL)<0)
	{
		errHandle("sigprocmask error\n");
	}
	sigemptyset(&mask);//don't want to another variable
        //printf("begin to play game now\n");
	if (playerType=='X')
	{
		notify();
	}

	while (!gameOver)
	{
		sigsuspend(&mask);
	}
/*
	do	
	{
		//printf("player %d is waiting , playerType=%c\n", getpid(), playerType);
		//readBoard(0);
		//pause();
		//printBoard(0);
	}
	while (!gameOver);
*/

}
		
void writeBoard(int index)
{
        if (lseek(fd, BoardPos+index, SEEK_SET)<0)
        {
		printf("the argument is %d\n", BoardPos+index);
                errHandle("lseek error when write board\n");
        }
	if (write(fd, &playerType, 1)!=1)
	{
		errHandle("write error\n");
	}
} 

void readBoard()
{
	if (lseek(fd, BoardPos, SEEK_SET)<0)
	{
		errHandle("lseek error when read board\n");
	}
	if (read(fd, board, BoardLength)!=BoardLength)
	{
		errHandle("read error when reading board\n");
	}
}


//read necessary info, this moment, parent has set up game data
//X player must finish first write
//this handler maybe executed twice by PlayerO, but it doesn't matter since 
//there is only reading operations.
//playerO will not receive notice from parent!!!
void beginPlay(int no)
{
	char buf[PlayerPidLength+1];
        int pos;
	int offset;

	if (playerType=='X')
	{
		offset=PlayerO_Pos;
	}
	else
	{
		offset=PlayerX_Pos;
	}
	//printf("fd is %d\n", fd);
	if (lseek(fd, offset, SEEK_SET)<0)
	{
		errHandle("seek error when begin play\n");
	}
	if (read(fd, buf, PlayerPidLength)!=PlayerPidLength)
	{
		errHandle("read error\n");
	}
	//the pid must be written with '\0' padded
	opponent=atoi(buf);
	//printf("opponent is %d\n", opponent);

        //the very first move done by X
        if (playerType=='X')
        {        
                //write first
                pos=rand()%9;
		//printf("pos=%d\n", pos);
                writeBoard(pos);
               // notify();
        }
	//printf("game start at beginplay of %c\n", playerType);
	//the later the safer!
/*
	if (playerType=='O')
	{
		gameStart=TRUE;
	}
*/
}


void initialize()
{
	//only playerX needs notice from parent
	//both player might be winner, so, still we need to set up handler
	if (signal(SIGUSR1, gameHandler)==SIG_ERR)
	{
		errHandle("signal error\n");
	}
	//they both need to notify each other by SIGUSR2
	if (signal(SIGUSR2, tictactoe)==SIG_ERR)
	{
		errHandle("signal error\n");
	}
	srand(getpid());
}
				

		
file name: errHandle.c
#include "myhead.h"


void errHandle(char* msg)
{
	if (msg!=NULL)
	{
		perror(msg);
	}
	else
	{
		strerror(errno);
	}		
	exit(errno);
}
	
file name: makefile
all:    main.exe player.exe
	@echo "make complete for main.exe "
	cp -f *.c ./labbackup/
	cp -f *.h ./labbackup/
	rm -f XXXX*
errHandle.o : errHandle.c myhead.h
	@echo "compiling errHanle module..."
	gcc -g -c errHandle.c -o errHandle.o

main.exe : main.c errHandle.o myhead.h 
	@echo "compiling main.o..." 
	gcc -g main.c errHandle.o -o main.exe

player.exe : player.c myhead.h errHandle.o
	@echo "compiling player.exe..."
	gcc -g player.c errHandle.o -o player.exe
clear :
	@echo "clear up...\n"
	rm *.o *.exe
	rm -f XXXX*


¡¡




How to run?
1. You need the file name as argument in command line
[qingz_hu@alamanni ~/lab1] ./main.exe 2000
Don't try that unless you are patient enough for waiting five minutes even though I did try that!




                                 back.gif (341 bytes)       up.gif (335 bytes)         next.gif (337 bytes)