740 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			740 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* gomoku - 5 in a row game		Author: ? */
 | |
| 
 | |
| /* This program plays a very old Japanese game called GO-MOKU,
 | |
|    perhaps better known as  5-in-line.   The game is played on
 | |
|    a board with 19 x 19 squares, and the object of the game is
 | |
|    to get 5 stones in a row.
 | |
| */
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <curses.h>
 | |
| #include <ctype.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| /* Size of the board */
 | |
| #define SIZE 19
 | |
| 
 | |
| /* Importance of attack (1..16) */
 | |
| #define AttackFactor 4
 | |
| 
 | |
| /* Value of having 0, 1,2,3,4 or 5 pieces in line */
 | |
| int Weight[7] = {0, 0, 4, 20, 100, 500, 0};
 | |
| 
 | |
| #define Null 0
 | |
| #define Horiz 1
 | |
| #define DownLeft 2
 | |
| #define DownRight 3
 | |
| #define Vert 4
 | |
| 
 | |
| /* The two players */
 | |
| #define Empty 0
 | |
| #define Cross 1
 | |
| #define Nought 2
 | |
| 
 | |
| char PieceChar[Nought + 1] = {' ', 'X', '0'};
 | |
| 
 | |
| int Board[SIZE + 1][SIZE + 1];/* The board */
 | |
| int Player;			/* The player whose move is next */
 | |
| int TotalLines;			/* The number of Empty lines left */
 | |
| int GameWon;			/* Set if one of the players has won */
 | |
| 
 | |
| int Line[4][SIZE + 1][SIZE + 1][Nought + 1];
 | |
| 
 | |
| /* Value of each square for each player */
 | |
| int Value[SIZE + 1][SIZE + 1][Nought + 1];
 | |
| 
 | |
| int X, Y;			/* Move coordinates */
 | |
| char Command;			/* Command from keyboard */
 | |
| int AutoPlay = FALSE;		/* The program plays against itself */
 | |
| 
 | |
| _PROTOTYPE(void Initialize, (void));
 | |
| _PROTOTYPE(int Abort, (char *s));
 | |
| _PROTOTYPE(void WriteLetters, (void));
 | |
| _PROTOTYPE(void WriteLine, (int j, int *s));
 | |
| _PROTOTYPE(void WriteBoard, (int N, int *Top, int *Middle, int *Bottom));
 | |
| _PROTOTYPE(void SetUpScreen, (void));
 | |
| _PROTOTYPE(void GotoSquare, (int x, int y));
 | |
| _PROTOTYPE(void PrintMove, (int Piece, int X, int Y));
 | |
| _PROTOTYPE(void ClearMove, (void));
 | |
| _PROTOTYPE(void PrintMsg, (char *Str));
 | |
| _PROTOTYPE(void ClearMsg, (void));
 | |
| _PROTOTYPE(void WriteCommand, (char *S));
 | |
| _PROTOTYPE(void ResetGame, (int FirstGame));
 | |
| _PROTOTYPE(int OpponentColor, (int Player));
 | |
| _PROTOTYPE(void BlinkRow, (int X, int Y, int Dx, int Dy, int Piece));
 | |
| _PROTOTYPE(void BlinkWinner, (int Piece, int X, int Y, int WinningLine));
 | |
| _PROTOTYPE(int Random, (int x));
 | |
| _PROTOTYPE(void Add, (int *Num));
 | |
| _PROTOTYPE(void Update, (int Lin[], int Valu[], int Opponent));
 | |
| _PROTOTYPE(void MakeMove, (int X, int Y));
 | |
| _PROTOTYPE(int GameOver, (void));
 | |
| _PROTOTYPE(void FindMove, (int *X, int *Y));
 | |
| _PROTOTYPE(char GetChar, (void));
 | |
| _PROTOTYPE(void ReadCommand, (int X, int Y, char *Command));
 | |
| _PROTOTYPE(void InterpretCommand, (int Command));
 | |
| _PROTOTYPE(void PlayerMove, (void));
 | |
| _PROTOTYPE(void ProgramMove, (void));
 | |
| _PROTOTYPE(int main, (void));
 | |
| 
 | |
| /* Set terminal to raw mode. */
 | |
| void Initialize()
 | |
