xref: /openbsd-src/games/backgammon/backgammon/main.c (revision bda84ce940729ea62ecb251ada05533d1b1163fc)
1 /*	$OpenBSD: main.c,v 1.25 2021/10/23 11:22:48 mestre 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 <err.h>
33 
34 #include "back.h"
35 #include "backlocal.h"
36 
37 #define MVPAUSE	5		/* time to sleep when stuck */
38 
39 extern const char   *const instruct[];		/* text of instructions */
40 
41 const char   *const helpm[] = {		/* help message */
42 	"Enter a space or newline to roll, or",
43 	"     R   to reprint the board\tD   to double",
44 	"     S   to save the game\tQ   to quit",
45 	0
46 };
47 
48 const char   *const contin[] = {		/* pause message */
49 	"(Type a newline to continue.)",
50 	"",
51 	0
52 };
53 
54 /*   *** Do game control through dm! ***
55  * static char user1a[] =
56  * 	"Sorry, you cannot play backgammon when there are more than ";
57  * static char user1b[] =
58  * 	" users\non the system.";
59  * static char user2a[] =
60  * 	"\nThere are now more than ";
61  * static char user2b[] =
62  * 	" users on the system, so you cannot play\nanother game.  ";
63  */
64 static const char rules[] = "\nDo you want the rules of the game?";
65 static const char noteach[] = "Teachgammon not available!\n\007";
66 static const char need[] = "Do you need instructions for this program?";
67 static const char askcol[] =
68 	"Enter 'r' to play red, 'w' to play white, 'b' to play both:";
69 static const char rollr[] = "Red rolls a ";
70 static const char rollw[] = ".  White rolls a ";
71 static const char rstart[] = ".  Red starts.\n";
72 static const char wstart[] = ".  White starts.\n";
73 static const char toobad1[] = "Too bad, ";
74 static const char unable[] = " is unable to use that roll.\n";
75 static const char toobad2[] = ".  Too bad, ";
76 static const char cantmv[] = " can't move.\n";
77 static const char bgammon[] = "Backgammon!  ";
78 static const char gammon[] = "Gammon!  ";
79 static const char again[] = ".\nWould you like to play again?";
80 static const char svpromt[] = "Would you like to save this game?";
81 
82 int
main(int argc,char ** argv)83 main (int argc, char **argv)
84 {
85 	int     i,l;		/* non-descript indices */
86 	char    c;		/* non-descript character storage */
87 
88 	signal(SIGINT, getout);	/* trap interrupts */
89 
90 	/* use whole screen for text */
91 	begscr = 0;
92 
93 	getarg(argc, argv);
94 
95 	initcurses();
96 
97 	/* check if restored game and save flag for later */
98 	if ((rfl = rflag)) {
99 		if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
100 			err(1, "pledge");
101 
102 		wrboard();	/* print board */
103 		/* if new game, pretend to be a non-restored game */
104 		if (cturn == 0)
105 			rflag = 0;
106 	} else {
107 		if (pledge("stdio rpath wpath cpath tty exec", NULL) == -1)
108 			err(1, "pledge");
109 
110 		rscore = wscore = 0;	/* zero score */
111 
112 		if (aflag) {	/* print rules */
113 			addstr(rules);
114 			if (yorn(0)) {
115 				endwin();
116 				execl(TEACH, "teachgammon", (char *)NULL);
117 
118 				err(1, "%s", noteach);
119 			} else {/* if not rules, then instructions */
120 				addstr(need);
121 				if (yorn(0)) {	/* print instructions */
122 					clear();
123 					text(instruct);
124 				}
125 			}
126 		}
127 
128 		if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
129 			err(1, "pledge");
130 
131 		init();		/* initialize board */
132 
133 		if (pnum == 2) {/* ask for color(s) */
134 			printw("\n%s", askcol);
135 			while (pnum == 2) {
136 				c = readc();
137 				switch (c) {
138 
139 				case 'R':	/* red */
140 					pnum = -1;
141 					break;
142 
143 				case 'W':	/* white */
144 					pnum = 1;
145 					break;
146 
147 				case 'B':	/* both */
148 					pnum = 0;
149 					break;
150 
151 				case 'P':	/* Control the dice */
152 					iroll = 1;
153 					addstr("\nDice controlled!\n");
154 					addstr(askcol);
155 					break;
156 
157 				default:	/* error */
158 					beep();
159 				}
160 			}
161 		}
162 
163 		wrboard();		/* print board */
164 
165 		move(18, 0);
166 	}
167 	/* limit text to bottom of screen */
168 	begscr = 17;
169 
170 	for (;;)  {			/* begin game! */
171 		/* initial roll if needed */
172 		if ((!rflag) || raflag)
173 			roll();
174 
175 		/* perform ritual of first roll */
176 		if (!rflag) {
177 			move(17, 0);
178 			while (D0 == D1)	/* no doubles */
179 				roll();
180 
181 			/* print rolls */
182 			printw("%s%d%s%d", rollr, D0, rollw, D1);
183 
184 			/* winner goes first */
185 			if (D0 > D1) {
186 				addstr(rstart);
187 				cturn = 1;
188 			} else {
189 				addstr(wstart);
190 				cturn = -1;
191 			}
192 		}
193 		/* initialize variables according to whose turn it is */
194 
195 		if (cturn == 1) {	/* red */
196 			home = 25;
197 			bar = 0;
198 			inptr = &in[1];
199 			inopp = &in[0];
200 			offptr = &off[1];
201 			offopp = &off[0];
202 			Colorptr = &color[1];
203 			colorptr = &color[3];
204 			colen = 3;
205 		} else {		/* white */
206 			home = 0;
207 			bar = 25;
208 			inptr = &in[0];
209 			inopp = &in[1];
210 			offptr = &off[0];
211 			offopp = &off[1];
212 			Colorptr = &color[0];
213 			colorptr = &color[2];
214 			colen = 5;
215 		}
216 
217 		/* do first move (special case) */
218 		if (!(rflag && raflag)) {
219 			if (cturn == pnum)	/* computer's move */
220 				domove(0);
221 			else {	/* player's move */
222 				mvlim = movallow();
223 				/* reprint roll */
224 				move(cturn == -1 ? 18 : 19, 0);
225 				proll();
226 				getmove();	/* get player's move */
227 			}
228 		}
229 		move(17, 0);
230 		clrtoeol();
231 		begscr = 18;
232 		/* no longer any difference between normal and recovered game. */
233 		rflag = 0;
234 
235 		/* move as long as it's someone's turn */
236 		while (cturn == 1 || cturn == -1) {
237 
238 			/* board maintainence */
239 			moveplayers();	/* fix board */
240 
241 			/* do computer's move */
242 			if (cturn == pnum) {
243 				domove(1);
244 
245 				/* see if double refused */
246 				if (cturn == -2 || cturn == 2)
247 					break;
248 
249 				/* check for winning move */
250 				if (*offopp == 15) {
251 					cturn *= -2;
252 					break;
253 				}
254 				continue;
255 
256 			}
257 			/* (player's move) */
258 
259 			/* clean screen if safe */
260 			if (hflag) {
261 				move(20, 0);
262 				clrtobot();
263 				hflag = 1;
264 			}
265 			/* if allowed, give him a chance to double */
266 			if (dflag && dlast != cturn && gvalue < 64) {
267 				move(cturn == -1 ? 18: 19, 0);
268 				addstr(*Colorptr);
269 				c = readc();
270 
271 				/* character cases */
272 				switch (c) {
273 
274 				case 'R':		/* reprint board */
275 					wrboard();
276 					break;
277 
278 				case 'S':		/* save game */
279 					raflag = 1;
280 					save(1);
281 					break;
282 
283 				case 'Q':		/* quit */
284 					quit();
285 					break;
286 
287 				case 'D':		/* double */
288 					dble();
289 					break;
290 
291 				case ' ':		/* roll */
292 				case '\n':
293 					roll();
294 					printw(" rolls %d %d.  ", D0, D1);
295 
296 					/* see if he can move */
297 					if ((mvlim = movallow()) == 0) {
298 
299 						/* can't move */
300 						printw("%s%s%s", toobad1, *colorptr, unable);
301 						if (pnum) {
302 							moveplayers();
303 							sleep(MVPAUSE);
304 						}
305 						nexturn();
306 						break;
307 					}
308 
309 					getmove();
310 
311 					/* okay to clean screen */
312 					hflag = 1;
313 					break;
314 
315 				default:		/* invalid character */
316 
317 					/* print help message */
318 					move(20, 0);
319 					text(helpm);
320 					move(cturn == -1 ? 18 : 19, 0);
321 
322 					/* don't erase */
323 					hflag = 0;
324 				}
325 			} else {/* couldn't double */
326 
327 				/* print roll */
328 				roll();
329 				move(cturn == -1 ? 18: 19, 0);
330 				proll();
331 
332 				/* can he move? */
333 				if ((mvlim = movallow()) == 0) {
334 
335 					/* he can't */
336 					printw("%s%s%s", toobad2, *colorptr, cantmv);
337 					moveplayers();
338 					sleep(MVPAUSE);
339 					nexturn();
340 					continue;
341 				}
342 
343 				getmove();
344 			}
345 		}
346 
347 		/* don't worry about who won if quit */
348 		if (cturn == 0)
349 			break;
350 
351 		/* fix cturn = winner */
352 		cturn /= -2;
353 
354 		/* final board pos. */
355 		moveplayers();
356 
357 		/* backgammon? */
358 		mflag = 0;
359 		l = bar + 7 * cturn;
360 		for (i = bar; i != l; i += cturn)
361 			if (board[i] * cturn)
362 				mflag++;
363 
364 		/* compute game value */
365 		move(20, 0);
366 		if (*offopp == 15) {
367 			if (mflag) {
368 				addstr(bgammon);
369 				gvalue *= 3;
370 			}
371 			else if (*offptr <= 0) {
372 				addstr(gammon);
373 				gvalue *= 2;
374 			}
375 		}
376 		/* report situation */
377 		if (cturn == -1) {
378 			addstr("Red wins ");
379 			rscore += gvalue;
380 		} else {
381 			addstr("White wins ");
382 			wscore += gvalue;
383 		}
384 		printw("%d point%s.\n", gvalue, (gvalue > 1) ? "s":"");
385 
386 		/* write score */
387 		wrscore();
388 
389 		/* see if he wants another game */
390 		addstr(again);
391 		if ((i = yorn('S')) == 0)
392 			break;
393 
394 		init();
395 		if (i == 2) {
396 			addstr("  Save.\n");
397 			cturn = 0;
398 			save(0);
399 		}
400 		/* yes, reset game */
401 		wrboard();
402 	}
403 
404 	/* give him a chance to save if game was recovered */
405 	if (rfl && cturn) {
406 		addstr(svpromt);
407 		if (yorn(0)) {
408 			/* re-initialize for recovery */
409 			init();
410 			cturn = 0;
411 			save(0);
412 		}
413 	}
414 	/* leave peacefully */
415 	getout(0);
416 	/* NOT REACHED */
417 }
418