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