|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T h
Length: 16883 (0x41f3) Types: TextFile Names: »hearts.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Hearts/hearts.c«
/* * hearts - human interface to hearts program * * All smarts are in heartsd, which is invoked by initial caller of hearts. * * By Bob Ankeney or Generic Computer Products * Bug reports to: * ...!tektronix!reed!bob * * * Commands to hearts client (r = rank, s = suit): * * Ars Add card to hand. * Rrs Remove card from hand. * En Erase window n. * G Get a card. * Pnrs<name> Play card from player n, whose name is <name>. * Snpptt<name> Score points (pp) and total points (tt) for player n. * Mn<text> Message <text> is placed in window n. * X Go away. * * Messages from client: * * Prs Pass/Play card from hand. * M<text> Send message <text> to all players. * */ #include <curses.h> #include <sys/errno.h> #include "misc.h" #include "defs.h" #include "local.h" int dist_socket, dealer_socket; char host[20]; typedef struct node *ptr; struct node { ptr llink, rlink; int rank; }; struct suit_list { ptr head, tail; int length; } my_hand[MAX_SUIT + 1]; WINDOW *card_window[MAX_SUIT + 1], *round_window, *lead_window, *inp_window, *text_window, *play_window, *tplay_window, *mesg_window, *tmesg_window, *header_window, *score_window, **window_ptr[MESG_WINDOW + 1]; int mesg_bottom, /* Bottom line of lower message window */ tmesg_bottom; /* Bottom line of upper message window */ char show_play, /* TRUE = show play/score window, FALSE = show mesg */ first_game, game_over; char *snames[] = { "", "clubs", "diamonds", "hearts", "spades" }, rnames[] = " 23456789TJQKA", got_char; init_suit(list) struct suit_list *list; { char *malloc(); list->length = 0; list->head = (ptr) malloc(sizeof(struct node)); list->tail = (ptr) malloc(sizeof(struct node)); list->head->llink = NULL; list->tail->rlink = NULL; list->head->rlink = list->tail; list->tail->llink = list->head; list->head->rank = MAX_RANK + 1; list->tail->rank = 0; } init() { int wimp_out(); int suit; char ch; char buffer[128]; char *pager, *getenv(); first_game = TRUE; (void) signal(SIGINT, wimp_out); initscr(); clearok(stdscr, FALSE); crmode(); init_windows(); mvwaddstr(play_window, 0, 0, "*** The Game of Hearts ***"); mvwaddstr(play_window, 2, 0, "Do you need instructions? "); wrefresh(play_window); ch = get_char(); if ((ch == 'y') || (ch == 'Y')) { endwin(); clear(); refresh(); if (!(pager = getenv ("PAGER"))) pager = "more"; (void) sprintf (buffer, "%s %s", pager, INSTRUCT); (void) system(buffer); crmode(); printf ("Type any key to continue: "); (void) fflush (stdout); (void) get_char(); } noecho(); for (suit = CLUBS; suit <= SPADES; suit++) init_suit(&my_hand[suit]); } get_going() { int dealer_port; char *name, *getenv(); /* * Connect to distributor. If not available, start it. */ if ((dist_socket = connect_to(host, PORT)) == 0) { start_distributor(); if ((dist_socket = connect_to(host, PORT)) == 0) death("Can't invoke distributor!"); } /* * Get dealer port and connect to it. * If port returned == 0, restart game with old dealer. */ if (dealer_port = select_game()) { if (!first_game) (void) close(dealer_socket); if ((dealer_socket = connect_to(host, dealer_port)) == 0) death("Can't connect to dealer!\n"); if ((name = getenv("HEARTS")) == NULL) /* get user name */ if ((name = getenv("NAME")) == NULL) name = getenv("USER"); write_socket(dealer_socket, name); /* tell dealer */ } werase(play_window); mvwaddstr(header_window, 0, 1, "player score total"); show_play = FALSE; toggle_mesg(); /* Show play and score windows */ } get_rank(rch) char rch; { int i; for (i = 1; i <= MAX_RANK; i++) if (rch == rnames[i]) return(i); return(0); } get_suit(sch) char sch; { int i; for (i = 1; i <= MAX_SUIT; i++) if (sch == *snames[i]) return(i); return(0); } /* * Screen window layout * * 2 4 6 7 * 0 0 0 0 9 * -------------------------------------------------------- * 0 | | | | | * 1 | card_window | card_window | card_window | card_window | * 2 | [CLUBS] | [DIAMONDS] | [HEARTS] | [SPADES] | * ~ ~ ~ ~ ~ * ~ ~ ~ ~ ~ * 16 | | | | | * |-------------------------------------------------------| * | | * if show_play: * * 2 5 7 * 0 2 8 9 * | | * --------------------------------------------------------- * 17 | round_window | |score_text_window| * |--------------| |-----------------| * 18 | | | | * |--------------| | | * 19 | lead_window | | | * |--------------| | | * 20 | inp_window | play_window | | * |--------------| | | * 21 | | | | * |--------------| | | * 22 | text_window | | | * |--------------|----------------------------------------| * 23 | | | * ~ ~ mesg_window ~ * ~ ~ ~ *LINES| | | * --------------------------------------------------------- * * if !show_play: * * 2 5 7 * 0 2 8 9 * | | * --------------------------------------------------------- * 17 | round_window | tplay_window | | * |--------------|----------------------------------------| * 18 | | | * |--------------| | * 19 | lead_window | | * |--------------| | * 20 | inp_window | tmesg_window | * |--------------| | * 21 | | | * |--------------| | * 22 | text_window | | * |--------------|----------------------------------------| * 23 | | | * ~ ~ mesg_window ~ * ~ ~ ~ *LINES| | | * --------------------------------------------------------- */ init_windows() { int i; for (i = CLUBS; i <= SPADES; i++) { card_window[i] = newwin(17, 20, 0, (i - 1) * 20); leaveok(card_window[i], TRUE); werase(card_window[i]); } round_window = newwin(1, 22, 17, 0); lead_window = newwin(1, 22, 19, 0); inp_window = newwin(1, 22, 20, 0); text_window = newwin(1, 22, 22, 0); play_window = newwin(6, 35, 17, 23); tplay_window = newwin(1, 57, 17, 23); header_window = newwin(1, 22, 17, 58); score_window = newwin(5, 22, 18, 58); mesg_window = newwin(0, 57, 23, 23); tmesg_window = newwin(5, 57, 18, 23); mesg_bottom = LINES - 24; tmesg_bottom = 4; scrollok(mesg_window, TRUE); scrollok(tmesg_window, TRUE); window_ptr[ROUND_WINDOW] = &round_window; window_ptr[LEAD_WINDOW] = &lead_window; window_ptr[INP_WINDOW] = &inp_window; window_ptr[TEXT_WINDOW] = &text_window; window_ptr[PLAY_WINDOW] = &play_window; window_ptr[SCORE_WINDOW] = &score_window; window_ptr[MESG_WINDOW] = &mesg_window; } /* * toggle_mesg - toggle whether showing message window or score/play windows */ toggle_mesg() { if (show_play) { touchwin(tplay_window); touchwin(tmesg_window); wrefresh(tplay_window); wrefresh(tmesg_window); } else { touchwin(play_window); touchwin(header_window); touchwin(score_window); wrefresh(play_window); wrefresh(header_window); wrefresh(score_window); } show_play = !show_play; } /* * ovl_refresh - refresh window only if visible */ ovl_refresh(window_num) int window_num; { switch (window_num) { case PLAY_WINDOW: if (show_play) wrefresh(play_window); break; case SCORE_WINDOW: if (show_play) wrefresh(score_window); break; default: wrefresh(*window_ptr[window_num]); } } /* * scroll_mesg - scroll mesg_window onto tmesg_window */ scroll_mesg() { int i; char ch; wmove(tmesg_window, tmesg_bottom, 56); scroll(tmesg_window); wmove(tmesg_window, tmesg_bottom, 0); for (i = 0; i < 56; i++) { wmove(mesg_window, 0, i); ch = winch(mesg_window); waddch(tmesg_window, ch); } scroll(mesg_window); if (!show_play) wrefresh(tmesg_window); } put_card(window, y, x, rank, suit) WINDOW *window; int y, x, rank, suit; { mvwaddstr(window, y, x, "+-----+"); mvwaddstr(window, y + 1, x, "| |"); mvwaddstr(window, y + 2, x, "| |"); mvwaddstr(window, y + 3, x, "| |"); mvwaddstr(window, y + 4, x, "+-----+"); if ((suit == HEARTS) || (suit == DIAMONDS)) wstandout(window); mvwaddch(window, y + 1, x + 1, rnames[rank]); waddch(window, *snames[suit]); mvwaddch(window, y + 3, x + 4, rnames[rank]); waddch(window, *snames[suit]); wstandend(window); } print_cards(suit) int suit; { int y, x; ptr p; werase(card_window[suit]); x = y = 0; p = my_hand[suit].head->rlink; while (p->rank) { if (x == 7) { ++x; y = 0; } put_card(card_window[suit], y, x++, p->rank, suit); y += 2; p = p->rlink; } wrefresh(card_window[suit]); } get_char() { char ch; if (read(0, &ch, 1) == -1) { perror("read"); abort(); } return(ch); } read_char() { char ch; while (!got_char) scan(); ch = got_char; got_char = FALSE; return(ch); } read_card() { int rank, suit; char buf[64]; do { do { werase(inp_window); wrefresh(inp_window); buf[0] = 'P'; buf[1] = read_char(); /* Get rank (not smelly) */ if (buf[1] >= 'a') buf[1] += 'A' - 'a'; if (!(rank = get_rank(buf[1]))) { werase(text_window); waddstr(text_window, "Bad rank!"); wrefresh(text_window); } } while (!rank); werase(text_window); wrefresh(text_window); wprintw(inp_window, "%c ", buf[1]); wrefresh(inp_window); buf[2] = read_char(); /* Get suit (not tuxedo) */ if ((buf[2] >= 'A') && (buf[2] <= 'Z')) buf[2] += 'a' - 'A'; if (!(suit = get_suit(buf[2])) && (buf[2] != _tty.sg_erase) && (buf[2] != _tty.sg_kill)) { werase(text_window); waddstr(text_window, "Bad suit!"); wrefresh(text_window); } } while (!suit); wprintw(inp_window, "of %s", snames[suit]); wrefresh(inp_window); buf[3] = '\0'; write_socket(dealer_socket, buf); } enter_card(rank, suit) int rank, suit; { ptr p, p1; p = my_hand[suit].head; ++my_hand[suit].length; while (p->rank > rank) p = p->rlink; p1 = (ptr) malloc(sizeof(struct node)); p1->llink = p->llink; p1->llink->rlink = p1; p->llink = p1; p1->rlink = p; p1->rank = rank; } remove_node(p) ptr p; { p->llink->rlink = p->rlink; p->rlink->llink = p->llink; free((char *) p); } ptr find_card(rank_to_play, suit_to_play) int rank_to_play, suit_to_play; { ptr card_to_play; card_to_play = my_hand[suit_to_play].head->rlink; while (card_to_play->rank != rank_to_play) card_to_play = card_to_play->rlink; remove_node(card_to_play); --my_hand[suit_to_play].length; return(card_to_play); } do_command(buf) char *buf; { char rch, sch; int player, suit, pts, total_pts, window_num; int temp_y, temp_x; switch (buf[0]) { case 'A' : /* Add card to hand */ enter_card(get_rank(buf[1]), suit = get_suit(buf[2])); print_cards(suit); break; case 'R' : /* Remove card from hand */ suit = get_suit(buf[2]); remove_node(find_card(get_rank(buf[1]), suit)); print_cards(suit); break; case 'E' : /* Erase window */ window_num = buf[1] - '0'; if (window_num == PLAY_WINDOW) { werase(tplay_window); if (!show_play) wrefresh(tplay_window); } werase(*window_ptr[window_num]); ovl_refresh(window_num); break; case 'G' : /* Get card */ read_card(); break; case 'P' : /* Play card from player */ (void) sscanf(buf+1, "%1d %c %c", &player, &rch, &sch); /* * Update standard play window */ put_card(play_window, 0, (player - 1) * 9, get_rank(rch), get_suit(sch)); if (buf[4] != '\0') mvwaddstr(play_window, 5, (player - 1) * 9, buf + 4); /* * Update alternate play window */ if ((sch == 'h') || (sch == 'd')) wstandout(tplay_window); mvwaddch(tplay_window, 0, (player - 1) * 9, rch); waddch(tplay_window, sch); wstandend(tplay_window); if (show_play) wrefresh(play_window); else wrefresh(tplay_window); sleep(1); break; case 'S' : /* Score points for player */ (void) sscanf(buf+1, "%1d %2d %2d", &player, &pts, &total_pts); mvwaddstr(score_window, player - 1, 1, " "); mvwaddstr(score_window, player - 1, 1, buf + 6); mvwprintw(score_window, player - 1, 9, " %2d %2d", pts, total_pts); ovl_refresh(SCORE_WINDOW); break; case 'M' : /* Print message in window */ getyx(curscr, temp_y, temp_x); window_num = buf[1] - '0'; if (window_num == MESG_WINDOW) { scroll_mesg(); mvwaddstr(mesg_window, mesg_bottom, 0, buf + 2); } else { werase(*window_ptr[window_num]); mvwaddstr(*window_ptr[window_num], 0, 0, buf + 2); } ovl_refresh(window_num); move(temp_y, temp_x); refresh(); break; case 'X' : /* Game over */ if (!show_play) toggle_mesg(); werase(round_window); wrefresh(round_window); werase(lead_window); wstandout(lead_window); mvwaddstr(lead_window, 0, 0, "--More--"); wstandend(lead_window); wrefresh(lead_window); (void) get_char(); first_game = FALSE; game_over = TRUE; } } /* * Scan input for redraw screen requests or ':' messages. * Also scan socket for incoming commands. */ scan() { int i, temp_y, temp_x; char buf[64]; fd_type read_fd; int nready; extern int errno; fd_init(dealer_socket, &read_fd); if (!got_char) fd_set(0, &read_fd); do { nready = select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0, (struct timeval *) 0); } while (nready == -1 && errno == EINTR); if (nready == 0) return; if (fd_isset(0, read_fd)) { got_char = get_char(); getyx(curscr, temp_y, temp_x); switch (got_char) { case '\f' : wrefresh(curscr); /* Redraw screen */ got_char = FALSE; break; case ' ' : case '\t' : case '\n' : case '\r' : got_char = FALSE; /* Ignore white space */ break; case 'M' : case 'm' : toggle_mesg(); got_char = FALSE; break; case ':' : scroll_mesg(); /* Message to all players */ mvwaddstr(mesg_window, mesg_bottom, 0, "message:"); wrefresh(mesg_window); buf[0] = 'M'; for (i = 1; (i < 48) && ((buf[i] = get_char()) != '\n'); i++) { if (buf[i] == _tty.sg_erase) { i -= 2; if (i < 0) i = 0; else { wmove(mesg_window, mesg_bottom, i+8); wdelch(mesg_window); } } else { if (buf[i] == _tty.sg_kill) { wmove(mesg_window, mesg_bottom, 8); wclrtoeol(mesg_window); i = 0; } else mvwaddch(mesg_window, mesg_bottom, i + 7, buf[i]); } wrefresh(mesg_window); } buf[i] = '\0'; write_socket(dealer_socket, buf); got_char = FALSE; } move(temp_y, temp_x); refresh(); } if (fd_isset(dealer_socket, read_fd)) { if (!read_socket(dealer_socket, buf)) death("Dealer died!!"); do_command(buf); } } close_windows() { (void) signal(SIGINT, SIG_IGN); clear(); refresh(); (void) fflush(stdout); endwin(); } death(buf) char *buf; { close_windows(); printf("%s\n", buf); exit(1); } wimp_out() { close_windows(); exit(0); } main(argc, argv) int argc; char **argv; { if (argc > 2) { fprintf(stderr, "usage: %s [hostname]\n", *argv); exit(1); } if (argc == 2) (void) strcpy (host, *++argv); else (void) gethostname(host, 20); /* host is this machine */ init(); for (;;) { game_over = FALSE; get_going(); do { scan(); } while (!game_over); } }