|
|
1.1 root 1: /*
2: * Copyright (c) 1980 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted
6: * provided that the above copyright notice and this paragraph are
7: * duplicated in all such forms and that any documentation,
8: * advertising materials, and other materials related to such
9: * distribution and use acknowledge that the software was developed
10: * by the University of California, Berkeley. The name of the
11: * University may not be used to endorse or promote products derived
12: * from this software without specific prior written permission.
13: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15: * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16: */
17:
18: #ifndef lint
19: char copyright[] =
20: "@(#) Copyright (c) 1980 Regents of the University of California.\n\
21: All rights reserved.\n";
22: #endif /* not lint */
23:
24: #ifndef lint
25: static char sccsid[] = "@(#)snake.c 5.6 (Berkeley) 6/18/88";
26: #endif /* not lint */
27:
28: /*
29: * snake - crt hack game.
30: *
31: * You move around the screen with arrow keys trying to pick up money
32: * without getting eaten by the snake. hjkl work as in vi in place of
33: * arrow keys. You can leave at the exit any time.
34: *
35: * compile as follows:
36: * cc -O snake.c move.c -o snake -lm -ltermlib
37: */
38:
39: #include <sys/param.h>
40: #include <pwd.h>
41: #include "snake.h"
42:
43: /*
44: * This is the data file for scorekeeping.
45: */
46: #ifndef SNAKERAWSCORES
47: #define SNAKERAWSCORES "/usr/games/lib/snakerawscores"
48: #endif
49:
50: /*
51: * If it exists, a log is kept here. Otherwise it isn't.
52: */
53: #ifndef LOGFILE
54: #define LOGFILE "/usr/games/lib/snake.log"
55: #endif
56:
57: #define PENALTY 10 /* % penalty for invoking spacewarp */
58:
59: #define EOT '\004'
60: #define LF '\n'
61: #define DEL '\177'
62:
63: #define ME 'I'
64: #define SNAKEHEAD 'S'
65: #define SNAKETAIL 's'
66: #define TREASURE '$'
67: #define GOAL '#'
68:
69: #define BSIZE 80
70:
71: struct point you;
72: struct point money;
73: struct point finish;
74: struct point snake[6];
75:
76: int loot, penalty;
77: int long tl, tm=0L;
78: int moves;
79: char stri[BSIZE];
80: char *p;
81: char ch, savec;
82: char *kl, *kr, *ku, *kd;
83: int fast=1;
84: int repeat=1;
85: long tv;
86: char *tn;
87:
88: main(argc,argv)
89: int argc;
90: char **argv;
91: {
92: extern char *optarg;
93: extern int optind;
94: int ch, i, j, k;
95: time_t time();
96: long atol();
97: int stop();
98:
99: (void)time(&tv);
100: srandom((int)tv);
101:
102: while ((ch = getopt(argc, argv, "l:w:")) != EOF)
103: switch((char)ch) {
104: #ifdef notdef
105: case 'd':
106: tv = atol(optarg);
107: break;
108: #endif
109: case 'w': /* width */
110: ccnt = atoi(optarg);
111: break;
112: case 'l': /* length */
113: lcnt = atoi(optarg);
114: break;
115: case '?':
116: default:
117: fputs("usage: snake [-d seed] [-w width] [-l length]\n", stderr);
118: exit(1);
119: }
120:
121: penalty = loot = 0;
122: getcap();
123:
124: i = MIN(lcnt, ccnt);
125: if (i < 4) {
126: cook();
127: printf("snake: screen too small for a fair game.\n");
128: exit(1);
129: }
130:
131: /*
132: * chunk is the amount of money the user gets for each $.
133: * The formula below tries to be fair for various screen sizes.
134: * We only pay attention to the smaller of the 2 edges, since
135: * that seems to be the bottleneck.
136: * This formula is a hyperbola which includes the following points:
137: * (24, $25) (original scoring algorithm)
138: * (12, $40) (experimentally derived by the "feel")
139: * (48, $15) (a guess)
140: * This will give a 4x4 screen $99/shot. We don't allow anything
141: * smaller than 4x4 because there is a 3x3 game where you can win
142: * an infinite amount of money.
143: */
144: if (i < 12) i = 12; /* otherwise it isn't fair */
145: /*
146: * Compensate for border. This really changes the game since
147: * the screen is two squares smaller but we want the default
148: * to be $25, and the high scores on small screens were a bit
149: * much anyway.
150: */
151: i += 2;
152: chunk = (675.0 / (i+6)) + 2.5; /* min screen edge */
153:
154: signal (SIGINT, stop);
155: putpad(TI); /* String to begin programs that use cm */
156: putpad(KS); /* Put terminal in keypad transmit mode */
157:
158: snrand(&finish);
159: snrand(&you);
160: snrand(&money);
161: snrand(&snake[0]);
162:
163: if ((orig.sg_ospeed < B9600) ||
164: ((! CM) && (! TA))) fast=0;
165: for(i=1;i<6;i++)
166: chase (&snake[i], &snake[i-1]);
167: setup();
168: mainloop();
169: }
170:
171: /* Main command loop */
172: mainloop()
173: {
174: int j, k;
175:
176: for (;;) {
177: int c,lastc,match;
178:
179: move(&you);
180: fflush(stdout);
181: if (((c = getchar() & 0177) <= '9') && (c >= '0')) {
182: ungetc(c,stdin);
183: j = scanf("%d",&repeat);
184: c = getchar() & 0177;
185: } else {
186: if (c != '.') repeat = 1;
187: }
188: if (c == '.') {
189: c = lastc;
190: }
191: if ((Klength > 0) &&
192: (c == *KL || c == *KR || c == *KU || c == *KD)) {
193: savec = c;
194: match = 0;
195: kl = KL;
196: kr = KR;
197: ku = KU;
198: kd = KD;
199: for (j=Klength;j>0;j--){
200: if (match != 1) {
201: match = 0;
202: if (*kl++ == c) {
203: ch = 'h';
204: match++;
205: }
206: if (*kr++ == c) {
207: ch = 'l';
208: match++;
209: }
210: if (*ku++ == c) {
211: ch = 'k';
212: match++;
213: }
214: if (*kd++ == c) {
215: ch = 'j';
216: match++;
217: }
218: if (match == 0) {
219: ungetc(c,stdin);
220: ch = savec;
221: /* Oops!
222: * This works if we figure it out on second character.
223: */
224: break;
225: }
226: }
227: savec = c;
228: if(j != 1) c = getchar() & 0177;
229: }
230: c = ch;
231: }
232: if (!fast) flushi();
233: lastc = c;
234: switch (c){
235: case CTRL('z'):
236: suspend();
237: continue;
238: case EOT:
239: case 'x':
240: case 0177: /* del or end of file */
241: ll();
242: length(moves);
243: logit("quit");
244: done();
245: case CTRL('l'):
246: setup();
247: winnings(cashvalue);
248: continue;
249: case 'p':
250: case 'd':
251: snap();
252: continue;
253: case 'w':
254: spacewarp(0);
255: continue;
256: case 'A':
257: repeat = you.col;
258: c = 'h';
259: break;
260: case 'H':
261: case 'S':
262: repeat = you.col - money.col;
263: c = 'h';
264: break;
265: case 'T':
266: repeat = you.line;
267: c = 'k';
268: break;
269: case 'K':
270: case 'E':
271: repeat = you.line - money.line;
272: c = 'k';
273: break;
274: case 'P':
275: repeat = ccnt - 1 - you.col;
276: c = 'l';
277: break;
278: case 'L':
279: case 'F':
280: repeat = money.col - you.col;
281: c = 'l';
282: break;
283: case 'B':
284: repeat = lcnt - 1 - you.line;
285: c = 'j';
286: break;
287: case 'J':
288: case 'C':
289: repeat = money.line - you.line;
290: c = 'j';
291: break;
292: }
293: for(k=1;k<=repeat;k++){
294: moves++;
295: switch(c) {
296: case 's':
297: case 'h':
298: case '\b':
299: if (you.col >0) {
300: if((fast)||(k == 1))
301: pchar(&you,' ');
302: you.col--;
303: if((fast) || (k == repeat) ||
304: (you.col == 0))
305: pchar(&you,ME);
306: }
307: break;
308: case 'f':
309: case 'l':
310: case ' ':
311: if (you.col < ccnt-1) {
312: if((fast)||(k == 1))
313: pchar(&you,' ');
314: you.col++;
315: if((fast) || (k == repeat) ||
316: (you.col == ccnt-1))
317: pchar(&you,ME);
318: }
319: break;
320: case CTRL('p'):
321: case 'e':
322: case 'k':
323: case 'i':
324: if (you.line > 0) {
325: if((fast)||(k == 1))
326: pchar(&you,' ');
327: you.line--;
328: if((fast) || (k == repeat) ||
329: (you.line == 0))
330: pchar(&you,ME);
331: }
332: break;
333: case CTRL('n'):
334: case 'c':
335: case 'j':
336: case LF:
337: case 'm':
338: if (you.line+1 < lcnt) {
339: if((fast)||(k == 1))
340: pchar(&you,' ');
341: you.line++;
342: if((fast) || (k == repeat) ||
343: (you.line == lcnt-1))
344: pchar(&you,ME);
345: }
346: break;
347: }
348:
349: if (same(&you,&money))
350: {
351: char xp[20];
352: struct point z;
353: loot += 25;
354: if(k < repeat)
355: pchar(&you,' ');
356: do {
357: snrand(&money);
358: } while (money.col == finish.col && money.line == finish.line ||
359: money.col < 5 && money.line == 0 ||
360: money.col == you.col && money.line == you.line);
361: pchar(&money,TREASURE);
362: winnings(cashvalue);
363: continue;
364: }
365: if (same(&you,&finish))
366: {
367: win(&finish);
368: ll();
369: cook();
370: printf("You have won with $%d.\n",cashvalue);
371: fflush(stdout);
372: logit("won");
373: post(cashvalue,0);
374: length(moves);
375: done();
376: }
377: if (pushsnake())break;
378: }
379: fflush(stdout);
380: }
381: }
382:
383: setup(){ /*
384: * setup the board
385: */
386: int i;
387:
388: clear();
389: pchar(&you,ME);
390: pchar(&finish,GOAL);
391: pchar(&money,TREASURE);
392: for(i=1; i<6; i++) {
393: pchar(&snake[i],SNAKETAIL);
394: }
395: pchar(&snake[0], SNAKEHEAD);
396: drawbox();
397: fflush(stdout);
398: }
399:
400: drawbox()
401: {
402: register int i;
403: struct point p;
404:
405: p.line = -1;
406: for (i= 0; i<ccnt; i++) {
407: p.col = i;
408: pchar(&p, '-');
409: }
410: p.col = ccnt;
411: for (i= -1; i<=lcnt; i++) {
412: p.line = i;
413: pchar(&p, '|');
414: }
415: p.col = -1;
416: for (i= -1; i<=lcnt; i++) {
417: p.line = i;
418: pchar(&p, '|');
419: }
420: p.line = lcnt;
421: for (i= 0; i<ccnt; i++) {
422: p.col = i;
423: pchar(&p, '-');
424: }
425: }
426:
427: snrand(sp)
428: struct point *sp;
429: {
430: struct point p;
431: register int i;
432:
433: for (;;) {
434: p.col = random() % ccnt;
435: p.line = random() % lcnt;
436:
437: /* make sure it's not on top of something else */
438: if (p.line == 0 && p.col < 5)
439: continue;
440: if (same(&p, &you))
441: continue;
442: if (same(&p, &money))
443: continue;
444: if (same(&p, &finish))
445: continue;
446: for (i = 0; i < 5; i++)
447: if (same(&p, &snake[i]))
448: break;
449: if (i < 5)
450: continue;
451: break;
452: }
453: *sp = p;
454: }
455:
456: post(iscore, flag)
457: int iscore, flag;
458: {
459: short score = iscore;
460: int rawscores;
461: short uid;
462: short oldbest=0;
463: short allbwho=0, allbscore=0;
464: struct passwd *p, *getpwuid();
465:
466: /*
467: * Neg uid, 0, and 1 cannot have scores recorded.
468: */
469: if ((uid=getuid()) > 1 && (rawscores=open(SNAKERAWSCORES,2))>=0) {
470: /* Figure out what happened in the past */
471: read(rawscores, &allbscore, sizeof(short));
472: read(rawscores, &allbwho, sizeof(short));
473: lseek(rawscores, ((long)uid)*sizeof(short), 0);
474: read(rawscores, &oldbest, sizeof(short));
475: if (flag) return (score > oldbest ? 1 : 0);
476:
477: /* Update this jokers best */
478: if (score > oldbest) {
479: lseek(rawscores, ((long)uid)*sizeof(short), 0);
480: write(rawscores, &score, sizeof(short));
481: printf("You bettered your previous best of $%d\n", oldbest);
482: } else
483: printf("Your best to date is $%d\n", oldbest);
484:
485: /* See if we have a new champ */
486: p = getpwuid(allbwho);
487: if (p == NULL || score > allbscore) {
488: lseek(rawscores, (long)0, 0);
489: write(rawscores, &score, sizeof(short));
490: write(rawscores, &uid, sizeof(short));
491: if (p != NULL)
492: printf("You beat %s's old record of $%d!\n", p->pw_name, allbscore);
493: else
494: printf("You set a new record!\n");
495: } else
496: printf("The highest is %s with $%d\n", p->pw_name, allbscore);
497: close(rawscores);
498: } else
499: if (!flag)
500: printf("Unable to post score.\n");
501: return (1);
502: }
503:
504: /*
505: * Flush typeahead to keep from buffering a bunch of chars and then
506: * overshooting. This loses horribly at 9600 baud, but works nicely
507: * if the terminal gets behind.
508: */
509: flushi()
510: {
511: stty(0, &new);
512: }
513: int mx [8] = {
514: 0, 1, 1, 1, 0,-1,-1,-1};
515: int my [8] = {
516: -1,-1, 0, 1, 1, 1, 0,-1};
517: float absv[8]= {
518: 1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
519: };
520: int oldw=0;
521: chase (np, sp)
522: struct point *sp, *np;
523: {
524: /* this algorithm has bugs; otherwise the
525: snake would get too good */
526: struct point d;
527: int w, i, wt[8];
528: double sqrt(), v1, v2, vp, max;
529: point(&d,you.col-sp->col,you.line-sp->line);
530: v1 = sqrt( (double) (d.col*d.col + d.line*d.line) );
531: w=0;
532: max=0;
533: for(i=0; i<8; i++)
534: {
535: vp = d.col*mx[i] + d.line*my[i];
536: v2 = absv[i];
537: if (v1>0)
538: vp = ((double)vp)/(v1*v2);
539: else vp=1.0;
540: if (vp>max)
541: {
542: max=vp;
543: w=i;
544: }
545: }
546: for(i=0; i<8; i++)
547: {
548: point(&d,sp->col+mx[i],sp->line+my[i]);
549: wt[i]=0;
550: if (d.col<0 || d.col>=ccnt || d.line<0 || d.line>=lcnt)
551: continue;
552: /*
553: * Change to allow snake to eat you if you're on the money,
554: * otherwise, you can just crouch there until the snake goes
555: * away. Not positive it's right.
556: *
557: * if (d.line == 0 && d.col < 5) continue;
558: */
559: if (same(&d,&money)) continue;
560: if (same(&d,&finish)) continue;
561: wt[i]= i==w ? loot/10 : 1;
562: if (i==oldw) wt [i] += loot/20;
563: }
564: for(w=i=0; i<8; i++)
565: w+= wt[i];
566: vp = (( rand() >> 6 ) & 01777) %w;
567: for(i=0; i<8; i++)
568: if (vp <wt[i])
569: break;
570: else
571: vp -= wt[i];
572: if (i==8) {
573: printf("failure\n");
574: i=0;
575: while (wt[i]==0) i++;
576: }
577: oldw=w=i;
578: point(np,sp->col+mx[w],sp->line+my[w]);
579: }
580:
581: spacewarp(w)
582: int w;{
583: struct point p;
584: int j;
585: char *str;
586:
587: snrand(&you);
588: point(&p,COLUMNS/2 - 8,LINES/2 - 1);
589: if (p.col < 0)
590: p.col = 0;
591: if (p.line < 0)
592: p.line = 0;
593: if (w) {
594: str = "BONUS!!!";
595: loot = loot - penalty;
596: penalty = 0;
597: } else {
598: str = "SPACE WARP!!!";
599: penalty += loot/PENALTY;
600: }
601: for(j=0;j<3;j++){
602: clear();
603: delay(5);
604: aprintf(&p,str);
605: delay(10);
606: }
607: setup();
608: winnings(cashvalue);
609: }
610: snap()
611: {
612: struct point p;
613: int i;
614:
615: if(you.line < 3){
616: pchar(point(&p,you.col,0),'-');
617: }
618: if(you.line > lcnt-4){
619: pchar(point(&p,you.col,lcnt-1),'_');
620: }
621: if(you.col < 10){
622: pchar(point(&p,0,you.line),'(');
623: }
624: if(you.col > ccnt-10){
625: pchar(point(&p,ccnt-1,you.line),')');
626: }
627: if (! stretch(&money)) if (! stretch(&finish)) delay(10);
628: if(you.line < 3){
629: point(&p,you.col,0);
630: remove(&p);
631: }
632: if(you.line > lcnt-4){
633: point(&p,you.col,lcnt-1);
634: remove(&p);
635: }
636: if(you.col < 10){
637: point(&p,0,you.line);
638: remove(&p);
639: }
640: if(you.col > ccnt-10){
641: point(&p,ccnt-1,you.line);
642: remove(&p);
643: }
644: fflush(stdout);
645: }
646: stretch(ps)
647: struct point *ps;{
648: struct point p;
649:
650: point(&p,you.col,you.line);
651: if(abs(ps->col-you.col) < 6){
652: if(you.line < ps->line){
653: for (p.line = you.line+1;p.line <= ps->line;p.line++)
654: pchar(&p,'v');
655: delay(10);
656: for (;p.line > you.line;p.line--)
657: remove(&p);
658: } else {
659: for (p.line = you.line-1;p.line >= ps->line;p.line--)
660: pchar(&p,'^');
661: delay(10);
662: for (;p.line < you.line;p.line++)
663: remove(&p);
664: }
665: return(1);
666: } else if(abs(ps->line-you.line) < 3){
667: p.line = you.line;
668: if(you.col < ps->col){
669: for (p.col = you.col+1;p.col <= ps->col;p.col++)
670: pchar(&p,'>');
671: delay(10);
672: for (;p.col > you.col;p.col--)
673: remove(&p);
674: } else {
675: for (p.col = you.col-1;p.col >= ps->col;p.col--)
676: pchar(&p,'<');
677: delay(10);
678: for (;p.col < you.col;p.col++)
679: remove(&p);
680: }
681: return(1);
682: }
683: return(0);
684: }
685:
686: surround(ps)
687: struct point *ps;{
688: struct point x;
689: int i,j;
690:
691: if(ps->col == 0)ps->col++;
692: if(ps->line == 0)ps->line++;
693: if(ps->line == LINES -1)ps->line--;
694: if(ps->col == COLUMNS -1)ps->col--;
695: aprintf(point(&x,ps->col-1,ps->line-1),"/*\\\r* *\r\\*/");
696: for (j=0;j<20;j++){
697: pchar(ps,'@');
698: delay(1);
699: pchar(ps,' ');
700: delay(1);
701: }
702: if (post(cashvalue,1)) {
703: aprintf(point(&x,ps->col-1,ps->line-1)," \ro.o\r\\_/");
704: delay(6);
705: aprintf(point(&x,ps->col-1,ps->line-1)," \ro.-\r\\_/");
706: delay(6);
707: }
708: aprintf(point(&x,ps->col-1,ps->line-1)," \ro.o\r\\_/");
709: }
710: win(ps)
711: struct point *ps;
712: {
713: struct point x;
714: int j,k;
715: int boxsize; /* actually diameter of box, not radius */
716:
717: boxsize = fast ? 10 : 4;
718: point(&x,ps->col,ps->line);
719: for(j=1;j<boxsize;j++){
720: for(k=0;k<j;k++){
721: pchar(&x,'#');
722: x.line--;
723: }
724: for(k=0;k<j;k++){
725: pchar(&x,'#');
726: x.col++;
727: }
728: j++;
729: for(k=0;k<j;k++){
730: pchar(&x,'#');
731: x.line++;
732: }
733: for(k=0;k<j;k++){
734: pchar(&x,'#');
735: x.col--;
736: }
737: }
738: fflush(stdout);
739: }
740:
741: pushsnake()
742: {
743: int i, bonus;
744: int issame = 0;
745:
746: /*
747: * My manual says times doesn't return a value. Furthermore, the
748: * snake should get his turn every time no matter if the user is
749: * on a fast terminal with typematic keys or not.
750: * So I have taken the call to times out.
751: */
752: for(i=4; i>=0; i--)
753: if (same(&snake[i], &snake[5]))
754: issame++;
755: if (!issame)
756: pchar(&snake[5],' ');
757: for(i=4; i>=0; i--)
758: snake[i+1]= snake[i];
759: chase(&snake[0], &snake[1]);
760: pchar(&snake[1],SNAKETAIL);
761: pchar(&snake[0],SNAKEHEAD);
762: for(i=0; i<6; i++)
763: {
764: if (same(&snake[i],&you))
765: {
766: surround(&you);
767: i = (cashvalue) % 10;
768: bonus = ((rand()>>8) & 0377)% 10;
769: ll();
770: printf("%d\n", bonus);
771: delay(30);
772: if (bonus == i) {
773: spacewarp(1);
774: logit("bonus");
775: flushi();
776: return(1);
777: }
778: if ( loot >= penalty ){
779: printf("You and your $%d have been eaten\n",cashvalue);
780: } else {
781: printf("The snake ate you. You owe $%d.\n",-cashvalue);
782: }
783: logit("eaten");
784: length(moves);
785: done();
786: }
787: }
788: return(0);
789: }
790:
791: remove(sp)
792: struct point *sp;
793: {
794: int j;
795:
796: if (same(sp,&money)) {
797: pchar(sp,TREASURE);
798: return(2);
799: }
800: if (same(sp,&finish)) {
801: pchar(sp,GOAL);
802: return(3);
803: }
804: if (same(sp,&snake[0])) {
805: pchar(sp,SNAKEHEAD);
806: return(4);
807: }
808: for(j=1;j<6;j++){
809: if(same(sp,&snake[j])){
810: pchar(sp,SNAKETAIL);
811: return(4);
812: }
813: }
814: if ((sp->col < 4) && (sp->line == 0)){
815: winnings(cashvalue);
816: if((you.line == 0) && (you.col < 4)) pchar(&you,ME);
817: return(5);
818: }
819: if (same(sp,&you)) {
820: pchar(sp,ME);
821: return(1);
822: }
823: pchar(sp,' ');
824: return(0);
825: }
826: winnings(won)
827: int won;
828: {
829: struct point p;
830:
831: p.line = p.col = 1;
832: if(won>0){
833: move(&p);
834: printf("$%d",won);
835: }
836: }
837:
838: stop(){
839: signal(SIGINT,1);
840: ll();
841: length(moves);
842: done();
843: }
844:
845: suspend()
846: {
847: char *sh;
848:
849: ll();
850: cook();
851: kill(getpid(), SIGTSTP);
852: raw();
853: setup();
854: winnings(cashvalue);
855: }
856:
857: length(num)
858: int num;
859: {
860: printf("You made %d moves.\n",num);
861: }
862:
863: logit(msg)
864: char *msg;
865: {
866: FILE *logfile;
867: long t;
868:
869: if ((logfile=fopen(LOGFILE, "a")) != NULL) {
870: time(&t);
871: fprintf(logfile, "%s $%d %dx%d %s %s", getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));
872: fclose(logfile);
873: }
874: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.