| {
 | |
|   srand(getpid() + 13);		/* Initialize the random seed with our pid */
 | |
|   initscr();
 | |
|   raw();
 | |
|   noecho();
 | |
|   clear();
 | |
| }
 | |
| 
 | |
| /* Reset terminal and exit from the program. */
 | |
| int Abort(s)
 | |
| char *s;
 | |
| {
 | |
|   move(LINES - 1, 0);
 | |
|   refresh();
 | |
|   endwin();
 | |
|   exit(0);
 | |
| }
 | |
| 
 | |
| /* Set up the screen ----------------------------------------------- */
 | |
| 
 | |
| /* Write the letters */
 | |
| void WriteLetters()
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   addch(' ');
 | |
|   addch(' ');
 | |
|   for (i = 1; i <= SIZE; i++) printw(" %c", 'A' + i - 1);
 | |
|   addch('\n');
 | |
| }
 | |
| 
 | |
| /* Write one line of the board */
 | |
| void WriteLine(j, s)
 | |
| int j;
 | |
| int *s;
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   printw("%2d ", j);
 | |
|   addch(s[0]);
 | |
|   for (i = 2; i <= SIZE - 1; i++) {
 | |
| 	addch(s[1]);
 | |
| 	addch(s[2]);
 | |
|   }
 | |
|   addch(s[1]);
 | |
|   addch(s[3]);
 | |
|   printw(" %-2d\n", j);
 | |
| }
 | |
| 
 | |
| /* Print the Empty board and the border */
 | |
| void WriteBoard(N, Top, Middle, Bottom)
 | |
| int N;
 | |
| int *Top, *Middle, *Bottom;
 | |
| {
 | |
|   int j;
 | |
| 
 | |
|   move(1, 0);
 | |
|   WriteLetters();
 | |
|   WriteLine(N, Top);
 | |
|   for (j = N - 1; j >= 2; j--) WriteLine(j, Middle);
 | |
|   WriteLine(1, Bottom);
 | |
|   WriteLetters();
 | |
| }
 | |
| 
 | |
| /* Sets up the screen with an Empty board */
 | |
| void SetUpScreen()
 | |
| {
 | |
|   int top[4], middle[4], bottom[4];
 | |
| 
 | |
|   top[0] = ACS_ULCORNER;
 | |
|   top[1] = ACS_HLINE;
 | |
|   top[2] = ACS_TTEE;
 | |
|   top[3] = ACS_URCORNER;
 | |
| 
 | |
|   middle[0] = ACS_LTEE;
 | |
|   middle[1] = ACS_HLINE;
 | |
|   middle[2] = ACS_PLUS;
 | |
|   middle[3] = ACS_RTEE;
 | |
| 
 | |
|   bottom[0] = ACS_LLCORNER;
 | |
|   bottom[1] = ACS_HLINE;
 | |
|   bottom[2] = ACS_BTEE;
 | |
|   bottom[3] = ACS_LRCORNER;
 | |
| 
 | |
|   WriteBoard(SIZE, top, middle, bottom);
 | |
| }
 | |
| 
 | |
| /* Show moves ----------------------------------------------- */
 | |
| 
 | |
| void GotoSquare(x, y)
 | |
| int x, y;
 | |
| {
 | |
|   move(SIZE + 2 - y, 1 + x * 2);
 | |
| }
 | |
| 
 | |
| /* Prints a move */
 | |
| void PrintMove(Piece, X, Y)
 | |
| int Piece;
 | |
| int X, Y;
 | |
| {
 | |
|   move(22, 49);
 | |
|   printw("%c %c %d", PieceChar[Piece], 'A' + X - 1, Y);
 | |
|   clrtoeol();
 | |
|   GotoSquare(X, Y);
 | |
|   addch(PieceChar[Piece]);
 | |
|   GotoSquare(X, Y);
 | |
|   refresh();
 | |
| }
 | |
| 
 | |
| /* Clears the line where a move is displayed */
 | |
| void ClearMove()
 | |
| {
 | |
|   move(22, 49);
 | |
|   clrtoeol();
 | |
| }
 | |
| 
 | |
| /* Message handling ---------------------------------------------- */
 | |
| 
 | |
| /* Prints a message */
 | |
| void PrintMsg(Str)
 | |
| char *Str;
 | |
| {
 | |
|   mvprintw(23, 1, "%s", Str);
 | |
| }
 | |
| 
 | |
