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