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