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