xref: /netbsd-src/games/backgammon/backgammon/main.c (revision 9a69f12d338a2b183268948a71393b9f3ecd75d6)
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