/* $Id$ */ #include "bbs.h" extern const double elo_exp_tab[1000]; enum Turn { BLK, RED }; enum Kind { KIND_K=1, KIND_A, KIND_E, KIND_R, KIND_H, KIND_C, KIND_P, }; #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 { rc_t from, to; } drc_t; struct CHCData { rc_t from, to, select, cursor; int lefttime; 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; }; static struct CHCData *chcd; static const char * const turn_color[2]={BLACK_COLOR, RED_COLOR}; /* some constant variable definition */ static const char * const turn_str[2] = {"黑的", "紅的"}; static const char * const num_str[2][10] = { {"", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, {"", "一", "二", "三", "四", "五", "六", "七", "八", "九"}, }; static const char * const chess_str[2][8] = { /* 0 1 2 3 4 5 6 7 */ {" ", "將", "士", "象", "車", "馬", "包", "卒"}, {" ", "帥", "仕", "相", "車", "傌", "炮", "兵"} }; static const char * const chess_brd[BRD_ROW * 2 - 1] = { /* 0 1 2 3 4 5 6 7 8 */ "┌─┬─┬─┬─┬─┬─┬─┬─┐", /* 0 */ "│ │ │ │\│/│ │ │ │", "├─┼─┼─┼─┼─┼─┼─┼─┤", /* 1 */ "│ │ │ │/│\│ │ │ │", "├─┼─┼─┼─┼─┼─┼─┼─┤", /* 2 */ "│ │ │ │ │ │ │ │ │", "├─┼─┼─┼─┼─┼─┼─┼─┤", /* 3 */ "│ │ │ │ │ │ │ │ │", "├─┴─┴─┴─┴─┴─┴─┴─┤", /* 4 */ "│ 楚 河 漢 界 │", "├─┬─┬─┬─┬─┬─┬─┬─┤", /* 5 */ "│ │ │ │ │ │ │ │ │", "├─┼─┼─┼─┼─┼─┼─┼─┤", /* 6 */ "│ │ │ │ │ │ │ │ │", "├─┼─┼─┼─┼─┼─┼─┼─┤", /* 7 */ "│ │ │ │\│/│ │ │ │", "├─┼─┼─┼─┼─┼─┼─┼─┤", /* 8 */ "│ │ │ │/│\│ │ │ │", "└─┴─┴─┴─┴─┴─┴─┴─┘" /* 9 */ }; static char * const hint_str[] = { " q 認輸離開", " p 要求和棋", "方向鍵 移動遊標", "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; } /* * End of the network communication function. */ /* * Start of the drawing function. */ static void chc_movecur(int r, int c) { move(r * 2 + 3, c * 4 + 4); } static char * getstep(board_t board, const rc_t *from, const rc_t *to, char buf[]) { int turn, fc, tc; char *dir; int twin = 0, twin_r = 0; int len = 0; turn = CHE_O(board[from->r][from->c]); if(CHE_P(board[from->r][from->c] != KIND_P)) { // TODO 目前不管兵卒前後 int i; for(i=0;i<10;i++) if(board[i][from->c]==board[from->r][from->c]) { if(i!=from->r) { twin=1; twin_r=i; } } } fc = (turn == (chcd->my ^ 1) ? from->c + 1 : 9 - from->c); tc = (turn == (chcd->my ^ 1) ? to->c + 1 : 9 - to->c); if (from->r == to->r) dir = "平"; else { if (from->c == to->c) tc = from->r - to->r; if (tc < 0) tc = -tc; if ((turn == (chcd->my ^ 1) && to->r > from->r) || (turn == chcd->my && to->r < from->r)) dir = "進"; else dir = "退"; } len=sprintf(buf, "%s", turn_color[turn]); /* 傌二|前傌 */ if(twin) { len+=sprintf(buf+len, "%s%s", ((from->r>twin_r)==(turn==(chcd->my^1)))?"前":"後", chess_str[turn][CHE_P(board[from->r][from->c])]); } else { len+=sprintf(buf+len, "%s%s", chess_str[turn][CHE_P(board[from->r][from->c])], num_str[turn][fc]); } /* 進三 */ len+=sprintf(buf+len, "%s%s" ANSI_RESET, dir, num_str[turn][tc]); /* :象 */ if(board[to->r][to->c]) { len+=sprintf(buf+len,":%s%s" ANSI_RESET, turn_color[turn^1], chess_str[turn^1][CHE_P(board[to->r][to->c])]); } return buf; } static void showstep(board_t board) { outs(chcd->last_movestr); } static void chc_drawline(board_t board, const chcusr_t *user1, const chcusr_t *user2, int line) { int i, j; 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 ? "[觀棋模式]" : ""); } 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) { prints("%s%s" ANSI_RESET, CHE_O(j) == BLK ? BLACK_REVERSE : RED_REVERSE, chess_str[CHE_O(j)][CHE_P(j)]); } else { prints("%s%s" ANSI_RESET, turn_color[CHE_O(j)], chess_str[CHE_O(j)][CHE_P(j)]); } } else prints("%c%c", chess_brd[line - 3][i * 4], chess_brd[line - 3][i * 4 + 1]); if (i != 8) prints("%c%c", chess_brd[line - 3][i * 4 + 2], chess_brd[line - 3][i * 4 + 3]); } if (chcd->photo) { outs(" "); if (line >= 3 && line < 3 + PHOTO_LINE) outs(chcd->photo + (line - 3) * PHOTO_COLUMN); else if (line == 3 + PHOTO_LINE + 1) prints(" %s%s" ANSI_RESET, TURN_COLOR, chcd->my == chcd->turn ? "輪到你下棋了" : "等待對方下棋"); else if (line == 3 + PHOTO_LINE + 2) prints(" 剩餘時間 %d:%02d", chcd->lefttime / 60, chcd->lefttime % 60); } else { outs(" "); if (line >= 3 && line < 3 + (int)dim(hint_str)) { 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]); } else if (line == TURN_ROW) { prints("%s%s" ANSI_RESET, TURN_COLOR, chcd->my == chcd->turn ? "輪到你下棋了" : "等待對方下棋"); } else if (line == STEP_ROW && !chcd->firststep) { showstep(board); } else if (line == TIME_ROW) { prints("剩餘時間 %d:%02d", chcd->lefttime / 60, chcd->lefttime % 60); } else if (line == WARN_ROW) { outs(chcd->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); } 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); } } } else if (line == 2 || line == 22) { outs(" "); if (line == 2) for (i = 1; i <= 9; i++) prints("%s ", num_str[0][i]); else for (i = 9; i >= 1; i--) prints("%s ", num_str[1][i]); } } 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. */ /* * 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) { char buf[80]; sprintf(buf, " %s\n", chcd->last_movestr); return chc_log(buf); } static int #if defined(__linux__) chc_filter(const struct dirent *dir) #else chc_filter(struct dirent *dir) #endif { if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0 ) return 0; return strstr(dir->d_name, ".poem") != NULL; } int chc_log_poem(void) { struct dirent **namelist; int n; // TODO use readdir(), don't use lots of memory n = scandir(BBSHOME"/etc/chess", &namelist, chc_filter, alphasort); if (n < 0) perror("scandir"); else { char buf[80]; FILE *fp; sprintf(buf, BBSHOME"/etc/chess/%s", namelist[random() % n]->d_name); if ((fp = fopen(buf, "r")) == NULL) return -1; while(fgets(buf, sizeof(buf), fp) != NULL) chc_log(buf); while(n--) free(namelist[n]); free(namelist); fclose(fp); } return 0; } /* * End of the log function. */ /* * Start of the rule function. */ static void chc_init_board(board_t board) { 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[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[6][0] = board[6][2] = board[6][4] = board[6][6] = board[6][8] = CHE(KIND_P, chcd->my); /* 兵 */ } static void chc_movechess(board_t board) { board[chcd->to.r][chcd->to.c] = board[chcd->from.r][chcd->from.c]; board[chcd->from.r][chcd->from.c] = 0; } /* 求兩座標行或列(rowcol)的距離 */ static int dist(rc_t from, rc_t to, int rowcol) { int d; d = rowcol ? from.c - to.c : from.r - to.r; return d > 0 ? d : -d; } /* 兩座標(行或列rowcol)中間有幾顆棋子 */ static int between(board_t board, rc_t from, rc_t to, int rowcol) { int i, rtv = 0; if (rowcol) { if (from.c > to.c) i = from.c, from.c = to.c, to.c = i; for (i = from.c + 1; i < to.c; i++) if (board[to.r][i]) rtv++; } else { if (from.r > to.r) i = from.r, from.r = to.r, to.r = i; for (i = from.r + 1; i < to.r; i++) if (board[i][to.c]) rtv++; } return rtv; } static int chc_canmove(board_t board, rc_t from, rc_t to) { int i; int rd, cd, turn; rd = dist(from, to, 0); cd = dist(from, to, 1); turn = CHE_O(board[from.r][from.c]); /* general check */ if (board[to.r][to.c] && CHE_O(board[to.r][to.c]) == turn) return 0; /* individual check */ switch (CHE_P(board[from.r][from.c])) { case KIND_K: /* 將 帥 */ 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) || 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) || 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)) return 0; /* 拐象腿 */ if (board[CENTER(from.r, to.r)][CENTER(from.c, to.c)]) return 0; break; case KIND_R: /* 車 */ if (!(rd > 0 && cd == 0) && !(rd == 0 && cd > 0)) return 0; if (between(board, from, to, rd == 0)) return 0; break; case KIND_H: /* 馬 傌 */ if (!(rd == 2 && cd == 1) && !(rd == 1 && cd == 2)) return 0; /* 拐馬腳 */ if (rd == 2) { if (board[CENTER(from.r, to.r)][from.c]) return 0; } else { if (board[from.r][CENTER(from.c, to.c)]) return 0; } break; case KIND_C: /* 包 炮 */ if (!(rd > 0 && cd == 0) && !(rd == 0 && cd > 0)) return 0; i = between(board, from, to, rd == 0); if ((i > 1) || (i == 1 && !board[to.r][to.c]) || (i == 0 && board[to.r][to.c])) return 0; break; case KIND_P: /* 卒 兵 */ 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)) && cd != 0) return 0; if ((turn == (chcd->my ^ 1) && to.r < from.r) || (turn == chcd->my && to.r > from.r)) return 0; break; } return 1; } /* 找 turn's king 的座標 */ static void findking(board_t board, int turn, rc_t * buf) { int i, r, c; r = (turn == (chcd->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 && CHE_O(board[r][c]) == turn) { buf->r = r, buf->c = c; return; } } static int chc_iskfk(board_t board) { rc_t from, to; findking(board, BLK, &to); findking(board, 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) { rc_t from, to; findking(board, 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)) return 1; return 0; } /* * End of the rule function. */ static void chcusr_put(userec_t *userec, const chcusr_t *user) { userec->chc_win = user->win; userec->chc_lose = user->lose; userec->chc_tie = user->tie; userec->chess_elo_rating = user->rating; } static void chcusr_get(const userec_t *userec, chcusr_t *user) { strlcpy(user->userid, userec->userid, sizeof(user->userid)); user->win = userec->chc_win; user->lose = userec->chc_lose; user->tie = userec->chc_tie; user->rating = userec->chess_elo_rating; if(user->rating == 0) user->rating = 1500; /* ELO initial value */ user->orig_rating = user->rating; } static int hisplay(int s, const chcusr_t *user1, const chcusr_t *user2, board_t board, board_t tmpbrd) { int start_time; int endgame = 0, endturn = 0; start_time = now; while (!endturn) { chcd->lefttime = CHC_TIMEOUT - (now - start_time); if (chcd->lefttime < 0) { chcd->lefttime = 0; /* to make him break out igetch() */ chcd->from.r = -2; chc_broadcast_send(chcd->act_list, board); } 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); chc_drawline(board, user1, user2, LTR(chcd->from.r)); chc_drawline(board, user1, user2, LTR(chcd->to.r)); } } break; } } return endgame; } static int myplay(int s, const chcusr_t *user1, const chcusr_t *user2, board_t board, board_t tmpbrd) { int ch, start_time; int endgame = 0, endturn = 0; chcd->ipass = 0, chcd->selected = 0; start_time = now; chcd->lefttime = CHC_TIMEOUT - (now - start_time); bell(); while (!endturn) { chc_drawline(board, user1, user2, TIME_ROW); chc_movecur(chcd->cursor.r, chcd->cursor.c); oflush(); ch = igetch(); chcd->lefttime = CHC_TIMEOUT - (now - start_time); if (chcd->lefttime < 0) ch = 'q'; 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); 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); 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; } } return endgame; } int round_to_int(double x) { /* assume that double cast to int will drop fraction parts */ if(x>=0) 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) { double k; double exp_res; int diff; int newrating; if(user1->rating < 1800) k = 30; else if(user1->rating < 2000) k = 25; else if(user1->rating < 2200) k = 20; else if(user1->rating < 2400) k = 15; else k = 10; //exp_res = 1.0/(1.0 + pow(10.0, (user2->rating-user1->rating)/400.0)); //user1->rating += (int)floor(k*(myres-exp_res)+0.5); diff=(int)user2->rating-(int)user1->rating; if(diff<=-1000 || diff>=1000) exp_res=diff>0?0.0:1.0; else if(diff>=0) exp_res=elo_exp_tab[diff]; else exp_res=1.0-elo_exp_tab[-diff]; newrating = (int)user1->rating + round_to_int(k*(myres-exp_res)); if(newrating > 3000) newrating = 3000; if(newrating < 1) newrating = 1; user1->rating = newrating; } static void mainloop(int s, chcusr_t *user1, chcusr_t *user2, board_t board, play_func_t play_func[2]) { 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); } if (chcd->mode & CHC_VERSUS) { user1->rating = user1->orig_rating; user1->lose--; if(chcd->my==RED) { /* 由紅方作 log. 記的是下棋前的原始分數 */ /* NOTE, 若紅方斷線則無 log */ 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); } if (endgame == 1) { strlcpy(chcd->warnmsg, "對方認輸了!", sizeof(chcd->warnmsg)); count_chess_elo_rating(user1, user2, 1.0); user1->win++; currutmp->chc_win++; } else if (endgame == 2) { strlcpy(chcd->warnmsg, "你認輸了!", sizeof(chcd->warnmsg)); 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++; } currutmp->chess_elo_rating = user1->rating; 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); } if (!my->turn) { if (!(chcd->mode & CHC_WATCH)) chc_broadcast_send(chcd->act_list, board); if (chcd->mode & CHC_VERSUS) user2->lose++; } 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 (chcd->photo) free(chcd->photo); free(chcd); chcd = 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; 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)); my_talk(uin, friend_stat(currutmp, uin), 'c'); return 0; } int chc_personal(void) { chc(0, CHC_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; }