|
|
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);
}
}