299 lines
6.8 KiB
C
Executable file
299 lines
6.8 KiB
C
Executable file
/* tic tac toe (noughts and crosses) Author: Warren Toomey */
|
|
|
|
/* Copyright 1988 by Warren Toomey wkt@cs.adfa.oz.au[@uunet.uu.net]
|
|
*
|
|
* You may freely copy or distribute this code as long as this notice
|
|
* remains intact.
|
|
*
|
|
* You may modify this code, as long as this notice remains intact, and
|
|
* you add another notice indicating that the code has been modified.
|
|
*
|
|
* You may NOT sell this code or in any way profit from this code without
|
|
* prior agreement from the author.
|
|
*/
|
|
|
|
/* Compile with cc -o tic tic.c -lcurses -ltermcap */
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#ifdef CURSES
|
|
#include <curses.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifndef CURSES
|
|
#define printw printf
|
|
#endif
|
|
|
|
|
|
typedef struct {
|
|
int value; /* The move returned by the */
|
|
int path; /* alphabeta consists of a value */
|
|
} MOVE; /* and an actual move (path) */
|
|
|
|
_PROTOTYPE(int main, (void));
|
|
_PROTOTYPE(int stateval, (int board [], int whosemove));
|
|
_PROTOTYPE(MOVE alphabeta, (int board [], int whosemove, int alpha, int beta));
|
|
_PROTOTYPE(void draw, (int board []));
|
|
_PROTOTYPE(void getmove, (int board []));
|
|
_PROTOTYPE(int endofgame, (int board []));
|
|
_PROTOTYPE(int randommove, (void));
|
|
|
|
/* Static evaluator. Returns 100 if we have 3 in a row -100 if they have 3
|
|
* in a row
|
|
*
|
|
* Board is array of 9 ints, where 0=empty square 1=our move 4= their move
|
|
*
|
|
* and board is indices 0 1 2 3 4 5 6 7 8 */
|
|
|
|
|
|
int stateval(board, whosemove)
|
|
int board[];
|
|
int whosemove;
|
|
{
|
|
static int row[8][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, /* Indices of 3in-a-rows */
|
|
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
|
|
{0, 4, 8}, {2, 4, 6}};
|
|
|
|
int temp; /* Temp row results */
|
|
int i, j; /* Loop counters */
|
|
int side; /* Depth multiplier */
|
|
int win, lose;
|
|
|
|
if (whosemove == 1) {
|
|
win = 100;
|
|
lose = -100;
|
|
side = 1;
|
|
} else {
|
|
/* Multiply by -1 if */
|
|
win = -100;
|
|
lose = 100;
|
|
side = -1;
|
|
} /* not out move */
|
|
for (i = 0; i < 8; i++) { /* For every 3-in-a-row */
|
|
temp = 0;
|
|
for (j = 0; j < 3; j++) /* Add up the board values */
|
|
temp += board[row[i][j]];
|
|
|
|
if (temp == 3) return(win); /* We've got 3 in a row */
|
|
if (temp == 12) return (lose); /* They've got 3 in a row */
|
|
}
|
|
return(0); /* Finally return sum */
|
|
}
|
|
|
|
|
|
MOVE alphabeta(board, whosemove, alpha, beta) /* Alphabeta: takes a board, */
|
|
int board[]; /* whose move, alpha & beta cutoffs, */
|
|
int whosemove; /* and returns a move to make and */
|
|
int alpha; /* the value that the move has */
|
|
int beta;
|
|
{
|
|
MOVE result, successor;
|
|
int best_score, i, best_path, mademove;
|
|
|
|
result.value = stateval(board, whosemove); /* Work out the board's */
|
|
/* Static value */
|
|
if ((result.value == 100) || /* If a win or loss already */
|
|
(result.value == -100))
|
|
return(result); /* return the result */
|
|
|
|
best_score = beta; /* Ok, set worst score */
|
|
mademove = 0; /* to the beta cutoff */
|
|
for (i = 0; i < 9; i++) {
|
|
if (board[i] == 0) { /* For all valid moves */
|
|
mademove = 1;
|
|
board[i] = whosemove; /* make the move on board */
|
|
successor = alphabeta(board, 5 - whosemove, -best_score - 1, -alpha - 1);
|
|
/* Get value of the move */
|
|
board[i] = 0; /* Take move back */
|
|
if (-successor.value > best_score) { /* If a better score */
|
|
best_score = -successor.value; /* update our score */
|
|
best_path = i; /* and move */
|
|
if (best_score > alpha)
|
|
break; /* If we've beaten alpha */
|
|
} /* return immediately */
|
|
}
|
|
}
|
|
if (mademove) {
|
|
result.value = best_score; /* Finally return best score */
|
|
result.path = best_path;/* and best move */
|
|
}
|
|
return(result); /* If no move, return static result */
|
|
}
|
|
|
|
|
|
void draw(board) /* Draw the board */
|
|
int board[];
|
|
{
|
|
int i, j, row;
|
|
static char out[] = " X O"; /* Lookup table for character */
|
|
|
|
row = 6;
|
|
#ifdef CURSES
|
|
move(row, 0);
|
|
#endif
|
|
for (j = 0; j < 9; j += 3) {
|
|
printw(" %d | %d | %d ", j, j + 1, j + 2);
|
|
for (i = 0; i < 3; i++) {
|
|
printw("%c ", out[board[j + i]]);
|
|
if (i < 2) printw("| ");
|
|
}
|
|
if (j < 4) {
|
|
#ifdef CURSES
|
|
move(++row, 0);
|
|
#else
|
|
printw("\n");
|
|
#endif
|
|
printw("---+---+--- ---+---+---");
|
|
}
|
|
#ifdef CURSES
|
|
move(++row, 0);
|
|
#else
|
|
printw("\n");
|
|
#endif
|
|
}
|
|
#ifdef CURSES
|
|
refresh();
|
|
#else
|
|
printw("\n");
|
|
#endif
|
|
}
|
|
|
|
|
|
void getmove(board) /* Get a player's move */
|
|
int board[];
|
|
{
|
|
int Move;
|
|
int ItemsRead;
|
|
char dumc;
|
|
|
|
do {
|
|
do {
|
|
#ifdef CURSES
|
|
move(9, 40);
|
|
printw("Your move: "); /* Prompt for move */
|
|
refresh();
|
|
#else
|
|
printw("Your move: "); /* Prompt for move */
|
|
#endif
|
|
ItemsRead = scanf("%d", &Move); /* Input the move */
|
|
if (ItemsRead == 0) scanf("%c", &dumc); /* Remove the offending character */
|
|
}
|
|
while (ItemsRead != 1);
|
|
}
|
|
while (board[Move]);
|
|
board[Move] = 4; /* If legal, add to board */
|
|
draw(board); /* Draw the board */
|
|
}
|
|
|
|
|
|
int endofgame(board) /* Determine end of the game */
|
|
int board[];
|
|
{
|
|
int eval;
|
|
int count;
|
|
|
|
eval = stateval(board, 1);
|
|
#ifdef CURSES
|
|
move(20, 25);
|
|
#endif
|
|
if (eval == 100) {
|
|
printw("I have beaten you.\n");
|
|
return(1);
|
|
}
|
|
if (eval == -100) {
|
|
printw("Bus error (core dumped)\n");
|
|
return(1);
|
|
}
|
|
count = 0;
|
|
for (eval = 0; eval < 9; eval++)
|
|
if (board[eval] != 0) count++;
|
|
if (count == 9) {
|
|
printw("A draw!\n");
|
|
return(1);
|
|
}
|
|
#ifdef CURSES
|
|
refresh();
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
|
|
int randommove()
|
|
{ /* Make an initial random move */
|
|
int i;
|
|
|
|
i = abs((int) time((long *) 0));
|
|
return(i % 9);
|
|
}
|
|
|
|
|
|
int main()
|
|
{ /* The actual game */
|
|
int i, board[9];
|
|
char ch;
|
|
MOVE ourmove;
|
|
|
|
for (i = 0; i < 9; i++) board[i] = 0; /* Initialise the board */
|
|
#ifdef CURSES
|
|
initscr();
|
|
clear();
|
|
refresh();
|
|
#endif
|
|
printw(" TIC TAC TOE \n\n");
|
|
printw(" Your moves are 'O'\n");
|
|
printw(" My moves are 'X'\n\n");
|
|
#ifdef CURSES
|
|
move(5, 0);
|
|
printw("Do you wish to move first: ");
|
|
refresh();
|
|
while (scanf("%c", &ch) != 1);
|
|
move(5, 0);
|
|
printw(" ......."); /* Kludge to get rid */
|
|
refresh();
|
|
move(5, 0);
|
|
printw(" "); /* of input letter */
|
|
refresh();
|
|
#else
|
|
do
|
|
printw("Do you wish to move first: ");
|
|
while (scanf("%c", &ch) != 1);
|
|
#endif
|
|
if ((ch != 'y') && (ch != 'Y')) {
|
|
i = randommove(); /* If we move first */
|
|
board[i] = 1; /* make it random */
|
|
#ifdef CURSES
|
|
move(7, 42);
|
|
printw("My move: %d\n", i);
|
|
refresh();
|
|
#else
|
|
printw("My move: %d\n", i);
|
|
#endif
|
|
}
|
|
draw(board);
|
|
getmove(board);
|
|
|
|
while (1) {
|
|
ourmove = alphabeta(board, 1, 99, -99); /* Get a move for us;
|
|
* return wins */
|
|
/* Immediately & ignore losses */
|
|
board[ourmove.path] = 1;/* and make it */
|
|
#ifdef CURSES
|
|
move(7, 42);
|
|
printw("My move: %d\n", ourmove.path);
|
|
refresh();
|
|
#else
|
|
printw("My move: %d\n", ourmove.path);
|
|
#endif
|
|
draw(board);
|
|
if (endofgame(board)) break; /* If end of game, exit */
|
|
getmove(board); /* Get opponent's move */
|
|
if (endofgame(board)) break; /* If end of game, exit */
|
|
}
|
|
#ifdef CURSES
|
|
endwin();
|
|
#endif
|
|
return(0);
|
|
}
|