From f9f4c71a8beec3d7c2931a266d3df20d00abaeba Mon Sep 17 00:00:00 2001
From: scw <scw@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>
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;
-
-    /* �p�ɥ�, [0] = mine, [1] = his */
-    int	    lefttime[2];
-    int     lefthand[2]; /* ���ɭ��B�ɥ�, = 0 �����ۥѮɶ��ΫD���ɭ��B�Ҧ� */
-
-    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 = "�i";
 	else
 	    dir = "�h";
@@ -230,7 +176,7 @@ getstep(board_t board, const rc_t *from, const rc_t *to, char buf[])
     /* �X�G|�e�X */
     if(twin) {
 	len+=sprintf(buf+len, "%s%s",
-		((from->r>twin_r)==(turn==(chcd->my^1)))?"�e":"��",
+		((from->r>twin_r)==(turn==(my^1)))?"�e":"��",
 		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) "   �H�ѹ��   " ANSI_COLOR(45) "%30s VS %-20s%10s" ANSI_RESET,
-	       user1->userid, user2->userid, chcd->mode & CHC_WATCH ? "[�[�ѼҦ�]" : "");
+	prints(ANSI_COLOR(1;46) "   �H�ѹ��   " 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 ? "����A�U�ѤF" : "���ݹ��U��");
+			info->my == info->turn ? "����A�U�ѤF" : "���ݹ��U��");
 	    else if (line == PHOTO_TIME_ROW1) {
-		if (chcd->lefthand[0])
-		    prints("�ڤ�Ѿl�ɶ� %d:%02d / %2d �B",
-			    chcd->lefttime[0] / 60, chcd->lefttime[0] % 60,
-			    chcd->lefthand[0]);
+		if (info->mode == CHESS_MODE_WATCH) {
+		    if (!info->timelimit)
+			prints("�C�⭭�ɤ�����");
+		    else
+			prints("����: %5s",
+				chc_timestr(info->timelimit->free_time));
+		} else if (info->lefthand[0])
+		    prints("�ڤ�Ѿl�ɶ� %s / %2d �B",
+			    chc_timestr(info->lefttime[0]),
+			    info->lefthand[0]);
 		else
-		    prints("�ڤ�Ѿl�ɶ� %d:%02d",
-			    chcd->lefttime[0] / 60, chcd->lefttime[0] % 60);
+		    prints("�ڤ�Ѿl�ɶ� %s",
+			    chc_timestr(info->lefttime[0]));
 	    } else if (line == PHOTO_TIME_ROW2) {
-		if (chcd->lefthand[1])
-		    prints("���Ѿl�ɶ� %d:%02d / %2d �B",
-			    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("�B��: %s / %2d �B",
+				    chc_timestr(info->timelimit->limit_time),
+				    info->timelimit->limit_hand);
+			else
+			    prints("��: %5d ��",
+				    info->timelimit->limit_time);
+		    }
+		} else if (info->lefthand[1])
+		    prints("���Ѿl�ɶ� %s / %2d �B",
+			    chc_timestr(info->lefttime[1]),
+			    info->lefthand[1]);
 		else
-		    prints("���Ѿl�ɶ� %d:%02d",
-			    chcd->lefttime[1] / 60, chcd->lefttime[1] % 60);
+		    prints("���Ѿl�ɶ� %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) "�A�O%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 ? "����A�U�ѤF" : "���ݹ��U��");
-	} else if (line == STEP_ROW && !chcd->firststep) {
-	    showstep(board);
+		    info->my == info->turn ? "����A�U�ѤF" : "���ݹ��U��");
+	} else if (line == STEP_ROW && info->last_movestr) {
+	    showstep(info);
 	} else if (line == REAL_TIME_ROW1) {
-	    if (chcd->lefthand[0])
-		prints("�ڤ�Ѿl�ɶ� %d:%02d / %2d �B",
-			chcd->lefttime[0] / 60, chcd->lefttime[0] % 60,
-			chcd->lefthand[0]);
+	    if (info->lefthand[0])
+		prints("�ڤ�Ѿl�ɶ� %s / %2d �B",
+			chc_timestr(info->lefttime[0]),
+			info->lefthand[0]);
 	    else
-		prints("�ڤ�Ѿl�ɶ� %d:%02d",
-			chcd->lefttime[0] / 60, chcd->lefttime[0] % 60);
+		prints("�ڤ�Ѿl�ɶ� %s",
+			chc_timestr(info->lefttime[0]));
 	} else if (line == REAL_TIME_ROW2) {
-	    if (chcd->lefthand[1])
-		prints("���Ѿl�ɶ� %d:%02d / %2d �B",
-			chcd->lefttime[1] / 60, chcd->lefttime[1] % 60,
-			chcd->lefthand[1]);
+	    if (info->lefthand[1])
+		prints("���Ѿl�ɶ� %s / %2d �B",
+			chc_timestr(info->lefttime[1]),
+			info->lefthand[1]);
 	    else
-		prints("���Ѿl�ɶ� %d:%02d",
-			chcd->lefttime[1] / 60, chcd->lefttime[1] % 60);
+		prints("���Ѿl�ɶ� %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) "�M" 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) "�M" 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, "=> �M��\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);	/* �N */
-    board[0][3] = board[0][5] = CHE(KIND_A, chcd->my ^ 1);	/* �h */
-    board[0][2] = board[0][6] = CHE(KIND_E, chcd->my ^ 1);	/* �H */
-    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);	/* �N */
+    board[0][3] = board[0][5] = CHE(KIND_A, my ^ 1);	/* �h */
+    board[0][2] = board[0][6] = CHE(KIND_E, my ^ 1);	/* �H */
+    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);	/* �K */
-    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);	/* �X */
-    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);	/* �K */
+    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);	/* �X */
+    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);	/* �L */
+	board[6][6] = board[6][8] = CHE(KIND_P, my);	/* �L */
 }
 
 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));
 }
 
 /* �D��y�Ц�ΦC(rowcol)���Z�� */
