BSD 4_4 release
[unix-history] / usr / src / games / backgammon / common_source / backgammon.c
CommitLineData
07d1919e 1/*-
e0719df9
KB
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
07d1919e 4 *
ad787160
C
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. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
07d1919e 32 */
c4c2d2dc 33
07d1919e 34#ifndef lint
ad787160 35static char sccsid[] = "@(#)backgammon.c 8.1 (Berkeley) 5/31/93";
07d1919e 36#endif /* not lint */
c4c2d2dc
KM
37
38/*
39** The game of Backgammon
40*/
41
42#include <stdio.h>
43
44#define WHITE 0
45#define BROWN 1
46#define NIL (-1)
47#define MAXGMOV 10
48#define MAXIMOVES 1000
49#define RULES "/usr/games/lib/backrules"
50
51char level; /*'b'=beginner, 'i'=intermediate, 'e'=expert*/
52
53int die1;
54int die2;
55int i;
56int j;
57int l;
58int m;
59int pflg = 1;
60int nobroll = 0;
61int count;
62int imoves;
63int goodmoves[MAXGMOV];
64int probmoves[MAXGMOV];
65
66int brown[] = { /* brown position table */
67 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
68 0, 0, 0, 0, 3, 0, 5, 0, 0, 0, 0, 0,
69 0, 0, 0, 0, 0, 0
70};
71
72int white[] = { /* white position table */
73 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
74 0, 0, 0, 0, 3, 0, 5, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0
76};
77
78int probability[] = {
79 0, 11, 12, 13, 14, 15, 16,
80 06, 05, 04, 03, 02, 01
81};
82
83struct {
84 int pos[4];
85 int mov[4];
86} moves[MAXIMOVES];
87
88main()
89{
90 int go[5], tvec[2];
91 int k, n, pid, ret, rpid, t;
92 char s[100];
93
94 srand(time(0));
95 go[5] = NIL;
96 fprintf(stdout, "Instructions? ");
97 gets(s);
98 if(*s == 'y')
99 instructions();
100 putchar('\n');
101 fprintf(stdout, "Opponent's level: b - beginner,\n");
102 fprintf(stdout, "i - intermediate, e - expert? ");
103 level='e';
104 gets(s);
105 if(*s == 'b')
106 level = 'b';
107 else if(*s == 'i')
108 level = 'i';
109 putchar('\n');
110 fprintf(stdout, "You will play brown.\n\n");
111 fprintf(stdout, "Would you like to roll your own dice? ");
112 gets(s);
113 putchar('\n');
114 if(*s == 'y')
115 nobroll = 1;
116 fprintf(stdout, "Would you like to go first? ");
117 gets(s);
118 putchar('\n');
119 if(*s == 'y')
120 goto nowhmove;
121whitesmv:
122 roll(WHITE);
123 fprintf(stdout, "white rolls %d, %d\n", die1, die2);
124 fprintf(stdout, "white's move is:");
125 if(nextmove(white, brown) == NIL)
126 goto nowhmove;
127 if(piececount(white, 0, 24) == 0){
128 fprintf(stdout, "White wins");
129 if(piececount(brown, 0, 6) != 0)
130 fprintf(stdout, " with a Backgammon!\n");
131 else if (piececount(brown, 0, 24) == 24)
132 fprintf(stdout, " with a Gammon.\n");
133 else
134 fprintf(stdout, ".\n");
135 exit(0);
136 }
137nowhmove:
138 if(pflg)
139 prtbrd();
140 roll(BROWN);
141retry:
142 fprintf(stdout, "\nYour roll is %d %d\n", die1, die2);
143 fprintf(stdout, "Move? ");
144 gets(s);
145 switch(*s) {
146 case '\0': /* empty line */
147 fprintf(stdout, "Brown's move skipped.\n");
148 goto whitesmv;
149
150 case 'b': /* how many beared off? */
151 fprintf(stdout, "Brown: %d\n", piececount(brown, 0, 24) - 15);
152 fprintf(stdout, "White: %d\n", piececount(white, 0, 24) - 15);
153 goto retry;
154
155 case 'p': /* print board */
156 prtbrd();
157 goto retry;
158
159 case 's': /* stop auto printing of board */
160 pflg = 0;
161 goto retry;
162
163 case 'r': /* resume auto printing */
164 pflg = 1;
165 goto retry;
166
167 case 'm': /* print possible moves */
168 pmoves();
169 goto retry;
170
171 case 'q': /* i give up */
172 exit(0);
173
174 case '!': /* escape to Shell */
175 if(s[1] != '\0')
176 system(s+1);
177 else if((pid = fork()) == 0) {
178 execl("/bin/sh", "sh", "-", 0);
179 fprintf(stderr, "back: cannot exec /bin/sh!\n");
180 exit(2);
181 }
182 while((rpid = wait(&ret)) != pid && rpid != -1)
183 ;
184 goto retry;
185
186 case '?': /* well, what can i do? */
187 fprintf(stdout, "<newline> skip this move\n");
188 fprintf(stdout, "b number beared off\n");
189 fprintf(stdout, "p print board\n");
190 fprintf(stdout, "q quit\n");
191 fprintf(stdout, "r resume auto print of board\n");
192 fprintf(stdout, "s stop auto print of board\n");
193 fprintf(stdout, "! escape to Shell\n");
194 goto retry;
195 }
196 n = sscanf(s,"%d%d%d%d%d",&go[0],&go[1],&go[2],&go[3],&go[4]);
197 if((die1 != die2 && n > 2) || n > 4){
198 fprintf(stdout, "Too many moves.\n");
199 goto retry;
200 }
201 go[n] = NIL;
202 if(*s=='-'){
203 go[0]= -go[0];
204 t=die1;
205 die1=die2;
206 die2=t;
207 }
208 for(k = 0; k < n; k++){
209 if(0 <= go[k] && go[k] <= 24)
210 continue;
211 else{
212 fprintf(stdout, "Move %d illegal.\n", go[k]);
213 goto retry;
214 }
215 }
216 if(play(brown, white, go))
217 goto retry;
218 if(piececount(brown, 0, 24) == 0){
219 fprintf(stdout, "Brown wins");
220 if(piececount(white, 0, 6) != 0)
221 fprintf(stdout, " with a Backgammon.\n");
222 else if(piececount(white, 0, 24) == 24)
223 fprintf(stdout, " with a gammon.\n");
224 else
225 fprintf(stdout, ".\n");
226 exit(0);
227 }
228 goto whitesmv;
229}
230
231play(player,playee,pos)
232int *player,*playee,pos[];
233{
234 int k, n, die, ipos;
235
236 for(k=0; k < player[0]; k++){ /*blots on player[0] must be moved first*/
237 if(pos[k] == NIL)
238 break;
239 if(pos[k] != 0){
240 fprintf(stdout, "Stone on bar must be moved first.\n");
241 return(NIL);
242 }
243 }
244 for(k = 0; (ipos=pos[k]) != NIL; k++){
245 die = k?die2:die1;
246 n = 25-ipos-die;
247 if(player[ipos] == 0)
248 goto badmove;
249 if(n > 0 && playee[n] >= 2)
250 goto badmove;
251 if(n <= 0){
252 if(piececount(player,0,18) != 0)
253 goto badmove;
254 if((ipos+die) != 25 && piececount(player,19,24-die)!=0)
255 goto badmove;
256 }
257 player[ipos]--;
258 player[ipos+die]++;
259 }
260 for(k = 0; pos[k] != NIL; k++){
261 die = k?die2:die1;
262 n = 25-pos[k]-die;
263 if(n>0 && playee[n]==1){
264 playee[n]=0;
265 playee[0]++;
266 }
267 }
268 return(0);
269
270badmove:
271 fprintf(stdout, "Move %d illegal.\n", ipos);
272 while(k--){
273 die=k?die2:die1;
274 player[pos[k]]++;
275 player[pos[k]+die]--;
276 }
277 return(NIL);
278}
279nextmove(player,playee)
280int *player,*playee;
281{
282 int k;
283
284 imoves=0;
285 movegen(player,playee);
286 if(die1!=die2){
287 k=die1;
288 die1=die2;
289 die2=k;
290 movegen(player,playee);
291 }
292 if(imoves==0){
293 fprintf(stdout, "no move possible.\n");
294 return(NIL);
295 }
296 k=strategy(player,playee); /*select kth possible move*/
297 prtmov(k);
298 update(player,playee,k);
299 return(0);
300}
301prtmov(k)
302int k;
303{
304 int n;
305
306 if(k == NIL)
307 fprintf(stdout, "No move possible\n");
308 else for(n = 0; n < 4; n++){
309 if(moves[k].pos[n] == NIL)
310 break;
311 fprintf(stdout, " %d, %d",25-moves[k].pos[n],moves[k].mov[n]);
312 }
313 fprintf(stdout, "\n");
314}
315update(player,playee,k)
316int *player,*playee,k;
317{
318 int n,t;
319
320 for(n = 0; n < 4; n++){
321 if(moves[k].pos[n] == NIL)
322 break;
323 player[moves[k].pos[n]]--;
324 player[moves[k].pos[n]+moves[k].mov[n]]++;
325 t=25-moves[k].pos[n]-moves[k].mov[n];
326 if(t>0 && playee[t]==1){
327 playee[0]++;
328 playee[t]--;
329 }
330 }
331}
332piececount(player,startrow,endrow)
333int *player,startrow,endrow;
334{
335 int sum;
336
337 sum=0;
338 while(startrow <= endrow)
339 sum += player[startrow++];
340 return(sum);
341}
342pmoves()
343{
344 int i1, i2;
345
346 fprintf(stdout, "Possible moves are:\n");
347 for(i1 = 0; i1 < imoves; i1++){
348 fprintf(stdout, "\n%d",i1);
349 for (i2 = 0; i2<4; i2++){
350 if(moves[i1].pos[i2] == NIL)
351 break;
352 fprintf(stdout, "%d, %d",moves[i1].pos[i2],moves[i1].mov[i2]);
353 }
354 }
355 fprintf(stdout, "\n");
356}
357
358roll(who)
359{
360 register n;
361 char s[10];
362
363 if(who == BROWN && nobroll) {
364 fprintf(stdout, "Roll? ");
365 gets(s);
366 n = sscanf(s, "%d%d", &die1, &die2);
367 if(n != 2 || die1 < 1 || die1 > 6 || die2 < 1 || die2 > 6)
368 fprintf(stdout, "Illegal - I'll do it!\n");
369 else
370 return;
371 }
372 die1 = ((rand()>>8) % 6) + 1;
373 die2 = ((rand()>>8) % 6) + 1;
374}
375
376movegen(mover,movee)
377int *mover,*movee;
378{
379 int k;
380
381 for(i = 0; i <= 24; i++){
382 count = 0;
383 if(mover[i] == 0)
384 continue;
385 if((k=25-i-die1) > 0 && movee[k] >= 2)
386 if(mover[0] > 0)
387 break;
388 else
389 continue;
390 if(k <= 0){
391 if(piececount(mover, 0, 18) != 0)
392 break;
393 if((i+die1) != 25 && piececount(mover,19,i-1) != 0)
394 break;
395 }
396 mover[i]--;
397 mover[i+die1]++;
398 count = 1;
399 for(j = 0; j <= 24; j++){
400 if(mover[j]==0)
401 continue;
402 if((k=25-j-die2) > 0 && movee[k] >= 2)
403 if(mover[0] > 0)
404 break;
405 else
406 continue;
407 if(k <= 0){
408 if(piececount(mover,0,18) != 0)
409 break;
410 if((j+die2) != 25 && piececount(mover,19,j-1) != 0)
411 break;
412 }
413 mover[j]--;
414 mover[j+die2]++;
415 count = 2;
416 if(die1 != die2){
417 moverecord(mover);
418 if(mover[0] > 0)
419 break;
420 else
421 continue;
422 }
423 for(l = 0; l <= 24; l++){
424 if(mover[l] == 0)
425 continue;
426 if((k=25-l-die1) > 0 && movee[k] >= 2)
427 if(mover[0] > 0)
428 break;
429 else
430 continue;
431 if(k <= 0){
432 if(piececount(mover, 0, 18) != 0)
433 break;
434 if((l+die2) != 25 && piececount(mover,19,l-1) != 0)
435 break;
436 }
437 mover[l]--;
438 mover[l+die1]++;
439 count=3;
440 for(m=0;m<=24;m++){
441 if(mover[m]==0)
442 continue;
443 if((k=25-m-die1) >= 0 && movee[k] >= 2)
444 if(mover[0] > 0)
445 break;
446 else
447 continue;
448 if(k <= 0){
449 if(piececount(mover,0,18) != 0)
450 break;
451 if((m+die2) != 25 && piececount(mover,19,m-1) != 0)
452 break;
453 }
454 count=4;
455 moverecord(mover);
456 if(mover[0] > 0)
457 break;
458 }
459 if(count == 3)
460 moverecord(mover);
461 else{
462 mover[l]++;
463 mover[l+die1]--;
464 }
465 if(mover[0] > 0)
466 break;
467 }
468 if(count == 2)
469 moverecord(mover);
470 else{
471 mover[j]++;
472 mover[j+die1]--;
473 }
474 if(mover[0] > 0)
475 break;
476 }
477 if(count == 1)
478 moverecord(mover);
479 else{
480 mover[i]++;
481 mover[i+die1]--;
482 }
483 if(mover[0] > 0)
484 break;
485 }
486}
487moverecord(mover)
488int *mover;
489{
490 int t;
491
492 if(imoves < MAXIMOVES) {
493 for(t = 0; t <= 3; t++)
494 moves[imoves].pos[t] = NIL;
495 switch(count) {
496 case 4:
497 moves[imoves].pos[3]=m;
498 moves[imoves].mov[3]=die1;
499
500 case 3:
501 moves[imoves].pos[2]=l;
502 moves[imoves].mov[2]=die1;
503
504 case 2:
505 moves[imoves].pos[1]=j;
506 moves[imoves].mov[1]=die2;
507
508 case 1:
509 moves[imoves].pos[0]=i;
510 moves[imoves].mov[0]=die1;
511 imoves++;
512 }
513 }
514 switch(count) {
515 case 4:
516 break;
517
518 case 3:
519 mover[l]++;
520 mover[l+die1]--;
521 break;
522
523 case 2:
524 mover[j]++;
525 mover[j+die2]--;
526 break;
527
528 case 1:
529 mover[i]++;
530 mover[i+die1]--;
531 }
532}
533
534strategy(player,playee)
535int *player,*playee;
536{
537 int k, n, nn, bestval, moveval, prob;
538
539 n = 0;
540 if(imoves == 0)
541 return(NIL);
542 goodmoves[0] = NIL;
543 bestval = -32000;
544 for(k = 0; k < imoves; k++){
545 if((moveval=eval(player,playee,k,&prob)) < bestval)
546 continue;
547 if(moveval > bestval){
548 bestval = moveval;
549 n = 0;
550 }
551 if(n<MAXGMOV){
552 goodmoves[n]=k;
553 probmoves[n++]=prob;
554 }
555 }
556 if(level=='e' && n>1){
557 nn=n;
558 n=0;
559 prob=32000;
560 for(k = 0; k < nn; k++){
561 if((moveval=probmoves[k]) > prob)
562 continue;
563 if(moveval<prob){
564 prob=moveval;
565 n=0;
566 }
567 goodmoves[n]=goodmoves[k];
568 probmoves[n++]=probmoves[k];
569 }
570 }
571 return(goodmoves[(rand()>>4)%n]);
572}
573
574eval(player,playee,k,prob)
575int *player,*playee,k,*prob;
576{
577 int newtry[31], newother[31], *r, *q, *p, n, sum, first;
578 int ii, lastwhite, lastbrown;
579
580 *prob = sum = 0;
581 r = player+25;
582 p = newtry;
583 q = newother;
584 while(player<r){
585 *p++= *player++;
586 *q++= *playee++;
587 }
588 q=newtry+31;
589 for(p = newtry+25; p < q; p++) /* zero out spaces for hit pieces */
590 *p = 0;
591 for(n = 0; n < 4; n++){
592 if(moves[k].pos[n] == NIL)
593 break;
594 newtry[moves[k].pos[n]]--;
595 newtry[ii=moves[k].pos[n]+moves[k].mov[n]]++;
596 if(ii<25 && newother[25-ii]==1){
597 newother[25-ii]=0;
598 newother[0]++;
599 if(ii<=15 && level=='e') /* hit if near other's home */
600 sum++;
601 }
602 }
603 for(lastbrown = 0; newother[lastbrown] == 0; lastbrown++);
604 ;
605 for(lastwhite = 0; newtry[lastwhite] == 0; lastwhite++)
606 ;
607 lastwhite = 25-lastwhite;
608 if(lastwhite<=6 && lastwhite<lastbrown)
609 sum=1000;
610 /* experts running game. */
611 /* first priority is to */
612 /* get all pieces into */
613 /* white's home */
614 if(lastwhite<lastbrown && level=='e' && lastwhite>6) {
615 for(sum = 1000; lastwhite > 6; lastwhite--)
616 sum = sum-lastwhite*newtry[25-lastwhite];
617 }
618 for(first = 0; first < 25; first++)
619 if(newother[first] != 0) /*find other's first piece*/
620 break;
621 q = newtry+25;
622 for(p = newtry+1; p < q;) /* blocked points are good */
623 if(*p++ > 1)
624 sum++;
625 if(first > 5) { /* only stress removing pieces if */
626 /* homeboard cannot be hit */
627 q = newtry+31;
628 p=newtry+25;
629 for(n = 6; p < q; n--)
630 sum += *p++ * n; /*remove pieces, but just barely*/
631 }
632 if(level != 'b'){
633 r = newtry+25-first; /*singles past this point can't be hit*/
634 for(p = newtry+7; p < r; )
635 if(*p++ == 1) /*singles are bad after 1st 6 points if they can be hit*/
636 sum--;
637 q = newtry+3;
638 for(p = newtry; p < q; ) /*bad to be on 1st three points*/
639 sum -= *p++;
640 }
641
642 for(n = 1; n <= 4; n++)
643 *prob += n*getprob(newtry,newother,6*n-5,6*n);
644 return(sum);
645}
646instructions()
647{
648 register fd, r;
649 char buf[BUFSIZ];
650
651 if((fd = open(RULES, 0)) < 0) {
652 fprintf(stderr, "back: cannot open %s\n", RULES);
653 return;
654 }
655 while(r = read(fd, buf, BUFSIZ))
656 write(1, buf, r);
657}
658
659getprob(player,playee,start,finish)
660int *player,*playee,start,finish;
661{ /*returns the probability (times 102) that any
662 pieces belonging to 'player' and lying between
663 his points 'start' and 'finish' will be hit
664 by a piece belonging to playee
665 */
666 int k, n, sum;
667
668 sum = 0;
669 for(; start <= finish; start++){
670 if(player[start] == 1){
671 for(k = 1; k <= 12; k++){
672 if((n=25-start-k) < 0)
673 break;
674 if(playee[n] != 0)
675 sum += probability[k];
676 }
677 }
678 }
679 return(sum);
680}
681prtbrd()
682{
683 int k;
684 static char undersc[]="______________________________________________________";
685
686 fprintf(stdout, "White's Home\n%s\r",undersc);
687 for(k = 1; k <= 6; k++)
688 fprintf(stdout, "%4d",k);
689 fprintf(stdout, " ");
690 for(k = 7; k <= 12; k++)
691 fprintf(stdout, "%4d",k);
692 putchar('\n');
693 numline(brown, white, 1, 6);
694 fprintf(stdout, " ");
695 numline(brown, white, 7, 12);
696 putchar('\n');
697 colorline(brown, 'B', white, 'W', 1, 6);
698 fprintf(stdout, " ");
699 colorline(brown, 'B', white, 'W', 7, 12);
700 putchar('\n');
701 if(white[0] != 0)
702 fprintf(stdout, "%28dW\n",white[0]);
703 else
704 putchar('\n');
705 if(brown[0] != 0)
706 fprintf(stdout, "%28dB\n", brown[0]);
707 else
708 putchar('\n');
709 colorline(white, 'W', brown, 'B', 1, 6);
710 fprintf(stdout, " ");
711 colorline(white, 'W', brown, 'B', 7, 12);
712 fprintf(stdout, "\n%s\r",undersc);
713 numline(white, brown, 1, 6);
714 fprintf(stdout, " ");
715 numline(white, brown, 7, 12);
716 putchar('\n');
717 for(k = 24; k >= 19; k--)
718 fprintf(stdout, "%4d",k);
719 fprintf(stdout, " ");
720 for(k = 18; k >= 13; k--)
721 fprintf(stdout, "%4d",k);
722 fprintf(stdout, "\nBrown's Home\n\n\n\n\n");
723}
724numline(upcol,downcol,start,fin)
725int *upcol,*downcol,start,fin;
726{
727 int k, n;
728
729 for(k = start; k <= fin; k++){
730 if((n = upcol[k]) != 0 || (n = downcol[25-k]) != 0)
731 fprintf(stdout, "%4d", n);
732 else
733 fprintf(stdout, " ");
734 }
735}
736colorline(upcol,c1,downcol,c2,start,fin)
737int *upcol,*downcol,start,fin;
738char c1,c2;
739{
740 int k;
741 char c;
742
743 for(k = start; k <= fin; k++){
744 c = ' ';
745 if(upcol[k] != 0)
746 c = c1;
747 if(downcol[25-k] != 0)
748 c = c2;
749 fprintf(stdout, " %c",c);
750 }
751}