| /* Clears the message about the winner */
 | |
| void ClearMsg()
 | |
| {
 | |
|   move(23, 1);
 | |
|   clrtoeol();
 | |
| }
 | |
| 
 | |
| /* Highlights the first letter of S */
 | |
| void WriteCommand(S)
 | |
| char *S;
 | |
| {
 | |
|   standout();
 | |
|   addch(*S);
 | |
|   standend();
 | |
|   printw("%s", S + 1);
 | |
| }
 | |
| 
 | |
| /* Display the board ----------------------------------------------- */
 | |
| 
 | |
| /* Resets global variables to start a new game */
 | |
| void ResetGame(FirstGame)
 | |
| int FirstGame;
 | |
| {
 | |
|   int I, J;
 | |
|   int C, D;
 | |
| 
 | |
|   SetUpScreen();
 | |
|   if (FirstGame) {
 | |
| 	move(1, 49);
 | |
| 	addstr("G O M O K U");
 | |
| 	move(3, 49);
 | |
| 	WriteCommand("Newgame    ");
 | |
| 	WriteCommand("Quit ");
 | |
| 	move(5, 49);
 | |
| 	WriteCommand("Auto");
 | |
| 	move(7, 49);
 | |
| 	WriteCommand("Play");
 | |
| 	move(9, 49);
 | |
| 	WriteCommand("Hint");
 | |
| 	move(14, 60);
 | |
| 	WriteCommand("Left, ");
 | |
| 	WriteCommand("Right, ");
 | |
| 	move(16, 60);
 | |
| 	WriteCommand("Up, ");
 | |
| 	WriteCommand("Down");
 | |
| 	move(18, 60);
 | |
| 	standout();
 | |
| 	addstr("SPACE");
 | |
| 	move(20, 49);
 | |
| 	WriteCommand(" NOTE: Use Num Lock & arrows");
 | |
| 	standend();
 | |
| 	mvaddstr(14, 49, "7  8  9");
 | |
| 	mvaddch(15, 52, ACS_UARROW);
 | |
| 	mvaddch(16, 49, '4');
 | |
| 	addch(ACS_LARROW);
 | |
| 	mvaddch(16, 54, ACS_RARROW);
 | |
| 	addch('6');
 | |
| 	mvaddch(17, 52, ACS_DARROW);
 | |
| 	mvaddstr(18, 49, "1  2  3");
 | |
| 	FirstGame = FALSE;
 | |
|   } else {
 | |
| 	ClearMsg();
 | |
| 	ClearMove();
 | |
|   }
 | |
| 
 | |
|   /* Clear tables */
 | |
|   for (I = 1; I <= SIZE; I++) for (J = 1; J <= SIZE; J++) {
 | |
| 		Board[I][J] = Empty;
 | |
| 		for (C = Cross; C <= Nought; C++) {
 | |
| 			Value[I][J][C] = 0;
 | |
| 			for (D = 0; D <= 3; D++) Line[D][I][J][C] = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
|   /* Cross starts */
 | |
|   Player = Cross;
 | |
|   /* Total number of lines */
 | |
|   TotalLines = 2 * 2 * (SIZE * (SIZE - 4) + (SIZE - 4) * (SIZE - 4));
 | |
|   GameWon = FALSE;
 | |
| }
 | |
| 
 | |
| int OpponentColor(Player)
 | |
| int Player;
 | |
| {
 | |
|   if (Player == Cross)
 | |
| 	return Nought;
 | |
|   else
 | |
| 	return Cross;
 | |
| }
 | |
| 
 | |
| /* Blink the row of 5 stones */
 | |
| void BlinkRow(X, Y, Dx, Dy, Piece)
 | |
| int X, Y, Dx, Dy, Piece;
 | |
| {
 | |
|   int I;
 | |
| 
 | |
|   attron(A_BLINK);
 | |
|   for (I = 1; I <= 5; I++) {
 | |
| 	GotoSquare(X, Y);
 | |
| 	addch(PieceChar[Piece]);
 | |
| 	X = X - Dx;
 | |
| 	Y = Y - Dy;
 | |
|   }
 | |
|   attroff(A_BLINK);
 | |
| }
 | |
| 
 | |
| /* Prints the 5 winning stones in blinking color */
 | |
| void BlinkWinner(Piece, X, Y, WinningLine)
 | |
| int Piece, X, Y, WinningLine;
 | |
| {
 | |
|   /* Used to store the position of the winning move */
 | |
|   int XHold, YHold;
 | |
|   /* Change in X and Y */
 | |
|   int Dx, Dy;
 | |
| 
 | |
|   /* Display winning move */
 | |
|   PrintMove(Piece, X, Y);
 | |
|   /* Preserve winning position */
 | |
|   XHold = X;
 | |
|   YHold = Y;
 | |
|   switch (WinningLine) {
 | |
|       case Horiz:
 | |
| 	{
 | |
| 		Dx = 1;
 | |
| 		Dy = 0;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
|       case DownLeft:
 | |
| 	{
 | |
| 		Dx = 1;
 | |
| 		Dy = 1;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
|       case Vert:
 | |
| 	{
 | |
| 		Dx = 0;
 | |
| 		Dy = 1;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
|       case DownRight:
 | |
| 	{
 | |
| 		Dx = -1;
 | |
| 		Dy = 1;
 | |
| 		break;
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   /* Go to topmost, leftmost */
 | |
|   while (Board[X + Dx][Y + Dy] != Empty && Board[X + Dx][Y + Dy] == Piece) {
 | |
| 	X = X + Dx;
 | |
| 	Y = Y + Dy;
 | |
|   }
 | |
|   BlinkRow(X, Y, Dx, Dy, Piece);
 | |
|   /* Restore winning position */
 | |
|   X = XHold;
 | |
|   Y = YHold;
 | |
|   /* Go back to winning square */
 | |
|   GotoSquare(X, Y);
 | |
| }
 | |
| 
 | |
| /* Functions for playing a game -------------------------------- */
 | |
| 
 | |
| int Random(x)
 | |
| int x;
 | |
| {
 | |
|   return((rand() / 19) % x);
 | |
| }
 | |
| 
 | |
| /* Adds one to the number of pieces in a line */
 | |
| void Add(Num)
 | |
| int *Num;
 | |
| {
 | |
|   /* Adds one to the number.     */
 | |
|   *Num = *Num + 1;
 | |
|   /* If it is the first piece in the line, then the opponent cannot use
 | |
|    * it any more.  */
 | |
|   if (*Num == 1) TotalLines = TotalLines - 1;
 | |
|   /* The game is won if there are 5 in line. */
 | |
|   if (*Num == 5) GameWon = TRUE;
 | |
| }
 | |
| 
 | |
| /* Updates the value of a square for each player, taking into
 | |
|    account that player has placed an extra piece in the square.
 | |
|    The value of a square in a usable line is Weight[Lin[Player]+1]
 | |
|    where Lin[Player] is the number of pieces already placed
 | |
| in the line */
 | |
| void Update(Lin, Valu, Opponent)
 | |
| int Lin[];
 | |
| int Valu[];
 | |
| int Opponent;
 | |
| {
 | |
|   /* If the opponent has no pieces in the line, then simply update the
 | |
|    * value for player */
 | |
|   if (Lin[Opponent] == 0)
 | |
| 	Valu[Player] += Weight[Lin[Player] + 1] - Weight[Lin[Player]];
 | |
|   else
 | |
| 	/* If it is the first piece in the line, then the line is
 | |
| 	 * spoiled for the opponent */
 | |
|   if (Lin[Player] == 1) Valu[Opponent] -= Weight[Lin[Opponent] + 1];
 | |
| }
 | |
| 
 | |
| /* Performs the move X,Y for player, and updates the global variables
 | |
| (Board, Line, Value, Player, GameWon, TotalLines and the screen) */
 | |
| void MakeMove(X, Y)
 | |
| int X, Y;
 | |
| {
 | |
|   int Opponent;
 | |
|   int X1, Y1;
 | |
|   int K, L, WinningLine;
 | |
| 
 | |
|   WinningLine = Null;
 | |
|   Opponent = OpponentColor(Player);
 | |
|   GameWon = FALSE;
 | |
| 
 | |
|   /* Each square of the board is part of 20 different lines. The adds
 | |
|    * one to the number of pieces in each of these lines. Then it
 | |
|    * updates the value for each of the 5 squares in each of the 20
 | |
|    * lines. Finally Board is updated, and the move is printed on the
 | |
|    * screen. */
 | |
| 
 | |
|   /* Horizontal lines, from left to right */
 | |
|   for (K = 0; K <= 4; K++) {
 | |
| 	X1 = X - K;		/* Calculate starting point */
 | |
| 	Y1 = Y;
 | |
| 	if ((1 <= X1) && (X1 <= SIZE - 4)) {	/* Check starting point */
 | |
| 		Add(&Line[0][X1][Y1][Player]);	/* Add one to line */
 | |
| 		if (GameWon && (WinningLine == Null))	/* Save winning line */
 | |
| 			WinningLine = Horiz;
 | |
| 		for (L = 0; L <= 4; L++)	/* Update value for the
 | |
| 						 * 5 squares in the line */
 | |
| 			Update(Line[0][X1][Y1], Value[X1 + L][Y1], Opponent);
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   for (K = 0; K <= 4; K++) {	/* Diagonal lines, from lower left to
 | |
| 				 * upper right */
 | |
| 	X1 = X - K;
 | |
| 	Y1 = Y - K;
 | |
| 	if ((1 <= X1) && (X1 <= SIZE - 4) &&
 | |
| 	    (1 <= Y1) && (Y1 <= SIZE - 4)) {
 | |
| 		Add(&Line[1][X1][Y1][Player]);
 | |
| 		if (GameWon && (WinningLine == Null))	/* Save winning line */
 | |
| 			WinningLine = DownLeft;
 | |
| 		for (L = 0; L <= 4; L++)
 | |
| 			Update(Line[1][X1][Y1], Value[X1 + L][Y1 + L], Opponent);
 | |
| 	}
 | |
|   }				/* for */
 | |
| 
 | |
|   for (K = 0; K <= 4; K++) {	/* Diagonal lines, down right to upper left */
 | |
| 	X1 = X + K;
 | |
| 	Y1 = Y - K;
 | |
| 	if ((5 <= X1) && (X1 <= SIZE) &&
 | |
| 	    (1 <= Y1) && (Y1 <= SIZE - 4)) {
 | |
| 		Add(&Line[3][X1][Y1][Player]);
 | |
| 		if (GameWon && (WinningLine == Null))	/* Save winning line */
 | |
| 			WinningLine = DownRight;
 | |
| 		for (L = 0; L <= 4; L++)
 | |
| 			Update(Line[3][X1][Y1], Value[X1 - L][Y1 + L], Opponent);
 | |
| 	}
 | |
|   }				/* for */
 | |
| 
 | |
|   for (K = 0; K <= 4; K++) {	/* Vertical lines, from down to up */
 | |
| 	X1 = X;
 | |
| 	Y1 = Y - K;
 | |
| 	if ((1 <= Y1) && (Y1 <= SIZE - 4)) {
 | |
| 		Add(&Line[2][X1][Y1][Player]);
 | |
| 		if (GameWon && (WinningLine == Null))	/* Save winning line */
 | |
| 			WinningLine = Vert;
 | |
| 		for (L = 0; L <= 4; L++)
 | |
| 			Update(Line[2][X1][Y1], Value[X1][Y1 + L], Opponent);
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   Board[X][Y] = Player;		/* Place piece in board */
 | |
|   if (GameWon)
 | |
| 	BlinkWinner(Player, X, Y, WinningLine);
 | |
|   else
 | |
| 	PrintMove(Player, X, Y);/* Print move on screen */
 | |
|   Player = Opponent;		/* The opponent is next to move */
 | |
| }
 | |
| 
 | |
| int GameOver()
 | |
| /* A game is over if one of the players have
 | |
| won, or if there are no more Empty lines */
 | |
| {
 | |
|   return(GameWon || (TotalLines <= 0));
 | |
| }
 | |
| 
 | |
| /* Finds a move X,Y for player, simply by picking the one with the
 | |
| highest value */
 | |
| void FindMove(X, Y)
 | |
| int *X, *Y;
 | |
| {
 | |
|   int Opponent;
 | |
|   int I, J;
 | |
|   int Max, Valu;
 | |
| 
 | |
|   Opponent = OpponentColor(Player);
 | |
|   Max = -10000;
 | |
|   /* If no square has a high value then pick the one in the middle */
 | |
|   *X = (SIZE + 1) / 2;
 | |
|   *Y = (SIZE + 1) / 2;
 | |
|   if (Board[*X][*Y] == Empty) Max = 4;
 | |
|   /* The evaluation for a square is simply the value of the square for
 | |
|    * the player (attack points) plus the value for the opponent
 | |
|    * (defense points). Attack is more important than defense, since it
 | |
|    * is better to get 5 in line yourself than to prevent the op- ponent
 | |
|    * from getting it. */
 | |
| 
 | |
|   /* For all Empty squares */
 | |
|   for (I = 1; I <= SIZE; I++) for (J = 1; J <= SIZE; J++)
 | |
| 		if (Board[I][J] == Empty) {
 | |
| 			/* Calculate evaluation */
 | |
| 			Valu = Value[I][J][Player] * (16 + AttackFactor) / 16 + Value[I][J][Opponent] + Random(4);
 | |
| 			/* Pick move with highest value */
 | |
| 			if (Valu > Max) {
 | |
| 				*X = I;
 | |
| 				*Y = J;
 | |
| 				Max = Valu;
 | |
| 			}
 | |
| 		}
 | |
| }
 | |
| 
 | |
| char GetChar()
 | |
| /* Get a character from the keyboard */
 | |
| {
 | |
|   int c;
 | |
| 
 | |
|   c = getch();
 | |
|   if (c < 0) abort();
 | |
|   if (c == '\033') {	/* arrow key */
 | |
| 	if ((c = getch()) == '[') {
 | |
| 		c = getch();
 | |
| 		switch (c) {
 | |
| 		case 'A': c = 'U'; break;
 | |
| 		case 'B': c = 'D'; break;
 | |
| 		case 'C': c = 'R'; break;
 | |
| 		case 'D': c = 'L'; break;
 | |
| 		default:
 | |
| 			c = '?';
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 		c = '?';
 | |
|   }
 | |
|   if (islower(c))
 | |
| 	return toupper(c);
 | |
|   else
 | |
| 	return c;
 | |
| }
 | |
| 
 | |
| /* Reads in a valid command character */
 | |
| void ReadCommand(X, Y, Command)
 | |
| int X, Y;
 | |
| char *Command;
 | |
| {
 | |
|   int ValidCommand;
 | |
| 
 | |
|   do {
 | |
| 	ValidCommand = TRUE;
 | |
| 	GotoSquare(X, Y);	/* Goto square */
 | |
| 	refresh();
 | |
| 	*Command = GetChar();	/* Read from keyboard */
 | |
| 	switch (*Command) {
 | |
| 	    case '\n':		/* '\n', '\r' or space means place a */
 | |
| 	    case '\r':
 | |
| 	    case ' ':
 | |
| 		*Command = 'E';
 | |
| 		break;		/* stone at the cursor position  */
 | |
| 
 | |
| 	    case 'L':
 | |
| 	    case 'R':
 | |
| 	    case 'U':
 | |
| 	    case 'D':
 | |
| 	    case '7':
 | |
| 	    case '9':
 | |
| 	    case '1':
 | |
| 	    case '3':
 | |
| 	    case 'N':
 | |
| 	    case 'Q':
 | |
| 	    case 'A':
 | |
| 	    case 'P':
 | |
| 	    case 'H':
 | |
| 		break;
 | |
| 
 | |
| 	    case '8':	*Command = 'U';	break;
 | |
| 	    case '2':	*Command = 'D';	break;
 | |
| 	    case '4':	*Command = 'L';	break;
 | |
| 	    case '6':	*Command = 'R';	break;
 | |
| 	    default:
 | |
| 		{
 | |
| 			if (GameOver())
 | |
| 				*Command = 'P';
 | |
| 			else
 | |
| 				ValidCommand = FALSE;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
|   } while (!ValidCommand);
 | |
| }
 | |
| 
 | |
| void InterpretCommand(Command)
 | |
| char Command;
 | |
| {
 | |
|   int Temp;
 | |
| 
 | |
|   switch (Command) {
 | |
|       case 'N':{		/* Start new game */
 | |
| 		ResetGame(FALSE);	/* ResetGame but only redraw
 | |
| 					 * the board */
 | |
| 		X = (SIZE + 1) / 2;
 | |
| 		Y = X;
 | |
| 		break;
 | |
| 	}
 | |
|       case 'H':
 | |
| 	FindMove(&X, &Y);
 | |
| 	break;			/* Give the user a hint */
 | |
|       case 'L':
 | |
| 	X = (X + SIZE - 2) % SIZE + 1;
 | |
| 	break;			/* Left  */
 | |
|       case 'R':
 | |
| 	X = X % SIZE + 1;
 | |
| 	break;			/* Right */
 | |
|       case 'D':
 | |
| 	Y = (Y + SIZE - 2) % SIZE + 1;
 | |
| 	break;			/* Down  */
 | |
|       case 'U':
 | |
| 	Y = Y % SIZE + 1;
 | |
| 	break;			/* Up    */
 | |
|       case '7':{
 | |
| 		if ((X == 1) || (Y == SIZE)) {	/* Move diagonally    *//* t
 | |
| 						 * owards upper left */
 | |
| 			Temp = X;
 | |
| 			X = Y;
 | |
| 			Y = Temp;
 | |
| 		} else {
 | |
| 			X = X - 1;
 | |
| 			Y = Y + 1;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
|       case '9':{		/* Move diagonally    */
 | |
| 		if (X == SIZE) {/* toward upper right */
 | |
| 			X = (SIZE - Y) + 1;
 | |
| 			Y = 1;
 | |
| 		} else if (Y == SIZE) {
 | |
| 			Y = (SIZE - X) + 1;
 | |
| 			X = 1;
 | |
| 		} else {
 | |
| 			X = X + 1;
 | |
| 			Y = Y + 1;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
|       case '1':{		/* Move diagonally   */
 | |
| 		if (Y == 1) {	/* toward lower left */
 | |
| 			Y = (SIZE - X) + 1;
 | |
| 			X = SIZE;
 | |
| 		} else if (X == 1) {
 | |
| 			X = (SIZE - Y) + 1;
 | |
| 			Y = SIZE;
 | |
| 		} else {
 | |
| 			X = X - 1;
 | |
| 			Y = Y - 1;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
|       case '3':{		/* Move diagonally    */
 | |
| 		if ((X == SIZE) || (Y == 1)) {	/* toward lower right */
 | |
| 			Temp = X;
 | |
| 			X = Y;
 | |
| 			Y = Temp;
 | |
| 		} else {
 | |
| 			X = X + 1;
 | |
| 			Y = Y - 1;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
|       case 'A':
 | |
| 	AutoPlay = TRUE;
 | |
| 	break;			/* Auto play mode */
 | |
|   }				/* case */
 | |
| }				/* InterpretCommand */
 | |
| 
 | |
| void PlayerMove()
 | |
| /* Enter and make a move */
 | |
| {
 | |
|   if (Board[X][Y] == Empty) {
 | |
| 	MakeMove(X, Y);
 | |
| 	if (GameWon) PrintMsg("Congratulations, You won!");
 | |
| 	Command = 'P';
 | |
|   }
 | |
|   refresh();
 | |
| }				/* PlayerMove */
 | |
| 
 | |
| void ProgramMove()
 | |
| /* Find and perform programs move */
 | |
| {
 | |
|   do {
 | |
| 	if (GameOver()) {
 | |
| 		AutoPlay = FALSE;
 | |
| 		if ((Command != 'Q') && (!GameWon)) PrintMsg("Tie game!");
 | |
| 	} else {
 | |
| 		FindMove(&X, &Y);
 | |
| 		MakeMove(X, Y);
 | |
| 		if (GameWon) PrintMsg("I won!");
 | |
| 	}
 | |
| 	refresh();
 | |
|   } while (AutoPlay);
 | |
| }
 | |
| 
 | |
| int main()
 | |
| {
 | |
|   Initialize();
 | |
|   ResetGame(TRUE);		/* ResetGame and draw the entire screen */
 | |
|   refresh();
 | |
|   X = (SIZE + 1) / 2;		/* Set starting position to */
 | |
|   Y = X;			/* the middle of the board  */
 | |
|   do {
 | |
| 	ReadCommand(X, Y, &Command);
 | |
| 	if (GameOver())
 | |
| 		if (Command != 'Q') Command = 'N';
 | |
| 	InterpretCommand(Command);
 | |
| 	if (Command == 'E') PlayerMove();
 | |
| 	if (Command == 'P' || Command == 'A') ProgramMove();
 | |
|   } while (Command != 'Q');
 | |
|   Abort("Good bye!");
 | |
|   return(0);
 | |
| }
 | 
