home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 10
/
aminetcdnumber101996.iso
/
Aminet
/
game
/
board
/
Backgammon.lha
/
Backgammon.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-12-27
|
17KB
|
530 lines
/************************************************************************/
/* */
/* Hoser BackGammon version 1.0 */
/* */
/* Robert Pfister */
/* */
/* Rfd#3 Box 2340 home:(207)-873-3520 */
/* Waterville, Maine 04901 */
/* */
/* Pfister_rob%dneast@dec.decwrl */
/* */
/* */
/* Copyright June,1987 */
/* */
/* This program will play a game of backgammon at the novice level */
/* */
/* The code is in 4 parts... */
/* / */
/* \/ 1) back.c - main driver */
/* 2) eval.c - evaluation of moves */
/* 3) backscn.c - screen stuff.. */
/* 4) backmenu.c - menu stuff, help text, and "decoder" */
/* */
/* this was compiled under Manx 3.20a, using long integers */
/* */
/************************************************************************/
/* Contains: */
/* ShowStandings main Restart StoreMove */
/* RecallMove Won UserMove Roll */
/* GameOver stats NewGame valid */
/* update */
/* most recent modification 11/ 8/93 E.M.Greene */
/* most recent modification 2/14/94 E.M.Greene */
/* most recent modification 3/ 9/95 E.M.Greene */
/* most recent modification 7/ 1/95 E.M.Greene */
/* Revisions, corrections, and improvements include:
1. Corrected errors in provided source code:
a. InitArea() call in Gsetup had incorrect value for number of points
thereby resulting in no pieces being displayed.
b. TextScreen() failed to perform CloseDevice() upon completion
thereby resulting in strange phenomena after being called
c. stats() failed to display anything if program initiated from
Workbench. Revised to call TextScreen.
d. Bug in eval() resulted in poor performance. Correction made the
computer a much more vigorous opponent -- it'll now eat your lunch
a goodly percentage of the time.
e. Numerous grammatical and spelling errors corrected, mainly "peice".
f. Revised the process of displaying numbers on pieces when more than
PerSpike pieces are on one point so that counts greater than nine
are shown correctly.
g. Corrected error in StoreMove so that "stack" overflow is properly
detected.
h. Added code to reset StackPos at start of new game. Otherwise, if
you play long enough, the stack would overflow and the bug of
preceding item will result in a flameout.
i. Eliminated Gerror from BackScreen and replaced with Alerts. Gerror
was trying to display error message about screen or window opening
failures on a screen or window which hadn't opened!
2. Revisions:
a. Eliminated dead code and variables. Variable names were revised in
a number of places to be more meaningful. Reformatted source and
added comments to make the source more readable.
b. Merged IntuiMessage processing into one routine and revised to look
for all messages currently queued. Menu operations are now much
more effective.
c. Revised the dice "throw" generation to reduce the instances of the
same values being repeatedly generated.
d. Reduced declared sizes for AreaBuf and StackSize to something more
reasonable.
e. Replaced the calls to the lightly-documented function RangeRand
with calls to Scott McMahan's implementation of the algorithm of
Pierre L'Ecuyer [L'Ecuyer, P. Efficient and Portable Combined Random
Number Generators. Commun. ACM 31, 6 (June, 1988) 742].
f. Replaced the routine CopyBoard in BackEval with a macro invoking
memcpy().
3. Enhancements:
a. Array point[] was added to eliminate need to constantly compute
board positions which are constants.
b. Loop count in BlinkPiece() was reduced to expedite moves.
c. Only one pair of dice are displayed thereby eliminating confusion
about which pair of numbers human is to play.
d. Revised color specification such that ColorRequestor can potentially
be added.
e. Added readout of relative standings of players.
f. Recompiled with Lattice C resulting in more randomness in dice throws.
[SAS Institute had ought to know how to produce a random number generator
since they are in the statistical analysis business!]
g. Revised game statistics to include number of points rolled but not
used ("wasted"). Also, now show average roll to one decimal place.
h. Revised as required to compile cleanly with SAS/C 6.3
i. Added "cost" of coming off bar to "wasted" count (11/16/94)
j. Added counting of total rolls and display (2/14/94).
k. Added total games and effective average roll to data display (12/27/95)
*/
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <libraries/dos.h>
#include "back.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <math.h>
#define StackSize 100
extern int AvgPos;
extern int Moves;
extern int minY;
extern struct RastPort *rp;
extern struct Screen *screen;
you_me_rec you, me;
USHORT total_games = 0;
static signed char
BoardPos[StackSize][26],/* stack of the previous positions */
DicePos[StackSize][4], /* stack of the dice rolls */
StackPos, /* how many moves back are we at? */
MoveList[4], /* stack of this turn's moves */
CurMove = 0; /* place in the list */
signed char Turn, /* whose turn is it? */
board[26], /* describes the board position */
/* 0 and 25 are the bar */
Dice[4]; /* describes the 2 or 4 dice availible */
const char version[] = "$VER: Backgammon 2.2 (95/12/27)";
void ShowStandings(void)
{
char s[6];
int Msum=0, Ysum=0, i;
for(i = 0; i <= 24; i++)
if (board[i] > 0)
Msum += (25 - i) * board[i];
for(i = 1; i <= 25; i++)
if (board[i] < 0)
Ysum -= i * board[i];
/* write box scores at top of window */
SetAPen(rp, tm1_color);
RectFill(rp, 400L, minY-3-screen->Font->ta_YSize, 520, minY-2);
SetAPen(rp,dice_color);
sprintf(s,"%4d", Msum);
if (board[0] != 0)
strcat(s,"+");
Move(rp, 400L, (long)minY - 6);
Text(rp,s,strlen(s));
sprintf(s,"%4d", Ysum);
if (board[25] != 0)
strcat(s,"+");
Move(rp, 460L, (long)minY - 6);
Text(rp, s, strlen(s));
}
double random(void);
unsigned short real_rand(void)
{
return (unsigned short)(random() * 6.0);
}
int Roll(BYTE d[4]) /* roll the dice (4 results) */
{
int n;
d[2] = d[3] = 0;
for (n = (real_rand() >> 5) & 7; n >= 0; n--)
d[0] += real_rand();
d[1] = (abs(d[0]) % 6) + 1; /* RangeRand(6L)+1; */
for (n = (real_rand() >> 7) & 7; n >= 0; n--)
d[0] += real_rand();
d[0] = (abs(d[0]) % 6) + 1; /* RangeRand(6L)+1; */
if (d[0]==d[1]) { /* if rolled doubles */
d[2] = d[3] = d[1];
return 1;
}
else
return 0;
}
int NewGame(signed char board[26], signed char Dice[4])
{
int i;
StackPos = 0;
memset(board, 0, sizeof_board); /* initialize the board */
board[ 1]= 2; board[24]=-2;
board[12]= 5; board[ 6]=-5;
board[17]= 3; board[ 8]=-3;
board[19]= 5; board[13]=-5;
memset(&you, 0, offsetof(you_me_rec, total.roll));
memset(&me, 0, offsetof(you_me_rec, total.roll));
AvgPos = 0;
for (i = 0; i <= 25;i ++)
PutSpike(i, board[i]);
while (Roll(Dice)); /* until no doubles */
if (Dice[0]>Dice[1]) /* see who goes first */
return(Uside);
else
return(Cside);
}
#include "intuition/intuition.h"
extern struct Window *w;
int UserMove(BYTE board[26], BYTE Dice[4], int side)
{
BYTE Pick = 0, Valid = 0; /* flags */
int i,x,y,
from_point, to_point;
struct IntuiMessage *message;
unsigned long Class, msg_flag = 1L << w->UserPort->mp_SigBit;
unsigned short Code;
while (Valid == 0) {
Wait(msg_flag);
while (message=(struct IntuiMessage *) GetMsg(w->UserPort)) {
Code = message->Code;
Class = message->Class;
x = message->MouseX;
y = message->MouseY;
ReplyMsg((struct Message *)message);
if (Class == MENUPICK) {
if (Code != MENUNULL)
Valid=DoMenu((MENUNUM(Code)*100)+(ITEMNUM(Code)*10)+SUBNUM(Code));
}
/* check if mouse pressed..... */
else if ((Class == MOUSEBUTTONS) && (Code == SELECTDOWN)) {
if ((to_point = decode(x,y)) == -1) { /* clicked dice */
you.wasted += Dice[0] + Dice[1] + Dice[2] + Dice[3];
Dice[0] = Dice[1] = Dice[2] = Dice[3] = 0;
Valid = 1;
you.incomplete++;
}
else
if (Pick == 0) { /* first click */
if (board[to_point] * side <= 0)
DoMenuStrip("No piece there!!");
else {
Pick = 1;
from_point = to_point;
}
break;
}
else if (Pick == 1) { /* second click */
Pick = 0;
for (i = 0; (Valid == 0) && (i < 4); i++)
if ((Dice[i] != 0) &&
(valid(board, from_point, to_point, Dice[i]))) {
if (from_point == 25)
you.wasted += 25 - to_point;
if (from_point == to_point)
you.wasted += (Dice[i] - from_point);
BlinkPiece(board, from_point);
update(board, from_point, to_point, side);
PutSpike(from_point , board[from_point]);
BlinkPiece(board, to_point);
PutSpike(0 , board[ 0]);
PutSpike(25, board[25]);
Valid = 1;
Dice[i] = 0;
}
UnDoMenuStrip();
} /* end Pick == 1 */
/* end else decode */
if (Valid == 0)
DoMenuStrip("Not Valid move");
}; /* end if mousebutton */
} /* end while message */
} /* end while Valid */
return (Valid != -1);
} /* end UserMove */
void StoreMove(void)
{
if (StackPos < StackSize - 1) { /* can only store that many */
memcpy(&BoardPos[StackPos][0], board, sizeof board);
memcpy(&DicePos[StackPos][0], Dice, sizeof Dice);
StackPos++;
}
/* printf("STORE: stack is at %d \n",StackPos); */
}
void stats(void)
{
char *stat_data[] = {
" Game Statistics",
"\n",
" Number of games: xx\n ",
"\n",
" Number of moves xx; Avg positions evaluated xxxx \n",
"\n",
" Avg. roll : User xx.x Computer xx.x \n ",
" Effect. avg.: User xx.x Computer xx.x \n ",
" Doubles : User xx Computer xx \n ",
" Inc. moves : User xx Computer xx \n ",
" Turns on bar: User xx Computer xx \n ",
" Wasted : User xx Computer xx \n ",
"\n",
" [click in window to continue]\n "};
if ((you.moves != 0) && (me.moves != 0)) {
sprintf(stat_data[2], "Number of games: %d\n", ++total_games);
sprintf(stat_data[4]," Number of moves %d;"
" Avg positions evaluated %d\n", me.moves, AvgPos / me.moves);
sprintf(stat_data[6]," Avg roll : User %5.1f Computer %5.1f \n",
(float)you.dice_total / you.moves, (float)me.dice_total / me.moves);
sprintf(stat_data[7]," Effect. avg.: User %5.1f Computer %5.1f \n",
(float)(you.dice_total - you.wasted) / you.moves,
(float)(me.dice_total - me.wasted) / me.moves);
sprintf(stat_data[8]," Doubles : User %2d Computer %2d \n",
you.doubles, me.doubles);
sprintf(stat_data[9]," Inc. moves : User %2d Computer %2d \n",
you.incomplete, me.incomplete);
sprintf(stat_data[10]," Turns on bar: User %2d Computer %2d\n",
you.on_bar, me.on_bar);
sprintf(stat_data[11]," Wasted : User %3d Computer %3d\n",
you.wasted, me.wasted);
TextScreen(stat_data, 14);
you.total.roll += you.dice_total;
me.total.roll += me.dice_total;
you.total.average_roll += (float)you.dice_total / you.moves;
me.total.average_roll += (float)me.dice_total / me.moves;
you.total.incomplete += you.incomplete;
me.total.incomplete += me.incomplete;
you.total.wasted += you.wasted;
me.total.wasted += me.wasted;
}
}
void GameOver(BOOL show_stats)
{
if (show_stats)
stats();
finit();
exit(0);
}
int Won(BYTE board[26], int side)
{
int i;
if (side == 1) {
for(i = 0;i <= 25;i++)
if (board[i] > 0)
return 0;
}
else
for(i = 0;i <= 25; i++) {
if (board[i] < 0)
return 0;
}
return(1);
}
void setseed(void);
main() /* main procedure */
{
Gsetup();
memset(&you, 0, sizeof you);
memset(&me, 0, sizeof me);
setseed();
Turn = NewGame(board,Dice);
for (;;) { /* go until game is over */
if (Turn == Uside)
DoMenuStrip("Your turn");
else
DoMenuStrip("My turn");
ShowDice(Dice,Turn);
ShowStandings();
if (Turn == Uside) {
StoreMove(); /* store this board position */
/* user stats */
if (Dice[2] > 0)
you.doubles++;
you.dice_total += Dice[0] + Dice[1] + Dice[2] + Dice[3];
you.moves++;
if (board[25] < 0)
you.on_bar++;
/* do the Move */
do
if (UserMove(board,Dice,Turn) == 0)
GameOver(TRUE); /* escape */
while ((Turn == Uside) && (Won(board,Turn) == 0) &&
(Dice[0] + Dice[1] + Dice[2] + Dice[3]!=0));
you.wasted += (Dice[0] + Dice[1] + Dice[2] + Dice[3]);
}
else { /* turn == Cside */
/* computer stats */
if (Dice[2] > 0)
me.doubles++;
me.dice_total += Dice[0] + Dice[1] + Dice[2] + Dice[3];
me.moves++;
if (board[0] > 0)
me.on_bar++;
GenerateMoves(board,Dice); /* do move */
DoMove(board,Dice);
}
Turn = -Turn;
Roll(Dice);
if (Turn == 0) /* signal to restart the game */
Turn = NewGame(board,Dice);
if (Won(board,Uside)) { /* did user win yet? */
ShowStandings();
stats();
if (requestor("I lose. Play again?","Yes","No"))
GameOver(FALSE);
else
Turn = NewGame(board,Dice);
}
if (Won(board,Cside)) { /* did computer win yet? */
ShowStandings();
stats();
if (requestor("Hee hee. I Win. Play Again?","Yes","No"))
GameOver(FALSE);
else
Turn = NewGame(board,Dice);
}
} /* end do forever */
} /* end of main */
void Restart(void)
{
Turn = 0;
}
void RecallMove(int how)
{
int i;
StackPos = StackPos-1 + how;
if (StackPos < 0)
StackPos = 0;
for (i = 0; i < 26; i++)
if (board[i] != BoardPos[StackPos][i]) { /* only refresh stuff changed */
board[i] = BoardPos[StackPos][i];
PutSpike(i,board[i]);
}
for (i = 0; i < 4; i++)
Dice[i] = DicePos[StackPos][i];
ShowDice(Dice,Uside);
StackPos++;
/* printf("RECALL: stack is at %d \n",StackPos); */
}
int valid(BYTE board[26], int from_point, int to_point, int die_value)
{ /* is the move valid for the board and one die? */
BYTE sign, i, All_In,
*bp;
All_In = -1;
if (Turn == Cside) { /* computer's move */
if ((board[0] != 0) && (from_point != 0))
return(0); /* man on bar and didn't select bar */
sign = Cside;
/* see if all pieces are in home base */
bp = &board[1];
for(i = 1; i <= 24; i++)
if (*(bp++) > 0) { /* find man fartherest from home */
All_In = i;
break;
}
/* check if can take it off */
if (to_point > 24) { /* if in home board */
if ((All_In > 18) && /* if all men in home board */
((from_point == (25 - die_value)) ||
((25 - die_value) < All_In) && (from_point == All_In)))
return(1);
else
return(0);
}
}
else { /* human's move */
if ((board[25] != 0) && (from_point != 25))
return(0); /* man on bar and didn't select bar */
sign = Uside;
/* see if all pieces are in home base */
bp = &board[25];
for(i = 24; i >= 1; i--)
if (*(--bp) < 0) { /* find man fartherest from home */
All_In = i;
break;
}
/* see if can take off */
if (to_point == from_point) { /* double-clicked? */
if ((All_In < 7) && /* all pieces in home board */
((from_point == die_value) ||
((die_value > All_In) && (from_point == All_In))))
return(1);
else
return(0);
}
}
if ((to_point-from_point) * sign != die_value)
return(0); /* distance to move != die value */
if (board[to_point] * sign < -1)
return(0); /* can't land at "to" point */
return(1); /* I suppose that's all there is.. */
}
int update(BYTE *board, int from_point, int to_point, BYTE sign)
{ /* put a given move onto the board */
/* remove the piece from the source spike */
board[from_point] -= sign;
/* if taking a piece off board or bar, don't go any further */
if ((to_point == from_point) || (to_point >= 25))
return(1);
/* add a piece to the destination spike and determine if capturing */
board[to_point] += sign;
if (board[to_point] == 0) { /* must have hit opponent */
int bar = 0;
if (sign < 0)
bar = 25;
board[to_point] = sign; /* put us onto point */
board[25-bar] -= sign; /* put opponent on bar */
}
return(0);
}