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