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