@@ -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:		/* �h �K */
 	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:		/* �H �� */
 	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;
 	/* ��H�L */
 	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 ���y�� */
 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) "�N�x!" 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) "�n�D�M��!" ANSI_RESET, sizeof(chcd->warnmsg));
-		    chc_drawline(board, user1, user2, WARN_ROW);
-		} else {
-		    /* �y���ܴ�
-		     *   (CHC_WATCH_PERSONAL �]�w��
-		     *    ���[�Ѫ̬ݪ��ѧ�����H���Ъ��ѧ�)
-		     *   �ѽL�ݭ˸m���M�p�~�n�ഫ
-		     */
-		    /* 1.�p�G�b�[�� �B�ѧ��O�O�H�b���� �B����A ��*/
-		    if ( ((chcd->mode & CHC_WATCH) && (chcd->mode & CHC_WATCH_PERSONAL)) ||
-			    /* 2.�ۤv�b���� */
-			    (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)
+	    /* ������N�� */
+	    *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) "�n�D�M��!" 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) "���i�H������" 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) "���i�H������" 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;
 }
 
+
+/* �H�ѥ\��i�J�I:
+ * 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) "�N�x!" 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. �O���O�U�ѫe����l���� */
 	    /* NOTE, �Y�����_�u�h�L 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?"�M":endgame==1?"��":"�t"),
-		    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 ? "�M" :
+		     result == CHESS_RESULT_WIN ? "��" : "�t"),
+		    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, "���{��F!", 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, "�A�{��F!", 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, "�M��", 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("�M��");
-    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] = {
-	"�z�w�w�w�w�w�w�{",
-	"�x ��         �x",
-	"�x    ��      �x",
-	"�x       ��   �x",
-	"�x          ���x",
-	"�|�w�w�w�w�w�w�}" 
-    };
-    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, "�L");
-	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, "<�N��> %s", cuser.userid);      break;
-	    case 1: sprintf(genbuf, "<�ʺ�> %.16s", cuser.nickname); break;
-	    case 2: sprintf(genbuf, "<�W��> %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), "           ��.��           ");
-    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, "�L");
-	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), "<�N��> %-16.16s ", xuser.userid);   break;
-	    case 1: sprintf(PHOTO(line), "<�ʺ�> %-16.16s ", xuser.nickname); break;
-	    case 2: sprintf(PHOTO(line), "<�W��> %-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 �@�w�n���b�̫��?
-    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;
-
-    /* �q���P�ӷ���l�ƦU���ܼ� */
-    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;
-}
-
-/* �H�ѥ\��i�J�I:
- * 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("�H�ѥ\\�ಧ�`");
-	return;
-    }
-    chcd = (struct CHCData*)malloc(sizeof(struct CHCData));
-    if(chcd == NULL) {
-	vmsg("����H�ѥ\\�ॢ��");
-	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("�L�k�����ѧ�");
-    
-    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("�O�_�N���бH�^�H�c�H[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, "[���e�~��]");
-    }
-    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("���e�~�ɤ���");
-    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("�H���ܧ�");
-    buf[0] = 0;
-    getdata(2, 0, "�ϥζDzμҦ� (T), ���ɭ��B�Ҧ� (L) �άO Ū���Ҧ� (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, "�г]�w���� (�ۥѮɶ�) �H���������:",
-		    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, "�г]�w�B��, �H���������:",
-		    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),
-		"�г]�w���B (�C %d �����ݨ��X�B):",
-		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, "�г]�w���� (�ۥѮɶ�) �H���������:",
-		    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, "�г]�wŪ��, �H�������",
-		    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, "���e�~�ɤ���");
 }
 
 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("�O�_�i���[��? [N/y]") != 'y')
-	return 0;
-
-    if ((sock = make_connection_to_somebody(uin, 10)) < 0) {
-	vmsg("�L�k�إ߳s�u");
-	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, "���e�~�ɤ���");
 }
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) " �U�� "
+	ANSI_COLOR(;31;47) " (��������)" ANSI_COLOR(30) " ����   "
+	ANSI_COLOR(31) "(�ť���/ENTER)" ANSI_COLOR(30) " �U�l "
+	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) " �e��@�B   "
+	ANSI_COLOR(31) "(����)" ANSI_COLOR(30) " �e��Q�B  "
+	ANSI_COLOR(31) "(PGUP/PGDN)" ANSI_COLOR(30) " �̪�/�ثe�L��  "
+	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) " �U�l "
+	ANSI_COLOR(31) "(q)" ANSI_COLOR(30) " ���} "
+	ANSI_RESET,
+
+	/* CHESS_MODE_REPLAY, ���� */
+	ANSI_COLOR(1;33;42) " ���� "
+	ANSI_COLOR(;31;47) " (����)" ANSI_COLOR(30) " �e��@�B   "
+	ANSI_COLOR(31) "(����)" ANSI_COLOR(30) " �e��Q�B  "
+	ANSI_COLOR(31) "(PGUP/PGDN)" ANSI_COLOR(30) " �̪�/�ثe�L��  "
+	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) "�n�D�M��!" 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) "�n�D�M��!" 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) "�ѧ��w����" 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: /* ���e�@�B */
+		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: /* ����@�B */
+		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: /* ���e�Q�B */
+		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: /* ����Q�B */
+		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: /* �_�l�L�� */
+		if (info->current_step == 0)
+		    bell();
+		else {
+		    info->actions->init_board(info, info->board);
+		    info->current_step = 0;
+		    ChessRedraw(info);
+		}
+		break;
+
+	    case KEY_PGDN: /* �̷s�L�� */
+		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, "[������H]", 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, "[���e�~��]", 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("�O�_�N���бH�^�H�c�H[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 = "���{��F!";
+		break;
+
+	    case CHESS_RESULT_LOST:
+		game_result_str = "�A�{��F!";
+		break;
+
+	    case CHESS_RESULT_TIE:
+		game_result_str = "�M��";
+		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, "�ϥζDzμҦ� (T), ���ɭ��B�Ҧ� (L) �άO Ū���Ҧ� (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, "�г]�w���� (�ۥѮɶ�) �H���������:",
+		    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, "�г]�w�B��, �H���������:",
+			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),
+		    "�г]�w���B (�C %d �����ݨ��X�B):",
+		    _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, "�г]�wŪ��, �H�������",
+			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("�O�_�i���[��? [N/y]") != 'y')
+	return 0;
+
+    if ((sock = make_connection_to_somebody(uin, 10)) < 0) {
+	vmsg("�L�k�إ߳s�u");
+	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] = {
+	"�z�w�w�w�w�w�w�{",
+	"�x ��         �x",
+	"�x    ��      �x",
+	"�x       ��   �x",
+	"�x          ���x",
+	"�|�w�w�w�w�w�w�}" 
+    };
+    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, "�L");
+	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, "<�N��> %s", xuser.userid);      break;
+	    case 1: sprintf(genbuf, "<�ʺ�> %.16s", xuser.nickname); break;
+	    case 2: sprintf(genbuf, "<�W��> %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), "           ��.��           ");
+    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, "�L");
+	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), "<�N��> %-16.16s ", xuser.userid);   break;
+	    case 1: sprintf(PHOTO(line), "<�ʺ�> %-16.16s ", xuser.nickname); break;
+	    case 2: sprintf(PHOTO(line), "<�W��> %-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, "�ϥζDzέp�ɤ覡, ��B���ɤ�����");
+    else if (_current_time_limit->time_mode == CHESS_TIMEMODE_MULTIHAND) {
+	mouts(10, 5, "�ϥέ��ɭ��B�W�h:");
+	move(12, 8);
+	prints("���� (�ۥѮɶ�): %2d �� %02d ��",
+		_current_time_limit->free_time / 60,
+		_current_time_limit->free_time % 60);
+	move(13, 8);
+	prints("���ɨB��:        %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, "�ϥ�Ū���W�h:");
+	move(12, 8);
+	prints("���� (�ۥѮɶ�): %2d �� %02d ��",
+		_current_time_limit->free_time / 60,
+		_current_time_limit->free_time % 60);
+	move(13, 8);
+	prints("Ū���ɶ�:   �C�� %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;		/* �קK�X�{�ʵe */
 
+    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]�A�@�W�� %d ���A�峹 %d �g\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),
 	    "�A�Q�� %s %s�ڡH�п��(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, "���઺��]�G", genbuf, 60, DOECHO))
 	    strlcpy(genbuf, "���i�D�A�� !! ^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�w����I�s�A��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