1/* $NetBSD: snake.c,v 1.31 2021/05/12 11:08:31 nia 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.31 2021/05/12 11:08:31 nia 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/* note t must be < 20 due to restrictions of usleep() */ 101#define delay(t) usleep(t * 50000); 102 103static struct point you; 104static struct point money; 105static struct point finish; 106static struct point snake[6]; 107 108static int loot, penalty; 109static int moves; 110static int fast = 1; 111 112static int rawscores; 113static FILE *logfile; 114 115static int lcnt, ccnt; /* user's idea of screen size */ 116static int chunk; /* amount of money given at a time */ 117 118static void chase(struct point *, struct point *); 119static int chk(const struct point *); 120static void drawbox(void); 121static void flushi(void); 122static void length(int); 123static void logit(const char *); 124static void mainloop(void) __dead; 125static struct point *point(struct point *, int, int); 126static int post(int, int); 127static int pushsnake(void); 128static void setup(void); 129static void snap(void); 130static void snrand(struct point *); 131static void spacewarp(int); 132static void stop(int) __dead; 133static int stretch(const struct point *); 134static void surround(struct point *); 135static void suspend(void); 136static void win(const struct point *); 137static void winnings(int); 138 139int 140main(int argc, char **argv) 141{ 142 int ch, i; 143 time_t tv; 144 145 /* Open score files then revoke setgid privileges */ 146 rawscores = open(SNAKE_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664); 147 if (rawscores < 0) { 148 warn("open %s", SNAKE_PATH_RAWSCORES); 149 sleep(2); 150 } else if (rawscores < 3) 151 exit(1); 152 logfile = fopen(SNAKE_PATH_LOGFILE, "a"); 153 if (logfile == NULL) { 154 warn("fopen %s", SNAKE_PATH_LOGFILE); 155 sleep(2); 156 } 157 setgid(getgid()); 158 159 (void) time(&tv); 160 161 while ((ch = getopt(argc, argv, "l:w:t")) != -1) 162 switch ((char) ch) { 163#ifdef DEBUG 164 case 'd': 165 tv = atol(optarg); 166 break; 167#endif 168 case 'w': /* width */ 169 ccnt = atoi(optarg); 170 break; 171 case 'l': /* length */ 172 lcnt = atoi(optarg); 173 break; 174 case 't': 175 fast = 0; 176 break; 177 case '?': 178 default: 179#ifdef DEBUG 180 fprintf(stderr, 181 "usage: %s [-d seed] [-w width] [-l length] [-t]\n", 182 getprogname()); 183#else 184 fprintf(stderr, 185 "usage: %s [-w width] [-l length] [-t]\n", 186 getprogname()); 187#endif 188 exit(1); 189 } 190 191 srandom((int) tv); 192 193 penalty = loot = 0; 194 if (!initscr()) 195 errx(0, "couldn't initialize screen"); 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 250static struct point * 251point(struct point *ps, int x, int y) 252{ 253 ps->col = x; 254 ps->line = y; 255 return (ps); 256} 257 258/* Main command loop */ 259static void 260mainloop(void) 261{ 262 int k; 263 int repeat = 1; 264 int lastc = 0; 265 266 for (;;) { 267 int c; 268 269 /* Highlight you, not left & above */ 270 move(you.line + 1, you.col + 1); 271 refresh(); 272 if (((c = getch()) <= '9') && (c >= '0')) { 273 repeat = c - '0'; 274 while (((c = getch()) <= '9') && (c >= '0')) 275 repeat = 10 * repeat + (c - '0'); 276 } else { 277 if (c != '.') 278 repeat = 1; 279 } 280 if (c == '.') { 281 c = lastc; 282 } 283 if (!fast) 284 flushi(); 285 lastc = c; 286 switch (c) { 287 case CTRL('z'): 288 suspend(); 289 continue; 290 case EOT: 291 case 'x': 292 case 0177: /* del or end of file */ 293 endwin(); 294 length(moves); 295 logit("quit"); 296 exit(0); 297 case CTRL('l'): 298 setup(); 299 winnings(cashvalue); 300 continue; 301 case 'p': 302 case 'd': 303 snap(); 304 continue; 305 case 'w': 306 spacewarp(0); 307 continue; 308 case 'A': 309 repeat = you.col; 310 c = 'h'; 311 break; 312 case 'H': 313 case 'S': 314 repeat = you.col - money.col; 315 c = 'h'; 316 break; 317 case 'T': 318 repeat = you.line; 319 c = 'k'; 320 break; 321 case 'K': 322 case 'E': 323 repeat = you.line - money.line; 324 c = 'k'; 325 break; 326 case 'P': 327 repeat = ccnt - 1 - you.col; 328 c = 'l'; 329 break; 330 case 'L': 331 case 'F': 332 repeat = money.col - you.col; 333 c = 'l'; 334 break; 335 case 'B': 336 repeat = lcnt - 1 - you.line; 337 c = 'j'; 338 break; 339 case 'J': 340 case 'C': 341 repeat = money.line - you.line; 342 c = 'j'; 343 break; 344 } 345 for (k = 1; k <= repeat; k++) { 346 moves++; 347 switch (c) { 348 case 's': 349 case 'h': 350#ifdef KEY_LEFT 351 case KEY_LEFT: 352#endif 353 case '\b': 354 if (you.col > 0) { 355 if ((fast) || (k == 1)) 356 pchar(&you, ' '); 357 you.col--; 358 if ((fast) || (k == repeat) || 359 (you.col == 0)) 360 pchar(&you, ME); 361 } 362 break; 363 case 'f': 364 case 'l': 365#ifdef KEY_RIGHT 366 case KEY_RIGHT: 367#endif 368 case ' ': 369 if (you.col < ccnt - 1) { 370 if ((fast) || (k == 1)) 371 pchar(&you, ' '); 372 you.col++; 373 if ((fast) || (k == repeat) || 374 (you.col == ccnt - 1)) 375 pchar(&you, ME); 376 } 377 break; 378 case CTRL('p'): 379 case 'e': 380 case 'k': 381#ifdef KEY_UP 382 case KEY_UP: 383#endif 384 case 'i': 385 if (you.line > 0) { 386 if ((fast) || (k == 1)) 387 pchar(&you, ' '); 388 you.line--; 389 if ((fast) || (k == repeat) || 390 (you.line == 0)) 391 pchar(&you, ME); 392 } 393 break; 394 case CTRL('n'): 395 case 'c': 396 case 'j': 397#ifdef KEY_DOWN 398 case KEY_DOWN: 399#endif 400 case LF: 401 case 'm': 402 if (you.line + 1 < lcnt) { 403 if ((fast) || (k == 1)) 404 pchar(&you, ' '); 405 you.line++; 406 if ((fast) || (k == repeat) || 407 (you.line == lcnt - 1)) 408 pchar(&you, ME); 409 } 410 break; 411 } 412 413 if (same(&you, &money)) { 414 loot += 25; 415 if (k < repeat) 416 pchar(&you, ' '); 417 do { 418 snrand(&money); 419 } while ((money.col == finish.col && 420 money.line == finish.line) || 421 (money.col < 5 && money.line == 0) || 422 (money.col == you.col && 423 money.line == you.line)); 424 pchar(&money, TREASURE); 425 winnings(cashvalue); 426 continue; 427 } 428 if (same(&you, &finish)) { 429 win(&finish); 430 flushi(); 431 endwin(); 432 printf("You have won with $%d.\n", cashvalue); 433 fflush(stdout); 434 logit("won"); 435 post(cashvalue, 1); 436 length(moves); 437 exit(0); 438 } 439 if (pushsnake()) 440 break; 441 } 442 } 443} 444 445/* 446 * setup the board 447 */ 448static void 449setup(void) 450{ 451 int i; 452 453 erase(); 454 pchar(&you, ME); 455 pchar(&finish, GOAL); 456 pchar(&money, TREASURE); 457 for (i = 1; i < 6; i++) { 458 pchar(&snake[i], SNAKETAIL); 459 } 460 pchar(&snake[0], SNAKEHEAD); 461 drawbox(); 462 refresh(); 463} 464 465static void 466drawbox(void) 467{ 468 int i; 469 470 for (i = 1; i <= ccnt; i++) { 471 mvaddch(0, i, '-'); 472 mvaddch(lcnt + 1, i, '-'); 473 } 474 for (i = 0; i <= lcnt + 1; i++) { 475 mvaddch(i, 0, '|'); 476 mvaddch(i, ccnt + 1, '|'); 477 } 478} 479 480static void 481snrand(struct point *sp) 482{ 483 struct point p; 484 int i; 485 486 for (;;) { 487 p.col = random() % ccnt; 488 p.line = random() % lcnt; 489 490 /* make sure it's not on top of something else */ 491 if (p.line == 0 && p.col < 5) 492 continue; 493 if (same(&p, &you)) 494 continue; 495 if (same(&p, &money)) 496 continue; 497 if (same(&p, &finish)) 498 continue; 499 for (i = 0; i < 6; i++) 500 if (same(&p, &snake[i])) 501 break; 502 if (i < 6) 503 continue; 504 break; 505 } 506 *sp = p; 507} 508 509static int 510post(int iscore, int flag) 511{ 512 short score = iscore; 513 short uid; 514 short oldbest = 0; 515 short allbwho = 0, allbscore = 0; 516 struct passwd *p; 517 518 /* I want to printf() the scores for terms that clear on cook(), 519 * but this routine also gets called with flag == 0 to see if 520 * the snake should wink. If (flag) then we're at game end and 521 * can printf. 522 */ 523 /* 524 * Neg uid, 0, and 1 cannot have scores recorded. 525 */ 526 if ((uid = getuid()) <= 1) { 527 if (flag) 528 printf("No saved scores for uid %d.\n", uid); 529 return (1); 530 } 531 if (rawscores < 0) { 532 /* Error reported earlier */ 533 return (1); 534 } 535 /* Figure out what happened in the past */ 536 read(rawscores, &allbscore, sizeof(short)); 537 read(rawscores, &allbwho, sizeof(short)); 538 lseek(rawscores, uid * sizeof(short), SEEK_SET); 539 read(rawscores, &oldbest, sizeof(short)); 540 if (!flag) { 541 lseek(rawscores, 0, SEEK_SET); 542 return (score > oldbest ? 1 : 0); 543 } 544 545 /* Update this jokers best */ 546 if (score > oldbest) { 547 lseek(rawscores, uid * sizeof(short), SEEK_SET); 548 write(rawscores, &score, sizeof(short)); 549 printf("You bettered your previous best of $%d\n", oldbest); 550 } else 551 printf("Your best to date is $%d\n", oldbest); 552 553 /* See if we have a new champ */ 554 p = getpwuid(allbwho); 555 if (score > allbscore) { 556 lseek(rawscores, 0, SEEK_SET); 557 write(rawscores, &score, sizeof(short)); 558 write(rawscores, &uid, sizeof(short)); 559 if (allbwho) { 560 if (p) 561 printf("You beat %s's old record of $%d!\n", 562 p->pw_name, allbscore); 563 else 564 printf("You beat (%d)'s old record of $%d!\n", 565 (int)allbwho, allbscore); 566 } 567 else 568 printf("You set a new record!\n"); 569 } else if (p) 570 printf("The highest is %s with $%d\n", p->pw_name, allbscore); 571 else 572 printf("The highest is (%d) with $%d\n", (int)allbwho, 573 allbscore); 574 lseek(rawscores, 0, SEEK_SET); 575 return (1); 576} 577 578/* 579 * Flush typeahead to keep from buffering a bunch of chars and then 580 * overshooting. This loses horribly at 9600 baud, but works nicely 581 * if the terminal gets behind. 582 */ 583static void 584flushi(void) 585{ 586 tcflush(0, TCIFLUSH); 587} 588 589static const int mx[8] = { 590 0, 1, 1, 1, 0, -1, -1, -1 591}; 592static const int my[8] = { 593 -1, -1, 0, 1, 1, 1, 0, -1 594}; 595static const float absv[8] = { 596 1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4 597}; 598static int oldw = 0; 599 600static void 601chase(struct point *np, struct point *sp) 602{ 603 /* this algorithm has bugs; otherwise the snake would get too good */ 604 struct point d; 605 int w, i, wt[8]; 606 double v1, v2, vp, max; 607 point(&d, you.col - sp->col, you.line - sp->line); 608 v1 = sqrt((double) (d.col * d.col + d.line * d.line)); 609 w = 0; 610 max = 0; 611 for (i = 0; i < 8; i++) { 612 vp = d.col * mx[i] + d.line * my[i]; 613 v2 = absv[i]; 614 if (v1 > 0) 615 vp = ((double) vp) / (v1 * v2); 616 else 617 vp = 1.0; 618 if (vp > max) { 619 max = vp; 620 w = i; 621 } 622 } 623 for (i = 0; i < 8; i++) { 624 point(&d, sp->col + mx[i], sp->line + my[i]); 625 wt[i] = 0; 626 if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt) 627 continue; 628 /* 629 * Change to allow snake to eat you if you're on the money, 630 * otherwise, you can just crouch there until the snake goes 631 * away. Not positive it's right. 632 * 633 * if (d.line == 0 && d.col < 5) continue; 634 */ 635 if (same(&d, &money)) 636 continue; 637 if (same(&d, &finish)) 638 continue; 639 wt[i] = i == w ? loot / 10 : 1; 640 if (i == oldw) 641 wt[i] += loot / 20; 642 } 643 for (w = i = 0; i < 8; i++) 644 w += wt[i]; 645 vp = ((random() >> 6) & 01777) % w; 646 for (i = 0; i < 8; i++) 647 if (vp < wt[i]) 648 break; 649 else 650 vp -= wt[i]; 651 if (i == 8) { 652 printw("failure\n"); 653 i = 0; 654 while (wt[i] == 0) 655 i++; 656 } 657 oldw = w = i; 658 point(np, sp->col + mx[w], sp->line + my[w]); 659} 660 661static void 662spacewarp(int w) 663{ 664 struct point p; 665 int j; 666 const char *str; 667 668 snrand(&you); 669 point(&p, COLS / 2 - 8, LINES / 2 - 1); 670 if (p.col < 0) 671 p.col = 0; 672 if (p.line < 0) 673 p.line = 0; 674 if (w) { 675 str = "BONUS!!!"; 676 loot = loot - penalty; 677 penalty = 0; 678 } else { 679 str = "SPACE WARP!!!"; 680 penalty += loot / PENALTY; 681 } 682 for (j = 0; j < 3; j++) { 683 erase(); 684 refresh(); 685 delay(5); 686 mvaddstr(p.line + 1, p.col + 1, str); 687 refresh(); 688 delay(10); 689 } 690 setup(); 691 winnings(cashvalue); 692} 693 694static void 695snap(void) 696{ 697#if 0 /* This code doesn't really make sense. */ 698 struct point p; 699 700 if (you.line < 3) { 701 mvaddch(1, you.col + 1, '-'); 702 } 703 if (you.line > lcnt - 4) { 704 mvaddch(lcnt, you.col + 1, '_'); 705 } 706 if (you.col < 10) { 707 mvaddch(you.line + 1, 1, '('); 708 } 709 if (you.col > ccnt - 10) { 710 mvaddch(you.line + 1, ccnt, ')'); 711 } 712#endif 713 if (!stretch(&money)) 714 if (!stretch(&finish)) { 715 pchar(&you, '?'); 716 refresh(); 717 delay(10); 718 pchar(&you, ME); 719 } 720#if 0 721 if (you.line < 3) { 722 point(&p, you.col, 0); 723 chk(&p); 724 } 725 if (you.line > lcnt - 4) { 726 point(&p, you.col, lcnt - 1); 727 chk(&p); 728 } 729 if (you.col < 10) { 730 point(&p, 0, you.line); 731 chk(&p); 732 } 733 if (you.col > ccnt - 10) { 734 point(&p, ccnt - 1, you.line); 735 chk(&p); 736 } 737#endif 738 refresh(); 739} 740 741static int 742stretch(const struct point *ps) 743{ 744 struct point p; 745 746 point(&p, you.col, you.line); 747 if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) { 748 if (you.line < ps->line) { 749 for (p.line = you.line + 1; p.line <= ps->line; p.line++) 750 pchar(&p, 'v'); 751 refresh(); 752 delay(10); 753 for (; p.line > you.line; p.line--) 754 chk(&p); 755 } else { 756 for (p.line = you.line - 1; p.line >= ps->line; p.line--) 757 pchar(&p, '^'); 758 refresh(); 759 delay(10); 760 for (; p.line < you.line; p.line++) 761 chk(&p); 762 } 763 return (1); 764 } else 765 if ((abs(ps->line - you.line) < (lcnt/7)) 766 && (you.col != ps->col)) { 767 p.line = you.line; 768 if (you.col < ps->col) { 769 for (p.col = you.col + 1; p.col <= ps->col; p.col++) 770 pchar(&p, '>'); 771 refresh(); 772 delay(10); 773 for (; p.col > you.col; p.col--) 774 chk(&p); 775 } else { 776 for (p.col = you.col - 1; p.col >= ps->col; p.col--) 777 pchar(&p, '<'); 778 refresh(); 779 delay(10); 780 for (; p.col < you.col; p.col++) 781 chk(&p); 782 } 783 return (1); 784 } 785 return (0); 786} 787 788static void 789surround(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 831static void 832win(const struct point *ps) 833{ 834 struct point x; 835 int j, k; 836 int boxsize; /* actually diameter of box, not radius */ 837 838 boxsize = fast ? 10 : 4; 839 point(&x, ps->col, ps->line); 840 for (j = 1; j < boxsize; j++) { 841 for (k = 0; k < j; k++) { 842 pchar(&x, '#'); 843 x.line--; 844 } 845 for (k = 0; k < j; k++) { 846 pchar(&x, '#'); 847 x.col++; 848 } 849 j++; 850 for (k = 0; k < j; k++) { 851 pchar(&x, '#'); 852 x.line++; 853 } 854 for (k = 0; k < j; k++) { 855 pchar(&x, '#'); 856 x.col--; 857 } 858 refresh(); 859 delay(1); 860 } 861} 862 863static int 864pushsnake(void) 865{ 866 int i, bonus; 867 int issame = 0; 868 struct point tmp; 869 870 /* 871 * My manual says times doesn't return a value. Furthermore, the 872 * snake should get his turn every time no matter if the user is 873 * on a fast terminal with typematic keys or not. 874 * So I have taken the call to times out. 875 */ 876 for (i = 4; i >= 0; i--) 877 if (same(&snake[i], &snake[5])) 878 issame++; 879 if (!issame) 880 pchar(&snake[5], ' '); 881 /* Need the following to catch you if you step on the snake's tail */ 882 tmp.col = snake[5].col; 883 tmp.line = snake[5].line; 884 for (i = 4; i >= 0; i--) 885 snake[i + 1] = snake[i]; 886 chase(&snake[0], &snake[1]); 887 pchar(&snake[1], SNAKETAIL); 888 pchar(&snake[0], SNAKEHEAD); 889 for (i = 0; i < 6; i++) { 890 if (same(&snake[i], &you) || same(&tmp, &you)) { 891 surround(&you); 892 i = (cashvalue) % 10; 893 bonus = ((random() >> 8) & 0377) % 10; 894 mvprintw(lcnt + 1, 0, "%d\n", bonus); 895 refresh(); 896 delay(15); 897 delay(15); 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 921static int 922chk(const struct point *sp) 923{ 924 int j; 925 926 if (same(sp, &money)) { 927 pchar(sp, TREASURE); 928 return (2); 929 } 930 if (same(sp, &finish)) { 931 pchar(sp, GOAL); 932 return (3); 933 } 934 if (same(sp, &snake[0])) { 935 pchar(sp, SNAKEHEAD); 936 return (4); 937 } 938 for (j = 1; j < 6; j++) { 939 if (same(sp, &snake[j])) { 940 pchar(sp, SNAKETAIL); 941 return (4); 942 } 943 } 944 if ((sp->col < 4) && (sp->line == 0)) { 945 winnings(cashvalue); 946 if ((you.line == 0) && (you.col < 4)) 947 pchar(&you, ME); 948 return (5); 949 } 950 if (same(sp, &you)) { 951 pchar(sp, ME); 952 return (1); 953 } 954 pchar(sp, ' '); 955 return (0); 956} 957 958static void 959winnings(int won) 960{ 961 if (won > 0) { 962 mvprintw(1, 1, "$%d", won); 963 } 964} 965 966static void 967stop(int dummy __unused) 968{ 969 signal(SIGINT, SIG_IGN); 970 endwin(); 971 length(moves); 972 exit(0); 973} 974 975static void 976suspend(void) 977{ 978 endwin(); 979 kill(getpid(), SIGTSTP); 980 refresh(); 981 winnings(cashvalue); 982} 983 984static void 985length(int num) 986{ 987 printf("You made %d moves.\n", num); 988} 989 990static void 991logit(const char *msg) 992{ 993 time_t t; 994 995 if (logfile != NULL) { 996 time(&t); 997 fprintf(logfile, "%s $%d %dx%d %s %s", 998 getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t)); 999 fflush(logfile); 1000 } 1001} 1002