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