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