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