From f9f4c71a8beec3d7c2931a266d3df20d00abaeba Mon Sep 17 00:00:00 2001 From: scw Date: Sun, 7 Aug 2005 20:21:12 +0000 Subject: New chess framework * Provides common parts of all chess games * Chinese chess fully ported * Improved watching * Leaving possibility to implement replaying !!!NOTE!!! Protocal not backward compatible, STOP ALL clients before upgrade. git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@3002 63ad8ddf-47c3-0310-b6dd-a9e9d9715204 --- mbbsd/Makefile | 2 +- mbbsd/bbs.c | 1 - mbbsd/chc.c | 1397 +++++++++++++++----------------------------------------- mbbsd/chess.c | 1181 +++++++++++++++++++++++++++++++++++++++++++++++ mbbsd/talk.c | 69 ++- 5 files changed, 1611 insertions(+), 1039 deletions(-) create mode 100644 mbbsd/chess.c (limited to 'mbbsd') diff --git a/mbbsd/Makefile b/mbbsd/Makefile index 5f1d5ad8..d06ad63d 100644 --- a/mbbsd/Makefile +++ b/mbbsd/Makefile @@ -15,7 +15,7 @@ OBJS= admin.o announce.o args.o assess.o bbs.o board.o cache.o cal.o card.o\ more.o name.o osdep.o othello.o page.o read.o record.o register.o\ screen.o stuff.o talk.o term.o topsong.o user.o brc.o vice.o vote.o\ xyz.o voteboard.o syspost.o var.o passwd.o calendar.o go.o file.o \ - pmore.o + pmore.o chess.o .if defined(DIET) OBJS+= random.o time.o diff --git a/mbbsd/bbs.c b/mbbsd/bbs.c index e0858d18..b1c68b18 100644 --- a/mbbsd/bbs.c +++ b/mbbsd/bbs.c @@ -6,7 +6,6 @@ static int recommend(int ent, fileheader_t * fhdr, const char *direct); static int do_add_recommend(const char *direct, fileheader_t *fhdr, int ent, const char *buf, int type); -int mailalert(const char *userid); #ifdef ASSESS static char * const badpost_reason[] = { diff --git a/mbbsd/chc.c b/mbbsd/chc.c index 20173251..21f536f5 100644 --- a/mbbsd/chc.c +++ b/mbbsd/chc.c @@ -1,12 +1,16 @@ /* $Id$ */ #include "bbs.h" +#include "chess.h" + +#define assert_not_reached() assert(!"Should never be here!!!") extern const double elo_exp_tab[1000]; enum Turn { - BLK, + BLK = 0, RED }; + enum Kind { KIND_K=1, KIND_A, @@ -18,52 +22,36 @@ enum Kind { }; #define CENTER(a, b) (((a) + (b)) >> 1) #define CHC_TIMEOUT 300 -#define CHC_LOG "chc_log" /* log file name */ #define PHOTO_LINE 15 #define PHOTO_COLUMN (256 + 25) -typedef int (*play_func_t) (int, const chcusr_t *, const chcusr_t *, board_t, board_t); - typedef struct drc_t { + ChessStepType type; /* necessary one */ rc_t from, to; } drc_t; -struct CHCData { - rc_t from, to, select, cursor; - - /* 計時用, [0] = mine, [1] = his */ - int lefttime[2]; - int lefthand[2]; /* 限時限步時用, = 0 表為自由時間或非限時限步模式 */ - - int my; /* 我方執紅或黑, 0 黑, 1 紅. 觀棋=1 */ - int turn, selected, firststep; - char mode; - char warnmsg[64]; - /* color(7)+step(4*2)+normal(3)+color(7)+eat(2*2)+normal(3)+1=33 */ - char last_movestr[36]; - char ipass, hepass; - /* chessfp is for logging the step */ - FILE *chessfp; - board_t *bp; - chc_act_list *act_list; - char *photo; -}; typedef struct { - int limit_hand; - int limit_time; - int free_time; - enum { - CHCTIME_ORIGINAL, CHCTIME_FREE, CHCTIME_LIMIT - } time_mode; -} CHCTimeLimit; + rc_t select; + char selected; +} chc_tag_data_t; + +/* chess framework action functions */ +static void chc_init_user(const userec_t *userec, ChessUser *user); +static void chc_init_board(const ChessInfo* info, board_t board); +static void chc_drawline(const ChessInfo* info, int line); +static void chc_movecur(int r, int c); +static void chc_prepare_play(ChessInfo* info); +static int chc_select(ChessInfo* info, rc_t location, ChessGameResult* result); +static void chc_prepare_step(ChessInfo* info, const void* step); +static int chc_movechess(board_t board, const drc_t* move); +static void chc_drawstep(ChessInfo* info, const drc_t* move); +static void chc_gameend(ChessInfo* info, ChessGameResult result); +static void chc_genlog(ChessInfo* info, FILE* fp, ChessGameResult result); -static struct CHCData *chcd; -static CHCTimeLimit *timelimit; static const char * const turn_color[2]={BLACK_COLOR, RED_COLOR}; - /* some constant variable definition */ static const char * const turn_str[2] = {"黑的", "紅的"}; @@ -109,76 +97,34 @@ static char * const hint_str[] = { "Enter 選擇/移動" }; -/* - * Start of the network communication function. - */ -static int -chc_recvmove(int s) -{ - drc_t buf; - - if (read(s, &buf, sizeof(buf)) != sizeof(buf)) - return 0; - chcd->from = buf.from, chcd->to = buf.to; - return 1; -} - -static int -chc_sendmove(int s) -{ - drc_t buf; - - buf.from = chcd->from, buf.to = chcd->to; - if (write(s, &buf, sizeof(buf)) != sizeof(buf)) - return 0; - return 1; -} - -// XXX return value -// XXX die because of SIGPIPE !? - -/* return false if your adversary is off-line */ -static void -chc_broadcast(chc_act_list **head, board_t board){ - chc_act_list *p = *head; - void (*orig_handler)(int); - - if (!p) - return; - - orig_handler = Signal(SIGPIPE, SIG_IGN); - if (!chc_sendmove(p->sock)) { - /* do nothing */ - } - - while(p->next){ - if (!chc_sendmove(p->next->sock)) { - chc_act_list *tmp = p->next->next; - free(p->next); - p->next = tmp; - } else - p = p->next; - } - Signal(SIGPIPE, orig_handler); -} - -static int -chc_broadcast_recv(chc_act_list *act_list, board_t board){ - if (!chc_recvmove(act_list->sock)) - return 0; - chc_broadcast(&act_list->next, board); - return 1; -} - -static int -chc_broadcast_send(chc_act_list *act_list, board_t board){ - chc_broadcast(&act_list, board); - return 1; -} +static const ChessActions chc_actions = { + &chc_init_user, + (void (*) (const ChessInfo*, void*)) &chc_init_board, + &chc_drawline, + &chc_movecur, + &chc_prepare_play, + &chc_select, + &chc_prepare_step, + (int (*) (void*, const void*)) &chc_movechess, + (void (*)(ChessInfo*, const void*)) &chc_drawstep, + &chc_gameend, + &chc_genlog +}; -/* - * End of the network communication function. - */ +static const ChessConstants chc_constants = { + sizeof(drc_t), + CHC_TIMEOUT, + BRD_ROW, + BRD_COL, + "photo_cchess", +#ifdef GLOBAL_CCHESS_LOG + GLOBAL_CCHESS_LOG, +#else + NULL, +#endif + { BLACK_COLOR, RED_COLOR }, + {"黑的", "紅的"} +}; /* * Start of the drawing function. @@ -190,7 +136,7 @@ chc_movecur(int r, int c) } static char * -getstep(board_t board, const rc_t *from, const rc_t *to, char buf[]) +getstep(board_t board, int my, const rc_t *from, const rc_t *to, char buf[]) { int turn, fc, tc; char *dir; @@ -208,8 +154,8 @@ getstep(board_t board, const rc_t *from, const rc_t *to, char buf[]) } } } - fc = (turn == (chcd->my ^ 1) ? from->c + 1 : 9 - from->c); - tc = (turn == (chcd->my ^ 1) ? to->c + 1 : 9 - to->c); + fc = (turn == (my ^ 1) ? from->c + 1 : 9 - from->c); + tc = (turn == (my ^ 1) ? to->c + 1 : 9 - to->c); if (from->r == to->r) dir = "平"; else { @@ -218,8 +164,8 @@ getstep(board_t board, const rc_t *from, const rc_t *to, char buf[]) if (tc < 0) tc = -tc; - if ((turn == (chcd->my ^ 1) && to->r > from->r) || - (turn == chcd->my && to->r < from->r)) + if ((turn == (my ^ 1) && to->r > from->r) || + (turn == my && to->r < from->r)) dir = "進"; else dir = "退"; @@ -230,7 +176,7 @@ getstep(board_t board, const rc_t *from, const rc_t *to, char buf[]) /* 傌二|前傌 */ if(twin) { len+=sprintf(buf+len, "%s%s", - ((from->r>twin_r)==(turn==(chcd->my^1)))?"前":"後", + ((from->r>twin_r)==(turn==(my^1)))?"前":"後", chess_str[turn][CHE_P(board[from->r][from->c])]); } else { len+=sprintf(buf+len, "%s%s", @@ -249,38 +195,50 @@ getstep(board_t board, const rc_t *from, const rc_t *to, char buf[]) } static void -showstep(board_t board) +showstep(const ChessInfo* info) { - outs(chcd->last_movestr); + outs(info->last_movestr); +} + +inline static const char* +chc_timestr(int second) +{ + static char str[10]; + snprintf(str, sizeof(str), "%d:%02d", second / 60, second % 60); + return str; } static void -chc_drawline(board_t board, const chcusr_t *user1, const chcusr_t *user2, int line) +chc_drawline(const ChessInfo* info, int line) { int i, j; - - if (line == TURN_ROW) - line = chcd->photo ? PHOTO_TURN_ROW : REAL_TURN_ROW; - else if (line == TIME_ROW) { - chc_drawline(board, user1, user2, - chcd->photo ? PHOTO_TIME_ROW1 : REAL_TIME_ROW1); - line = chcd->photo ? PHOTO_TIME_ROW2 : REAL_TIME_ROW2; - } else if (line == WARN_ROW) { - line = chcd->photo ? PHOTO_WARN_ROW : REAL_WARN_ROW; - } + board_p board = (board_p) info->board; + chc_tag_data_t *tag = info->tag; + + if (line == CHESS_DRAWING_TURN_ROW) + line = info->photo ? PHOTO_TURN_ROW : REAL_TURN_ROW; + else if (line == CHESS_DRAWING_TIME_ROW) { + chc_drawline(info, info->photo ? PHOTO_TIME_ROW1 : REAL_TIME_ROW1); + line = info->photo ? PHOTO_TIME_ROW2 : REAL_TIME_ROW2; + } else if (line == CHESS_DRAWING_WARN_ROW) + line = info->photo ? PHOTO_WARN_ROW : REAL_WARN_ROW; + else if (line == CHESS_DRAWING_STEP_ROW) + line = STEP_ROW; move(line, 0); clrtoeol(); if (line == 0) { - prints(ANSI_COLOR(1;46) " 象棋對戰 " ANSI_COLOR(45) "%30s VS %-20s%10s" ANSI_RESET, - user1->userid, user2->userid, chcd->mode & CHC_WATCH ? "[觀棋模式]" : ""); + prints(ANSI_COLOR(1;46) " 象棋對戰 " ANSI_COLOR(45) + "%30s VS %-20s%10s" ANSI_RESET, + info->user1.userid, info->user2.userid, + info->mode == CHESS_MODE_WATCH ? "[觀棋模式]" : ""); } else if (line >= 3 && line <= 21) { outs(" "); for (i = 0; i < 9; i++) { j = board[RTL(line)][i]; if ((line & 1) == 1 && j) { - if (chcd->selected && - chcd->select.r == RTL(line) && chcd->select.c == i) { + if (tag->selected && + tag->select.r == RTL(line) && tag->select.c == i) { prints("%s%s" ANSI_RESET, CHE_O(j) == BLK ? BLACK_REVERSE : RED_REVERSE, chess_str[CHE_O(j)][CHE_P(j)]); @@ -307,34 +265,51 @@ chc_drawline(board_t board, const chcusr_t *user1, const chcusr_t *user2, int li prints("%s ", num_str[1][i]); } - if (chcd->photo) { - if (line >= 3 && line < 3 + PHOTO_LINE) { + if (info->photo) { + if (line >= 3 && line < 3 + CHESS_PHOTO_LINE) { outs(" "); - outs(chcd->photo + (line - 3) * PHOTO_COLUMN); + outs(info->photo + (line - 3) * CHESS_PHOTO_COLUMN); } else if (line >= PHOTO_TURN_ROW && line <= PHOTO_WARN_ROW) { outs(" "); if (line == PHOTO_TURN_ROW) prints("%s%s" ANSI_RESET, TURN_COLOR, - chcd->my == chcd->turn ? "輪到你下棋了" : "等待對方下棋"); + info->my == info->turn ? "輪到你下棋了" : "等待對方下棋"); else if (line == PHOTO_TIME_ROW1) { - if (chcd->lefthand[0]) - prints("我方剩餘時間 %d:%02d / %2d 步", - chcd->lefttime[0] / 60, chcd->lefttime[0] % 60, - chcd->lefthand[0]); + if (info->mode == CHESS_MODE_WATCH) { + if (!info->timelimit) + prints("每手限時五分鐘"); + else + prints("局時: %5s", + chc_timestr(info->timelimit->free_time)); + } else if (info->lefthand[0]) + prints("我方剩餘時間 %s / %2d 步", + chc_timestr(info->lefttime[0]), + info->lefthand[0]); else - prints("我方剩餘時間 %d:%02d", - chcd->lefttime[0] / 60, chcd->lefttime[0] % 60); + prints("我方剩餘時間 %s", + chc_timestr(info->lefttime[0])); } else if (line == PHOTO_TIME_ROW2) { - if (chcd->lefthand[1]) - prints("對方剩餘時間 %d:%02d / %2d 步", - chcd->lefttime[1] / 60, chcd->lefttime[1] % 60, - chcd->lefthand[1]); + if (info->mode == CHESS_MODE_WATCH) { + if (info->timelimit) { + if (info->timelimit->time_mode == + CHESS_TIMEMODE_MULTIHAND) + prints("步時: %s / %2d 步", + chc_timestr(info->timelimit->limit_time), + info->timelimit->limit_hand); + else + prints("讀秒: %5d 秒", + info->timelimit->limit_time); + } + } else if (info->lefthand[1]) + prints("對方剩餘時間 %s / %2d 步", + chc_timestr(info->lefttime[1]), + info->lefthand[1]); else - prints("對方剩餘時間 %d:%02d", - chcd->lefttime[1] / 60, chcd->lefttime[1] % 60); + prints("對方剩餘時間 %s", + chc_timestr(info->lefttime[1])); } else if (line == PHOTO_WARN_ROW) - outs(chcd->warnmsg); + outs(info->warnmsg); } } else if (line >= 3 && line <= HISWIN_ROW) { outs(" "); @@ -342,57 +317,49 @@ chc_drawline(board_t board, const chcusr_t *user1, const chcusr_t *user2, int li outs(hint_str[line - 3]); } else if (line == SIDE_ROW) { prints(ANSI_COLOR(1) "你是%s%s" ANSI_RESET, - turn_color[chcd->my], - turn_str[chcd->my]); + turn_color[(int) info->my], + turn_str[(int) info->my]); } else if (line == REAL_TURN_ROW) { prints("%s%s" ANSI_RESET, TURN_COLOR, - chcd->my == chcd->turn ? "輪到你下棋了" : "等待對方下棋"); - } else if (line == STEP_ROW && !chcd->firststep) { - showstep(board); + info->my == info->turn ? "輪到你下棋了" : "等待對方下棋"); + } else if (line == STEP_ROW && info->last_movestr) { + showstep(info); } else if (line == REAL_TIME_ROW1) { - if (chcd->lefthand[0]) - prints("我方剩餘時間 %d:%02d / %2d 步", - chcd->lefttime[0] / 60, chcd->lefttime[0] % 60, - chcd->lefthand[0]); + if (info->lefthand[0]) + prints("我方剩餘時間 %s / %2d 步", + chc_timestr(info->lefttime[0]), + info->lefthand[0]); else - prints("我方剩餘時間 %d:%02d", - chcd->lefttime[0] / 60, chcd->lefttime[0] % 60); + prints("我方剩餘時間 %s", + chc_timestr(info->lefttime[0])); } else if (line == REAL_TIME_ROW2) { - if (chcd->lefthand[1]) - prints("對方剩餘時間 %d:%02d / %2d 步", - chcd->lefttime[1] / 60, chcd->lefttime[1] % 60, - chcd->lefthand[1]); + if (info->lefthand[1]) + prints("對方剩餘時間 %s / %2d 步", + chc_timestr(info->lefttime[1]), + info->lefthand[1]); else - prints("對方剩餘時間 %d:%02d", - chcd->lefttime[1] / 60, chcd->lefttime[1] % 60); + prints("對方剩餘時間 %s", + chc_timestr(info->lefttime[1])); } else if (line == REAL_WARN_ROW) { - outs(chcd->warnmsg); + outs(info->warnmsg); } else if (line == MYWIN_ROW) { prints(ANSI_COLOR(1;33) "%12.12s " ANSI_COLOR(1;31) "%2d" ANSI_COLOR(37) "勝 " ANSI_COLOR(34) "%2d" ANSI_COLOR(37) "敗 " ANSI_COLOR(36) "%2d" ANSI_COLOR(37) "和" ANSI_RESET, - user1->userid, - user1->win, user1->lose - 1, user1->tie); + info->user1.userid, + info->user1.win, info->user1.lose - 1, info->user1.tie); } else if (line == HISWIN_ROW) { prints(ANSI_COLOR(1;33) "%12.12s " ANSI_COLOR(1;31) "%2d" ANSI_COLOR(37) "勝 " ANSI_COLOR(34) "%2d" ANSI_COLOR(37) "敗 " ANSI_COLOR(36) "%2d" ANSI_COLOR(37) "和" ANSI_RESET, - user2->userid, - user2->win, user2->lose - 1, user2->tie); + info->user2.userid, + info->user2.win, info->user2.lose - 1, info->user2.tie); } } } - -static void -chc_redraw(const chcusr_t *user1, const chcusr_t *user2, board_t board) -{ - int i; - for (i = 0; i <= 22; i++) - chc_drawline(board, user1, user2, i); -} /* * End of the drawing function. */ @@ -401,43 +368,14 @@ chc_redraw(const chcusr_t *user1, const chcusr_t *user2, board_t board) /* * Start of the log function. */ -int -chc_log_open(const chcusr_t *user1, const chcusr_t *user2, const char *file) -{ - char buf[128]; - if ((chcd->chessfp = fopen(file, "w")) == NULL) - return -1; - if(chcd->my == RED) - sprintf(buf, "%s V.S. %s\n", user1->userid, user2->userid); - else - sprintf(buf, "%s V.S. %s\n", user2->userid, user1->userid); - fputs(buf, chcd->chessfp); - return 0; -} - void -chc_log_close(void) -{ - if (chcd->chessfp) { - fclose(chcd->chessfp); - chcd->chessfp=NULL; - } -} - -int -chc_log(const char *desc) -{ - if (chcd->chessfp) - return fputs(desc, chcd->chessfp); - return -1; -} - -int -chc_log_step(board_t board, const rc_t *from, const rc_t *to) +chc_log_step(FILE* fp, board_t board, int my, const drc_t *step) { char buf[80]; - sprintf(buf, " %s\n", chcd->last_movestr); - return chc_log(buf); + buf[0] = buf[1] = ' '; + getstep(board, my, &step->from, &step->to, buf + 2); + fputs(buf, fp); + fputc('\n', fp); } static int @@ -452,8 +390,8 @@ chc_filter(struct dirent *dir) return strstr(dir->d_name, ".poem") != NULL; } -int -chc_log_poem(void) +static int +chc_log_poem(FILE* outfp) { struct dirent **namelist; int n; @@ -470,7 +408,7 @@ chc_log_poem(void) return -1; while(fgets(buf, sizeof(buf), fp) != NULL) - chc_log(buf); + fputs(buf, outfp); while(n--) free(namelist[n]); free(namelist); @@ -478,6 +416,37 @@ chc_log_poem(void) } return 0; } + +static void +chc_genlog(ChessInfo* info, FILE* fp, ChessGameResult result) +{ + const int nStep = info->history.used; + board_t board; + int i; + + if (info->my == RED) + fprintf(fp, "%s V.S. %s\n", info->user1.userid, info->user2.userid); + else + fprintf(fp, "%s V.S. %s\n", info->user2.userid, info->user1.userid); + + chc_init_board(info, board); + for (i = 0; i < nStep; ++i) { + const drc_t *move = (const drc_t*) ChessHistoryRetrieve(info, i); + chc_log_step(fp, board, info->my, move); + chc_movechess(board, move); + } + + if (result == CHESS_RESULT_TIE) + fprintf(fp, "=> 和局\n"); + else if (result == CHESS_RESULT_WIN || result == CHESS_RESULT_LOST) + fprintf(fp, "=> %s 勝\n", + (info->my == RED) == (result== CHESS_RESULT_WIN) ? + "紅" : "黑"); + + fputs("\n--\n\n", fp); + + chc_log_poem(fp); +} /* * End of the log function. */ @@ -486,35 +455,55 @@ chc_log_poem(void) /* * Start of the rule function. */ - static void -chc_init_board(board_t board) +chc_init_board(const ChessInfo* info, board_t board) { + const int my = info->my; + memset(board, 0, sizeof(board_t)); - board[0][4] = CHE(KIND_K, chcd->my ^ 1); /* 將 */ - board[0][3] = board[0][5] = CHE(KIND_A, chcd->my ^ 1); /* 士 */ - board[0][2] = board[0][6] = CHE(KIND_E, chcd->my ^ 1); /* 象 */ - board[0][0] = board[0][8] = CHE(KIND_R, chcd->my ^ 1); /* 車 */ - board[0][1] = board[0][7] = CHE(KIND_H, chcd->my ^ 1); /* 馬 */ - board[2][1] = board[2][7] = CHE(KIND_C, chcd->my ^ 1); /* 包 */ + board[0][4] = CHE(KIND_K, my ^ 1); /* 將 */ + board[0][3] = board[0][5] = CHE(KIND_A, my ^ 1); /* 士 */ + board[0][2] = board[0][6] = CHE(KIND_E, my ^ 1); /* 象 */ + board[0][0] = board[0][8] = CHE(KIND_R, my ^ 1); /* 車 */ + board[0][1] = board[0][7] = CHE(KIND_H, my ^ 1); /* 馬 */ + board[2][1] = board[2][7] = CHE(KIND_C, my ^ 1); /* 包 */ board[3][0] = board[3][2] = board[3][4] = - board[3][6] = board[3][8] = CHE(KIND_P, chcd->my ^ 1); /* 卒 */ - - board[9][4] = CHE(KIND_K, chcd->my); /* 帥 */ - board[9][3] = board[9][5] = CHE(KIND_A, chcd->my); /* 仕 */ - board[9][2] = board[9][6] = CHE(KIND_E, chcd->my); /* 相 */ - board[9][0] = board[9][8] = CHE(KIND_R, chcd->my); /* 車 */ - board[9][1] = board[9][7] = CHE(KIND_H, chcd->my); /* 傌 */ - board[7][1] = board[7][7] = CHE(KIND_C, chcd->my); /* 炮 */ + board[3][6] = board[3][8] = CHE(KIND_P, my ^ 1); /* 卒 */ + + board[9][4] = CHE(KIND_K, my); /* 帥 */ + board[9][3] = board[9][5] = CHE(KIND_A, my); /* 仕 */ + board[9][2] = board[9][6] = CHE(KIND_E, my); /* 相 */ + board[9][0] = board[9][8] = CHE(KIND_R, my); /* 車 */ + board[9][1] = board[9][7] = CHE(KIND_H, my); /* 傌 */ + board[7][1] = board[7][7] = CHE(KIND_C, my); /* 炮 */ board[6][0] = board[6][2] = board[6][4] = - board[6][6] = board[6][8] = CHE(KIND_P, chcd->my); /* 兵 */ + board[6][6] = board[6][8] = CHE(KIND_P, my); /* 兵 */ } static void -chc_movechess(board_t board) +chc_prepare_step(ChessInfo* info, const void* step) { - board[chcd->to.r][chcd->to.c] = board[chcd->from.r][chcd->from.c]; - board[chcd->from.r][chcd->from.c] = 0; + const drc_t* move = (const drc_t*) step; + getstep((board_p) info->board, info->my, + &move->from, &move->to, info->last_movestr); +} + +static int +chc_movechess(board_t board, const drc_t* move) +{ + int end = (CHE_P(board[move->to.r][move->to.c]) == KIND_K); + + board[move->to.r][move->to.c] = board[move->from.r][move->from.c]; + board[move->from.r][move->from.c] = 0; + + return end; +} + +static void +chc_drawstep(ChessInfo* info, const drc_t* move) +{ + info->actions->drawline(info, LTR(move->from.r)); + info->actions->drawline(info, LTR(move->to.r)); } /* 求兩座標行或列(rowcol)的距離 */ @@ -550,7 +539,7 @@ between(board_t board, rc_t from, rc_t to, int rowcol) } static int -chc_canmove(board_t board, rc_t from, rc_t to) +chc_canmove(board_t board, int my, rc_t from, rc_t to) { int i; int rd, cd, turn; @@ -569,24 +558,24 @@ chc_canmove(board_t board, rc_t from, rc_t to) if (!(rd == 1 && cd == 0) && !(rd == 0 && cd == 1)) return 0; - if ((turn == (chcd->my ^ 1) && to.r > 2) || - (turn == chcd->my && to.r < 7) || + if ((turn == (my ^ 1) && to.r > 2) || + (turn == my && to.r < 7) || to.c < 3 || to.c > 5) return 0; break; case KIND_A: /* 士 仕 */ if (!(rd == 1 && cd == 1)) return 0; - if ((turn == (chcd->my ^ 1) && to.r > 2) || - (turn == chcd->my && to.r < 7) || + if ((turn == (my ^ 1) && to.r > 2) || + (turn == my && to.r < 7) || to.c < 3 || to.c > 5) return 0; break; case KIND_E: /* 象 相 */ if (!(rd == 2 && cd == 2)) return 0; - if ((turn == (chcd->my ^ 1) && to.r > 4) || - (turn == chcd->my && to.r < 5)) + if ((turn == (my ^ 1) && to.r > 4) || + (turn == my && to.r < 5)) return 0; /* 拐象腿 */ if (board[CENTER(from.r, to.r)][CENTER(from.c, to.c)]) @@ -626,12 +615,12 @@ chc_canmove(board_t board, rc_t from, rc_t to) if (!(rd == 1 && cd == 0) && !(rd == 0 && cd == 1)) return 0; - if (((turn == (chcd->my ^ 1) && to.r < 5) || - (turn == chcd->my && to.r > 4)) && + if (((turn == (my ^ 1) && to.r < 5) || + (turn == my && to.r > 4)) && cd != 0) return 0; - if ((turn == (chcd->my ^ 1) && to.r < from.r) || - (turn == chcd->my && to.r > from.r)) + if ((turn == (my ^ 1) && to.r < from.r) || + (turn == my && to.r > from.r)) return 0; break; } @@ -640,11 +629,11 @@ chc_canmove(board_t board, rc_t from, rc_t to) /* 找 turn's king 的座標 */ static void -findking(board_t board, int turn, rc_t * buf) +findking(board_t board, int my, int turn, rc_t * buf) { int i, r, c; - r = (turn == (chcd->my ^ 1)) ? 0 : 7; + r = (turn == (my ^ 1) ? 0 : 7); for (i = 0; i < 3; r++, i++) for (c = 3; c < 6; c++) if (CHE_P(board[r][c]) == KIND_K && @@ -652,75 +641,42 @@ findking(board_t board, int turn, rc_t * buf) buf->r = r, buf->c = c; return; } + assert_not_reached(); } static int -chc_iskfk(board_t board) +chc_iskfk(board_t board, int my) { rc_t from, to; - findking(board, BLK, &to); - findking(board, RED, &from); + /* the `my' here doesn't matter */ + findking(board, my, BLK, &to); + findking(board, my, RED, &from); if (from.c == to.c && between(board, from, to, 0) == 0) return 1; return 0; } static int -chc_ischeck(board_t board, int turn) +chc_ischeck(board_t board, int my, int turn) { rc_t from, to; - findking(board, turn, &to); + findking(board, my, turn, &to); for (from.r = 0; from.r < BRD_ROW; from.r++) for (from.c = 0; from.c < BRD_COL; from.c++) if (board[from.r][from.c] && CHE_O(board[from.r][from.c]) != turn) - if (chc_canmove(board, from, to)) + if (chc_canmove(board, my, from, to)) return 1; return 0; } - -static int -time_countdown(int who, int length) -{ - chcd->lefttime[who] -= length; - - if (!timelimit) /* traditional mode, only left time is considered */ - return chcd->lefttime[who] < 0; - - if (chcd->lefttime[who] < 0) { /* only allowed when in free time */ - if (chcd->lefthand[who]) - return 1; - chcd->lefttime[who] = timelimit->limit_time; - chcd->lefthand[who] = timelimit->limit_hand; - return 0; - } - - return 0; -} - -static void -step_made(int who) -{ - if (!timelimit) - chcd->lefttime[who] = CHC_TIMEOUT; - else if ( - (chcd->lefthand[who] && (--(chcd->lefthand[who]) == 0)) - || - (chcd->lefthand[who] == 0 && chcd->lefttime[who] <= 0) - ) { - chcd->lefthand[who] = timelimit->limit_hand; - chcd->lefttime[who] = timelimit->limit_time; - } -} - /* * End of the rule function. */ static void -chcusr_put(userec_t *userec, const chcusr_t *user) +chcusr_put(userec_t* userec, const ChessUser* user) { userec->chc_win = user->win; userec->chc_lose = user->lose; @@ -729,7 +685,7 @@ chcusr_put(userec_t *userec, const chcusr_t *user) } static void -chcusr_get(const userec_t *userec, chcusr_t *user) +chc_init_user(const userec_t *userec, ChessUser *user) { strlcpy(user->userid, userec->userid, sizeof(user->userid)); user->win = userec->chc_win; @@ -741,189 +697,91 @@ chcusr_get(const userec_t *userec, chcusr_t *user) user->orig_rating = user->rating; } + +static void +chc_prepare_play(ChessInfo* info) +{ + if (chc_ischeck((board_p) info->board, info->my, info->turn)) { + strlcpy(info->warnmsg, ANSI_COLOR(1;31) "將軍!" ANSI_RESET, + sizeof(info->warnmsg)); + bell(); + } else + info->warnmsg[0] = 0; +} + +inline static void +chc_reverse(rc_t* coor) +{ + coor->r = BRD_ROW - 1 - coor->r; + coor->c = BRD_COL - 1 - coor->c; +} + static int -hisplay(int s, const chcusr_t *user1, const chcusr_t *user2, board_t board, board_t tmpbrd) +chc_select(ChessInfo* info, rc_t location, ChessGameResult* result) { - int last_time; - int endgame = 0, endturn = 0; + chc_tag_data_t* tag = (chc_tag_data_t*) info->tag; + board_p board = (board_p) info->board; - last_time = now; - while (!endturn) { - if (time_countdown(1, now - last_time)) { - chcd->lefttime[1] = 0; + assert(tag); - /* to make him break out igetch() */ - chcd->from.r = -2; - chc_broadcast_send(chcd->act_list, board); + if (!tag->selected) { + /* trying to pick something */ + if (board[location.r][location.c] && + CHE_O(board[location.r][location.c]) == info->turn) { + /* they can pick up this */ + tag->selected = 1; + tag->select = location; + chc_drawline(info, LTR(location.r)); } - last_time = now; - chc_drawline(board, user1, user2, TIME_ROW); - move(1, 0); - oflush(); - switch (igetch()) { - case 'q': - endgame = 2; - endturn = 1; - break; - case 'p': - if (chcd->hepass) { - chcd->from.r = -1; - chc_broadcast_send(chcd->act_list, board); - endgame = 3; - endturn = 1; - } - break; - case I_OTHERDATA: - if (!chc_broadcast_recv(chcd->act_list, board)) { /* disconnect */ - endturn = 1; - endgame = 1; - } else { - if (chcd->from.r == -1) { - chcd->hepass = 1; - strlcpy(chcd->warnmsg, ANSI_COLOR(1;33) "要求和局!" ANSI_RESET, sizeof(chcd->warnmsg)); - chc_drawline(board, user1, user2, WARN_ROW); - } else { - /* 座標變換 - * (CHC_WATCH_PERSONAL 設定時 - * 表觀棋者看的棋局為單人打譜的棋局) - * 棋盤需倒置的清況才要轉換 - */ - /* 1.如果在觀棋 且棋局是別人在打譜 且輪到你 或*/ - if ( ((chcd->mode & CHC_WATCH) && (chcd->mode & CHC_WATCH_PERSONAL)) || - /* 2.自己在打譜 */ - (chcd->mode & CHC_PERSONAL) || - ((chcd->mode & CHC_WATCH) && !chcd->turn) - ) - ; // do nothing - else { - chcd->from.r = 9 - chcd->from.r, chcd->from.c = 8 - chcd->from.c; - chcd->to.r = 9 - chcd->to.r, chcd->to.c = 8 - chcd->to.c; - } - chcd->cursor = chcd->to; - if (CHE_P(board[chcd->to.r][chcd->to.c]) == KIND_K) - endgame = 2; - endturn = 1; - chcd->hepass = 0; - getstep(board, &chcd->from, &chcd->to, chcd->last_movestr); - chc_drawline(board, user1, user2, STEP_ROW); - chc_log_step(board, &chcd->from, &chcd->to); - chc_movechess(board); - step_made(1); - chc_drawline(board, user1, user2, LTR(chcd->from.r)); - chc_drawline(board, user1, user2, LTR(chcd->to.r)); - } - } - break; + return 0; + } else if (tag->select.r == location.r && tag->select.c == location.c) { + /* cancel selection */ + tag->selected = 0; + chc_drawline(info, LTR(location.r)); + return 0; + } else if (chc_canmove(board, info->my, tag->select, location)) { + /* moving the chess */ + drc_t moving = { CHESS_STEP_NORMAL, tag->select, location }; + board_t tmpbrd; + int valid_step = 1; + + if (CHE_P(board[location.r][location.c]) == KIND_K) + /* 移到對方將帥 */ + *result = CHESS_RESULT_WIN; + else { + memcpy(tmpbrd, board, sizeof(board_t)); + chc_movechess(tmpbrd, &moving); + valid_step = !chc_iskfk(tmpbrd, info->my); } - } - time_countdown(1, now - last_time); - return endgame; -} -static int -myplay(int s, const chcusr_t *user1, const chcusr_t *user2, board_t board, board_t tmpbrd) -{ - int ch, last_time; - int endgame = 0, endturn = 0; - - chcd->ipass = 0, chcd->selected = 0; - last_time = now; - bell(); - while (!endturn) { - chc_drawline(board, user1, user2, TIME_ROW); - chc_movecur(chcd->cursor.r, chcd->cursor.c); - oflush(); - ch = igetch(); - if (time_countdown(0, now - last_time)) - ch = 'q'; - last_time = now; - switch (ch) { - case I_OTHERDATA: - if (!chc_broadcast_recv(chcd->act_list, board)) { /* disconnect */ - endgame = 1; - endturn = 1; - } else if (chcd->from.r == -1 && chcd->ipass) { - endgame = 3; - endturn = 1; - } - break; - case KEY_UP: - chcd->cursor.r--; - if (chcd->cursor.r < 0) - chcd->cursor.r = BRD_ROW - 1; - break; - case KEY_DOWN: - chcd->cursor.r++; - if (chcd->cursor.r >= BRD_ROW) - chcd->cursor.r = 0; - break; - case KEY_LEFT: - chcd->cursor.c--; - if (chcd->cursor.c < 0) - chcd->cursor.c = BRD_COL - 1; - break; - case KEY_RIGHT: - chcd->cursor.c++; - if (chcd->cursor.c >= BRD_COL) - chcd->cursor.c = 0; - break; - case 'q': - endgame = 2; - endturn = 1; - break; - case 'p': - chcd->ipass = 1; - chcd->from.r = -1; - chc_broadcast_send(chcd->act_list, board); - strlcpy(chcd->warnmsg, ANSI_COLOR(1;33) "要求和棋!" ANSI_RESET, sizeof(chcd->warnmsg)); - chc_drawline(board, user1, user2, WARN_ROW); + if (valid_step) { + getstep(board, info->my, &moving.from, &moving.to, info->last_movestr); + + chc_movechess(board, &moving); + chc_drawline(info, LTR(moving.from.r)); + chc_drawline(info, LTR(moving.to.r)); + + ChessHistoryAppend(info, &moving); + ChessStepBroadcast(info, &moving); + + chc_reverse(&moving.from); + chc_reverse(&moving.to); + ChessStepSendOpposite(info, &moving); + + tag->selected = 0; + return 1; + } else { + /* 王見王 */ + strlcpy(info->warnmsg, + ANSI_COLOR(1;33) "不可以王見王" ANSI_RESET, + sizeof(info->warnmsg)); bell(); - break; - case '\r': - case '\n': - case ' ': - if (chcd->selected) { - if (chcd->cursor.r == chcd->select.r && - chcd->cursor.c == chcd->select.c) { - chcd->selected = 0; - chc_drawline(board, user1, user2, LTR(chcd->cursor.r)); - } else if (chc_canmove(board, chcd->select, chcd->cursor)) { - if (CHE_P(board[chcd->cursor.r][chcd->cursor.c]) == KIND_K) - endgame = 1; - chcd->from = chcd->select; - chcd->to = chcd->cursor; - if (!endgame) { - memcpy(tmpbrd, board, sizeof(board_t)); - chc_movechess(tmpbrd); - } - if (endgame || !chc_iskfk(tmpbrd)) { - getstep(board, &chcd->from, &chcd->to, chcd->last_movestr); - chc_drawline(board, user1, user2, STEP_ROW); - chc_log_step(board, &chcd->from, &chcd->to); - chc_movechess(board); - step_made(0); - chc_broadcast_send(chcd->act_list, board); - chcd->selected = 0; - chc_drawline(board, user1, user2, LTR(chcd->from.r)); - chc_drawline(board, user1, user2, LTR(chcd->to.r)); - endturn = 1; - } else { - strlcpy(chcd->warnmsg, ANSI_COLOR(1;33) "不可以王見王" ANSI_RESET, sizeof(chcd->warnmsg)); - bell(); - chc_drawline(board, user1, user2, WARN_ROW); - } - } - } else if (board[chcd->cursor.r][chcd->cursor.c] && - CHE_O(board[chcd->cursor.r][chcd->cursor.c]) == chcd->turn) { - chcd->selected = 1; - chcd->select = chcd->cursor; - chc_drawline(board, user1, user2, LTR(chcd->cursor.r)); - } - break; + chc_drawline(info, WARN_ROW); + return 0; } - } - time_countdown(0, now - last_time); - return endgame; + } else + /* nothing happened */ + return 0; } int round_to_int(double x) @@ -933,13 +791,13 @@ int round_to_int(double x) return (int)(x+0.5); return (int)(x-0.5); } - + /* * ELO rating system * see http://www.wordiq.com/definition/ELO_rating_system */ static void -count_chess_elo_rating(chcusr_t *user1, const chcusr_t *user2, double myres) +count_chess_elo_rating(ChessUser* user1, const ChessUser* user2, double myres) { double k; double exp_res; @@ -972,54 +830,71 @@ count_chess_elo_rating(chcusr_t *user1, const chcusr_t *user2, double myres) user1->rating = newrating; } + +/* 象棋功能進入點: + * chc_main: 對奕 + * chc_personal: 打譜 + * chc_watch: 觀棋 + * talk.c: 對奕 + */ +void +chc(int s, ChessGameMode mode) +{ + ChessInfo* info = NewChessInfo(&chc_actions, &chc_constants, s, mode); + board_t board; + chc_tag_data_t tag; + + chc_init_board(info, board); + tag.selected = 0; + + info->board = board; + info->tag = &tag; + + if (mode == CHESS_MODE_WATCH) + setutmpmode(CHESSWATCHING); + else + setutmpmode(CHC); + + ChessPlay(info); + + DeleteChessInfo(info); +} + static void -mainloop(int s, chcusr_t *user1, chcusr_t *user2, board_t board, play_func_t play_func[2]) +chc_gameend(ChessInfo* info, ChessGameResult result) { - int endgame; - char buf[80]; - board_t tmpbrd; - - if (!(chcd->mode & CHC_WATCH)) - chcd->turn = 1; - for (endgame = 0; !endgame; chcd->turn ^= 1) { - chcd->firststep = 0; - chc_drawline(board, user1, user2, TURN_ROW); - if (chc_ischeck(board, chcd->turn)) { - strlcpy(chcd->warnmsg, ANSI_COLOR(1;31) "將軍!" ANSI_RESET, sizeof(chcd->warnmsg)); - bell(); - } else - chcd->warnmsg[0] = 0; - chc_drawline(board, user1, user2, WARN_ROW); - endgame = play_func[chcd->turn] (s, user1, user2, board, tmpbrd); - } + ChessUser* const user1 = &info->user1; + ChessUser* const user2 = &info->user2; - if (chcd->mode & CHC_VERSUS) { - user1->rating = user1->orig_rating; - user1->lose--; - if(chcd->my==RED) { + if (info->mode == CHESS_MODE_VERSUS) { + if (info->my == RED) { /* 由紅方作 log. 記的是下棋前的原始分數 */ /* NOTE, 若紅方斷線則無 log */ - time_t t=time(NULL); + time_t t = time(NULL); char buf[100]; - sprintf(buf, "%s %s(%d,W%d/D%d/L%d) %s %s(%d,W%d/D%d/L%d)\n", ctime(&t), - user1->userid, user1->rating, user1->win, user1->tie, user1->lose, - (endgame==3?"和":endgame==1?"勝":"負"), - user2->userid, user2->rating, user2->win, user2->tie, user2->lose); - buf[24]=' '; // replace '\n' - log_file(BBSHOME"/log/chc.log", LOG_CREAT, buf); + sprintf(buf, "%s %s(%d,W%d/D%d/L%d) %s %s(%d,W%d/D%d/L%d)\n", + ctime(&t), + user1->userid, user1->rating, user1->win, + user1->tie, user1->lose, + (result == CHESS_RESULT_TIE ? "和" : + result == CHESS_RESULT_WIN ? "勝" : "負"), + user2->userid, user2->rating, user2->win, + user2->tie, user2->lose); + buf[24] = ' '; // replace '\n' + log_file(BBSHOME "/log/chc.log", LOG_CREAT, buf); } - if (endgame == 1) { - strlcpy(chcd->warnmsg, "對方認輸了!", sizeof(chcd->warnmsg)); + + user1->rating = user1->orig_rating; + user1->lose--; + if (result == CHESS_RESULT_WIN) { count_chess_elo_rating(user1, user2, 1.0); user1->win++; currutmp->chc_win++; - } else if (endgame == 2) { - strlcpy(chcd->warnmsg, "你認輸了!", sizeof(chcd->warnmsg)); + } else if (result == CHESS_RESULT_LOST) { count_chess_elo_rating(user1, user2, 0.0); user1->lose++; currutmp->chc_lose++; } else { - strlcpy(chcd->warnmsg, "和棋", sizeof(chcd->warnmsg)); count_chess_elo_rating(user1, user2, 0.5); user1->tie++; currutmp->chc_tie++; @@ -1028,547 +903,23 @@ mainloop(int s, chcusr_t *user1, chcusr_t *user2, board_t board, play_func_t pla chcusr_put(&cuser, user1); passwd_update(usernum, &cuser); } - else if (chcd->mode & CHC_WATCH) { - strlcpy(chcd->warnmsg, "結束觀棋", sizeof(chcd->warnmsg)); - } - else { - strlcpy(chcd->warnmsg, "結束打譜", sizeof(chcd->warnmsg)); - } - - chc_log("=> "); - if (endgame == 3) - chc_log("和局"); - else{ - sprintf(buf, "%s勝\n", (chcd->my==RED) == (endgame == 1) ? "紅" : "黑"); - chc_log(buf); - } - - chc_drawline(board, user1, user2, WARN_ROW); - bell(); - oflush(); -} - -static void -chc_init_play_func(chcusr_t *user1, chcusr_t *user2, play_func_t play_func[2]) -{ - char userid[2][IDLEN + 1]; - userec_t xuser; - - if (chcd->mode & CHC_PERSONAL) { - strlcpy(userid[0], cuser.userid, sizeof(userid[0])); - strlcpy(userid[1], cuser.userid, sizeof(userid[1])); - play_func[0] = play_func[1] = myplay; - } - else if (chcd->mode & CHC_WATCH) { - userinfo_t *uinfo = search_ulist_userid(currutmp->mateid); - strlcpy(userid[0], uinfo->userid, sizeof(userid[0])); - strlcpy(userid[1], uinfo->mateid, sizeof(userid[1])); - play_func[0] = play_func[1] = hisplay; - } - else { - strlcpy(userid[0], cuser.userid, sizeof(userid[0])); - strlcpy(userid[1], currutmp->mateid, sizeof(userid[1])); - play_func[chcd->my] = myplay; - play_func[chcd->my ^ 1] = hisplay; - } - - getuser(userid[0], &xuser); - chcusr_get(&xuser, user1); - getuser(userid[1], &xuser); - chcusr_get(&xuser, user2); -} - -static void -chc_init_photo(void) -{ - char genbuf[256]; - int line; - FILE* fp; - static const char * const blank_photo[6] = { - "┌──────┐", - "│ 空 │", - "│ 白 │", - "│ 照 │", - "│ 片│", - "└──────┘" - }; - char country[5], level[11]; - userec_t xuser; - - chcd->photo = NULL; - if (!(chcd->mode & CHC_VERSUS)) - return; - - setuserfile(genbuf, "photo_cchess"); - if (!dashf(genbuf)) { - sethomefile(genbuf, currutmp->mateid, "photo_cchess"); - if (!dashf(genbuf)) - return; - } - - chcd->photo = (char*) calloc(PHOTO_LINE * PHOTO_COLUMN, sizeof(char)); - - /* yack, but I copied these from gomo.c (scw) */ - setuserfile(genbuf, "photo_cchess"); - fp = fopen(genbuf, "r"); - - if (fp == NULL) { - strcpy(country, "無"); - level[0] = 0; - } else { - int i, j; - for (line = 1; line < 8; ++line) - fgets(genbuf, sizeof(genbuf), fp); - - fgets(genbuf, sizeof(genbuf), fp); - chomp(genbuf); - strip_ansi(genbuf + 11, genbuf + 11, - STRIP_ALL); /* country name may have color */ - for (i = 11, j = 0; genbuf[i] && j < 4; ++i) - if (genbuf[i] != ' ') /* and spaces */ - country[j++] = genbuf[i]; - country[j] = 0; /* two chinese words */ - - fgets(genbuf, sizeof(genbuf), fp); - chomp(genbuf); - strlcpy(level, genbuf + 11, 11); /* five chinese words*/ - rewind(fp); - } - - /* simulate chcd->photo as two dimensional array */ -#define PHOTO(X) (chcd->photo + (X) * PHOTO_COLUMN) - for (line = 0; line < 6; ++line) { - if (fp != NULL) { - if (fgets(genbuf, sizeof(genbuf), fp)) { - chomp(genbuf); - sprintf(PHOTO(line), "%s ", genbuf); - } else - strcpy(PHOTO(line), " "); - } else - strcpy(PHOTO(line), blank_photo[line]); - - switch (line) { - case 0: sprintf(genbuf, "<代號> %s", cuser.userid); break; - case 1: sprintf(genbuf, "<暱稱> %.16s", cuser.nickname); break; - case 2: sprintf(genbuf, "<上站> %d", cuser.numlogins); break; - case 3: sprintf(genbuf, "<文章> %d", cuser.numposts); break; - case 4: sprintf(genbuf, "<職位> %-4s %s", country, level); break; - case 5: sprintf(genbuf, "<來源> %.16s", cuser.lasthost); break; - default: genbuf[0] = 0; - } - strcat(PHOTO(line), genbuf); - } - if (fp != NULL) - fclose(fp); - - sprintf(PHOTO(6), " %s%2.2s棋" ANSI_RESET, - turn_color[chcd->my], turn_str[chcd->my]); - strcpy(PHOTO(7), " V.S "); - sprintf(PHOTO(8), " %s%2.2s棋" ANSI_RESET, - turn_color[chcd->my ^ 1], turn_str[chcd->my ^ 1]); - - getuser(currutmp->mateid, &xuser); - sethomefile(genbuf, currutmp->mateid, "photo_cchess"); - fp = fopen(genbuf, "r"); - - if (fp == NULL) { - strcpy(country, "無"); - level[0] = 0; - } else { - int i, j; - for (line = 1; line < 8; ++line) - fgets(genbuf, sizeof(genbuf), fp); - - fgets(genbuf, sizeof(genbuf), fp); - chomp(genbuf); - strip_ansi(genbuf + 11, genbuf + 11, - STRIP_ALL); /* country name may have color */ - for (i = 11, j = 0; genbuf[i] && j < 4; ++i) - if (genbuf[i] != ' ') /* and spaces */ - country[j++] = genbuf[i]; - country[j] = 0; /* two chinese words */ - - fgets(genbuf, sizeof(genbuf), fp); - chomp(genbuf); - strlcpy(level, genbuf + 11, 11); /* five chinese words*/ - rewind(fp); - } - - for (line = 9; line < 15; ++line) { - move(line, 37); - switch (line - 9) { - case 0: sprintf(PHOTO(line), "<代號> %-16.16s ", xuser.userid); break; - case 1: sprintf(PHOTO(line), "<暱稱> %-16.16s ", xuser.nickname); break; - case 2: sprintf(PHOTO(line), "<上站> %-16d ", xuser.numlogins); break; - case 3: sprintf(PHOTO(line), "<文章> %-16d ", xuser.numposts); break; - case 4: sprintf(PHOTO(line), "<職位> %-4s %-10s ", country, level); break; - case 5: sprintf(PHOTO(line), "<來源> %-16.16s ", xuser.lasthost); break; - } - - if (fp != NULL) { - if (fgets(genbuf, 200, fp)) { - chomp(genbuf); - strcat(PHOTO(line), genbuf); - } else - strcat(PHOTO(line), " "); - } else - strcat(PHOTO(line), blank_photo[line - 9]); - } - if (fp != NULL) - fclose(fp); - -#undef PHOTO -} - -static void -chc_watch_request(int signo) -{ - chc_act_list *tmp; -#if _TO_SYNC_ - sigset_t mask; -#endif - - if (!(currstat & CHC)) - return; - if(chcd == NULL) return; - for(tmp = chcd->act_list; tmp->next != NULL; tmp = tmp->next); // XXX 一定要接在最後嗎? - tmp->next = (chc_act_list *)malloc(sizeof(chc_act_list)); - tmp->next->sock = establish_talk_connection(&SHM->uinfo[currutmp->destuip]); - if (tmp->next->sock < 0) { - free(tmp->next); - tmp->next = NULL; - return; - } - - tmp = tmp->next; - tmp->next = NULL; - -#if _TO_SYNC_ - /* 借用 SIGALRM */ - sigfillset(&mask); - sigdelset(&mask, SIGALRM); - sigsuspend(&mask); -#endif - - /* what if the spectator get off-line intentionally !? (SIGPIPE) */ - write(tmp->sock, chcd->bp, sizeof(board_t)); - write(tmp->sock, &chcd->my, sizeof(chcd->my)); - write(tmp->sock, &chcd->turn, sizeof(chcd->turn)); - write(tmp->sock, &currutmp->turn, sizeof(currutmp->turn)); - write(tmp->sock, &chcd->firststep, sizeof(chcd->firststep)); - write(tmp->sock, &chcd->mode, sizeof(chcd->mode)); -} - -static int -chc_init(int s, chcusr_t *user1, chcusr_t *user2, board_t board, play_func_t play_func[2]) -{ - userinfo_t *my = currutmp; - userec_t xuser; - - if (chcd->mode & CHC_WATCH) - setutmpmode(CHESSWATCHING); - else - setutmpmode(CHC); - clear(); - chcd->warnmsg[0] = 0; - - /* 從不同來源初始化各個變數 */ - if (!(chcd->mode & CHC_WATCH)) { - if (chcd->mode & CHC_PERSONAL) - chcd->my = RED; - else - chcd->my = my->turn; - chcd->firststep = 1; - chc_init_board(board); - chcd->cursor.r = 9, chcd->cursor.c = 0; - } - else { - char mode; - userinfo_t *uin = &SHM->uinfo[currutmp->destuip]; - if (uin == NULL) - return -1; -#if _TO_SYNC_ - // choose one signal execpt SIGUSR1 - kill(uin->pid, SIGALRM); -#endif - if(read(s, board, sizeof(board_t)) != sizeof(board_t) || - read(s, &chcd->my, sizeof(chcd->my)) != sizeof(chcd->my) || - read(s, &chcd->turn, sizeof(chcd->turn)) != sizeof(chcd->turn) || - read(s, &my->turn, sizeof(my->turn)) != sizeof(my->turn) || - read(s, &chcd->firststep, sizeof(chcd->firststep)) - != sizeof(chcd->firststep) || - read(s, &mode, sizeof(mode)) != sizeof(mode)){ - add_io(0, 0); - close(s); - return -1; - } - if (mode & CHC_PERSONAL) - chcd->mode |= CHC_WATCH_PERSONAL; - } - - chc_init_photo(); - - chcd->act_list = (chc_act_list *)malloc(sizeof(*chcd->act_list)); - chcd->act_list->sock = s; - chcd->act_list->next = 0; - - chc_init_play_func(user1, user2, play_func); - - chc_redraw(user1, user2, board); - add_io(s, 0); - - if (!(chcd->mode & CHC_WATCH)) { - Signal(SIGUSR1, chc_watch_request); - } - -// if (my->turn && !(chcd->mode & CHC_WATCH)) -// chc_broadcast_recv(chcd->act_list, board); - - if (chcd->mode & CHC_VERSUS) { - user1->lose++; - count_chess_elo_rating(user1, user2, 0.0); - passwd_query(usernum, &xuser); - chcusr_put(&xuser, user1); - passwd_update(usernum, &xuser); - - /* exchanging timing information */ - if (my->turn) { - char mode; - read(s, &mode, 1); - if (mode == 'L') { - timelimit = (CHCTimeLimit*) malloc(sizeof(CHCTimeLimit)); - read(s, timelimit, sizeof(CHCTimeLimit)); - } else - timelimit = NULL; - } else { - if (!timelimit) - write(s, "T", 1); /* traditional */ - else { - write(s, "L", 1); /* limited */ - write(s, timelimit, sizeof(CHCTimeLimit)); - } - } - } - - if (!my->turn) { - if (!(chcd->mode & CHC_WATCH)) - chc_broadcast_send(chcd->act_list, board); - if (chcd->mode & CHC_VERSUS) - user2->lose++; - } - - chcd->lefthand[0] = chcd->lefthand[1] = 0; - chcd->lefttime[0] = chcd->lefttime[1] = - timelimit ? timelimit->free_time : CHC_TIMEOUT; - - chc_redraw(user1, user2, board); - - return 0; -} - -/* 象棋功能進入點: - * chc_main: 對奕 - * chc_personal: 打譜 - * chc_watch: 觀棋 - * talk.c: 對奕 - */ -void -chc(int s, int mode) -{ - chcusr_t user1, user2; - play_func_t play_func[2]; - board_t board; - char mode0 = currutmp->mode; - char file[80]; - - if(chcd != NULL) { - vmsg("象棋功\能異常"); - return; - } - chcd = (struct CHCData*)malloc(sizeof(struct CHCData)); - if(chcd == NULL) { - vmsg("執行象棋功\能失敗"); - return; - } - memset(chcd, 0, sizeof(struct CHCData)); - chcd->mode = mode; - - if (!(chcd->mode & CHC_WATCH)) - Signal(SIGUSR1, SIG_IGN); - - chcd->bp = &board; - if (chc_init(s, &user1, &user2, board, play_func) < 0) { - free(chcd); - chcd = NULL; - return; - } - - setuserfile(file, CHC_LOG); - if (chc_log_open(&user1, &user2, file) < 0) - vmsg("無法紀錄棋局"); - - mainloop(s, &user1, &user2, board, play_func); - - /* close these fd */ - if (chcd->mode & CHC_PERSONAL) - chcd->act_list = chcd->act_list->next; - while(chcd->act_list){ - close(chcd->act_list->sock); - chcd->act_list = chcd->act_list->next; - } - - add_io(0, 0); - if (chcd->my == RED) - pressanykey(); - - currutmp->mode = mode0; - - if (getans("是否將棋譜寄回信箱?[N/y]") == 'y') { - char title[80]; - if(chcd->my == RED) - sprintf(title, "%s V.S. %s", user1.userid, user2.userid); - else - sprintf(title, "%s V.S. %s", user2.userid, user1.userid); - chc_log("\n--\n\n"); - chc_log_poem(); - chc_log_close(); - mail_id(cuser.userid, title, file, "[楚河漢界]"); - } - else - chc_log_close(); - - if (!(chcd->mode & CHC_WATCH)) - Signal(SIGUSR1, talk_request); - - if (timelimit) - free(timelimit); - if (chcd->photo) - free(chcd->photo); - free(chcd); - chcd = NULL; - timelimit = NULL; -} - -static userinfo_t * -chc_init_utmp(void) -{ - char uident[16]; - userinfo_t *uin; - - stand_title("楚河漢界之爭"); - CompleteOnlineUser(msg_uid, uident); - if (uident[0] == '\0') - return NULL; - - if ((uin = search_ulist_userid(uident)) == NULL) - return NULL; - - uin->sig = SIG_CHC; - return uin; } int chc_main(void) { - userinfo_t *uin; - char buf[4]; - - if ((uin = chc_init_utmp()) == NULL) - return -1; - uin->turn = 1; - currutmp->turn = 0; - strlcpy(uin->mateid, currutmp->userid, sizeof(uin->mateid)); - strlcpy(currutmp->mateid, uin->userid, sizeof(currutmp->mateid)); - - stand_title("象棋邀局"); - buf[0] = 0; - getdata(2, 0, "使用傳統模式 (T), 限時限步模式 (L) 或是 讀秒模式 (C)? (T/l/c)", - buf, 3, DOECHO); - if (buf[0] == 'l' || buf[0] == 'L') { - char display_buf[128]; - - timelimit = (CHCTimeLimit*) malloc(sizeof(CHCTimeLimit)); - do { - getdata_str(3, 0, "請設定局時 (自由時間) 以分鐘為單位:", - buf, 3, DOECHO, "30"); - timelimit->free_time = atoi(buf); - } while (timelimit->free_time < 0 || timelimit->free_time > 90); - timelimit->free_time *= 60; /* minute -> second */ - - do { - getdata_str(4, 0, "請設定步時, 以分鐘為單位:", - buf, 3, DOECHO, "5"); - timelimit->limit_time = atoi(buf); - } while (timelimit->limit_time < 0 || timelimit->limit_time > 30); - timelimit->limit_time *= 60; /* minute -> second */ - - snprintf(display_buf, sizeof(display_buf), - "請設定限步 (每 %d 分鐘需走幾步):", - timelimit->limit_time / 60); - do { - getdata_str(5, 0, display_buf, buf, 3, DOECHO, "10"); - timelimit->limit_hand = atoi(buf); - } while (timelimit->limit_hand < 1); - } else if (buf[0] == 'c' || buf[0] == 'C') { - timelimit = (CHCTimeLimit*) malloc(sizeof(CHCTimeLimit)); - do { - getdata_str(3, 0, "請設定局時 (自由時間) 以分鐘為單位:", - buf, 3, DOECHO, "30"); - timelimit->free_time = atoi(buf); - } while (timelimit->free_time < 0 || timelimit->free_time > 90); - timelimit->free_time *= 60; /* minute -> second */ - - timelimit->limit_hand = 1; - - do { - getdata_str(4, 0, "請設定讀秒, 以秒為單位", - buf, 3, DOECHO, "60"); - timelimit->limit_time = atoi(buf); - } while (timelimit->limit_time < 0); - } else - timelimit = NULL; - - my_talk(uin, friend_stat(currutmp, uin), 'c'); - return 0; + return ChessStartGame('c', SIG_CHC, "楚河漢界之爭"); } int chc_personal(void) { - chc(0, CHC_PERSONAL); + chc(0, CHESS_MODE_PERSONAL); return 0; } int chc_watch(void) { - int sock, msgsock; - userinfo_t *uin; - - if ((uin = chc_init_utmp()) == NULL) - return -1; - - if (uin->uid == currutmp->uid || uin->mode != CHC) - return -1; - - if (getans("是否進行觀棋? [N/y]") != 'y') - return 0; - - if ((sock = make_connection_to_somebody(uin, 10)) < 0) { - vmsg("無法建立連線"); - return -1; - } -#if defined(Solaris) && __OS_MAJOR_VERSION__ == 5 && __OS_MINOR_VERSION__ < 7 - msgsock = accept(sock, (struct sockaddr *) 0, 0); -#else - msgsock = accept(sock, (struct sockaddr *) 0, (socklen_t *) 0); -#endif - close(sock); - if (msgsock < 0) - return -1; - - strlcpy(currutmp->mateid, uin->userid, sizeof(currutmp->mateid)); - chc(msgsock, CHC_WATCH); - close(msgsock); - return 0; + return ChessWatchGame(&chc, CHC, "楚河漢界之爭"); } diff --git a/mbbsd/chess.c b/mbbsd/chess.c new file mode 100644 index 00000000..c59f4099 --- /dev/null +++ b/mbbsd/chess.c @@ -0,0 +1,1181 @@ +/* $Id$ */ +#include "bbs.h" +#include "chess.h" + +#define assert_not_reached() assert(!"Should never be here!!!") + +#define CHESS_HISTORY_INITIAL_BUFFER_SIZE 300 +#define CHESS_HISTORY_BUFFER_INCREMENT 50 + +static ChessInfo * CurrentPlayingGameInfo; + +/* XXX: This is a BAD way to pass information. + * Fix this by handling chess request ourselves. + */ +static ChessTimeLimit * _current_time_limit; + +#define CHESS_HISTORY_ENTRY(INFO,N) \ + ((INFO)->history.body + (N) * (INFO)->constants->step_entry_size) +static void +ChessHistoryInit(ChessHistory* history, int entry_size) +{ + history->size = CHESS_HISTORY_INITIAL_BUFFER_SIZE; + history->used = 0; + history->body = + calloc(CHESS_HISTORY_INITIAL_BUFFER_SIZE, + entry_size); +} + +const void* +ChessHistoryRetrieve(ChessInfo* info, int n) +{ + assert(n >= 0 && n < info->history.used); + return CHESS_HISTORY_ENTRY(info, n); +} + +void +ChessHistoryAppend(ChessInfo* info, void* step) +{ + if (info->history.used == info->history.size) + info->history.body = realloc(info->history.body, + (info->history.size += CHESS_HISTORY_BUFFER_INCREMENT) + * info->constants->step_entry_size); + + memmove(CHESS_HISTORY_ENTRY(info, info->history.used), + step, info->constants->step_entry_size); + ++(info->history.used); +} + +static void +ChessBroadcastListInit(ChessBroadcastList* list) +{ + list->head.next = NULL; +} + +static void +ChessBroadcastListClear(ChessBroadcastList* list) +{ + ChessBroadcastListNode* p = list->head.next; + while (p) { + ChessBroadcastListNode* t = p->next; + close(p->sock); + free(p); + p = t; + } +} + +static ChessBroadcastListNode* +ChessBroadcastListInsert(ChessBroadcastList* list) +{ + ChessBroadcastListNode* p = + (ChessBroadcastListNode*) malloc(sizeof(ChessBroadcastListNode)); + + p->next = list->head.next; + list->head.next = p; + return p; +} + +static void +ChessDrawHelpLine(const ChessInfo* info) +{ + const static char* const HelpStr[] = + { + /* CHESS_MODE_VERSUS, 對奕 */ + ANSI_COLOR(1;33;42) " 下棋 " + ANSI_COLOR(;31;47) " (←↑↓→)" ANSI_COLOR(30) " 移動 " + ANSI_COLOR(31) "(空白鍵/ENTER)" ANSI_COLOR(30) " 下子 " + ANSI_COLOR(31) "(q)" ANSI_COLOR(30) " 認輸 " + ANSI_COLOR(31) "(p)" ANSI_COLOR(30) " 虛手 " + ANSI_RESET, + + /* CHESS_MODE_WATCH, 觀棋 */ + ANSI_COLOR(1;33;42) " 觀棋 " + ANSI_COLOR(;31;47) " (←→)" ANSI_COLOR(30) " 前後一步 " + ANSI_COLOR(31) "(↑↓)" ANSI_COLOR(30) " 前後十步 " + ANSI_COLOR(31) "(PGUP/PGDN)" ANSI_COLOR(30) " 最初/目前盤面 " + ANSI_COLOR(31) "(q)" ANSI_COLOR(30) " 離開 " + ANSI_RESET, + + /* CHESS_MODE_PERSONAL, 打譜 */ + ANSI_COLOR(1;33;42) " 打譜 " + ANSI_COLOR(;31;47) " (←↑↓→)" ANSI_COLOR(30) " 移動 " + ANSI_COLOR(31) "(空白鍵/ENTER)" ANSI_COLOR(30) " 下子 " + ANSI_COLOR(31) "(q)" ANSI_COLOR(30) " 離開 " + ANSI_RESET, + + /* CHESS_MODE_REPLAY, 看譜 */ + ANSI_COLOR(1;33;42) " 看譜 " + ANSI_COLOR(;31;47) " (←→)" ANSI_COLOR(30) " 前後一步 " + ANSI_COLOR(31) "(↑↓)" ANSI_COLOR(30) " 前後十步 " + ANSI_COLOR(31) "(PGUP/PGDN)" ANSI_COLOR(30) " 最初/目前盤面 " + ANSI_COLOR(31) "(q)" ANSI_COLOR(30) " 離開 " + ANSI_RESET, + }; + + mouts(b_lines, 0, HelpStr[info->mode]); +} + +static void +ChessRedraw(const ChessInfo* info) +{ + int i; + for (i = 0; i < b_lines; ++i) + info->actions->drawline(info, i); + ChessDrawHelpLine(info); +} + +inline static int +ChessTimeCountDownCalc(ChessInfo* info, int who, int length) +{ + info->lefttime[who] -= length; + + if (!info->timelimit) /* traditional mode, only left time is considered */ + return info->lefttime[who] < 0; + + if (info->lefttime[who] < 0) { /* only allowed when in free time */ + if (info->lefthand[who]) + return 1; + info->lefttime[who] = info->timelimit->limit_time; + info->lefthand[who] = info->timelimit->limit_hand; + return 0; + } + + return 0; +} + +int +ChessTimeCountDown(ChessInfo* info, int who, int length) +{ + int result = ChessTimeCountDownCalc(info, who, length); + info->actions->drawline(info, CHESS_DRAWING_TIME_ROW); + return result; +} + +void +ChessStepMade(ChessInfo* info, int who) +{ + if (!info->timelimit) + info->lefttime[who] = info->constants->traditional_timeout; + else if ( + (info->lefthand[who] && (--(info->lefthand[who]) == 0)) + || + (info->lefthand[who] == 0 && info->lefttime[who] <= 0) + ) { + info->lefthand[who] = info->timelimit->limit_hand; + info->lefttime[who] = info->timelimit->limit_time; + } +} + +/* + * Start of the network communication function. + */ +inline static ChessStepType +ChessRecvMove(ChessInfo* info, int sock, void *step) +{ + if (read(sock, step, info->constants->step_entry_size) + != info->constants->step_entry_size) + return CHESS_STEP_FAILURE; + return *(ChessStepType*) step; +} + +inline static int +ChessSendMove(ChessInfo* info, int sock, const void *step) +{ + if (write(sock, step, info->constants->step_entry_size) + != info->constants->step_entry_size) + return 0; + return 1; +} + +ChessStepType +ChessStepReceive(ChessInfo* info, void* step) +{ + ChessStepType result = ChessRecvMove(info, info->sock, step); + + if (result != CHESS_STEP_FAILURE) { + /* automatical routing */ + ChessStepBroadcast(info, step); + + /* and logging */ + ChessHistoryAppend(info, step); + } + + return result; +} + +int +ChessStepSendOpposite(ChessInfo* info, const void* step) +{ + void (*orig_handler)(int); + int result = 1; + + orig_handler = Signal(SIGPIPE, SIG_IGN); + + if (!ChessSendMove(info, info->sock, step)) + result = 0; + + Signal(SIGPIPE, orig_handler); + return result; +} + +void +ChessStepBroadcast(ChessInfo* info, const void *step) +{ + ChessBroadcastListNode *p = &(info->broadcast_list.head); + void (*orig_handler)(int); + + orig_handler = Signal(SIGPIPE, SIG_IGN); + + while(p->next){ + if (!ChessSendMove(info, p->next->sock, step)) { + /* remove viewer */ + ChessBroadcastListNode *tmp = p->next->next; + free(p->next); + p->next = tmp; + } else + p = p->next; + } + + Signal(SIGPIPE, orig_handler); +} + +int +ChessStepSend(ChessInfo* info, const void* step) +{ + /* send to opposite... */ + if (!ChessStepSendOpposite(info, step)) + return 0; + + /* and watchers */ + ChessStepBroadcast(info, step); + + return 1; +} + +int +ChessMessageSend(ChessInfo* info, ChessStepType type) +{ + return ChessStepSend(info, &type); +} + +inline static void +ChessReplayUntil(ChessInfo* info, int n) +{ + const void* step; + + while (info->current_step < n - 1) { + info->actions->apply_step(info->board, + ChessHistoryRetrieve(info, info->current_step)); + ++(info->current_step); + } + + /* spcial for last one to maintian information correct */ + step = ChessHistoryRetrieve(info, info->current_step); + info->actions->prepare_step(info, step); + info->actions->apply_step(info->board, step); + ++(info->current_step); +} + +static ChessGameResult +ChessPlayFuncMy(ChessInfo* info) +{ + int last_time = now; + int endturn = 0; + ChessGameResult game_result = CHESS_RESULT_CONTINUE; + int ch; + + info->ipass = 0; + bell(); + + while (!endturn) { + ChessStepType result; + + info->actions->drawline(info, CHESS_DRAWING_TIME_ROW); + info->actions->movecur(info->cursor.r, info->cursor.c); + oflush(); + + ch = igetch(); + if (ChessTimeCountDown(info, 0, now - last_time)) + ch = 'q'; + last_time = now; + + switch (ch) { + case I_OTHERDATA: + result = ChessStepReceive(info, &info->step_tmp); + + if (result == CHESS_STEP_FAILURE || + result == CHESS_STEP_DROP) { + game_result = CHESS_RESULT_WIN; + endturn = 1; + } else if (result == CHESS_STEP_PASS && info->ipass) { + game_result = CHESS_RESULT_TIE; + endturn = 1; + } + break; + + case KEY_UP: + --(info->cursor.r); + if (info->cursor.r < 0) + info->cursor.r = info->constants->board_height - 1; + break; + + case KEY_DOWN: + ++(info->cursor.r); + if (info->cursor.r >= info->constants->board_height) + info->cursor.r = 0; + break; + + case KEY_LEFT: + --(info->cursor.c); + if (info->cursor.c < 0) + info->cursor.c = info->constants->board_width - 1; + break; + + case KEY_RIGHT: + ++(info->cursor.c); + if (info->cursor.c >= info->constants->board_width) + info->cursor.c = 0; + break; + + case 'q': + game_result = CHESS_RESULT_LOST; + endturn = 1; + break; + + case 'p': + info->ipass = 1; + ChessMessageSend(info, CHESS_STEP_PASS); + strlcpy(info->warnmsg, + ANSI_COLOR(1;33) "要求和棋!" ANSI_RESET, + sizeof(info->warnmsg)); + info->actions->drawline(info, CHESS_DRAWING_WARN_ROW); + bell(); + break; + + case '\r': + case '\n': + case ' ': + endturn = info->actions->select(info, info->cursor, &game_result); + break; + } + } + ChessTimeCountDown(info, 0, now - last_time); + ChessStepMade(info, 0); + info->actions->drawline(info, CHESS_DRAWING_TIME_ROW); + info->actions->drawline(info, CHESS_DRAWING_STEP_ROW); + return game_result; +} + +static ChessGameResult +ChessPlayFuncHis(ChessInfo* info) +{ + int last_time = now; + int endturn = 0; + ChessGameResult game_result = CHESS_RESULT_CONTINUE; + + while (!endturn) { + ChessStepType result; + + if (ChessTimeCountDown(info, 1, now - last_time)) { + info->lefttime[1] = 0; + + /* to make him break out igetch() */ + ChessMessageSend(info, CHESS_STEP_NOP); + } + last_time = now; + + info->actions->drawline(info, CHESS_DRAWING_TIME_ROW); + move(1, 0); + oflush(); + + switch (igetch()) { + case 'q': + game_result = CHESS_RESULT_LOST; + endturn = 1; + break; + + case 'p': + if (info->hepass) { + ChessMessageSend(info, CHESS_STEP_PASS); + game_result = CHESS_RESULT_TIE; + endturn = 1; + } + break; + + case I_OTHERDATA: + result = ChessStepReceive(info, &info->step_tmp); + + if (result == CHESS_STEP_FAILURE || + result == CHESS_STEP_DROP) { + game_result = CHESS_RESULT_WIN; + endturn = 1; + } else if (result == CHESS_STEP_PASS) { + info->hepass = 1; + strlcpy(info->warnmsg, + ANSI_COLOR(1;33) "要求和局!" ANSI_RESET, + sizeof(info->warnmsg)); + info->actions->drawline(info, CHESS_DRAWING_WARN_ROW); + } else { + info->actions->prepare_step(info, &info->step_tmp); + if (info->actions->apply_step(info->board, &info->step_tmp)) + game_result = CHESS_RESULT_LOST; + endturn = 1; + info->hepass = 0; + ChessStepMade(info, 1); + info->actions->drawstep(info, &info->step_tmp); + } + } + } + ChessTimeCountDown(info, 1, now - last_time); + info->actions->drawline(info, CHESS_DRAWING_TIME_ROW); + info->actions->drawline(info, CHESS_DRAWING_STEP_ROW); + return game_result; +} + +static ChessGameResult +ChessPlayFuncWatch(ChessInfo* info) +{ + int end_watch = 0; + + while (!end_watch) { + info->actions->prepare_play(info); + if (info->sock == -1) + strlcpy(info->warnmsg, ANSI_COLOR(1;33) "棋局已結束" ANSI_RESET, + sizeof(info->warnmsg)); + + info->actions->drawline(info, CHESS_DRAWING_WARN_ROW); + move(1, 0); + + switch (igetch()) { + case I_OTHERDATA: /* new step */ + if (ChessStepReceive(info, &info->step_tmp) == CHESS_STEP_FAILURE) { + add_io(0, 0); + info->sock = -1; + break; + } + + if (info->current_step == info->history.used - 1) { + /* was watching up-to-date board */ + info->actions->prepare_step(info, &info->step_tmp); + info->actions->apply_step(info->board, &info->step_tmp); + info->actions->drawstep(info, &info->step_tmp); + ++(info->current_step); + } + break; + + case KEY_LEFT: /* 往前一步 */ + if (info->current_step == 0) + bell(); + else { + /* TODO: implement without re-apply all steps */ + int current = info->current_step; + + info->actions->init_board(info, info->board); + info->current_step = 0; + + ChessReplayUntil(info, current - 1); + ChessRedraw(info); + } + break; + + case KEY_RIGHT: /* 往後一步 */ + if (info->current_step == info->history.used) + bell(); + else { + const void* step = + ChessHistoryRetrieve(info, info->current_step); + info->actions->prepare_step(info, step); + info->actions->apply_step(info->board, step); + info->actions->drawstep(info, step); + ++(info->current_step); + } + break; + + case KEY_UP: /* 往前十步 */ + if (info->current_step == 0) + bell(); + else { + /* TODO: implement without re-apply all steps */ + int current = info->current_step; + + info->actions->init_board(info, info->board); + info->current_step = 0; + + if (current > 10) + ChessReplayUntil(info, current - 10); + + ChessRedraw(info); + } + break; + + case KEY_DOWN: /* 往後十步 */ + if (info->current_step == info->history.used) + bell(); + else { + ChessReplayUntil(info, + MIN(info->current_step + 10, info->history.used)); + ChessRedraw(info); + } + break; + + case KEY_PGUP: /* 起始盤面 */ + if (info->current_step == 0) + bell(); + else { + info->actions->init_board(info, info->board); + info->current_step = 0; + ChessRedraw(info); + } + break; + + case KEY_PGDN: /* 最新盤面 */ + if (info->current_step == info->history.used) + bell(); + else { + ChessReplayUntil(info, info->history.used); + ChessRedraw(info); + } + break; + + case 'q': + end_watch = 1; + } + } + + return CHESS_RESULT_END; +} + +static void +ChessWatchRequest(int sig) +{ + int sock = establish_talk_connection(&SHM->uinfo[currutmp->destuip]); + ChessBroadcastListNode* node; + + if (sock < 0) + return; + + node = ChessBroadcastListInsert(&CurrentPlayingGameInfo->broadcast_list); + node->sock = sock; + +#define SEND(X) write(sock, &(X), sizeof(X)) + SEND(CurrentPlayingGameInfo->my); + SEND(CurrentPlayingGameInfo->turn); + + if (!CurrentPlayingGameInfo->timelimit) + write(sock, "T", 1); + else { + write(sock, "L", 1); + SEND(*(CurrentPlayingGameInfo->timelimit)); + } + + SEND(CurrentPlayingGameInfo->history.used); + write(sock, CurrentPlayingGameInfo->history.body, + CurrentPlayingGameInfo->constants->step_entry_size + * CurrentPlayingGameInfo->history.used); +#undef SEND +} + +static void +ChessReceiveWatchInfo(ChessInfo* info) +{ + char time_mode; +#define RECV(X) read(info->sock, &(X), sizeof(X)) + RECV(info->my); + RECV(info->turn); + + RECV(time_mode); + if (time_mode == 'L') { + info->timelimit = (ChessTimeLimit*) malloc(sizeof(ChessTimeLimit)); + RECV(*(info->timelimit)); + } + + RECV(info->history.used); + for (info->history.size = CHESS_HISTORY_INITIAL_BUFFER_SIZE; + info->history.size < info->history.used; + info->history.size += CHESS_HISTORY_BUFFER_INCREMENT); + info->history.body = + calloc(info->history.size, info->constants->step_entry_size); + read(info->sock, info->history.body, + info->history.used * info->constants->step_entry_size); +#undef RECV +} + +static void +ChessGenLogGlobal(ChessInfo* info, ChessGameResult result) +{ + fileheader_t log_header; + FILE *fp; + char buf[256]; + int bid; + + if ((bid = getbnum(info->constants->log_board)) == 0) + return; + + setbpath(buf, info->constants->log_board); + stampfile(buf, &log_header); + + fp = fopen(buf, "w"); + if (fp != NULL) { + info->actions->genlog(info, fp, result); + fclose(fp); + + strlcpy(log_header.owner, "[棋譜機器人]", sizeof(log_header.owner)); + snprintf(log_header.title, sizeof(log_header.title), + ANSI_COLOR(37;41) "棋譜" ANSI_RESET " %s VS %s", + info->user1.userid, info->user2.userid); + + setbdir(buf, info->constants->log_board); + append_record(buf, &log_header, sizeof(log_header)); + + setbtotal(bid); + } +} + +static void +ChessGenLogUser(ChessInfo* info, ChessGameResult result) +{ + fileheader_t log_header; + FILE *fp; + char buf[256]; + + sethomepath(buf, cuser.userid); + stampfile(buf, &log_header); + + fp = fopen(buf, "w"); + if (fp != NULL) { + info->actions->genlog(info, fp, result); + fclose(fp); + + strlcpy(log_header.owner, "[楚河漢界]", sizeof(log_header.owner)); + if(info->my == 0) + sprintf(log_header.title, "%s V.S. %s", + info->user1.userid, info->user2.userid); + else + sprintf(log_header.title, "%s V.S. %s", + info->user2.userid, info->user1.userid); + log_header.filemode = 0; + + sethomedir(buf, cuser.userid); + append_record_forward(buf, &log_header, sizeof(log_header), + cuser.userid); + + mailalert(cuser.userid); + } +} + +static void +ChessGenLog(ChessInfo* info, ChessGameResult result) +{ + if (info->mode == CHESS_MODE_VERSUS && info->my == 0 && + info->constants->log_board) { + ChessGenLogGlobal(info, result); + } + + if (getans("是否將棋譜寄回信箱?[N/y]") == 'y') + ChessGenLogUser(info, result); +} + +void +ChessPlay(ChessInfo* info) +{ + ChessGameResult game_result; + sighandler_t old_handler; + const char* game_result_str = 0; + + if (info == NULL) + return; + + /* XXX */ + if (!info->timelimit) { + info->timelimit = _current_time_limit; + _current_time_limit = NULL; + } + + CurrentPlayingGameInfo = info; + old_handler = Signal(SIGUSR1, &ChessWatchRequest); + + if (info->mode == CHESS_MODE_WATCH) { + int i; + for (i = 0; i < info->history.used; ++i) + info->actions->apply_step(info->board, + ChessHistoryRetrieve(info, i)); + info->current_step = info->history.used; + } + + /* playing initialization */ + ChessRedraw(info); + info->turn = 1; + info->lefttime[0] = info->lefttime[1] = info->timelimit ? + info->timelimit->free_time : info->constants->traditional_timeout; + info->lefthand[0] = info->lefthand[1] = 0; + + /* main loop */ + add_io(info->sock, 0); + for (game_result = CHESS_RESULT_CONTINUE; + game_result == CHESS_RESULT_CONTINUE; + info->turn ^= 1) { + info->actions->prepare_play(info); + info->actions->drawline(info, CHESS_DRAWING_TURN_ROW); + info->actions->drawline(info, CHESS_DRAWING_WARN_ROW); + game_result = info->play_func[(int) info->turn](info); + } + add_io(0, 0); + + if (info->sock) + close(info->sock); + + /* end processing */ + if (info->mode == CHESS_MODE_VERSUS) { + switch (game_result) { + case CHESS_RESULT_WIN: + game_result_str = "對方認輸了!"; + break; + + case CHESS_RESULT_LOST: + game_result_str = "你認輸了!"; + break; + + case CHESS_RESULT_TIE: + game_result_str = "和棋"; + break; + + default: + assert_not_reached(); + } + } else if (info->mode == CHESS_MODE_WATCH) + game_result_str = "結束觀棋"; + else if (info->mode == CHESS_MODE_PERSONAL) + game_result_str = "結束打譜"; + else if (info->mode == CHESS_MODE_REPLAY) + game_result_str = "結束看譜"; + + if (game_result_str) { + strlcpy(info->warnmsg, game_result_str, sizeof(info->warnmsg)); + info->actions->drawline(info, CHESS_DRAWING_WARN_ROW); + } + + info->actions->gameend(info, game_result); + ChessGenLog(info, game_result); + + Signal(SIGUSR1, old_handler); + CurrentPlayingGameInfo = NULL; +} + +static userinfo_t* +ChessSearchUser(int sig, const char* title) +{ + char uident[16]; + userinfo_t *uin; + + stand_title(title); + CompleteOnlineUser(msg_uid, uident); + if (uident[0] == '\0') + return NULL; + + if ((uin = search_ulist_userid(uident)) == NULL) + return NULL; + + if (sig >= 0) + uin->sig = sig; + return uin; +} + +int +ChessStartGame(char func_char, int sig, const char* title) +{ + userinfo_t *uin; + char buf[4]; + + if ((uin = ChessSearchUser(sig, title)) == NULL) + return -1; + uin->turn = 1; + currutmp->turn = 0; + strlcpy(uin->mateid, currutmp->userid, sizeof(uin->mateid)); + strlcpy(currutmp->mateid, uin->userid, sizeof(currutmp->mateid)); + + stand_title(title); + buf[0] = 0; + getdata(2, 0, "使用傳統模式 (T), 限時限步模式 (L) 或是 讀秒模式 (C)? (T/l/c)", + buf, 3, DOECHO); + + if (buf[0] == 'l' || buf[0] == 'L' || + buf[0] == 'c' || buf[0] == 'C') { + + _current_time_limit = (ChessTimeLimit*) malloc(sizeof(ChessTimeLimit)); + if (buf[0] == 'l' || buf[0] == 'L') + _current_time_limit->time_mode = CHESS_TIMEMODE_MULTIHAND; + else + _current_time_limit->time_mode = CHESS_TIMEMODE_COUNTING; + + do { + getdata_str(3, 0, "請設定局時 (自由時間) 以分鐘為單位:", + buf, 3, DOECHO, "30"); + _current_time_limit->free_time = atoi(buf); + } while (_current_time_limit->free_time < 0 || _current_time_limit->free_time > 90); + _current_time_limit->free_time *= 60; /* minute -> second */ + + if (_current_time_limit->time_mode == CHESS_TIMEMODE_MULTIHAND) { + char display_buf[128]; + + do { + getdata_str(4, 0, "請設定步時, 以分鐘為單位:", + buf, 3, DOECHO, "5"); + _current_time_limit->limit_time = atoi(buf); + } while (_current_time_limit->limit_time < 0 || _current_time_limit->limit_time > 30); + _current_time_limit->limit_time *= 60; /* minute -> second */ + + snprintf(display_buf, sizeof(display_buf), + "請設定限步 (每 %d 分鐘需走幾步):", + _current_time_limit->limit_time / 60); + do { + getdata_str(5, 0, display_buf, buf, 3, DOECHO, "10"); + _current_time_limit->limit_hand = atoi(buf); + } while (_current_time_limit->limit_hand < 1); + } else { + _current_time_limit->limit_hand = 1; + + do { + getdata_str(4, 0, "請設定讀秒, 以秒為單位", + buf, 3, DOECHO, "60"); + _current_time_limit->limit_time = atoi(buf); + } while (_current_time_limit->limit_time < 0); + } + } else + _current_time_limit = NULL; + + my_talk(uin, friend_stat(currutmp, uin), func_char); + return 0; +} + +int +ChessWatchGame(void (*play)(int, ChessGameMode), int game, const char* title) +{ + int sock, msgsock; + userinfo_t *uin; + + if ((uin = ChessSearchUser(-1, title)) == NULL) + return -1; + + if (uin->uid == currutmp->uid || uin->mode != game) + return -1; + + if (getans("是否進行觀棋? [N/y]") != 'y') + return 0; + + if ((sock = make_connection_to_somebody(uin, 10)) < 0) { + vmsg("無法建立連線"); + return -1; + } +#if defined(Solaris) && __OS_MAJOR_VERSION__ == 5 && __OS_MINOR_VERSION__ < 7 + msgsock = accept(sock, (struct sockaddr *) 0, 0); +#else + msgsock = accept(sock, (struct sockaddr *) 0, (socklen_t *) 0); +#endif + close(sock); + if (msgsock < 0) + return -1; + + strlcpy(currutmp->mateid, uin->userid, sizeof(currutmp->mateid)); + play(msgsock, CHESS_MODE_WATCH); + close(msgsock); + return 0; +} + +static void +ChessInitUser(ChessInfo* info) +{ + char userid[2][IDLEN + 1]; + userec_t xuser; + + if (info->mode == CHESS_MODE_PERSONAL) { + strlcpy(userid[0], cuser.userid, sizeof(userid[0])); + strlcpy(userid[1], cuser.userid, sizeof(userid[1])); + } + else if (info->mode == CHESS_MODE_WATCH) { + userinfo_t *uinfo = search_ulist_userid(currutmp->mateid); + strlcpy(userid[0], uinfo->userid, sizeof(userid[0])); + strlcpy(userid[1], uinfo->mateid, sizeof(userid[1])); + } + else { + strlcpy(userid[0], cuser.userid, sizeof(userid[0])); + strlcpy(userid[1], currutmp->mateid, sizeof(userid[1])); + } + + getuser(userid[0], &xuser); + info->actions->init_user(&xuser, &info->user1); + getuser(userid[1], &xuser); + info->actions->init_user(&xuser, &info->user2); +} + +static char* +ChessPhotoInitial(ChessInfo* info) +{ + char genbuf[256]; + int line; + FILE* fp; + static const char * const blank_photo[6] = { + "┌──────┐", + "│ 空 │", + "│ 白 │", + "│ 照 │", + "│ 片│", + "└──────┘" + }; + char country[5], level[11]; + userec_t xuser; + char* photo; + + if (info->mode == CHESS_MODE_REPLAY) + return NULL; + + sethomefile(genbuf, info->user1.userid, info->constants->photo_file_name); + if (!dashf(genbuf)) { + sethomefile(genbuf, info->user2.userid, info->constants->photo_file_name); + if (!dashf(genbuf)) + return NULL; + } + + photo = (char*) calloc( + CHESS_PHOTO_LINE * CHESS_PHOTO_COLUMN, sizeof(char)); + + /* simulate photo as two dimensional array */ +#define PHOTO(X) (photo + (X) * CHESS_PHOTO_COLUMN) + + getuser(info->user1.userid, &xuser); + sethomefile(genbuf, info->user1.userid, info->constants->photo_file_name); + fp = fopen(genbuf, "r"); + + if (fp == NULL) { + strcpy(country, "無"); + level[0] = 0; + } else { + int i, j; + for (line = 1; line < 8; ++line) + fgets(genbuf, sizeof(genbuf), fp); + + fgets(genbuf, sizeof(genbuf), fp); + chomp(genbuf); + strip_ansi(genbuf + 11, genbuf + 11, + STRIP_ALL); /* country name may have color */ + for (i = 11, j = 0; genbuf[i] && j < 4; ++i) + if (genbuf[i] != ' ') /* and spaces */ + country[j++] = genbuf[i]; + country[j] = 0; /* two chinese words */ + + fgets(genbuf, sizeof(genbuf), fp); + chomp(genbuf); + strlcpy(level, genbuf + 11, 11); /* five chinese words*/ + rewind(fp); + } + + for (line = 0; line < 6; ++line) { + if (fp != NULL) { + if (fgets(genbuf, sizeof(genbuf), fp)) { + chomp(genbuf); + sprintf(PHOTO(line), "%s ", genbuf); + } else + strcpy(PHOTO(line), " "); + } else + strcpy(PHOTO(line), blank_photo[line]); + + switch (line) { + case 0: sprintf(genbuf, "<代號> %s", xuser.userid); break; + case 1: sprintf(genbuf, "<暱稱> %.16s", xuser.nickname); break; + case 2: sprintf(genbuf, "<上站> %d", xuser.numlogins); break; + case 3: sprintf(genbuf, "<文章> %d", xuser.numposts); break; + case 4: sprintf(genbuf, "<職位> %-4s %s", country, level); break; + case 5: sprintf(genbuf, "<來源> %.16s", xuser.lasthost); break; + default: genbuf[0] = 0; + } + strcat(PHOTO(line), genbuf); + } + if (fp != NULL) + fclose(fp); + + sprintf(PHOTO(6), " %s%2.2s棋" ANSI_RESET, + info->constants->turn_color[(int) info->my], + info->constants->turn_str[(int) info->my]); + strcpy(PHOTO(7), " V.S "); + sprintf(PHOTO(8), " %s%2.2s棋" ANSI_RESET, + info->constants->turn_color[info->my ^ 1], + info->constants->turn_str[info->my ^ 1]); + + getuser(info->user2.userid, &xuser); + sethomefile(genbuf, info->user2.userid, "photo_cchess"); + fp = fopen(genbuf, "r"); + + if (fp == NULL) { + strcpy(country, "無"); + level[0] = 0; + } else { + int i, j; + for (line = 1; line < 8; ++line) + fgets(genbuf, sizeof(genbuf), fp); + + fgets(genbuf, sizeof(genbuf), fp); + chomp(genbuf); + strip_ansi(genbuf + 11, genbuf + 11, + STRIP_ALL); /* country name may have color */ + for (i = 11, j = 0; genbuf[i] && j < 4; ++i) + if (genbuf[i] != ' ') /* and spaces */ + country[j++] = genbuf[i]; + country[j] = 0; /* two chinese words */ + + fgets(genbuf, sizeof(genbuf), fp); + chomp(genbuf); + strlcpy(level, genbuf + 11, 11); /* five chinese words*/ + rewind(fp); + } + + for (line = 9; line < 15; ++line) { + move(line, 37); + switch (line - 9) { + case 0: sprintf(PHOTO(line), "<代號> %-16.16s ", xuser.userid); break; + case 1: sprintf(PHOTO(line), "<暱稱> %-16.16s ", xuser.nickname); break; + case 2: sprintf(PHOTO(line), "<上站> %-16d ", xuser.numlogins); break; + case 3: sprintf(PHOTO(line), "<文章> %-16d ", xuser.numposts); break; + case 4: sprintf(PHOTO(line), "<職位> %-4s %-10s ", country, level); break; + case 5: sprintf(PHOTO(line), "<來源> %-16.16s ", xuser.lasthost); break; + } + + if (fp != NULL) { + if (fgets(genbuf, 200, fp)) { + chomp(genbuf); + strcat(PHOTO(line), genbuf); + } else + strcat(PHOTO(line), " "); + } else + strcat(PHOTO(line), blank_photo[line - 9]); + } + if (fp != NULL) + fclose(fp); +#undef PHOTO + + return photo; +} + +static void +ChessInitPlayFunc(ChessInfo* info) +{ + switch (info->mode) { + case CHESS_MODE_VERSUS: + info->play_func[(int) info->my] = &ChessPlayFuncMy; + info->play_func[info->my ^ 1] = &ChessPlayFuncHis; + break; + + case CHESS_MODE_WATCH: + info->play_func[0] = info->play_func[1] = &ChessPlayFuncWatch; + break; + + case CHESS_MODE_PERSONAL: + info->play_func[0] = info->play_func[1] = &ChessPlayFuncMy; + break; + + case CHESS_MODE_REPLAY: + /* TODO: not implemented yet */ + assert_not_reached(); + break; + } +} + +ChessInfo* +NewChessInfo(const ChessActions* actions, const ChessConstants* constants, + int sock, ChessGameMode mode) +{ + /* allocate memory for the structure and extra space for temporary + * steping information storage (step_tmp[0]). */ + ChessInfo* info = + (ChessInfo*) calloc(1, sizeof(ChessInfo) + constants->step_entry_size); + + /* compiler don't know it's actually const... */ + info->actions = (ChessActions*) actions; + info->constants = (ChessConstants*) constants; + info->mode = mode; + info->sock = sock; + + if (mode == CHESS_MODE_VERSUS) + info->my = currutmp->turn; + else if (mode == CHESS_MODE_PERSONAL) + info->my = 1; + else if (mode == CHESS_MODE_WATCH) + ChessReceiveWatchInfo(info); + + ChessInitUser(info); + + info->photo = ChessPhotoInitial(info); + + if (mode != CHESS_MODE_WATCH) + ChessHistoryInit(&info->history, constants->step_entry_size); + + ChessBroadcastListInit(&info->broadcast_list); + ChessInitPlayFunc(info); + + return info; +} + +void +DeleteChessInfo(ChessInfo* info) +{ +#define NULL_OR_FREE(X) if (X) free(X); else (void) 0 + NULL_OR_FREE(info->timelimit); + NULL_OR_FREE(info->photo); + NULL_OR_FREE(info->history.body); + + ChessBroadcastListClear(&info->broadcast_list); +#undef NULL_OR_FREE +} + +void +ChessEstablishRequest(int sock) +{ + /* XXX */ + if (!_current_time_limit) + write(sock, "T", 1); /* traditional */ + else { + write(sock, "L", 1); /* limited */ + write(sock, _current_time_limit, sizeof(ChessTimeLimit)); + } +} + +void +ChessAcceptingRequest(int sock) +{ + /* XXX */ + char mode; + read(sock, &mode, 1); + if (mode == 'T') + _current_time_limit = NULL; + else { + _current_time_limit = (ChessTimeLimit*) malloc(sizeof(ChessTimeLimit)); + read(sock, _current_time_limit, sizeof(ChessTimeLimit)); + } +} + +void +ChessShowRequest(void) +{ + /* XXX */ + if (!_current_time_limit) + mouts(10, 5, "使用傳統計時方式, 單步限時五分鐘"); + else if (_current_time_limit->time_mode == CHESS_TIMEMODE_MULTIHAND) { + mouts(10, 5, "使用限時限步規則:"); + move(12, 8); + prints("局時 (自由時間): %2d 分 %02d 秒", + _current_time_limit->free_time / 60, + _current_time_limit->free_time % 60); + move(13, 8); + prints("限時步時: %2d 分 %02d 秒 / %2d 手", + _current_time_limit->limit_time / 60, + _current_time_limit->limit_time % 60, + _current_time_limit->limit_hand); + } else if (_current_time_limit->time_mode == CHESS_TIMEMODE_COUNTING) { + mouts(10, 5, "使用讀秒規則:"); + move(12, 8); + prints("局時 (自由時間): %2d 分 %02d 秒", + _current_time_limit->free_time / 60, + _current_time_limit->free_time % 60); + move(13, 8); + prints("讀秒時間: 每手 %2d 秒", _current_time_limit->limit_time); + } +} + diff --git a/mbbsd/talk.c b/mbbsd/talk.c index 704cca6b..3d5b7cbf 100644 --- a/mbbsd/talk.c +++ b/mbbsd/talk.c @@ -1446,7 +1446,7 @@ my_talk(userinfo_t * uin, int fri_stat, char defact) return; } strlcpy(currutmp->mateid, uin->userid, sizeof(currutmp->mateid)); - chc(msgsock, CHC_WATCH); + chc(msgsock, CHESS_MODE_WATCH); } } else @@ -1546,7 +1546,23 @@ my_talk(userinfo_t * uin, int fri_stat, char defact) add_io(0, 0); close(sock); currutmp->sockactive = NA; - read(msgsock, &c, sizeof c); + + if (uin->sig == SIG_CHC) + ChessEstablishRequest(msgsock); + + add_io(msgsock, 0); + while ((ch = igetch()) != I_OTHERDATA) { + if (ch == Ctrl('D')) { + add_io(0, 0); + close(msgsock); + unlockutmpmode(); + return; + } + } + + if (read(msgsock, &c, sizeof(c)) != sizeof(c)) + c = 'n'; + add_io(0, 0); if (c == 'y') { snprintf(save_page_requestor, sizeof(save_page_requestor), @@ -1563,7 +1579,7 @@ my_talk(userinfo_t * uin, int fri_stat, char defact) gomoku(msgsock); break; case SIG_CHC: - chc(msgsock, CHC_VERSUS); + chc(msgsock, CHESS_MODE_VERSUS); break; case SIG_GO: gochess(msgsock); @@ -2954,7 +2970,10 @@ talkreply(void) char genbuf[200]; int a, sig = currutmp->sig; int currstat0 = currstat; + int r; + int is_chess; userec_t xuser; + void (*sig_pipe_handle)(int); uip = &SHM->uinfo[currutmp->destuip]; snprintf(page_requestor, sizeof(page_requestor), @@ -2962,6 +2981,17 @@ talkreply(void) currutmp->destuid = uip->uid; currstat = REPLY; /* 避免出現動畫 */ + is_chess = (sig == SIG_CHC); + + a = reply_connection_request(uip); + if (a < 0) { + clear(); + currstat = currstat0; + return; + } + if (is_chess) + ChessAcceptingRequest(a); + clear(); outs("\n\n"); @@ -2985,27 +3015,38 @@ talkreply(void) currutmp->msgs[0].msgmode = MSGMODE_TALK; prints("對方來自 [%s],共上站 %d 次,文章 %d 篇\n", uip->from, xuser.numlogins, xuser.numposts); - showplans(uip->userid); - show_call_in(0, 0); + + if (is_chess) + ChessShowRequest(); + else { + showplans(uip->userid); + show_call_in(0, 0); + } snprintf(genbuf, sizeof(genbuf), "你想跟 %s %s啊?請選擇(Y/N/A/B/C/D/E/F/1/2)[N] ", page_requestor, sig_des[sig]); getdata(0, 0, genbuf, buf, sizeof(buf), LCECHO); - a = reply_connection_request(uip); - if (a < 0) { - clear(); - currstat = currstat0; - return; - } if (!buf[0] || !strchr("yabcdef12", buf[0])) buf[0] = 'n'; - write(a, buf, 1); + + sig_pipe_handle = Signal(SIGPIPE, SIG_IGN); + r = write(a, buf, 1); if (buf[0] == 'f' || buf[0] == 'F') { if (!getdata(b_lines, 0, "不能的原因:", genbuf, 60, DOECHO)) strlcpy(genbuf, "不告訴你咧 !! ^o^", sizeof(genbuf)); - write(a, genbuf, 60); + r = write(a, genbuf, 60); + } + Signal(SIGPIPE, sig_pipe_handle); + + if (r == -1) { + snprintf(genbuf, sizeof(genbuf), + "%s已停止呼叫,按Enter繼續...", page_requestor); + getdata(0, 0, genbuf, buf, sizeof(buf), LCECHO); + clear(); + currstat = currstat0; + return; } uip->destuip = currutmp - &SHM->uinfo[0]; @@ -3021,7 +3062,7 @@ talkreply(void) gomoku(a); break; case SIG_CHC: - chc(a, CHC_VERSUS); + chc(a, CHESS_MODE_VERSUS); break; case SIG_GO: gochess(a); -- cgit v1.2.3