xref: /dragonfly/games/cribbage/io.c (revision 9629eb35)
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