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