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