4d2a9e6bde5053e130881969eb194f4fb0ad4110
[dragonfly.git] / games / cribbage / io.c
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 = getline()))
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 = getline()) || *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  * getline:
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 getline(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 }