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