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
main(int argc __unused,char ** argv)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