739 lines
16 KiB
C
739 lines
16 KiB
C
/* 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);
|
|
}
|