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