1 /*- 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#)io.c 8.1 (Berkeley) 5/31/93 30 * $FreeBSD: src/games/cribbage/io.c,v 1.5.2.2 2001/02/18 02:20:31 kris Exp $ 31 * $DragonFly: src/games/cribbage/io.c,v 1.4 2005/08/03 13:31:00 eirikn Exp $ 32 */ 33 34 #include <ctype.h> 35 #include <curses.h> 36 #include <signal.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "deck.h" 43 #include "cribbage.h" 44 #include "cribcur.h" 45 46 #define LINESIZE 128 47 48 #ifdef CTRL 49 #undef CTRL 50 #endif 51 #define CTRL(X) (X - 'A' + 1) 52 53 char linebuf[LINESIZE]; 54 55 const char *const rankname[RANKS] = { 56 "ACE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", 57 "EIGHT", "NINE", "TEN", "JACK", "QUEEN", "KING" 58 }; 59 60 const char *const rankchar[RANKS] = { 61 "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K" 62 }; 63 64 const char *const suitname[SUITS] = {"SPADES", "HEARTS", "DIAMONDS", "CLUBS"}; 65 66 const char *const suitchar[SUITS] = {"S", "H", "D", "C"}; 67 68 static bool incard(CARD *); 69 static bool msgcrd(CARD, bool, const char *, bool); 70 static void printcard(WINDOW *, int, CARD, bool); 71 static int readchar(void); 72 static void wait_for(int); 73 74 /* 75 * msgcard: 76 * Call msgcrd in one of two forms 77 */ 78 bool 79 msgcard(CARD c, bool brief) 80 { 81 if (brief) 82 return (msgcrd(c, true, NULL, true)); 83 else 84 return (msgcrd(c, false, " of ", false)); 85 } 86 87 /* 88 * msgcrd: 89 * Print the value of a card in ascii 90 */ 91 static bool 92 msgcrd(CARD c, bool brfrank, const char *mid, bool brfsuit) 93 { 94 if (c.rank == EMPTY || c.suit == EMPTY) 95 return (false); 96 if (brfrank) 97 addmsg("%1.1s", rankchar[c.rank]); 98 else 99 addmsg("%s", rankname[c.rank]); 100 if (mid != NULL) 101 addmsg("%s", mid); 102 if (brfsuit) 103 addmsg("%1.1s", suitchar[c.suit]); 104 else 105 addmsg("%s", suitname[c.suit]); 106 return (true); 107 } 108 109 /* 110 * printcard: 111 * Print out a card. 112 */ 113 static void 114 printcard(WINDOW *win, int cardno, CARD c, bool blank) 115 { 116 prcard(win, cardno * 2, cardno, c, blank); 117 } 118 119 /* 120 * prcard: 121 * Print out a card on the window at the specified location 122 */ 123 void 124 prcard(WINDOW *win, int y, int x, CARD c, bool blank) 125 { 126 if (c.rank == EMPTY) 127 return; 128 129 mvwaddstr(win, y + 0, x, "+-----+"); 130 mvwaddstr(win, y + 1, x, "| |"); 131 mvwaddstr(win, y + 2, x, "| |"); 132 mvwaddstr(win, y + 3, x, "| |"); 133 mvwaddstr(win, y + 4, x, "+-----+"); 134 if (!blank) { 135 mvwaddch(win, y + 1, x + 1, rankchar[c.rank][0]); 136 waddch(win, suitchar[c.suit][0]); 137 mvwaddch(win, y + 3, x + 4, rankchar[c.rank][0]); 138 waddch(win, suitchar[c.suit][0]); 139 } 140 } 141 142 /* 143 * prhand: 144 * Print a hand of n cards 145 */ 146 void 147 prhand(CARD h[], int n, WINDOW *win, bool blank) 148 { 149 int i; 150 151 werase(win); 152 for (i = 0; i < n; i++) 153 printcard(win, i, *h++, blank); 154 wrefresh(win); 155 } 156 157 /* 158 * infrom: 159 * reads a card, supposedly in hand, accepting unambiguous brief 160 * input, returns the index of the card found... 161 */ 162 int 163 infrom(CARD hand[], int n, const char *prompt) 164 { 165 int i, j; 166 CARD crd; 167 168 if (n < 1) { 169 printf("\nINFROM: %d = n < 1!!\n", n); 170 exit(74); 171 } 172 for (;;) { 173 msg("%s", prompt); 174 if (incard(&crd)) { /* if card is full card */ 175 if (!isone(crd, hand, n)) 176 msg("That's not in your hand"); 177 else { 178 for (i = 0; i < n; i++) 179 if (hand[i].rank == crd.rank && 180 hand[i].suit == crd.suit) 181 break; 182 if (i >= n) { 183 printf("\nINFROM: isone or something messed up\n"); 184 exit(77); 185 } 186 return (i); 187 } 188 } else /* if not full card... */ 189 if (crd.rank != EMPTY) { 190 for (i = 0; i < n; i++) 191 if (hand[i].rank == crd.rank) 192 break; 193 if (i >= n) 194 msg("No such rank in your hand"); 195 else { 196 for (j = i + 1; j < n; j++) 197 if (hand[j].rank == crd.rank) 198 break; 199 if (j < n) 200 msg("Ambiguous rank"); 201 else 202 return (i); 203 } 204 } else 205 msg("Sorry, I missed that"); 206 } 207 /* NOTREACHED */ 208 } 209 210 /* 211 * incard: 212 * Inputs a card in any format. It reads a line ending with a CR 213 * and then parses it. 214 */ 215 static bool 216 incard(CARD *crd) 217 { 218 int i; 219 int rnk, sut; 220 char *line, *p, *p1; 221 bool retval; 222 223 retval = false; 224 rnk = sut = EMPTY; 225 if (!(line = get_line())) 226 goto gotit; 227 p = p1 = line; 228 while (*p1 != ' ' && *p1 != '\0') 229 ++p1; 230 *p1++ = '\0'; 231 if (*p == '\0') 232 goto gotit; 233 234 /* IMPORTANT: no real card has 2 char first name */ 235 if (strlen(p) == 2) { /* check for short form */ 236 rnk = EMPTY; 237 for (i = 0; i < RANKS; i++) { 238 if (*p == *rankchar[i]) { 239 rnk = i; 240 break; 241 } 242 } 243 if (rnk == EMPTY) 244 goto gotit; /* it's nothing... */ 245 ++p; /* advance to next char */ 246 sut = EMPTY; 247 for (i = 0; i < SUITS; i++) { 248 if (*p == *suitchar[i]) { 249 sut = i; 250 break; 251 } 252 } 253 if (sut != EMPTY) 254 retval = true; 255 goto gotit; 256 } 257 rnk = EMPTY; 258 for (i = 0; i < RANKS; i++) { 259 if (!strcmp(p, rankname[i]) || !strcmp(p, rankchar[i])) { 260 rnk = i; 261 break; 262 } 263 } 264 if (rnk == EMPTY) 265 goto gotit; 266 p = p1; 267 while (*p1 != ' ' && *p1 != '\0') 268 ++p1; 269 *p1++ = '\0'; 270 if (*p == '\0') 271 goto gotit; 272 if (!strcmp("OF", p)) { 273 p = p1; 274 while (*p1 != ' ' && *p1 != '\0') 275 ++p1; 276 *p1++ = '\0'; 277 if (*p == '\0') 278 goto gotit; 279 } 280 sut = EMPTY; 281 for (i = 0; i < SUITS; i++) { 282 if (!strcmp(p, suitname[i]) || !strcmp(p, suitchar[i])) { 283 sut = i; 284 break; 285 } 286 } 287 if (sut != EMPTY) 288 retval = true; 289 gotit: 290 (*crd).rank = rnk; 291 (*crd).suit = sut; 292 return (retval); 293 } 294 295 /* 296 * getuchar: 297 * Reads and converts to upper case 298 */ 299 int 300 getuchar(void) 301 { 302 int c; 303 304 c = readchar(); 305 if (islower(c)) 306 c = toupper(c); 307 waddch(Msgwin, c); 308 return (c); 309 } 310 311 /* 312 * number: 313 * Reads in a decimal number and makes sure it is between "lo" and 314 * "hi" inclusive. 315 */ 316 int 317 number(int lo, int hi, const char *prompt) 318 { 319 char *p; 320 int sum; 321 322 for (sum = 0;;) { 323 msg("%s", prompt); 324 if (!(p = get_line()) || *p == '\0') { 325 msg(quiet ? "Not a number" : 326 "That doesn't look like a number"); 327 continue; 328 } 329 sum = 0; 330 331 if (!isdigit(*p)) 332 sum = lo - 1; 333 else 334 while (isdigit(*p)) { 335 sum = 10 * sum + (*p - '0'); 336 ++p; 337 } 338 339 if (*p != ' ' && *p != '\t' && *p != '\0') 340 sum = lo - 1; 341 if (sum >= lo && sum <= hi) 342 break; 343 if (sum == lo - 1) 344 msg("that doesn't look like a number, try again --> "); 345 else 346 msg("%d is not between %d and %d inclusive, try again --> ", 347 sum, lo, hi); 348 } 349 return (sum); 350 } 351 352 /* 353 * msg: 354 * Display a message at the top of the screen. 355 */ 356 char Msgbuf[BUFSIZ] = {'\0'}; 357 int Mpos = 0; 358 static int Newpos = 0; 359 360 void 361 msg(const char *fmt, ...) 362 { 363 va_list ap; 364 365 va_start(ap, fmt); 366 vsprintf(&Msgbuf[Newpos], fmt, ap); 367 va_end(ap); 368 endmsg(); 369 } 370 371 /* 372 * addmsg: 373 * Add things to the current message 374 */ 375 void 376 addmsg(const char *fmt, ...) 377 { 378 va_list ap; 379 380 va_start(ap, fmt); 381 vsprintf(&Msgbuf[Newpos], fmt, ap); 382 va_end(ap); 383 } 384 385 /* 386 * endmsg: 387 * Display a new msg. 388 */ 389 int Lineno = 0; 390 391 void 392 endmsg(void) 393 { 394 static int lastline = 0; 395 int len; 396 char *mp, *omp; 397 398 /* All messages should start with uppercase */ 399 mvaddch(lastline + Y_MSG_START, SCORE_X, ' '); 400 if (islower(Msgbuf[0]) && Msgbuf[1] != ')') 401 Msgbuf[0] = toupper(Msgbuf[0]); 402 mp = Msgbuf; 403 len = strlen(mp); 404 if (len / MSG_X + Lineno >= MSG_Y) { 405 while (Lineno < MSG_Y) { 406 wmove(Msgwin, Lineno++, 0); 407 wclrtoeol(Msgwin); 408 } 409 Lineno = 0; 410 } 411 mvaddch(Lineno + Y_MSG_START, SCORE_X, '*'); 412 lastline = Lineno; 413 do { 414 mvwaddstr(Msgwin, Lineno, 0, mp); 415 if ((len = strlen(mp)) > MSG_X) { 416 omp = mp; 417 for (mp = &mp[MSG_X - 1]; *mp != ' '; mp--) 418 continue; 419 while (*mp == ' ') 420 mp--; 421 mp++; 422 wmove(Msgwin, Lineno, mp - omp); 423 wclrtoeol(Msgwin); 424 } 425 if (++Lineno >= MSG_Y) 426 Lineno = 0; 427 } while (len > MSG_X); 428 wclrtoeol(Msgwin); 429 Mpos = len; 430 Newpos = 0; 431 wrefresh(Msgwin); 432 refresh(); 433 wrefresh(Msgwin); 434 } 435 436 /* 437 * do_wait: 438 * Wait for the user to type ' ' before doing anything else 439 */ 440 void 441 do_wait(void) 442 { 443 static char prompt[] = {'-', '-', 'M', 'o', 'r', 'e', '-', '-', '\0'}; 444 445 if (Mpos + (int)sizeof(prompt) < MSG_X) 446 wmove(Msgwin, Lineno > 0 ? Lineno - 1 : MSG_Y - 1, Mpos); 447 else { 448 mvwaddch(Msgwin, Lineno, 0, ' '); 449 wclrtoeol(Msgwin); 450 if (++Lineno >= MSG_Y) 451 Lineno = 0; 452 } 453 waddstr(Msgwin, prompt); 454 wrefresh(Msgwin); 455 wait_for(' '); 456 } 457 458 /* 459 * wait_for 460 * Sit around until the guy types the right key 461 */ 462 static void 463 wait_for(int ch) 464 { 465 char c; 466 467 if (ch == '\n') 468 while ((c = readchar()) != '\n') 469 continue; 470 else 471 while (readchar() != ch) 472 continue; 473 } 474 475 /* 476 * readchar: 477 * Reads and returns a character, checking for gross input errors 478 */ 479 static int 480 readchar(void) 481 { 482 int cnt; 483 char c; 484 485 over: 486 cnt = 0; 487 while (read(STDIN_FILENO, &c, sizeof(char)) <= 0) 488 if (cnt++ > 100) { /* if we are getting infinite EOFs */ 489 bye(); /* quit the game */ 490 exit(1); 491 } 492 if (c == CTRL('L')) { 493 wrefresh(curscr); 494 goto over; 495 } 496 if (c == '\r') 497 return ('\n'); 498 else 499 return (c); 500 } 501 502 /* 503 * get_line: 504 * Reads the next line up to '\n' or EOF. Multiple spaces are 505 * compressed to one space; a space is inserted before a ',' 506 */ 507 char * 508 get_line(void) 509 { 510 char *sp; 511 int c, oy, ox; 512 WINDOW *oscr; 513 514 oscr = stdscr; 515 stdscr = Msgwin; 516 getyx(stdscr, oy, ox); 517 refresh(); 518 /* loop reading in the string, and put it in a temporary buffer */ 519 for (sp = linebuf; (c = readchar()) != '\n'; clrtoeol(), refresh()) { 520 if (c == -1) 521 continue; 522 else 523 if (c == erasechar()) { /* process erase character */ 524 if (sp > linebuf) { 525 int i; 526 527 sp--; 528 for (i = strlen(unctrl(*sp)); i; i--) 529 addch('\b'); 530 } 531 continue; 532 } else 533 if (c == killchar()) { /* process kill 534 * character */ 535 sp = linebuf; 536 move(oy, ox); 537 continue; 538 } else 539 if (sp == linebuf && c == ' ') 540 continue; 541 if (sp >= &linebuf[LINESIZE - 1] || !(isprint(c) || c == ' ')) 542 putchar(CTRL('G')); 543 else { 544 if (islower(c)) 545 c = toupper(c); 546 *sp++ = c; 547 addstr(unctrl(c)); 548 Mpos++; 549 } 550 } 551 *sp = '\0'; 552 stdscr = oscr; 553 return (linebuf); 554 } 555 556 void 557 intr(int signo __unused) 558 { 559 bye(); 560 exit(1); 561 } 562 563 /* 564 * bye: 565 * Leave the program, cleaning things up as we go. 566 */ 567 void 568 bye(void) 569 { 570 signal(SIGINT, SIG_IGN); 571 mvcur(0, COLS - 1, LINES - 1, 0); 572 fflush(stdout); 573 endwin(); 574 putchar('\n'); 575 } 576