1 /*- 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 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. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)snake.c 8.2 (Berkeley) 1/7/94 31 * $FreeBSD: src/games/snake/snake/snake.c,v 1.11.2.1 2000/08/17 06:21:44 jhb Exp $ 32 */ 33 34 /* 35 * snake - crt hack game. 36 * 37 * You move around the screen with arrow keys trying to pick up money 38 * without getting eaten by the snake. hjkl work as in vi in place of 39 * arrow keys. You can leave at the exit any time. 40 * 41 * compile as follows: 42 * cc -O snake.c move.c -o snake -lm -ltermlib 43 */ 44 45 #include <sys/param.h> 46 47 #include <curses.h> 48 #include <fcntl.h> 49 #include <pwd.h> 50 #include <time.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <sys/types.h> 54 #include <err.h> 55 #include <math.h> 56 #include <signal.h> 57 #include <stdio.h> 58 #include <string.h> 59 #include <termios.h> 60 61 #include "pathnames.h" 62 63 #define cashvalue chunk*(loot-penalty)/25 64 65 struct point { 66 int col, line; 67 }; 68 69 #define same(s1, s2) ((s1)->line == (s2)->line && (s1)->col == (s2)->col) 70 71 #define PENALTY 10 /* % penalty for invoking spacewarp */ 72 73 #define EOT '\004' 74 #define LF '\n' 75 #define DEL '\177' 76 77 #define ME 'I' 78 #define SNAKEHEAD 'S' 79 #define SNAKETAIL 's' 80 #define TREASURE '$' 81 #define GOAL '#' 82 83 #ifndef MIN 84 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 85 #endif 86 87 #define pchar(point, c) mvaddch((point)->line + 1, (point)->col + 1, (c)) 88 #define delay(t) usleep(t * 50000); 89 90 struct point you; 91 struct point money; 92 struct point finish; 93 struct point snake[6]; 94 95 int loot, penalty; 96 int moves; 97 int fast = 1; 98 99 int rawscores; 100 FILE *logfile; 101 102 int lcnt, ccnt; /* user's idea of screen size */ 103 int chunk; /* amount of money given at a time */ 104 105 void chase(struct point *, struct point *); 106 int chk(const struct point *); 107 void drawbox(void); 108 void flushi(void); 109 void home(void); 110 void length(int); 111 void logit(const char *); 112 int main(int, char **); 113 void mainloop(void) __attribute__((__noreturn__)); 114 struct point *point(struct point *, int, int); 115 int post(int, int); 116 int pushsnake(void); 117 void right(const struct point *); 118 void setup(void); 119 void snap(void); 120 void snrand(struct point *); 121 void spacewarp(int); 122 void stop(int) __attribute__((__noreturn__)); 123 int stretch(const struct point *); 124 void surround(struct point *); 125 void suspend(void); 126 void win(const struct point *); 127 void winnings(int); 128 129 int 130 main(int argc, char **argv) 131 { 132 int ch, i; 133 time_t tv; 134 135 /* Open score files then revoke setgid privileges */ 136 rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664); 137 if (rawscores < 0) { 138 warn("open %s", _PATH_RAWSCORES); 139 sleep(2); 140 } else if (rawscores < 3) 141 exit(1); 142 logfile = fopen(_PATH_LOGFILE, "a"); 143 if (logfile == NULL) { 144 warn("fopen %s", _PATH_LOGFILE); 145 sleep(2); 146 } 147 setgid(getgid()); 148 149 time(&tv); 150 151 while ((ch = getopt(argc, argv, "l:w:t")) != -1) 152 switch ((char) ch) { 153 #ifdef DEBUG 154 case 'd': 155 tv = atol(optarg); 156 break; 157 #endif 158 case 'w': /* width */ 159 ccnt = atoi(optarg); 160 break; 161 case 'l': /* length */ 162 lcnt = atoi(optarg); 163 break; 164 case 't': 165 fast = 0; 166 break; 167 case '?': 168 default: 169 #ifdef DEBUG 170 fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr); 171 #else 172 fputs("usage: snake [-w width] [-l length] [-t]\n", stderr); 173 #endif 174 exit(1); 175 } 176 177 srandom((int) tv); 178 179 penalty = loot = 0; 180 initscr(); 181 cbreak(); 182 noecho(); 183 #ifdef KEY_LEFT 184 keypad(stdscr, TRUE); 185 #endif 186 if (!lcnt || lcnt > LINES - 2) 187 lcnt = LINES - 2; 188 if (!ccnt || ccnt > COLS - 2) 189 ccnt = COLS - 2; 190 191 i = MIN(lcnt, ccnt); 192 if (i < 4) { 193 endwin(); 194 errx(1, "screen too small for a fair game."); 195 } 196 197 /* 198 * chunk is the amount of money the user gets for each $. 199 * The formula below tries to be fair for various screen sizes. 200 * We only pay attention to the smaller of the 2 edges, since 201 * that seems to be the bottleneck. 202 * This formula is a hyperbola which includes the following points: 203 * (24, $25) (original scoring algorithm) 204 * (12, $40) (experimentally derived by the "feel") 205 * (48, $15) (a guess) 206 * This will give a 4x4 screen $99/shot. We don't allow anything 207 * smaller than 4x4 because there is a 3x3 game where you can win 208 * an infinite amount of money. 209 */ 210 if (i < 12) /* otherwise it isn't fair */ 211 i = 12; 212 /* 213 * Compensate for border. This really changes the game since 214 * the screen is two squares smaller but we want the default 215 * to be $25, and the high scores on small screens were a bit 216 * much anyway. 217 */ 218 i += 2; 219 chunk = (675.0 / (i + 6)) + 2.5; /* min screen edge */ 220 221 signal(SIGINT, stop); 222 223 snrand(&finish); 224 snrand(&you); 225 snrand(&money); 226 snrand(&snake[0]); 227 228 for (i = 1; i < 6; i++) 229 chase(&snake[i], &snake[i - 1]); 230 setup(); 231 mainloop(); 232 /* NOTREACHED */ 233 return (0); 234 } 235 236 struct point * 237 point(struct point *ps, int x, int y) 238 { 239 ps->col = x; 240 ps->line = y; 241 return (ps); 242 } 243 244 /* Main command loop */ 245 void 246 mainloop(void) 247 { 248 int k; 249 int repeat = 1; 250 int lastc = 0; 251 252 for (;;) { 253 int c; 254 255 /* Highlight you, not left & above */ 256 move(you.line + 1, you.col + 1); 257 refresh(); 258 if (((c = getch()) <= '9') && (c >= '0')) { 259 repeat = c - '0'; 260 while (((c = getch()) <= '9') && (c >= '0')) 261 repeat = 10 * repeat + (c - '0'); 262 } else { 263 if (c != '.') 264 repeat = 1; 265 } 266 if (c == '.') { 267 c = lastc; 268 } 269 if (!fast) 270 flushi(); 271 lastc = c; 272 switch (c) { 273 case CTRL('z'): 274 suspend(); 275 continue; 276 case EOT: 277 case 'x': 278 case 0177: /* del or end of file */ 279 endwin(); 280 length(moves); 281 logit("quit"); 282 exit(0); 283 case CTRL('l'): 284 setup(); 285 winnings(cashvalue); 286 continue; 287 case 'p': 288 case 'd': 289 snap(); 290 continue; 291 case 'w': 292 spacewarp(0); 293 continue; 294 case 'A': 295 repeat = you.col; 296 c = 'h'; 297 break; 298 case 'H': 299 case 'S': 300 repeat = you.col - money.col; 301 c = 'h'; 302 break; 303 case 'T': 304 repeat = you.line; 305 c = 'k'; 306 break; 307 case 'K': 308 case 'E': 309 repeat = you.line - money.line; 310 c = 'k'; 311 break; 312 case 'P': 313 repeat = ccnt - 1 - you.col; 314 c = 'l'; 315 break; 316 case 'L': 317 case 'F': 318 repeat = money.col - you.col; 319 c = 'l'; 320 break; 321 case 'B': 322 repeat = lcnt - 1 - you.line; 323 c = 'j'; 324 break; 325 case 'J': 326 case 'C': 327 repeat = money.line - you.line; 328 c = 'j'; 329 break; 330 } 331 for (k = 1; k <= repeat; k++) { 332 moves++; 333 switch (c) { 334 case 's': 335 case 'h': 336 #ifdef KEY_LEFT 337 case KEY_LEFT: 338 #endif 339 case '\b': 340 if (you.col > 0) { 341 if ((fast) || (k == 1)) 342 pchar(&you, ' '); 343 you.col--; 344 if ((fast) || (k == repeat) || 345 (you.col == 0)) 346 pchar(&you, ME); 347 } 348 break; 349 case 'f': 350 case 'l': 351 #ifdef KEY_RIGHT 352 case KEY_RIGHT: 353 #endif 354 case ' ': 355 if (you.col < ccnt - 1) { 356 if ((fast) || (k == 1)) 357 pchar(&you, ' '); 358 you.col++; 359 if ((fast) || (k == repeat) || 360 (you.col == ccnt - 1)) 361 pchar(&you, ME); 362 } 363 break; 364 case CTRL('p'): 365 case 'e': 366 case 'k': 367 #ifdef KEY_UP 368 case KEY_UP: 369 #endif 370 case 'i': 371 if (you.line > 0) { 372 if ((fast) || (k == 1)) 373 pchar(&you, ' '); 374 you.line--; 375 if ((fast) || (k == repeat) || 376 (you.line == 0)) 377 pchar(&you, ME); 378 } 379 break; 380 case CTRL('n'): 381 case 'c': 382 case 'j': 383 #ifdef KEY_DOWN 384 case KEY_DOWN: 385 #endif 386 case LF: 387 case 'm': 388 if (you.line + 1 < lcnt) { 389 if ((fast) || (k == 1)) 390 pchar(&you, ' '); 391 you.line++; 392 if ((fast) || (k == repeat) || 393 (you.line == lcnt - 1)) 394 pchar(&you, ME); 395 } 396 break; 397 } 398 399 if (same(&you, &money)) { 400 loot += 25; 401 if (k < repeat) 402 pchar(&you, ' '); 403 do { 404 snrand(&money); 405 } while ((money.col == finish.col && 406 money.line == finish.line) || 407 (money.col < 5 && money.line == 0) || 408 (money.col == you.col && 409 money.line == you.line)); 410 pchar(&money, TREASURE); 411 winnings(cashvalue); 412 continue; 413 } 414 if (same(&you, &finish)) { 415 win(&finish); 416 flushi(); 417 endwin(); 418 printf("You have won with $%d.\n", cashvalue); 419 fflush(stdout); 420 logit("won"); 421 post(cashvalue, 1); 422 length(moves); 423 exit(0); 424 } 425 if (pushsnake()) 426 break; 427 } 428 } 429 } 430 431 /* 432 * setup the board 433 */ 434 void 435 setup(void) 436 { 437 int i; 438 439 erase(); 440 pchar(&you, ME); 441 pchar(&finish, GOAL); 442 pchar(&money, TREASURE); 443 for (i = 1; i < 6; i++) { 444 pchar(&snake[i], SNAKETAIL); 445 } 446 pchar(&snake[0], SNAKEHEAD); 447 drawbox(); 448 refresh(); 449 } 450 451 void 452 drawbox(void) 453 { 454 int i; 455 456 for (i = 1; i <= ccnt; i++) { 457 mvaddch(0, i, '-'); 458 mvaddch(lcnt + 1, i, '-'); 459 } 460 for (i = 0; i <= lcnt + 1; i++) { 461 mvaddch(i, 0, '|'); 462 mvaddch(i, ccnt + 1, '|'); 463 } 464 } 465 466 void 467 snrand(struct point *sp) 468 { 469 struct point p; 470 int i; 471 472 for (;;) { 473 p.col = random() % ccnt; 474 p.line = random() % lcnt; 475 476 /* make sure it's not on top of something else */ 477 if (p.line == 0 && p.col < 5) 478 continue; 479 if (same(&p, &you)) 480 continue; 481 if (same(&p, &money)) 482 continue; 483 if (same(&p, &finish)) 484 continue; 485 for (i = 0; i < 6; i++) 486 if (same(&p, &snake[i])) 487 break; 488 if (i < 6) 489 continue; 490 break; 491 } 492 *sp = p; 493 } 494 495 int 496 post(int iscore, int flag) 497 { 498 short score = iscore; 499 short uid; 500 short oldbest = 0; 501 short allbwho = 0, allbscore = 0; 502 struct passwd *p; 503 504 /* I want to printf() the scores for terms that clear on cook(), 505 * but this routine also gets called with flag == 0 to see if 506 * the snake should wink. If (flag) then we're at game end and 507 * can printf. 508 */ 509 /* 510 * Neg uid, 0, and 1 cannot have scores recorded. 511 */ 512 if ((uid = getuid()) <= 1) { 513 if (flag) 514 printf("No saved scores for uid %d.\n", uid); 515 return (1); 516 } 517 if (rawscores < 0) { 518 /* Error reported earlier */ 519 return (1); 520 } 521 /* Figure out what happened in the past */ 522 read(rawscores, &allbscore, sizeof(short)); 523 read(rawscores, &allbwho, sizeof(short)); 524 lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET); 525 read(rawscores, &oldbest, sizeof(short)); 526 if (!flag) { 527 lseek(rawscores, 0, SEEK_SET); 528 return (score > oldbest ? 1 : 0); 529 } 530 531 /* Update this jokers best */ 532 if (score > oldbest) { 533 lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET); 534 write(rawscores, &score, sizeof(short)); 535 printf("You bettered your previous best of $%d\n", oldbest); 536 } else 537 printf("Your best to date is $%d\n", oldbest); 538 539 /* See if we have a new champ */ 540 p = getpwuid(allbwho); 541 if (score > allbscore) { 542 lseek(rawscores, 0, SEEK_SET); 543 write(rawscores, &score, sizeof(short)); 544 write(rawscores, &uid, sizeof(short)); 545 if (allbwho) { 546 if (p) 547 printf("You beat %s's old record of $%d!\n", 548 p->pw_name, allbscore); 549 else 550 printf("You beat (%d)'s old record of $%d!\n", 551 (int)allbwho, allbscore); 552 } 553 else 554 printf("You set a new record!\n"); 555 } else if (p) 556 printf("The highest is %s with $%d\n", p->pw_name, allbscore); 557 else 558 printf("The highest is (%d) with $%d\n", (int)allbwho, 559 allbscore); 560 lseek(rawscores, 0, SEEK_SET); 561 return (1); 562 } 563 564 /* 565 * Flush typeahead to keep from buffering a bunch of chars and then 566 * overshooting. This loses horribly at 9600 baud, but works nicely 567 * if the terminal gets behind. 568 */ 569 void 570 flushi(void) 571 { 572 tcflush(0, TCIFLUSH); 573 } 574 575 int mx[8] = { 576 0, 1, 1, 1, 0, -1, -1, -1 577 }; 578 int my[8] = { 579 -1, -1, 0, 1, 1, 1, 0, -1 580 }; 581 float absv[8] = { 582 1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4 583 }; 584 int oldw = 0; 585 586 void 587 chase(struct point *np, struct point *sp) 588 { 589 /* this algorithm has bugs; otherwise the snake would get too good */ 590 struct point d; 591 int w, i, wt[8]; 592 double v1, v2, vp, max; 593 point(&d, you.col - sp->col, you.line - sp->line); 594 v1 = sqrt((double)(d.col * d.col + d.line * d.line)); 595 w = 0; 596 max = 0; 597 for (i = 0; i < 8; i++) { 598 vp = d.col * mx[i] + d.line * my[i]; 599 v2 = absv[i]; 600 if (v1 > 0) 601 vp = ((double)vp) / (v1 * v2); 602 else 603 vp = 1.0; 604 if (vp > max) { 605 max = vp; 606 w = i; 607 } 608 } 609 for (i = 0; i < 8; i++) { 610 point(&d, sp->col + mx[i], sp->line + my[i]); 611 wt[i] = 0; 612 if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt) 613 continue; 614 /* 615 * Change to allow snake to eat you if you're on the money, 616 * otherwise, you can just crouch there until the snake goes 617 * away. Not positive it's right. 618 * 619 * if (d.line == 0 && d.col < 5) continue; 620 */ 621 if (same(&d, &money)) 622 continue; 623 if (same(&d, &finish)) 624 continue; 625 wt[i] = i == w ? loot / 10 : 1; 626 if (i == oldw) 627 wt[i] += loot / 20; 628 } 629 for (w = i = 0; i < 8; i++) 630 w += wt[i]; 631 vp = ((random() >> 6) & 01777) % w; 632 for (i = 0; i < 8; i++) 633 if (vp < wt[i]) 634 break; 635 else 636 vp -= wt[i]; 637 if (i == 8) { 638 printw("failure\n"); 639 i = 0; 640 while (wt[i] == 0) 641 i++; 642 } 643 oldw = w = i; 644 point(np, sp->col + mx[w], sp->line + my[w]); 645 } 646 647 void 648 spacewarp(int w) 649 { 650 struct point p; 651 int j; 652 const char *str; 653 654 snrand(&you); 655 point(&p, COLS / 2 - 8, LINES / 2 - 1); 656 if (p.col < 0) 657 p.col = 0; 658 if (p.line < 0) 659 p.line = 0; 660 if (w) { 661 str = "BONUS!!!"; 662 loot = loot - penalty; 663 penalty = 0; 664 } else { 665 str = "SPACE WARP!!!"; 666 penalty += loot / PENALTY; 667 } 668 for (j = 0; j < 3; j++) { 669 erase(); 670 refresh(); 671 delay(5); 672 mvaddstr(p.line + 1, p.col + 1, str); 673 refresh(); 674 delay(10); 675 } 676 setup(); 677 winnings(cashvalue); 678 } 679 680 void 681 snap(void) 682 { 683 if (!stretch(&money)) 684 if (!stretch(&finish)) { 685 pchar(&you, '?'); 686 refresh(); 687 delay(10); 688 pchar(&you, ME); 689 } 690 refresh(); 691 } 692 693 int 694 stretch(const struct point *ps) 695 { 696 struct point p; 697 698 point(&p, you.col, you.line); 699 if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) { 700 if (you.line < ps->line) { 701 for (p.line = you.line + 1; p.line <= ps->line; p.line++) 702 pchar(&p, 'v'); 703 refresh(); 704 delay(10); 705 for (; p.line > you.line; p.line--) 706 chk(&p); 707 } else { 708 for (p.line = you.line - 1; p.line >= ps->line; p.line--) 709 pchar(&p, '^'); 710 refresh(); 711 delay(10); 712 for (; p.line < you.line; p.line++) 713 chk(&p); 714 } 715 return (1); 716 } else 717 if ((abs(ps->line - you.line) < (lcnt / 7)) 718 && (you.col != ps->col)) { 719 p.line = you.line; 720 if (you.col < ps->col) { 721 for (p.col = you.col + 1; p.col <= ps->col; p.col++) 722 pchar(&p, '>'); 723 refresh(); 724 delay(10); 725 for (; p.col > you.col; p.col--) 726 chk(&p); 727 } else { 728 for (p.col = you.col - 1; p.col >= ps->col; p.col--) 729 pchar(&p, '<'); 730 refresh(); 731 delay(10); 732 for (; p.col < you.col; p.col++) 733 chk(&p); 734 } 735 return (1); 736 } 737 return (0); 738 } 739 740 void 741 surround(struct point *ps) 742 { 743 int j; 744 745 if (ps->col == 0) 746 ps->col++; 747 if (ps->line == 0) 748 ps->line++; 749 if (ps->line == LINES - 1) 750 ps->line--; 751 if (ps->col == COLS - 1) 752 ps->col--; 753 mvaddstr(ps->line, ps->col, "/*\\"); 754 mvaddstr(ps->line + 1, ps->col, "* *"); 755 mvaddstr(ps->line + 2, ps->col, "\\*/"); 756 for (j = 0; j < 20; j++) { 757 pchar(ps, '@'); 758 refresh(); 759 delay(1); 760 pchar(ps, ' '); 761 refresh(); 762 delay(1); 763 } 764 if (post(cashvalue, 0)) { 765 mvaddstr(ps->line, ps->col, " "); 766 mvaddstr(ps->line + 1, ps->col, "o.o"); 767 mvaddstr(ps->line + 2, ps->col, "\\_/"); 768 refresh(); 769 delay(6); 770 mvaddstr(ps->line, ps->col, " "); 771 mvaddstr(ps->line + 1, ps->col, "o.-"); 772 mvaddstr(ps->line + 2, ps->col, "\\_/"); 773 refresh(); 774 delay(6); 775 } 776 mvaddstr(ps->line, ps->col, " "); 777 mvaddstr(ps->line + 1, ps->col, "o.o"); 778 mvaddstr(ps->line + 2, ps->col, "\\_/"); 779 refresh(); 780 delay(6); 781 } 782 783 void 784 win(const struct point *ps) 785 { 786 struct point x; 787 int j, k; 788 int boxsize; /* actually diameter of box, not radius */ 789 790 boxsize = fast ? 10 : 4; 791 point(&x, ps->col, ps->line); 792 for (j = 1; j < boxsize; j++) { 793 for (k = 0; k < j; k++) { 794 pchar(&x, '#'); 795 x.line--; 796 } 797 for (k = 0; k < j; k++) { 798 pchar(&x, '#'); 799 x.col++; 800 } 801 j++; 802 for (k = 0; k < j; k++) { 803 pchar(&x, '#'); 804 x.line++; 805 } 806 for (k = 0; k < j; k++) { 807 pchar(&x, '#'); 808 x.col--; 809 } 810 refresh(); 811 delay(1); 812 } 813 } 814 815 int 816 pushsnake(void) 817 { 818 int i, bonus; 819 int issame = 0; 820 struct point tmp; 821 822 /* 823 * My manual says times doesn't return a value. Furthermore, the 824 * snake should get his turn every time no matter if the user is 825 * on a fast terminal with typematic keys or not. 826 * So I have taken the call to times out. 827 */ 828 for (i = 4; i >= 0; i--) 829 if (same(&snake[i], &snake[5])) 830 issame++; 831 if (!issame) 832 pchar(&snake[5], ' '); 833 /* Need the following to catch you if you step on the snake's tail */ 834 tmp.col = snake[5].col; 835 tmp.line = snake[5].line; 836 for (i = 4; i >= 0; i--) 837 snake[i + 1] = snake[i]; 838 chase(&snake[0], &snake[1]); 839 pchar(&snake[1], SNAKETAIL); 840 pchar(&snake[0], SNAKEHEAD); 841 for (i = 0; i < 6; i++) { 842 if (same(&snake[i], &you) || same(&tmp, &you)) { 843 surround(&you); 844 i = (cashvalue) % 10; 845 bonus = ((random() >> 8) & 0377) % 10; 846 mvprintw(lcnt + 1, 0, "%d\n", bonus); 847 refresh(); 848 delay(30); 849 if (bonus == i) { 850 spacewarp(1); 851 logit("bonus"); 852 flushi(); 853 return (1); 854 } 855 flushi(); 856 endwin(); 857 if (loot >= penalty) { 858 printf("\nYou and your $%d have been eaten\n", 859 cashvalue); 860 } else { 861 printf("\nThe snake ate you. You owe $%d.\n", 862 -cashvalue); 863 } 864 logit("eaten"); 865 length(moves); 866 exit(0); 867 } 868 } 869 return (0); 870 } 871 872 int 873 chk(const struct point *sp) 874 { 875 int j; 876 877 if (same(sp, &money)) { 878 pchar(sp, TREASURE); 879 return (2); 880 } 881 if (same(sp, &finish)) { 882 pchar(sp, GOAL); 883 return (3); 884 } 885 if (same(sp, &snake[0])) { 886 pchar(sp, SNAKEHEAD); 887 return (4); 888 } 889 for (j = 1; j < 6; j++) { 890 if (same(sp, &snake[j])) { 891 pchar(sp, SNAKETAIL); 892 return (4); 893 } 894 } 895 if ((sp->col < 4) && (sp->line == 0)) { 896 winnings(cashvalue); 897 if ((you.line == 0) && (you.col < 4)) 898 pchar(&you, ME); 899 return (5); 900 } 901 if (same(sp, &you)) { 902 pchar(sp, ME); 903 return (1); 904 } 905 pchar(sp, ' '); 906 return (0); 907 } 908 909 void 910 winnings(int won) 911 { 912 if (won > 0) { 913 mvprintw(1, 1, "$%d", won); 914 } 915 } 916 917 void 918 stop(int dummy __unused) 919 { 920 signal(SIGINT, SIG_IGN); 921 endwin(); 922 length(moves); 923 exit(0); 924 } 925 926 void 927 suspend(void) 928 { 929 endwin(); 930 kill(getpid(), SIGTSTP); 931 refresh(); 932 winnings(cashvalue); 933 } 934 935 void 936 length(int num) 937 { 938 printf("You made %d moves.\n", num); 939 } 940 941 void 942 logit(const char *msg) 943 { 944 time_t t; 945 946 if (logfile != NULL) { 947 time(&t); 948 fprintf(logfile, "%s $%d %dx%d %s %s", 949 getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t)); 950 fflush(logfile); 951 } 952 } 953