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