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