summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/chess.h1
-rw-r--r--include/proto.h2
-rw-r--r--mbbsd/chc.c160
-rw-r--r--mbbsd/chess.c100
-rw-r--r--mbbsd/gomo.c76
-rw-r--r--mbbsd/pmore.c4
-rw-r--r--mbbsd/talk.c1
7 files changed, 317 insertions, 27 deletions
diff --git a/include/chess.h b/include/chess.h
index 8a151ace..904db6cf 100644
--- a/include/chess.h
+++ b/include/chess.h
@@ -175,6 +175,7 @@ void ChessPlay(ChessInfo* info);
int ChessStartGame(char func_char, int sig, const char* title);
int ChessWatchGame(void (*play)(int, ChessGameMode),
int game, const char* title);
+int ChessReplayGame(const char* fname);
ChessInfo* NewChessInfo(const ChessActions* actions,
const ChessConstants* constants, int sock, ChessGameMode mode);
diff --git a/include/proto.h b/include/proto.h
index 7a33212b..4c0003d2 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -197,6 +197,7 @@ void chc(int s, ChessGameMode mode);
int chc_main(void);
int chc_personal(void);
int chc_watch(void);
+ChessInfo* chc_replay(FILE* fp);
/* chicken */
void ch_buyitem(int money, const char *picture, int *item, int haveticket);
@@ -297,6 +298,7 @@ void gomoku(int s, ChessGameMode mode);
int gomoku_main(void);
int gomoku_personal(void);
int gomoku_watch(void);
+ChessInfo* gomoku_replay(FILE* fp);
/* guess */
int guess_main(void);
diff --git a/mbbsd/chc.c b/mbbsd/chc.c
index 0dbe3514..37831aa1 100644
--- a/mbbsd/chc.c
+++ b/mbbsd/chc.c
@@ -260,7 +260,7 @@ chc_drawline(const ChessInfo* info, int line)
/*
* Start of the log function.
*/
-void
+static void
chc_log_step(FILE* fp, board_t board, const drc_t *step)
{
char buf[80];
@@ -270,6 +270,22 @@ chc_log_step(FILE* fp, board_t board, const drc_t *step)
fputc('\n', fp);
}
+static void
+chc_log_machine_step(FILE* fp, board_t board, const drc_t *step)
+{
+ const static char chess_char[8] = {
+ 0, 'K', 'A', 'B', 'R', 'N', 'C', 'P'
+ };
+ /* We have black at bottom in rc_t but the standard is
+ * the red side at bottom, so that a rotation is needed. */
+ fprintf(fp, "%c%c%d%c%c%d ",
+ chess_char[CHE_P(board[step->from.r][step->from.c])],
+ BRD_COL - step->from.c - 1 + 'a', BRD_ROW - step->from.r - 1,
+ board[step->to.r][step->to.c] ? 'x' : '-',
+ BRD_COL - step->to.c - 1 + 'a', BRD_ROW - step->to.r - 1
+ );
+}
+
static int
#if defined(__linux__)
chc_filter(const struct dirent *dir)
@@ -337,11 +353,60 @@ chc_genlog(ChessInfo* info, FILE* fp, ChessGameResult result)
(info->myturn == RED) == (result== CHESS_RESULT_WIN) ?
"紅" : "黑");
- fputs("\n--\n\n", fp);
+ /* generate machine readable log.
+ * http://www.elephantbase.net/protocol/cchess_pgn.htm */
+ {
+ /* machine readable header */
+ time_t temp = (time_t)*clock;
+ struct tm *mytm = localtime(&temp);
+
+ fprintf(fp,
+ "\n\n<chclog>\n"
+ "[Game \"Chinese Chess\"]\n"
+ "[Date \"%d.%d.%d\"]\n"
+ "[Red \"%s\"]\n"
+ "[Black \"%s\"]\n"
+ "[Result \"%s\"]\n"
+ "[Notation \"Coord\"]\n"
+ "[FEN \"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/9/1C5C1/9/RN2K2NR"
+ " r - - 0 1\"]\n",
+ mytm->tm_year + 1900, mytm->tm_mon + 1, mytm->tm_mday,
+ info->myturn == RED ? info->user1.userid : info->user2.userid,
+ info->myturn == RED ? info->user2.userid : info->user1.userid,
+ result == CHESS_RESULT_TIE ? "0.5-0.5" :
+ (info->myturn == RED) == (result== CHESS_RESULT_WIN) ?
+ "1-0" : "0-1"
+ );
+ }
+ chc_init_board(board);
+ for (i = 0; i < nStep - 1; i += 2) {
+ const drc_t *move = (const drc_t*) ChessHistoryRetrieve(info, i);
+ fprintf(fp, "%2d. ", i / 2 + 1);
+ if (move->type == CHESS_STEP_NORMAL) {
+ chc_log_machine_step(fp, board, move);
+ chc_movechess(board, move);
+ }
+
+ fputs(" ", fp);
- /* TODO: generate machine readable log.
- * e.g. http://www.nchess.com/ccff.html */
+ move = (const drc_t*) ChessHistoryRetrieve(info, i + 1);
+ if (move->type == CHESS_STEP_NORMAL) {
+ chc_log_machine_step(fp, board, move);
+ chc_movechess(board, move);
+ }
+
+ fputc('\n', fp);
+ }
+ if (i < nStep) {
+ const drc_t *move = (const drc_t*) ChessHistoryRetrieve(info, i);
+ if (move->type == CHESS_STEP_NORMAL) {
+ fprintf(fp, "%2d. ", i / 2 + 1);
+ chc_log_machine_step(fp, board, move);
+ fputc('\n', fp);
+ }
+ }
+ fputs("</chclog>\n\n--\n\n", fp);
chc_log_poem(fp);
}
/*
@@ -599,6 +664,18 @@ chc_init_user(const userinfo_t *uinfo, ChessUser *user)
user->orig_rating = user->rating;
}
+static void
+chc_init_user_userec(const userec_t *urec, ChessUser *user)
+{
+ strlcpy(user->userid, urec->userid, sizeof(user->userid));
+ user->win = urec->chc_win;
+ user->lose = urec->chc_lose;
+ user->tie = urec->chc_tie;
+ user->rating = urec->chess_elo_rating;
+ if(user->rating == 0)
+ user->rating = 1500; /* ELO initial value */
+ user->orig_rating = user->rating;
+}
static void
chc_prepare_play(ChessInfo* info)
@@ -833,3 +910,78 @@ chc_watch(void)
{
return ChessWatchGame(&chc, CHC, "楚河漢界之爭");
}
+
+ChessInfo*
+chc_replay(FILE* fp)
+{
+ ChessInfo *info;
+ char buf[256];
+
+ info = NewChessInfo(&chc_actions, &chc_constants,
+ 0, CHESS_MODE_REPLAY);
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (strcmp("</chclog>\n", buf) == 0)
+ break;
+ if (buf[0] == '[') {
+ if (strncmp(buf + 1, "Red", 3) == 0 ||
+ strncmp(buf + 1, "Black", 5) == 0) {
+ /* /\[(Red|Black) "([a-zA-Z0-9]+)"\]/; $2 */
+ userec_t rec;
+ char *userid;
+ ChessUser *user =
+ (buf[0] == 'R' ? &info->user1 : &info->user2);
+
+ strtok(buf, "\"");
+ userid = strtok(NULL, "\"");
+ if (userid != NULL && getuser(userid, &rec))
+ chc_init_user_userec(&rec, user);
+ }
+ } else {
+ /* " 1. Ch2-e2 Nb9-c7" */
+ drc_t step = { CHESS_STEP_NORMAL };
+ const char *p = strchr(buf, '.');
+
+ if (p == NULL) continue;
+
+ ++p; /* skip '.' */
+ while(*p && isspace(*p)) ++p;
+ if (!*p) continue;
+
+ /* p -> "Ch2-e2 ...." */
+ step.from.c = p[1] - 'a';
+ step.from.r = BRD_ROW - 1 - (p[2] - '0');
+ step.to.c = p[4] - 'a';
+ step.to.r = BRD_ROW - 1 - (p[5] - '0');
+
+#define INVALID_ROW(R) ((R) < 0 || (R) >= BRD_ROW)
+#define INVALID_COL(C) ((C) < 0 || (C) >= BRD_COL)
+#define INVALID_LOC(S) (INVALID_ROW(S.r) || INVALID_COL(S.c))
+ if (INVALID_LOC(step.from) || INVALID_LOC(step.to))
+ continue;
+ ChessHistoryAppend(info, &step);
+
+ p += 6;
+ while(*p && isspace(*p)) ++p;
+ if (!*p) continue;
+
+ /* p -> "Nb9-c7\n" */
+ step.from.c = p[1] - 'a';
+ step.from.r = BRD_ROW - 1 - (p[2] - '0');
+ step.to.c = p[4] - 'a';
+ step.to.r = BRD_ROW - 1 - (p[5] - '0');
+
+ if (INVALID_LOC(step.from) || INVALID_LOC(step.to))
+ continue;
+ ChessHistoryAppend(info, &step);
+ }
+ }
+
+ info->board = malloc(sizeof(board_t));
+ info->tag = malloc(sizeof(chc_tag_data_t));
+
+ chc_init_board(info->board);
+ ((chc_tag_data_t*) info->tag)->selected = 0;
+
+ return info;
+}
diff --git a/mbbsd/chess.c b/mbbsd/chess.c
index e131459d..b35d386f 100644
--- a/mbbsd/chess.c
+++ b/mbbsd/chess.c
@@ -32,6 +32,16 @@ static const char * const ChessHintStr[] = {
"Enter 選擇/移動"
};
+static const struct {
+ const char* name;
+ int name_len;
+ ChessInfo* (*func)(FILE* fp);
+} ChessReplayMap[] = {
+ { "gomoku", 6, &gomoku_replay },
+ { "chc", 3, &chc_replay },
+ { NULL }
+};
+
static ChessInfo * CurrentPlayingGameInfo;
/* XXX: This is a BAD way to pass information.
@@ -625,6 +635,7 @@ ChessPlayFuncWatch(ChessInfo* info)
sizeof(info->warnmsg));
ChessDrawLine(info, CHESS_DRAWING_WARN_ROW);
+ ChessDrawLine(info, CHESS_DRAWING_STEP_ROW);
move(1, 0);
switch (igetch()) {
@@ -954,7 +965,9 @@ ChessPlay(ChessInfo* info)
}
info->actions->gameend(info, game_result);
- ChessGenLog(info, game_result);
+
+ if (info->mode != CHESS_MODE_REPLAY)
+ ChessGenLog(info, game_result);
currutmp->sig = -1;
Signal(SIGUSR1, old_handler);
@@ -1083,31 +1096,80 @@ ChessWatchGame(void (*play)(int, ChessGameMode), int game, const char* title)
return 0;
}
+int
+ChessReplayGame(const char* fname)
+{
+ ChessInfo *info;
+ FILE *fp = fopen(fname, "r");
+ int found = -1;
+ char buf[256];
+ screen_backup_t oldscreen;
+
+ while (found == -1 && fgets(buf, sizeof(buf), fp)) {
+ if (buf[0] == '<') {
+ const int line_len = strlen(buf);
+ if (strcmp(buf + line_len - 5, "log>\n") == 0) {
+ int i;
+ for (i = 0; ChessReplayMap[i].name; ++i)
+ if (ChessReplayMap[i].name_len == line_len - 6 &&
+ strncmp(buf + 1, ChessReplayMap[i].name,
+ ChessReplayMap[i].name_len) == 0) {
+ found = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if (found == -1) {
+ fclose(fp);
+ return -1;
+ }
+
+ info = ChessReplayMap[found].func(fp);
+ fclose(fp);
+
+ screen_backup(&oldscreen);
+ ChessPlay(info);
+ screen_restore(&oldscreen);
+
+ return 0;
+}
+
static void
ChessInitUser(ChessInfo* info)
{
char userid[2][IDLEN + 1];
const userinfo_t* uinfo;
- 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]));
+ switch (info->mode) {
+ case CHESS_MODE_PERSONAL:
+ strlcpy(userid[0], cuser.userid, sizeof(userid[0]));
+ strlcpy(userid[1], cuser.userid, sizeof(userid[1]));
+ break;
+
+ case CHESS_MODE_WATCH:
+ uinfo = search_ulist_userid(currutmp->mateid);
+ strlcpy(userid[0], uinfo->userid, sizeof(userid[0]));
+ strlcpy(userid[1], uinfo->mateid, sizeof(userid[1]));
+ break;
+
+ case CHESS_MODE_VERSUS:
+ strlcpy(userid[0], cuser.userid, sizeof(userid[0]));
+ strlcpy(userid[1], currutmp->mateid, sizeof(userid[1]));
+ break;
+
+ case CHESS_MODE_REPLAY:
+ return;
}
- else if (info->mode == CHESS_MODE_VERSUS) {
- strlcpy(userid[0], cuser.userid, sizeof(userid[0]));
- strlcpy(userid[1], currutmp->mateid, sizeof(userid[1]));
- } else
- assert_not_reached();
uinfo = search_ulist_userid(userid[0]);
- info->actions->init_user(uinfo, &info->user1);
+ if (uinfo)
+ info->actions->init_user(uinfo, &info->user1);
+
uinfo = search_ulist_userid(userid[1]);
- info->actions->init_user(uinfo, &info->user2);
+ if (uinfo)
+ info->actions->init_user(uinfo, &info->user2);
}
#ifdef CHESSCOUNTRY
@@ -1269,17 +1331,13 @@ ChessInitPlayFunc(ChessInfo* info)
break;
case CHESS_MODE_WATCH:
+ case CHESS_MODE_REPLAY:
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;
}
}
diff --git a/mbbsd/gomo.c b/mbbsd/gomo.c
index 521fdb76..872648e5 100644
--- a/mbbsd/gomo.c
+++ b/mbbsd/gomo.c
@@ -5,7 +5,7 @@
#define QCAST int (*)(const void *, const void *)
#define BOARD_LINE_ON_SCREEN(X) ((X) + 2)
-static const char* turn_color[] = { ANSI_COLOR(30;43), ANSI_COLOR(37;43) };
+static const char* turn_color[] = { ANSI_COLOR(37;43), ANSI_COLOR(30;43) };
enum Turn {
WHT = 0,
@@ -261,6 +261,15 @@ gomo_init_user(const userinfo_t* uinfo, ChessUser* user)
}
static void
+gomo_init_user_userec(const userec_t* urec, ChessUser* user)
+{
+ strlcpy(user->userid, urec->userid, sizeof(user->userid));
+ user->win = urec->five_win;
+ user->lose = urec->five_lose;
+ user->tie = urec->five_tie;
+}
+
+static void
gomo_init_board(board_t board)
{
memset(board, 0xff, sizeof(board_t));
@@ -402,6 +411,9 @@ gomo_gameend(ChessInfo* info, ChessGameResult result)
cuser.five_tie = user1->tie;
passwd_update(usernum, &cuser);
+ } else if (info->mode == CHESS_MODE_REPLAY) {
+ free(info->board);
+ free(info->tag);
}
}
@@ -488,3 +500,65 @@ gomoku_watch(void)
{
return ChessWatchGame(&gomoku, M_FIVE, "五子棋");
}
+
+ChessInfo*
+gomoku_replay(FILE* fp)
+{
+ ChessInfo *info;
+ char buf[256];
+
+ info = NewChessInfo(&gomo_actions, &gomo_constants,
+ 0, CHESS_MODE_REPLAY);
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (strcmp("</gomokulog>\n", buf) == 0)
+ break;
+ else if (strncmp("black:", buf, 6) == 0 ||
+ strncmp("white:", buf, 6) == 0) {
+ /* /(black|white):([a-zA-Z0-9]+)/; $2 */
+ userec_t rec;
+ ChessUser *user = (buf[0] == 'b' ? &info->user1 : &info->user2);
+
+ chomp(buf);
+ if (getuser(buf + 6, &rec))
+ gomo_init_user_userec(&rec, user);
+ } else if (buf[0] == '[') {
+ /* "[ 1]● ==> H8 [ 2]○ ==> H9" *
+ * 012345678901234567890123456789 */
+ gomo_step_t step = { CHESS_STEP_NORMAL, BLK };
+ int c = buf[11] - 'A';
+ int r = BRDSIZ - 1 - (buf[12] - '0');
+
+#define INVALID_ROW(R) ((R) < 0 || (R) >= BRDSIZ)
+#define INVALID_COL(C) ((C) < 0 || (C) >= BRDSIZ)
+ if (INVALID_COL(c) || INVALID_ROW(r))
+ continue;
+
+ step.loc.r = r;
+ step.loc.c = c;
+ ChessHistoryAppend(info, &step);
+
+ if (strlen(buf) < 28)
+ continue;
+
+ c = buf[28] - 'A';
+ r = BRDSIZ - (buf[29] - '0');
+
+ if (INVALID_COL(c) || INVALID_ROW(r))
+ continue;
+
+ step.color = WHT;
+ step.loc.r = r;
+ step.loc.c = c;
+ ChessHistoryAppend(info, &step);
+ }
+ }
+
+ info->board = malloc(sizeof(board_t));
+ info->tag = malloc(sizeof(int));
+
+ gomo_init_board(info->board);
+ *(int*)(info->tag) = 0;
+
+ return info;
+}
diff --git a/mbbsd/pmore.c b/mbbsd/pmore.c
index 3eddc925..7459a6a4 100644
--- a/mbbsd/pmore.c
+++ b/mbbsd/pmore.c
@@ -2352,6 +2352,10 @@ pmore(char *fpath, int promptend)
MFDISP_DIRTY();
break;
#endif
+
+ case 'z':
+ ChessReplayGame(fpath);
+ break;
}
/* DO NOT DO ANYTHING HERE. NOT SAFE RIGHT NOW. */
}
diff --git a/mbbsd/talk.c b/mbbsd/talk.c
index ce7c8efd..3b7beab5 100644
--- a/mbbsd/talk.c
+++ b/mbbsd/talk.c
@@ -1430,7 +1430,6 @@ my_talk(userinfo_t * uin, int fri_stat, char defact)
(!ch && (uin->chatid[0] == 1 || uin->chatid[0] == 3)) ||
uin->lockmode == M_FIVE || uin->lockmode == CHC) {
if (ch == CHC || ch == M_FIVE || ch == CHESSWATCHING) {
- kill(uin->pid, SIGUSR1);
sock = make_connection_to_somebody(uin, 20);
if (sock < 0)
vmsg("無法建立連線");