Home | History | Annotate | Line # | Download | only in backgammon
      1 /*	$NetBSD: main.c,v 1.36 2024/08/22 20:46:40 rillig Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
     35  The Regents of the University of California.  All rights reserved.");
     36 #endif /* not lint */
     37 
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 5/31/93";
     41 #else
     42 __RCSID("$NetBSD: main.c,v 1.36 2024/08/22 20:46:40 rillig Exp $");
     43 #endif
     44 #endif				/* not lint */
     45 
     46 #include <time.h>
     47 
     48 #include "back.h"
     49 #include "backlocal.h"
     50 
     51 #define MVPAUSE	5		/* time to sleep when stuck */
     52 
     53 extern const char   *const instr[];		/* text of instructions */
     54 extern const char   *const message[];		/* update message */
     55 
     56 static const char *const helpm[] = {		/* help message */
     57 	"Enter a space or newline to roll, or",
     58 	"     R   to reprint the board\tD   to double",
     59 	"     S   to save the game\tQ   to quit",
     60 	0
     61 };
     62 
     63 static const char *const contin[] = {		/* pause message */
     64 	"(Type a newline to continue.)",
     65 	"",
     66 	0
     67 };
     68 static const char rules[] = "\nDo you want the rules of the game?";
     69 static const char noteach[] = "Teachgammon not available!\n\a";
     70 static const char need[] = "Do you need instructions for this program?";
     71 static const char askcol[] =
     72 "Enter 'r' to play red, 'w' to play white, 'b' to play both:";
     73 static const char rollr[] = "Red rolls a ";
     74 static const char rollw[] = ".  White rolls a ";
     75 static const char rstart[] = ".  Red starts.\n";
     76 static const char wstart[] = ".  White starts.\n";
     77 static const char toobad1[] = "Too bad, ";
     78 static const char unable[] = " is unable to use that roll.\n";
     79 static const char toobad2[] = ".  Too bad, ";
     80 static const char cantmv[] = " can't move.\n";
     81 static const char bgammon[] = "Backgammon!  ";
     82 static const char gammon[] = "Gammon!  ";
     83 static const char again[] = ".\nWould you like to play again?";
     84 static const char svpromt[] = "Would you like to save this game?";
     85 
     86 static const char password[] = "losfurng";
     87 static char pbuf[10];
     88 
     89 int
     90 main(int argc __unused, char **argv)
     91 {
     92 	int     i;		/* non-descript index */
     93 	int     l;		/* non-descript index */
     94 	char    c;		/* non-descript character storage */
     95 	time_t  t;		/* time for random num generator */
     96 	struct move mmstore, *mm;
     97 
     98 	/* revoke setgid privileges */
     99 	setgid(getgid());
    100 
    101 	/* initialization */
    102 	bflag = 2;		/* default no board */
    103 	signal(SIGINT, getout);	/* trap interrupts */
    104 	if (tcgetattr(0, &old) == -1)	/* get old tty mode */
    105 		errexit("backgammon(gtty)");
    106 	noech = old;
    107 	noech.c_lflag &= ~ECHO;
    108 	raw = noech;
    109 	raw.c_lflag &= ~ICANON;	/* set up modes */
    110 	ospeed = cfgetospeed(&old);	/* for termlib */
    111 
    112 	/* get terminal capabilities, and decide if it can cursor address */
    113 	tflag = getcaps(getenv("TERM"));
    114 	/* use whole screen for text */
    115 	if (tflag)
    116 		begscr = 0;
    117 	t = time(NULL);
    118 	srandom((unsigned)t);	/* 'random' seed */
    119 
    120 	/* need this now beceause getarg() may try to load a game */
    121 	mm = &mmstore;
    122 	move_init(mm);
    123 	while (*++argv != 0)	/* process arguments */
    124 		getarg(mm, &argv);
    125 	args[acnt] = '\0';
    126 	if (tflag) {		/* clear screen */
    127 		noech.c_oflag &= ~(ONLCR | OXTABS);
    128 		raw.c_oflag &= ~(ONLCR | OXTABS);
    129 		clear();
    130 	}
    131 	fixtty(&raw);		/* go into raw mode */
    132 
    133 	/* check if restored game and save flag for later */
    134 	if ((rfl = rflag) != 0) {
    135 		wrtext(message);	/* print message */
    136 		wrtext(contin);
    137 		wrboard();	/* print board */
    138 		/* if new game, pretend to be a non-restored game */
    139 		if (cturn == 0)
    140 			rflag = 0;
    141 	} else {
    142 		rscore = wscore = 0;	/* zero score */
    143 		wrtext(message);	/* update message without pausing */
    144 
    145 		if (aflag) {	/* print rules */
    146 			writel(rules);
    147 			if (yorn(0)) {
    148 
    149 				fixtty(&old);	/* restore tty */
    150 				execl(TEACH, "teachgammon", args[0]?args:0,
    151 				      (char *) 0);
    152 
    153 				tflag = 0;	/* error! */
    154 				writel(noteach);
    155 				exit(1);
    156 			} else {/* if not rules, then instructions */
    157 				writel(need);
    158 				if (yorn(0)) {	/* print instructions */
    159 					clear();
    160 					wrtext(instr);
    161 				}
    162 			}
    163 		}
    164 		init();		/* initialize board */
    165 
    166 		if (pnum == 2) {/* ask for color(s) */
    167 			writec('\n');
    168 			writel(askcol);
    169 			while (pnum == 2) {
    170 				c = readc();
    171 				switch (c) {
    172 
    173 				case 'R':	/* red */
    174 					pnum = -1;
    175 					break;
    176 
    177 				case 'W':	/* white */
    178 					pnum = 1;
    179 					break;
    180 
    181 				case 'B':	/* both */
    182 					pnum = 0;
    183 					break;
    184 
    185 				case 'P':
    186 					if (iroll)
    187 						break;
    188 					if (tflag)
    189 						curmove(curr, 0);
    190 					else
    191 						writec('\n');
    192 					writel("Password:");
    193 					signal(SIGALRM, getout);
    194 					cflag = 1;
    195 					alarm(10);
    196 					for (i = 0; i < 10; i++) {
    197 						pbuf[i] = readc();
    198 						if (pbuf[i] == '\n')
    199 							break;
    200 					}
    201 					if (i == 10)
    202 						while (readc() != '\n');
    203 					alarm(0);
    204 					cflag = 0;
    205 					if (i < 10)
    206 						pbuf[i] = '\0';
    207 					for (i = 0; i < 9; i++)
    208 						if (pbuf[i] != password[i])
    209 							getout(0);
    210 					iroll = 1;
    211 					if (tflag)
    212 						curmove(curr, 0);
    213 					else
    214 						writec('\n');
    215 					writel(askcol);
    216 					break;
    217 
    218 				default:	/* error */
    219 					writec('\007');
    220 				}
    221 			}
    222 		} else
    223 			if (!aflag)
    224 				/* pause to read message */
    225 				wrtext(contin);
    226 
    227 		wrboard();	/* print board */
    228 
    229 		if (tflag)
    230 			curmove(18, 0);
    231 		else
    232 			writec('\n');
    233 	}
    234 	/* limit text to bottom of screen */
    235 	if (tflag)
    236 		begscr = 17;
    237 
    238 	for (;;) {		/* begin game! */
    239 		/* initial roll if needed */
    240 		if ((!rflag) || raflag)
    241 			roll(mm);
    242 
    243 		/* perform ritual of first roll */
    244 		if (!rflag) {
    245 			if (tflag)
    246 				curmove(17, 0);
    247 			while (mm->D0 == mm->D1)	/* no doubles */
    248 				roll(mm);
    249 
    250 			/* print rolls */
    251 			writel(rollr);
    252 			writec(mm->D0 + '0');
    253 			writel(rollw);
    254 			writec(mm->D1 + '0');
    255 
    256 			/* winner goes first */
    257 			if (mm->D0 > mm->D1) {
    258 				writel(rstart);
    259 				cturn = 1;
    260 			} else {
    261 				writel(wstart);
    262 				cturn = -1;
    263 			}
    264 		}
    265 		/* initialize variables according to whose turn it is */
    266 
    267 		if (cturn == 1) {	/* red */
    268 			home = 25;
    269 			bar = 0;
    270 			inptr = &in[1];
    271 			inopp = &in[0];
    272 			offptr = &off[1];
    273 			offopp = &off[0];
    274 			Colorptr = &color[1];
    275 			colorptr = &color[3];
    276 			colen = 3;
    277 		} else {	/* white */
    278 			home = 0;
    279 			bar = 25;
    280 			inptr = &in[0];
    281 			inopp = &in[1];
    282 			offptr = &off[0];
    283 			offopp = &off[1];
    284 			Colorptr = &color[0];
    285 			colorptr = &color[2];
    286 			colen = 5;
    287 		}
    288 
    289 		/* do first move (special case) */
    290 		if (!(rflag && raflag)) {
    291 			if (cturn == pnum)	/* computer's move */
    292 				move(mm, 0);
    293 			else {	/* player's move */
    294 				mm->mvlim = movallow(mm);
    295 				/* reprint roll */
    296 				if (tflag)
    297 					curmove(cturn == -1 ? 18 : 19, 0);
    298 				proll(mm);
    299 				getmove(mm);	/* get player's move */
    300 			}
    301 		}
    302 		if (tflag) {
    303 			curmove(17, 0);
    304 			cline();
    305 			begscr = 18;
    306 		}
    307 		/* no longer any diff- erence between normal game and
    308 		 * recovered game. */
    309 		rflag = 0;
    310 
    311 		/* move as long as it's someone's turn */
    312 		while (cturn == 1 || cturn == -1) {
    313 
    314 			/* board maintenance */
    315 			if (tflag)
    316 				refresh();	/* fix board */
    317 			else
    318 				/* redo board if -p */
    319 				if (cturn == bflag || bflag == 0)
    320 					wrboard();
    321 
    322 			/* do computer's move */
    323 			if (cturn == pnum) {
    324 				move(mm, 1);
    325 
    326 				/* see if double refused */
    327 				if (cturn == -2 || cturn == 2)
    328 					break;
    329 
    330 				/* check for winning move */
    331 				if (*offopp == 15) {
    332 					cturn *= -2;
    333 					break;
    334 				}
    335 				continue;
    336 
    337 			}
    338 			/* (player's move) */
    339 
    340 			/* clean screen if safe */
    341 			if (tflag && hflag) {
    342 				curmove(20, 0);
    343 				clend();
    344 				hflag = 1;
    345 			}
    346 			/* if allowed, give him a chance to double */
    347 			if (dlast != cturn && gvalue < 64) {
    348 				if (tflag)
    349 					curmove(cturn == -1 ? 18 : 19, 0);
    350 				writel(*Colorptr);
    351 				c = readc();
    352 
    353 				/* character cases */
    354 				switch (c) {
    355 
    356 					/* reprint board */
    357 				case 'R':
    358 					wrboard();
    359 					break;
    360 
    361 					/* save game */
    362 				case 'S':
    363 					raflag = 1;
    364 					save(mm, 1);
    365 					break;
    366 
    367 					/* quit */
    368 				case 'Q':
    369 					quit(mm);
    370 					break;
    371 
    372 					/* double */
    373 				case 'D':
    374 					dble();
    375 					break;
    376 
    377 					/* roll */
    378 				case ' ':
    379 				case '\n':
    380 					roll(mm);
    381 					writel(" rolls ");
    382 					writec(mm->D0 + '0');
    383 					writec(' ');
    384 					writec(mm->D1 + '0');
    385 					writel(".  ");
    386 
    387 					/* see if he can move */
    388 					if ((mm->mvlim = movallow(mm)) == 0) {
    389 
    390 						/* can't move */
    391 						writel(toobad1);
    392 						writel(*colorptr);
    393 						writel(unable);
    394 						if (tflag) {
    395 							if (pnum) {
    396 								buflush();
    397 								sleep(MVPAUSE);
    398 							}
    399 						}
    400 						nexturn();
    401 						break;
    402 					}
    403 					/* get move */
    404 					getmove(mm);
    405 
    406 					/* okay to clean screen */
    407 					hflag = 1;
    408 					break;
    409 
    410 					/* invalid character */
    411 				default:
    412 
    413 					/* print help message */
    414 					if (tflag)
    415 						curmove(20, 0);
    416 					else
    417 						writec('\n');
    418 					wrtext(helpm);
    419 					if (tflag)
    420 						curmove(cturn == -1 ?
    421 						    18 : 19, 0);
    422 					else
    423 						writec('\n');
    424 
    425 					/* don't erase */
    426 					hflag = 0;
    427 				}
    428 			} else {/* couldn't double */
    429 
    430 				/* print roll */
    431 				roll(mm);
    432 				if (tflag)
    433 					curmove(cturn == -1 ? 18 : 19, 0);
    434 				proll(mm);
    435 
    436 				/* can he move? */
    437 				if ((mm->mvlim = movallow(mm)) == 0) {
    438 
    439 					/* he can't */
    440 					writel(toobad2);
    441 					writel(*colorptr);
    442 					writel(cantmv);
    443 					buflush();
    444 					sleep(MVPAUSE);
    445 					nexturn();
    446 					continue;
    447 				}
    448 				/* get move */
    449 				getmove(mm);
    450 			}
    451 		}
    452 
    453 		/* don't worry about who won if quit */
    454 		if (cturn == 0)
    455 			break;
    456 
    457 		/* fix cturn = winner */
    458 		cturn /= -2;
    459 
    460 		/* final board pos. */
    461 		if (tflag)
    462 			refresh();
    463 
    464 		/* backgammon? */
    465 		mflag = 0;
    466 		l = bar + 7 * cturn;
    467 		for (i = bar; i != l; i += cturn)
    468 			if (board[i] && cturn)
    469 				mflag++;
    470 
    471 		/* compute game value */
    472 		if (tflag)
    473 			curmove(20, 0);
    474 		if (*offopp == 15 && (*offptr == 0 || *offptr == -15)) {
    475 			if (mflag) {
    476 				writel(bgammon);
    477 				gvalue *= 3;
    478 			} else {
    479 				writel(gammon);
    480 				gvalue *= 2;
    481 			}
    482 		}
    483 		/* report situation */
    484 		if (cturn == -1) {
    485 			writel("Red wins ");
    486 			rscore += gvalue;
    487 		} else {
    488 			writel("White wins ");
    489 			wscore += gvalue;
    490 		}
    491 		wrint(gvalue);
    492 		writel(" point");
    493 		if (gvalue > 1)
    494 			writec('s');
    495 		writel(".\n");
    496 
    497 		/* write score */
    498 		wrscore();
    499 
    500 		/* see if he wants another game */
    501 		writel(again);
    502 		if ((i = yorn('S')) == 0)
    503 			break;
    504 
    505 		init();
    506 		if (i == 2) {
    507 			writel("  Save.\n");
    508 			cturn = 0;
    509 			save(mm, 0);
    510 		}
    511 		/* yes, reset game */
    512 		wrboard();
    513 	}
    514 
    515 	/* give him a chance to save if game was recovered */
    516 	if (rfl && cturn) {
    517 		writel(svpromt);
    518 		if (yorn(0)) {
    519 			/* re-initialize for recovery */
    520 			init();
    521 			cturn = 0;
    522 			save(mm, 0);
    523 		}
    524 	}
    525 	/* leave peacefully */
    526 	getout(0);
    527 	/* NOTREACHED */
    528 	return (0);
    529 }
    530