ox3d - marinus-lab/z88dk GitHub Wiki
{{:examples:snippets:ox3d.gif|}}
/* OX3D - an aggressive 3D noughts and crosses player. */
/* Bugfixed and compacted - S.Bodrato 2013 */
/* Tidiead up and improved MLA 1997 */
/* Translated to C by MLA 1990 */
/* FORTRAN version developed on Prime by MLA 1986 */
/* Based on an earlier algorithm developed by MLA 1974 - 1981 */
/* This version is dedicated to the proposition that a Real Programmer */
/* can write FORTRAN in *any* language */
/*
* zcc +zx -lndos -create-app -zorg=26500 -O3 -lm -DHELP -o ox3d ox3d.c
* zcc +osca -lndos -oox3d.exe -O3 ox3d.c
*/
#pragma output osca_notimer
#include `<stdio.h>`
#include `<string.h>`
#include `<time.h>`
#include `<ctype.h>`
#include `<stdlib.h>`
//#include `<math.h>`
#define COMSIZ (1817 * sizeof (int) + 128 * sizeof (long))
//#define COMSIZ (1817 * sizeof (char) + 128 * sizeof (long))
char board [64]; /* The playing board */
char cplane [384]; /* Cell plane lists */
int linsco [76]; /* Individual line scores */
char lcells [304]; /* Line cell lists */
char clines [448]; /* Cell line lists */
char clinh [448]; /* Cell line histograms */
int plnsco [18]; /* Individual plane scores */
int player; /* Shows whose move it is */
int sign; /* Stores the sign of a value */
int winner; /* Indicates the winner */
char win [2]; /* Forced win flags */
char kkill [2]; /* Stores forced moves */
int move; /* A move */
int movcnt; /* Move counter */
int strcnt; /* Strategic search depth */
int lincnt; /* Number of lines left */
char strmov [64]; /* Strategic moves */
long tacval [64]; /* Cell tactical values */
char bckup1 [COMSIZ]; /* Backup array for strategic lookahead */
char bckup2 [COMSIZ]; /* Ditto for tactical evaluations */
char bckup3 [COMSIZ]; /* Ditto for move suggestions */
int mute; /* Keep quiet flag */
int ttytyp; /* Terminal type */
int frcpnt; /* Forced move "pointer" */
int frccnt; /* Forced move counter */
int frcmov [64]; /* Forced moves */
int vismov; /* Move in visual representation */
int who [2]; /* Shows who plays which side */
char rwho [2]; /* Shows who started playing which side */
char *plname [2] = /* Player names */
{
"Noughts",
"Crosses"
};
char icplan [384] =
{
0, 4, 8, 12, 14, 16, 0, 5, 8, 14, -1, -1,
0, 6, 8, 14, -1, -1, 0, 7, 8, 13, 14, 17,
0, 4, 9, 12, -1, -1, 0, 5, 9, 16, -1, -1,
0, 6, 9, 17, -1, -1, 0, 7, 9, 13, -1, -1,
0, 4, 10, 12, -1, -1, 0, 5, 10, 17, -1, -1,
0, 6, 10, 16, -1, -1, 0, 7, 10, 13, -1, -1,
0, 4, 11, 12, 15, 17, 0, 5, 11, 15, -1, -1,
0, 6, 11, 15, -1, -1, 0, 7, 11, 13, 15, 16,
1, 4, 8, 16, -1, -1, 1, 5, 8, 12, -1, -1,
1, 6, 8, 13, -1, -1, 1, 7, 8, 17, -1, -1,
1, 4, 9, 14, -1, -1, 1, 5, 9, 12, 14, 16,
1, 6, 9, 13, 14, 17, 1, 7, 9, 14, -1, -1,
1, 4, 10, 15, -1, -1, 1, 5, 10, 12, 15, 17,
1, 6, 10, 13, 15, 16, 1, 7, 10, 15, -1, -1,
1, 4, 11, 17, -1, -1, 1, 5, 11, 12, -1, -1,
1, 6, 11, 13, -1, -1, 1, 7, 11, 16, -1, -1,
2, 4, 8, 16, -1, -1, 2, 5, 8, 13, -1, -1,
2, 6, 8, 12, -1, -1, 2, 7, 8, 17, -1, -1,
2, 4, 9, 15, -1, -1, 2, 5, 9, 13, 15, 16,
2, 6, 9, 12, 15, 17, 2, 7, 9, 15, -1, -1,
2, 4, 10, 14, -1, -1, 2, 5, 10, 13, 14, 17,
2, 6, 10, 12, 14, 16, 2, 7, 10, 14, -1, -1,
2, 4, 11, 17, -1, -1, 2, 5, 11, 13, -1, -1,
2, 6, 11, 12, -1, -1, 2, 7, 11, 16, -1, -1,
3, 4, 8, 13, 15, 16, 3, 5, 8, 15, -1, -1,
3, 6, 8, 15, -1, -1, 3, 7, 8, 12, 15, 17,
3, 4, 9, 13, -1, -1, 3, 5, 9, 16, -1, -1,
3, 6, 9, 17, -1, -1, 3, 7, 9, 12, -1, -1,
3, 4, 10, 13, -1, -1, 3, 5, 10, 17, -1, -1,
3, 6, 10, 16, -1, -1, 3, 7, 10, 12, -1, -1,
3, 4, 11, 13, 14, 17, 3, 5, 11, 14, -1, -1,
3, 6, 11, 14, -1, -1, 3, 7, 11, 12, 14, 16
};
char ilcell [304] =
{
0, 1, 2, 3, 0, 4, 8, 12,
0, 5, 10, 15, 0, 16, 32, 48,
0, 17, 34, 51, 0, 20, 40, 60,
0, 21, 42, 63, 1, 5, 9, 13,
1, 17, 33, 49, 1, 21, 41, 61,
2, 6, 10, 14, 2, 18, 34, 50,
2, 22, 42, 62, 3, 6, 9, 12,
3, 7, 11, 15, 3, 18, 33, 48,
3, 19, 35, 51, 3, 22, 41, 60,
3, 23, 43, 63, 4, 5, 6, 7,
4, 20, 36, 52, 4, 21, 38, 55,
5, 21, 37, 53, 6, 22, 38, 54,
7, 22, 37, 52, 7, 23, 39, 55,
8, 9, 10, 11, 8, 24, 40, 56,
8, 25, 42, 59, 9, 25, 41, 57,
10, 26, 42, 58, 11, 26, 41, 56,
11, 27, 43, 59, 12, 13, 14, 15,
12, 24, 36, 48, 12, 25, 38, 51,
12, 28, 44, 60, 12, 29, 46, 63,
13, 25, 37, 49, 13, 29, 45, 61,
14, 26, 38, 50, 14, 30, 46, 62,
15, 26, 37, 48, 15, 27, 39, 51,
15, 30, 45, 60, 15, 31, 47, 63,
16, 17, 18, 19, 16, 20, 24, 28,
16, 21, 26, 31, 17, 21, 25, 29,
18, 22, 26, 30, 19, 22, 25, 28,
19, 23, 27, 31, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 32, 36, 40, 44,
32, 37, 42, 47, 33, 37, 41, 45,
34, 38, 42, 46, 35, 38, 41, 44,
35, 39, 43, 47, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 48, 52, 56, 60,
48, 53, 58, 63, 49, 53, 57, 61,
50, 54, 58, 62, 51, 54, 57, 60,
51, 55, 59, 63, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63
};
char icline [448] =
{
0, 1, 2, 3, 4, 5, 6, 0, 7, 8, 9, -1, -1, -1,
0, 10, 11, 12, -1, -1, -1, 0, 13, 14, 15, 16, 17, 18,
1, 19, 20, 21, -1, -1, -1, 2, 7, 19, 22, -1, -1, -1,
10, 13, 19, 23, -1, -1, -1, 14, 19, 24, 25, -1, -1, -1,
1, 26, 27, 28, -1, -1, -1, 7, 13, 26, 29, -1, -1, -1,
2, 10, 26, 30, -1, -1, -1, 14, 26, 31, 32, -1, -1, -1,
1, 13, 33, 34, 35, 36, 37, 7, 33, 38, 39, -1, -1, -1,
10, 33, 40, 41, -1, -1, -1, 2, 14, 33, 42, 43, 44, 45,
3, 46, 47, 48, -1, -1, -1, 4, 8, 46, 49, -1, -1, -1,
11, 15, 46, 50, -1, -1, -1, 16, 46, 51, 52, -1, -1, -1,
5, 20, 47, 53, -1, -1, -1, 6, 9, 21, 22, 48, 49, 53,
12, 17, 23, 24, 50, 51, 53, 18, 25, 52, 53, -1, -1, -1,
27, 34, 47, 54, -1, -1, -1, 28, 29, 35, 38, 49, 51, 54,
30, 31, 40, 42, 48, 50, 54, 32, 43, 52, 54, -1, -1, -1,
36, 47, 51, 55, -1, -1, -1, 37, 39, 49, 55, -1, -1, -1,
41, 44, 50, 55, -1, -1, -1, 45, 48, 52, 55, -1, -1, -1,
3, 56, 57, 58, -1, -1, -1, 8, 15, 56, 59, -1, -1, -1,
4, 11, 56, 60, -1, -1, -1, 16, 56, 61, 62, -1, -1, -1,
20, 34, 57, 63, -1, -1, -1, 22, 24, 38, 42, 58, 59, 63,
21, 23, 35, 40, 60, 61, 63, 25, 43, 62, 63, -1, -1, -1,
5, 27, 57, 64, -1, -1, -1, 9, 17, 29, 31, 59, 61, 64,
6, 12, 28, 30, 58, 60, 64, 18, 32, 62, 64, -1, -1, -1,
36, 57, 61, 65, -1, -1, -1, 39, 44, 59, 65, -1, -1, -1,
37, 41, 60, 65, -1, -1, -1, 45, 58, 62, 65, -1, -1, -1,
3, 15, 34, 42, 66, 67, 68, 8, 38, 66, 69, -1, -1, -1,
11, 40, 66, 70, -1, -1, -1, 4, 16, 35, 43, 66, 71, 72,
20, 24, 67, 73, -1, -1, -1, 22, 68, 69, 73, -1, -1, -1,
23, 70, 71, 73, -1, -1, -1, 21, 25, 72, 73, -1, -1, -1,
27, 31, 67, 74, -1, -1, -1, 29, 69, 71, 74, -1, -1, -1,
30, 68, 70, 74, -1, -1, -1, 28, 32, 72, 74, -1, -1, -1,
5, 17, 36, 44, 67, 71, 75, 9, 39, 69, 75, -1, -1, -1,
12, 41, 70, 75, -1, -1, -1, 6, 18, 37, 45, 68, 72, 75
};
char iclinh [64] =
{
7, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 4, 7, 4, 4, 7,
4, 4, 4, 4, 4, 7, 7, 4, 4, 7, 7, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 7, 7, 4, 4, 7, 7, 4, 4, 4, 4, 4,
7, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 4, 7, 4, 4, 7
};
/************************************************************************/
/* Display current board. */
void display ()
{
int i, j, k;
int kmax;
char symbol;
for (i = 0; i < 13; i+=4)
{
printf ("\n");
for (j = i; j < 61; j += 16)
{
printf (" ");
kmax = j + 4;
for (k = j; k < kmax; k++)
{
symbol = '.';
if (k == win [0] || k == win [1] ||
k == kkill [0] || k == kkill [1])
symbol = '+';
if (board [k] == -1)
symbol = 'O';
else if (board [k] == 1)
symbol = 'X';
else if (board [k] == 3)
symbol = '*';
//putchar (' ');
putchar (symbol);
}
}
}
putchar ('\n');
putchar ('\n');
}
/************************************************************************/
/* There is no simple sort in ANSI C library! */
void sort_it (numbers, index, size)
long *numbers;
int *index;
int size;
{
int step;
int temp;
int do_more;
int i;
for (i = 0; i < size; i++)
*(index + i) = i;
step = size;
while (step > 1)
{
step = step / 3 + 1;
do_more = 1;
while (do_more)
{
do_more = 0;
for (i = 0; i + step < size; i++)
{
if (*(numbers + *(index + i)) < *(numbers + *(index + i + step)))
{
temp = *(index + i);
*(index + i) = *(index + i + step);
*(index + i + step) = temp;
do_more = 1;
}
}
}
}
}
/************************************************************************/
/* A brief statement of the rules */
void intro ()
{
printf ("\nThe game of 3D 0&X is played on a 4*4*4 board. The aim is to complete ");
printf ("a straight line of own cells (in any direction). Cells are referred to ");
printf ("by 3-digit numbers indicating the tier (1 to 4), the row within the tier ");
printf ("(1 to 4) and the cell within the row (1 to 4). E.g. 213 indicates 2nd ");
printf ("tier, 1st row, 3rd cell. The following commands are also available (all ");
printf ("abbreviable to a single character):\n\n");
printf ("Restart - aborts the current game\n");
printf ("Quit - aborts the game\n");
printf ("Display - re-display the board\n");
printf ("Suggest - asks for a suggestion\n");
printf ("Atomatic - takes over the current player\n");
#ifdef HELP
printf ("Help - lists available commands\n");
#endif
// printf ("\nYou can play just one side, or both sides (in order to pre-set a position)\n");
// printf ("or ask the program to play both sides and just sit and watch the show.\n");
}
/************************************************************************/
/* A function to get and parse answer to a question. */
int get_reply (prompt, options, dflt)
char *prompt;
char *options;
char dflt;
{
char *rptr;
char repc;
char reply [10];
while (1)
{
printf (prompt);
fgets (reply, sizeof (reply), stdin);
if (*reply == '\n' && dflt)
return (tolower (dflt));
rptr = options;
repc = tolower (*reply);
while (*rptr)
{
if (tolower (*rptr) == repc)
return (repc);
rptr++;
}
puts ("Please answer the question!");
}
}
/************************************************************************/
/* A more detailed help. */
#ifdef HELP
void help ()
{
puts ("You can either specify your move, or enter any of the commands listed");
puts ("below (all can be abbreviated to a single character):");
putchar ('\n');
puts ("Suggest - asks the program to suggest the best move available to you");
puts (" in the current position.");
puts ("Automatic - will cause the program to start playing for the current player.");
puts (" It does not mean that you are given the control of the other");
puts (" player, however, since the program is perfectly capable of playing");
puts (" against itself. This command is useful for asking the program to ");
puts (" play one (or both) sides of a position you have just pre-set.");
puts (" Also, when your opponent announces a forced win and you cannot");
puts (" be bothered to play through to the inevitable defeat, you may");
puts (" use this command to see how the win is arrived at, without");
puts (" having to play the moves.");
puts ("Restart - will abandon the current game and give you an option of starting");
puts (" a new one. Useful in a tight corner, but your opponent may");
puts (" grumble if he has a forced win.");
puts ("Quit - quits out of the game altogether. This command is, in fact,");
puts (" available to you whenever you are asked for any input.");
puts ("Display - will completely redraw the board. Handy if your display has got");
puts (" messed up somehow.");
puts ("Help - Prints this text.");
putchar ('\n');
switch (get_reply ("More? (Y/N/Q) [Y]: ", "ynq", 'y'))
{
case 'n':
display ();
return;
case 'q':
exit (0);
case 'y':
break;
}
putchar ('\n');
puts ("Moves are specified by three digit numbers, with the digits in the range ");
puts ("from 1 to four inclusive. No spaces or other separators are allowed between");
puts ("individual digits. The 1st digit describes the tier on which you want to ");
puts ("place your marker. These are numbered from left to right or (in real 3d)");
puts ("from top to bottom). The second digit specifies the row within the tier,");
puts ("the count starting from the topmost row. Finally, the 3rd digit shows the");
puts ("cell within the row. Cells are numbered from left to right.");
putchar ('\n');
puts ("For example the following diasplay shows a nought in the position 324 and");
puts ("a cross in the position 144.");
putchar ('\n');
puts (" . . . . . . . . . . . . . . . .");
puts (" . . . . . . . . . . . O . . . .");
puts (" . . . . . . . . . . . . . . . .");
puts (" . . . X . . . . . . . . . . . .");
puts ("");
puts ("A plus sign on the display indicates a cell in an almost complete line.");
puts ("I.e. by occupying it, one of the players would win the game. When the");
puts ("game is won, all plus signs are removed from the board and the winning");
puts ("line is picked out with asterisks.");
putchar ('\n');
while (1)
{
if (get_reply ("Finished reading? [Y]: ", "yn", 'y') == 'y')
break;
}
display ();
return;
}
#endif
/************************************************************************/
/* Opening moves are special! */
short diags [16] = /* The four body diagonals */
{
0, 21, 42, 63,
3 ,22, 41, 60,
12, 25, 38, 51,
15, 26, 37, 48
};
void opening ()
{
short i, j; /* Loop counters */
short cell; /* A cell */
short myone; /* Position of my cell in diagonal */
short hisone; /* Ditto for his cell */
short spoilt; /* The spoilt diagonal id */
if (movcnt <= 0)
/* None of the important cells occupied. Choose any one. */
{
move = diags [rand () % 16];
return;
}
/* Check through the diagonals and ignore free ones. If there is a */
/* spoiled one, note it. If an owned diagonal is found, we want to */
/* move into it! */
spoilt = -1; /* No spoiled so far */
for (i = 0; i <= 3; i++)
{
myone = 0;
hisone = 0;
/* Work out the ownership of this body diagonal. */
for (j = 0; j <= 3; j++)
{
cell = diags [4 * i + j];
if (board [cell] == 0) continue;
if (board [cell] == sign)
{
if (myone == 0)
myone = j + 1;
else
myone = -myone;
}
else
{
if (hisone == 0)
hisone = j + 1;
else
hisone = -hisone;
}
}
if (myone == 0 && hisone == 0) continue; /* Free line */
if (myone != 0 && hisone != 0) /* Overcrowded line */
{
spoilt = i; /* Remember spoiled line */
continue;
}
if (hisone == 0)
{
move = diags [4 - myone + 4 * i];
return;
}
else
{
if (hisone != 1 && hisone != 4)
{
strmov [0] = diags [0 + 4 * i];
strmov [1] = diags [3 + 4 * i];
}
else
{
strmov [0] = diags [1 + 4 * i];
strmov [1] = diags [2 + 4 * i];
}
move = strmov [rand () % 2];
return;
}
}
if (spoilt == -1)
{
move = diags [rand () % 16];
return;
}
/* The choice consists of all body diagonal cells, except for the
* spoiled diagonal.
*/
strcnt = 0;
for (i = 0; i < 4; i++)
{
if (i != spoilt)
for (j = 0; j < 4; j++)
{
cell = diags [j + 4 * i];
if (board [cell] == 0)
strmov [strcnt++] = cell;
}
}
move = strmov [rand () % strcnt];
}
/************************************************************************/
/* Initialise everything for a new game. */
void init ()
{
int i, j;
char prompt [80];
who [0] = who [1] = 0;
for (player = 0; player < 2; player++)
{
sprintf (prompt, "Who plays %s, me or you? ", plname [player]);
if (rwho [player] == 'y')
strcat(prompt, "[me again] ");
else if (rwho [player] == 'm')
strcat (prompt, "[you again] ");
else
rwho [player] = 0;
switch (get_reply (prompt, "myq", rwho [player]))
{
case 'm': /* He says Me, so... */
rwho [player] = 'm';
who [player] = 'h'; /* Him or Human */
printf ("OK... You play ");
if (who [0] == who [1])
printf ("%s *and* %s!\n", plname [0], plname [1]);
else
printf ("%s.\n", plname [player]);
break;
case 'y': /* He says You, so... */
rwho [player] = 'y';
who [player] = 'm'; /* Me or Machine */
printf ("OK... I'll play ");
if (who [0] == who [1])
printf ("%s *and* %s!\n", plname [0], plname [1]);
else
printf ("%s.\n", plname [player]);
break;
case 'q':
puts ("If you wish...\n");
exit (0);
}
}
srand (time (NULL));
memset (clinh, '\0', sizeof (clinh));
memset (linsco, '\0', sizeof (linsco));
memset (plnsco, '\0', sizeof (plnsco));
memcpy (clines, icline, sizeof (clines));
memcpy (lcells, ilcell, sizeof (lcells));
memcpy (cplane, icplan, sizeof (cplane));
for (i = 0, j = 3; i < 64; i++, j +=7)
{
board [i] = 0;
clinh [j] = iclinh [i];
}
move = 0;
lincnt = 76;
frccnt = 0;
movcnt = 0;
mute = 0;
winner = -1;
win [0] = -1;
win [1] = -1;
kkill [0] = -1;
kkill [1] = -1;
player = 0;
sign = -1;
vismov = 0;
if (who [0] != 'm' || who [1] != 'm')
display ();
else
putchar ('\n');
}
/*********************************************************************/
/* Check a proposed move */
int check_move (text)
char *text;
{
int i;
char *cptr = text;
for (i = 0; i < 3; i++, cptr++)
{
if (*cptr `< '1' || *cptr >` '4')
break;
}
if (i == 0)
return (1);
if (i < 3 || (*cptr && *cptr != '\n'))
{
printf ("%s is not a valid move description.\n", text);
#ifdef HELP
printf ("If in doubt, type H for help.\n");
#endif
return (1);
}
return (0);
}
/************************************************************************/
/* Get move from player. */
void get_move ()
{
char reply [10];
while (1)
{
if (who [0] == who [1])
printf ("%s' move ", plname [player]);
else
printf ("Your move ");
if (vismov > 0)
printf ("[%d]: ", vismov);
else
printf ("[s]: ");
fgets (reply, sizeof (reply), stdin);
*(reply + strlen (reply) - 1) = '\0';
if (*reply == '\0')
{
if (vismov > 0)
{
move = 16 * (vismov / 100) + 4 * ((vismov / 10 ) % 10) +
vismov % 10 - 21;
vismov = 0;
return;
}
else
{
move = 0;
reply [0] = 's';
reply [1] = '\0';
}
}
else
move = atoi (reply);
if (move == 0)
{
switch (move = tolower (*reply))
{
case 'a':
who [player] = 'm';
move = 'a';
return;
case 'd':
display ();
move = 'd';
break;
#ifdef HELP
case 'h':
help ();
move = 'h';
break;
#endif
case 'r':
move = 'r';
return;
case 'q':
move = 'q';
return;
case 's':
move = 's';
return;
default:
(void) check_move (reply);
break;
}
}
else if (check_move (reply) == 0)
{
move =
16 * (*reply - '1') + 4 * (*(reply + 1) - '1') + *(reply + 2) - '1';
if (board [move] == 0)
return;
printf ("Cell %s already occupied. Please try again.\n", reply);
}
}
}
/************************************************************************/
/* Do the housekeeping... */
void update ()
{
int i, j, k;
int ic, jc, kc;
int score;
int newsco;
int ownsco;
int abssco;
char cell, line, plane;
int temp;
winner = -1;
vismov = 0;
if (win [0] == move)
win [0] = -1;
if (kkill [0] == move)
kkill [0] = -1;
if (win [0] < 0)
win [0] = kkill [0];
if (win [1] == move)
win [1] = -1;
if (kkill [1] == move)
kkill [1] = -1;
if (win [1] < 0)
win [1] = kkill [1];
/* Update the "balance of power" scores for each plane passing
* therough the newly occupied cell.
*/
for (i = 6 * move, ic = 0; ic< 6; i++, ic++)
{
if ((plane = cplane [i]) < 0)
break;
plnsco [plane] += sign;
}
/* Now consider all lines passing through that cell. */
for (i = 7 * move, ic = 0; ic < 7; i++, ic++)
{
if ((line = clines [i]) < 0) /* End of line list */
continue;
score = linsco [line];
/* If the owner of the cell is not the owner of the line,
* the line is now spoilt.
*/
if ((ownsco = score * sign) < 0)
{
linsco [line] = 99; /* Spoilt line */
lincnt--;
newsco = 99;
}
else
/* If line not spoilt, update the degree of its ownership. */
{
newsco = score + sign;
linsco [line] = newsco;
/* If the ownership is 4, somebody has just won! */
abssco = newsco * sign;
if (abssco == 4)
winner = player;
}
/* Now loop through all cells in this line. */
for (j = 4 * line, jc = 0; jc < 4; j++, jc++)
{
cell = lcells [j];
/* Do we have a winner? If so, we overwrite the existing cell owner value
* with a special value wgich will display as '*'.
*/
if (winner >= 0)
{
if (cell < 0)
cell = -(cell + 1);
board [cell] = 3;
continue;
}
/* No winner. Nothing to do for occupied cells. */
if (cell < 0)
continue;
/* Is it the cell we have just moved into? If so, just note that it
* is now occupied.
*/
if (move == cell)
{
lcells [j] = -(cell + 1);
continue;
}
/* Cell still free: update histogram of line scores for this cell. */
temp = score + 7 * cell + 3;
clinh [temp] -= 1;
/* Ignore spoilt lines */
if (newsco != 99)
{
clinh [temp - score + newsco] += 1;
/* Is it a forced line? */
if (abssco == 3)
{
if (win [player] >= 0)
kkill [player] = cell;
else
win [player] = cell;
continue;
}
}
else
{
/* Line spoilt. Mark it as such in list of lines passing through
* this cell.
*/
for (k = 7 * cell, kc = 0; kc < 7; k++, kc++)
{
if (line == clines [k])
{
clines [k] = -1;
break;
}
}
}
}
if (winner >= 0)
return;
}
/* End of complicated houskeeping. Now for the trivial stuff... */
board [move] = sign;
movcnt++;
player = 1 - player;
sign = -sign;
return;
}
/************************************************************************/
/* Evaluate a move. */
void eval ()
{
char plane;
char cell;
char line;
char cplanh [7];
int minus2;
int minus1;
int zero;
int plus1;
int plus2;
int adjust;
int temp;
int i, j, k;
int ic, jc, kc;
long value;
long svalue;
long tvalue;
long sbest = -1;
strcnt = 0;
/* Loop through all empty cells. */
for (i = 0; i < 64; i++)
{
tacval [i] = -100;
if (board [i] != 0)
continue;
/* Go through all planes passing through this cell and compile the
* 'histogram' of plane ownerships.
*/
memset (cplanh, '\0', sizeof (cplanh));
for (j = 6 * i, jc = 0; jc < 6; j++, jc++)
{
if ((plane = cplane [j]) < 0)
break;
temp = sign * plnsco [plane] + 3; /* Empty plane is element 3 */
if (temp < 0) /* Treat ownerships in excess ... */
temp = 0; /* ... of 3, as 3 (plus or minus) */
if (temp > 6)
temp = 6;
(cplanh [temp])++;
}
/* For ease of manipulation, pull out into appropriately named
* variables the various line counts for this cell.
*/
temp = 7 * i + 1;
if (sign < 0)
temp += 4;
minus2 = clinh [temp];
temp += sign;
minus1 = clinh [temp];
temp += sign;
zero = clinh [temp];
temp += sign;
plus1 = clinh [temp];
temp += sign;
plus2 = clinh [temp];
/* Calculate preliminary base value... */
value = 2 * (minus1 + 8 * (zero + 8 * (minus2 + 8 * plus1)));
/* If we have one forcing line passing through here, must be careful.
* Work out if we need to beware of setting up a win for the
* opponent. Loop through all lines passing through this cell. If
* the line would be forced by the currently considered move, find
* the other blank cell on that line. If that is on the opponent's 2
* line, then for strategic purposes this cell is useless and we
* might as well forget that there is a forcing line for this cell.
*/
if (plus2 == 1)
{
for (j = 7 * i, jc = 0; jc < 7; j++, jc++)
{
if ((line = clines [j]) < 0)
continue;
if (sign * linsco [line] != 2)
continue;
for (k = 4 * line, kc = 0; kc < 4; k++, kc++)
{
if ((cell = lcells [k]) < 0)
continue;
if (cell == i)
continue;
if ((temp = clinh [ 7 * cell + 3 - 2 * sign]) == 0)
continue;
if (temp > 0)
goto no_good;
plus2 = 0;
break;
}
break;
}
}
/* A few ad hoc heuristics follow... */
adjust = 0;
if (player == 1 && mute `<= 0 && zero + 2 * cplanh [5] + cplanh [0] >` 4)
cplanh [0] += minus2;
else
{
temp = minus1 - plus1;
if (temp > 0 && lincnt != 74)
{
adjust = temp;
(cplanh [4])--;
}
}
if (cplanh [0] == 1 && minus1 + 2 * minus2 < 3)
cplanh [0] = 0;
/* Finish off calculating the base value. */
value += 8198L * (cplanh [6] + 8 * cplanh [0]);
/* Unless exploring strategies, calculate the tactical value. */
if (mute <= 0)
{
tvalue = value + 1020L * adjust;
if (minus2 > 2)
tvalue += 262144L;
tacval [i] = tvalue;
}
/* Calculate the strategic value of the cell. */
if (plus2 == 0)
continue;
svalue = value + 128L * (cplanh [5] + 512L * (plus1 + 8 * plus2));
if (sbest < svalue) /* Is this a better strategic move? */
{
strcnt = 1;
strmov [0] = i;
sbest = svalue;
}
else if (sbest == svalue) /* No, but it is just as good! */
strmov [strcnt++] = i;
no_good:
continue;
}
}
/************************************************************************/
/* Back up the game info */
void backup (bckup)
char *bckup;
{
char *bck;
bck = bckup;
memcpy (bck, (char *) board, sizeof (board));
bck += sizeof (board);
memcpy (bck, (char *) cplane, sizeof (cplane));
bck += sizeof (cplane);
memcpy (bck, (char *) linsco, sizeof (linsco));
bck += sizeof (linsco);
memcpy (bck, (char *) lcells, sizeof (lcells));
bck += sizeof (lcells);
memcpy (bck, (char *) clines, sizeof (clines));
bck += sizeof (clines);
memcpy (bck, (char *) clinh, sizeof (clinh));
bck += sizeof (clinh);
memcpy (bck, (char *) plnsco, sizeof (plnsco));
bck += sizeof (plnsco);
memcpy (bck, &player, sizeof (player));
bck += sizeof (player);
memcpy (bck, &sign, sizeof (sign));
bck += sizeof (sign);
memcpy (bck, &winner, sizeof (winner));
bck += sizeof (winner);
memcpy (bck, (char *) win, sizeof (win));
bck += sizeof (win);
memcpy (bck, (char *) kkill, sizeof (kkill));
bck += sizeof (kkill);
memcpy (bck, &move, sizeof (move));
bck += sizeof (move);
memcpy (bck, &movcnt, sizeof (movcnt));
bck += sizeof (movcnt);
memcpy (bck, &strcnt, sizeof (strcnt));
bck += sizeof (strcnt);
memcpy (bck, &lincnt, sizeof (lincnt));
bck += sizeof (lincnt);
memcpy (bck, (char *) strmov, sizeof (strmov));
bck += sizeof (strmov);
memcpy (bck, (char *) tacval, sizeof (tacval));
bck += sizeof (tacval);
}
/************************************************************************/
/* Restore game info */
void restore (bckup)
char *bckup;
{
char *bck = bckup;
memcpy ((char *) board, bck, sizeof (board));
bck += sizeof (board);
memcpy ((char *) cplane, bck, sizeof (cplane));
bck += sizeof (cplane);
memcpy ((char *) linsco, bck, sizeof (linsco));
bck += sizeof (linsco);
memcpy ((char *) lcells, bck, sizeof (lcells));
bck += sizeof (lcells);
memcpy ((char *) clines, bck, sizeof (clines));
bck += sizeof (clines);
memcpy ((char *) clinh, bck, sizeof (clinh));
bck += sizeof (clinh);
memcpy ((char *) plnsco, bck, sizeof (plnsco));
bck += sizeof (plnsco);
memcpy (&player, bck, sizeof (player));
bck += sizeof (player);
memcpy (&sign, bck, sizeof (sign));
bck += sizeof (sign);
memcpy (&winner, bck, sizeof (winner));
bck += sizeof (winner);
memcpy ((char *) win, bck, sizeof (win));
bck += sizeof (win);
memcpy ((char *) kkill, bck, sizeof (kkill));
bck += sizeof (kkill);
memcpy (&move, bck, sizeof (move));
bck += sizeof (move);
memcpy (&movcnt, bck, sizeof (movcnt));
bck += sizeof (movcnt);
memcpy (&strcnt, bck, sizeof (strcnt));
bck += sizeof (strcnt);
memcpy (&lincnt, bck, sizeof (lincnt));
bck += sizeof (lincnt);
memcpy ((char *) strmov, bck, sizeof (strmov));
bck += sizeof (strmov);
memcpy ((char *) tacval, bck, sizeof (tacval));
bck += sizeof (tacval);
}
/************************************************************************/
/* Prune the forcing sequence */
void prune ()
{
int tmppnt;
for (frcpnt = 0; frcpnt < frccnt; frcpnt++)
{
restore (bckup1);
frcmov [frcpnt] = -(frcmov [frcpnt] + 1);
for (tmppnt = 0; tmppnt < frccnt; tmppnt++)
{
if ((move = frcmov [tmppnt]) < 0)
continue;
update ();
if ((move = win [1 - player]) < 0)
break;
update ();
if (win [player] >= 0)
goto skip;
if (win [1 - player] >= 0)
break;
}
frcmov [frcpnt] = -(frcmov [frcpnt] + 1);
skip:
continue;
}
frcpnt = 0;
for (tmppnt = 0; tmppnt < frccnt; tmppnt++)
{
if (frcmov [tmppnt] >= 0)
frcmov [frcpnt++] = frcmov [tmppnt];
}
frccnt = frcpnt + 1;
restore (bckup1);
}
/************************************************************************/
/* Make a strategic move selection. */
void strsel ()
{
int temp;
frccnt = 0;
move = -1;
if (strcnt == 0)
return;
backup (bckup1);
while (strcnt)
{
move = strmov [rand () % strcnt];
frcmov [frccnt++] = move;
update ();
if (win [player] >= 0)
goto done;
move = win [1 - player];
update ();
if (winner >= 0)
break;
if ((move = win [player]) >= 0)
goto force;
eval ();
}
restore (bckup1);
move = -1;
frccnt = 0;
return;
force:
frcmov [frccnt++] = move;
done:
restore (bckup1);
if (mute != 2)
prune ();
frcpnt = 1;
move = frcmov [0];
return;
}
/************************************************************************/
/* Make a tactical move selection. */
void tacsel ()
{
int index [64];
int tacmov [64];
int temp;
int taccnt = 0;
int i, j;
long adjval;
long tbest = -100;
long crtval;
/* Note that it is dangerous to resort to tactical forcing if you
* are in a bad position. Hence if our top choices got rejected by
* the tactical look-ahead, we artificially devalue forcing moves.
*/
backup (bckup2);
if (mute >= 0)
mute = 2;
sort_it (tacval, index, 64);
//crtval = 0.95 * tacval [index [0]];
crtval = (95 * tacval [index [0]])/100;
for (i = 0; i < 64; i++)
{
move = index [i];
adjval = tacval [move];
if (adjval < tbest)
goto done;
temp = -1;
update ();
if (win [1 - player] >= 0)
{
if (adjval < 200 || adjval < crtval)
adjval /= 128;
}
else
{
eval ();
strsel ();
temp = move; /* Move is about to be overwritten! */
}
restore (bckup2);
move = index [i];
if (temp > 0)
adjval = frccnt;
frccnt = 0;
if (adjval < tbest)
continue;
if (adjval > tbest)
{
taccnt = 1;
tacmov [0] = move;
tbest = adjval;
}
else
tacmov [taccnt++] = move;
}
done:
if (mute >= 0)
mute = 1;
move = tacmov [rand () % taccnt];
}
/************************************************************************/
/* Select a move. */
void select_move ()
{
int plane;
int line;
int cell;
int forced = 0;
move = -1; /* No move selected so far */
if (movcnt <= 4)
opening (); /* Special move selection in the opening */
else if (win [0] >= 0 || win [1] >= 0)
{
if (win [player] >= 0)
move = win [player];
else
move = win [1 - player];
forced = 1;
}
else if (frccnt == 0)
{
eval (); /* Evaluate free cells */
if (mute == 0)
mute = 1; /* Suppress display */
strsel (); /* Make strategic selection */
if (move == -1) /* Got something strategic? */
tacsel (); /* No... Select tactically */
}
else
move = frcmov [frcpnt++]; /* Forced move */
/* Decompose the selected move and (just in case!) check its legality. */
cell = move % 4 + 1;
line = (move / 4) % 4 + 1;
plane = move / 16 + 1;
if (mute > 0)
mute = 0;
vismov = 100 * plane + 10 * line + cell;
if (move `< 0 || move >`= 64 || board [move] != 0)
{
printf ("OX3D logic error!\n");
if (move `< 0 || move >`= 64)
printf ("%d is not a legal move!\n", vismov);
else
printf ("Selected cell %d is already occupied!\n", vismov);
exit (1);
}
/* If a forced win found, announce it! */
if (frccnt > 0 && frcpnt == 1 && forced == 0)
{
if (who [0] == 'm' && who [1] == 'm' && player == 0)
putchar ('\n');
if (mute >= 0)
printf ("\n**** Forced win for %s in %d moves ****\n\n",
plname [player], frccnt);
else
printf ("\n=== A forced win available to %s ===\n\n", plname [player]);
}
/* State the chosen move. */
if (mute < 0)
printf ("Suggested");
else if (who [0] == who [1])
printf ("%s'", plname [player]);
else
printf ("My");
printf (" move is %d", vismov);
if (kkill [player] >= 0)
puts (" - Mate!");
else if (frccnt > 0)
puts (" ... Check!");
else
putchar ('\n');
}
/************************************************************************/
/* Main module */
main ()
{
int reply;
printf ("OX3D - MLA version 3.1, 01 Feb 87\n\n");
reply = get_reply ("Do you need instructions? [Y]: ", "ynq", 'y');
if (reply == 'q')
exit (0);
if (reply == 'y')
intro ();
putchar ('\n');
rwho [0] = 0;
rwho [1] = 0;
while (1)
{
init ();
while (1)
{
if (who [player] == 'h')
{
get_move ();
if (move == 's')
{
backup (bckup3);
mute = -1;
select_move ();
mute = 0;
restore (bckup3);
if (frccnt > 0)
{
frccnt = 0;
memset (frcmov, '\0', sizeof (frcmov));
}
winner = -1;
continue;
}
if (move == 'q' || move == 'r')
break;
if (move == 'a' || move == 'h' || move == 'd')
continue;
}
else
{
if (player == 0 && who [0] == 'm' && who [1] == 'm' && movcnt > 0)
{
char buf [6];
printf ("More? Y/N/R/Q [Y]: ");
fgets (buf, sizeof (buf), stdin);
if ((move = tolower (*buf)) == 'q' || move == 'r')
break;
}
select_move ();
vismov = -1;
}
update ();
if (winner >= 0)
break;
display ();
if (lincnt == 0) /* No lines left -- a draw */
break;
if (move < 0)
break;
}
if (winner >= 0)
{
display ();
if (who [0] == who [1])
printf ("%s wins.\n", plname [winner]);
else if (who [winner] == 'm')
printf ("I win. Hard luck...\n");
else
printf ("You win. Good game!\n");
}
else if (lincnt == 0)
printf ("No lines left - a draw.\n");
else
{
if (who [player] != 'm')
{
if (frccnt == 0)
printf ("Well, all right...\n");
else
printf ("Quitter! I've got you mated, so I win anyway.\n");
}
}
if (move == 'q')
exit (0);
if (move == 'r')
continue;
if (get_reply ("\nAnother game? [Y]: ", "ynq", 'y') != 'y')
break;
puts ("Good! Let's have another game...\n");
}
printf ("Well, next time perhaps...\n");
}