summaryrefslogtreecommitdiffstats
path: root/mbbsd/reversi.c
diff options
context:
space:
mode:
authorscw <scw@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2007-05-30 23:42:18 +0800
committerscw <scw@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2007-05-30 23:42:18 +0800
commit715542867ba891acf8c1fd6234f5d6ea2fc285f6 (patch)
tree35727b8664dce3eb0f7421dd0cbb28c6636c672b /mbbsd/reversi.c
parent05df0b0f9445089d03f7a2933875b9c7266eb96e (diff)
downloadpttbbs-715542867ba891acf8c1fd6234f5d6ea2fc285f6.tar
pttbbs-715542867ba891acf8c1fd6234f5d6ea2fc285f6.tar.gz
pttbbs-715542867ba891acf8c1fd6234f5d6ea2fc285f6.tar.bz2
pttbbs-715542867ba891acf8c1fd6234f5d6ea2fc285f6.tar.lz
pttbbs-715542867ba891acf8c1fd6234f5d6ea2fc285f6.tar.xz
pttbbs-715542867ba891acf8c1fd6234f5d6ea2fc285f6.tar.zst
pttbbs-715542867ba891acf8c1fd6234f5d6ea2fc285f6.zip
Reversi (multiplayer othello) added
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@3523 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd/reversi.c')
-rw-r--r--mbbsd/reversi.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/mbbsd/reversi.c b/mbbsd/reversi.c
new file mode 100644
index 00000000..c396ef0a
--- /dev/null
+++ b/mbbsd/reversi.c
@@ -0,0 +1,506 @@
+/* $Id$ */
+
+#include "bbs.h"
+
+#define MAX_TIME (300)
+#define BRDSIZ (8) /* 棋盤單邊大小 */
+
+#define NONE_CHESS " "
+#define WHITE_CHESS "●"
+#define BLACK_CHESS "○"
+#define HINT_CHESS "#"
+#define NONE 0
+#define HINT 1
+#define BLACK 2
+#define WHITE 3
+
+#define STARTY 10
+
+#define INVERT(COLOR) (((COLOR))==WHITE?BLACK:WHITE)
+
+#define IS_BLANK(COLOR) ((COLOR) < BLACK) /* NONE or HINT */
+#define IS_CHESS(COLOR) ((COLOR) >= BLACK)
+#define TURN_TO_COLOR(TURN) (WHITE - (TURN))
+#define COLOR_TO_TURN(COLOR) (WHITE - (COLOR))
+
+typedef char color_t;
+typedef color_t board_t[BRDSIZ + 2][BRDSIZ + 2];
+typedef color_t (*board_p)[BRDSIZ + 2];
+/* [0] & [9] are dummy */
+
+typedef struct {
+ ChessStepType type; /* necessary one */
+ color_t color;
+ rc_t loc;
+} reversi_step_t;
+
+typedef struct {
+ int number[2];
+} reversi_tag_t;
+
+/* chess framework action functions */
+static void reversi_init_user(const userinfo_t *uinfo, ChessUser *user);
+static void reversi_init_user_userec(const userec_t *urec, ChessUser *user);
+static void reversi_init_board(board_t board);
+static void reversi_drawline(const ChessInfo* info, int line);
+static void reversi_movecur(int r, int c);
+static int reversi_prepare_play(ChessInfo* info);
+static int reversi_select(ChessInfo* info, rc_t scrloc, ChessGameResult* result);
+static void reversi_prepare_step(ChessInfo* info, const reversi_step_t* step);
+static ChessGameResult reversi_apply_step(board_t board, const reversi_step_t* step);
+static void reversi_drawstep(ChessInfo* info, const void* move);
+static ChessGameResult reversi_post_game(ChessInfo* info);
+static void reversi_gameend(ChessInfo* info, ChessGameResult result);
+static void reversi_genlog(ChessInfo* info, FILE* fp, ChessGameResult result);
+
+static const char *CHESS_TYPE[] = {NONE_CHESS, HINT_CHESS, BLACK_CHESS, WHITE_CHESS};
+static const char DIRX[] = {-1, -1, -1, 0, 1, 1, 1, 0};
+static const char DIRY[] = {-1, 0, 1, 1, 1, 0, -1, -1};
+
+static const ChessActions reversi_actions = {
+ &reversi_init_user,
+ &reversi_init_user_userec,
+ (void (*) (void*)) &reversi_init_board,
+ &reversi_drawline,
+ &reversi_movecur,
+ &reversi_prepare_play,
+ NULL, /* process_key */
+ &reversi_select,
+ (void (*)(ChessInfo*, const void*)) &reversi_prepare_step,
+ (ChessGameResult (*)(void*, const void*)) &reversi_apply_step,
+ &reversi_drawstep,
+ &reversi_post_game,
+ &reversi_gameend,
+ &reversi_genlog
+};
+
+const static ChessConstants reversi_constants = {
+ sizeof(reversi_step_t),
+ MAX_TIME,
+ BRDSIZ,
+ BRDSIZ,
+ 0,
+ "黑白棋",
+ "photo_reversi",
+#ifdef GLOBAL_REVERSI_LOG
+ GLOBAL_REVERSI_LOG,
+#else
+ NULL,
+#endif
+ { "", "" },
+ { "白棋", "黑棋" },
+};
+
+static int
+can_put(board_t board, color_t who, int x, int y)
+{
+ int i, temp, checkx, checky;
+
+ if (IS_BLANK(board[x][y]))
+ for (i = 0; i < 8; ++i) {
+ checkx = x + DIRX[i];
+ checky = y + DIRY[i];
+ temp = board[checkx][checky];
+ if (IS_BLANK(temp))
+ continue;
+ if (temp != who) {
+ while (board[checkx += DIRX[i]][checky += DIRY[i]] == temp);
+ if (board[checkx][checky] == who)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+caculate_hint(board_t board, color_t who)
+{
+ int i, j, count = 0;
+
+ for (i = 1; i <= 8; i++)
+ for (j = 1; j <= 8; j++) {
+ if (board[i][j] == HINT)
+ board[i][j] = NONE;
+ if (can_put(board, who, i, j)) {
+ board[i][j] = HINT;
+ ++count;
+ }
+ }
+ return count;
+}
+
+static void
+reversi_init_user(const userinfo_t* uinfo, ChessUser* user)
+{
+ strlcpy(user->userid, uinfo->userid, sizeof(user->userid));
+ user->win =
+ user->lose =
+ user->tie = 0;
+}
+
+static void
+reversi_init_user_userec(const userec_t* urec, ChessUser* user)
+{
+ strlcpy(user->userid, urec->userid, sizeof(user->userid));
+ user->win =
+ user->lose =
+ user->tie = 0;
+}
+
+static void
+reversi_init_board(board_t board)
+{
+ memset(board, NONE, sizeof(board_t));
+ board[4][4] = board[5][5] = WHITE;
+ board[4][5] = board[5][4] = BLACK;
+
+ caculate_hint(board, BLACK);
+}
+
+static void
+reversi_drawline(const ChessInfo* info, int line){
+ static const char* num_str[] =
+ {"", "1", "2", "3", "4", "5", "6", "7", "8"};
+ if(line)
+ move(line, STARTY);
+
+ if (line == 0) {
+ 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 == 2)
+ outs(" A B C D E F G H");
+ else if (line == 3)
+ outs("┌─┬─┬─┬─┬─┬─┬─┬─┐");
+ else if (line == 19)
+ outs("└─┴─┴─┴─┴─┴─┴─┴─┘");
+ else if (line == 20)
+ prints(" (黑) %-15s%2d%*s",
+ info->myturn ? info->user1.userid : info->user2.userid,
+ ((reversi_tag_t*)info->tag)->number[COLOR_TO_TURN(BLACK)],
+ 34 - 24, "");
+ else if (line == 21)
+ prints(" (白) %-15s%2d%*s",
+ info->myturn ? info->user2.userid : info->user1.userid,
+ ((reversi_tag_t*)info->tag)->number[COLOR_TO_TURN(WHITE)],
+ 34 - 24, "");
+ else if (line > 3 && line < 19) {
+ if ((line & 1) == 1)
+ outs("├─┼─┼─┼─┼─┼─┼─┼─┤");
+ else {
+ int x = line / 2 - 1;
+ int y;
+ board_p board = (board_p) info->board;
+
+ move(line, STARTY - 2);
+ prints("%s│", num_str[x]);
+ for(y = 1; y <= 8; ++y)
+ prints("%s│", CHESS_TYPE[(int) board[x][y]]);
+ }
+ }
+
+ ChessDrawExtraInfo(info, line, 4);
+}
+
+static void
+reversi_movecur(int r, int c)
+{
+ move(r * 2 + 4, c * 4 + STARTY + 2);
+}
+
+static int
+reversi_prepare_play(ChessInfo* info)
+{
+ int x, y;
+ int result;
+ board_p board = (board_p) info->board;
+ reversi_tag_t* tag = (reversi_tag_t*) info->tag;
+
+ tag->number[0] = tag->number[1] = 0;
+ for(x = 1; x <= 8; ++x)
+ for(y = 1; y <= 8; ++y)
+ if (IS_CHESS(board[x][y]))
+ ++tag->number[COLOR_TO_TURN(board[x][y])];
+
+ result = !caculate_hint(board, TURN_TO_COLOR(info->turn));
+ if (result) {
+ reversi_step_t step = { CHESS_STEP_SPECIAL, TURN_TO_COLOR(info->turn) };
+ if (info->turn == info->myturn) {
+ ChessStepSend(info, &step);
+ ChessHistoryAppend(info, &step);
+ strcpy(info->last_movestr, "你必須放棄這一步!!");
+ } else {
+ ChessStepReceive(info, &step);
+ strcpy(info->last_movestr, "對方必須放棄這一步!!");
+ }
+ }
+
+ ChessRedraw(info);
+ return result;
+}
+
+static int
+reversi_select(ChessInfo* info, rc_t loc, ChessGameResult* result)
+{
+ board_p board = (board_p) info->board;
+
+ ++loc.r; ++loc.c;
+ if (can_put(board, TURN_TO_COLOR(info->turn), loc.r, loc.c)) {
+ reversi_step_t step = { CHESS_STEP_NORMAL,
+ TURN_TO_COLOR(info->turn), loc };
+ reversi_apply_step(board, &step);
+
+ snprintf(info->last_movestr, sizeof(info->last_movestr),
+ "%c%d", step.loc.c - 1 + 'A', step.loc.r);
+
+ ChessStepSend(info, &step);
+ ChessHistoryAppend(info, &step);
+
+ return 1;
+ } else
+ return 0;
+}
+
+static ChessGameResult
+reversi_apply_step(board_t board, const reversi_step_t* step)
+{
+ int i;
+ color_t opposite = INVERT(step->color);
+
+ if (step->type != CHESS_STEP_NORMAL)
+ return CHESS_RESULT_CONTINUE;
+
+ for (i = 0; i < 8; ++i) {
+ int x = step->loc.r;
+ int y = step->loc.c;
+
+ while (board[x += DIRX[i]][y += DIRY[i]] == opposite);
+
+ if (board[x][y] == step->color) {
+ x = step->loc.r;
+ y = step->loc.c;
+
+ while (board[x += DIRX[i]][y += DIRY[i]] == opposite)
+ board[x][y] = step->color;
+ }
+ }
+ board[step->loc.r][step->loc.c] = step->color;
+
+ return CHESS_RESULT_CONTINUE;
+}
+
+static void
+reversi_prepare_step(ChessInfo* info, const reversi_step_t* step)
+{
+ if (step->type == CHESS_STEP_NORMAL)
+ snprintf(info->last_movestr, sizeof(info->last_movestr),
+ "%c%d", step->loc.c - 1 + 'A', step->loc.r);
+ else if (step->color == TURN_TO_COLOR(info->myturn))
+ strcpy(info->last_movestr, "你必須放棄這一步!!");
+ else
+ strcpy(info->last_movestr, "對方必須放棄這一步!!");
+}
+
+static void
+reversi_drawstep(ChessInfo* info, const void* move)
+{
+ ChessRedraw(info);
+}
+
+static ChessGameResult
+reversi_post_game(ChessInfo* info)
+{
+ int x, y;
+ board_p board = (board_p) info->board;
+ reversi_tag_t* tag = (reversi_tag_t*) info->tag;
+
+ tag->number[0] = tag->number[1] = 0;
+ for(x = 1; x <= 8; ++x)
+ for(y = 1; y <= 8; ++y)
+ if (board[x][y] == HINT)
+ board[x][y] = NONE;
+ else if (IS_CHESS(board[x][y]))
+ ++tag->number[COLOR_TO_TURN(board[x][y])];
+
+ ChessRedraw(info);
+
+ if (tag->number[0] == tag->number[1])
+ return CHESS_RESULT_TIE;
+ else if (tag->number[(int) info->myturn] < tag->number[info->myturn ^ 1])
+ return CHESS_RESULT_LOST;
+ else
+ return CHESS_RESULT_WIN;
+}
+
+static void
+reversi_gameend(ChessInfo* info, ChessGameResult result)
+{
+ /* nothing to do now
+ * TODO game record */
+}
+
+static void
+reversi_genlog(ChessInfo* info, FILE* fp, ChessGameResult result)
+{
+ const int nStep = info->history.used;
+ int i;
+
+ for (i = 2; i <= 21; i++)
+ fprintf(fp, "%.*s\n", big_picture[i].len, big_picture[i].data);
+
+ fprintf(fp, "\n");
+ fprintf(fp, "按 z 可進入打譜模式\n");
+ fprintf(fp, "\n");
+
+ fprintf(fp, "<reversilog>\nblack:%s\nwhite:%s\n",
+ info->myturn ? info->user1.userid : info->user2.userid,
+ info->myturn ? info->user2.userid : info->user1.userid);
+
+ for (i = 0; i < nStep; ++i) {
+ const reversi_step_t* const step =
+ (const reversi_step_t*) ChessHistoryRetrieve(info, i);
+ if (step->type == CHESS_STEP_NORMAL)
+ fprintf(fp, "[%2d]%s ==> %c%-5d", i + 1,
+ CHESS_TYPE[(int) step->color],
+ 'A' + step->loc.c - 1, step->loc.r);
+ else
+ fprintf(fp, "[%2d]%s ==> pass ", i + 1,
+ CHESS_TYPE[(int) step->color]);
+ if (i % 2)
+ fputc('\n', fp);
+ }
+
+ if (i % 2)
+ fputc('\n', fp);
+ fputs("</reversilog>\n", fp);
+}
+
+static int
+reversi_loadlog(FILE *fp, ChessInfo *info)
+{
+ char buf[256];
+
+#define INVALID_ROW(R) ((R) <= 0 || (R) > 8)
+#define INVALID_COL(C) ((C) <= 0 || (C) > 8)
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (strcmp("</reversilog>\n", buf) == 0)
+ return 1;
+ 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))
+ reversi_init_user_userec(&rec, user);
+ } else if (buf[0] == '[') {
+ /* "[ 1]● ==> C4 [ 2]○ ==> C5" */
+ reversi_step_t step = { CHESS_STEP_NORMAL };
+ int c, r;
+ const char *p = buf;
+ int i;
+
+ for(i=0; i<2; i++) {
+ p = strchr(p, '>');
+
+ if (p == NULL) break;
+
+ ++p; /* skip '>' */
+ while (*p && isspace(*p)) ++p;
+ if (!*p) break;
+
+ /* i=0, p -> "C4 ..." */
+ /* i=1, p -> "C5\n" */
+
+ if (strncmp(p, "pass", 4) == 0)
+ /* [..] .. => pass */
+ step.type = CHESS_STEP_SPECIAL;
+ else {
+ c = p[0] - 'A' + 1;
+ r = atoi(p + 1);
+
+ if (INVALID_COL(c) || INVALID_ROW(r))
+ break;
+
+ step.loc.r = r;
+ step.loc.c = c;
+ }
+
+ step.color = i==0 ? BLACK : WHITE;
+ ChessHistoryAppend(info, &step);
+ }
+ }
+ }
+#undef INVALID_ROW
+#undef INVALID_COL
+ return 0;
+}
+
+void
+reversi(int s, ChessGameMode mode)
+{
+ ChessInfo* info = NewChessInfo(&reversi_actions, &reversi_constants, s, mode);
+ board_t board;
+ reversi_tag_t tag = { { 2, 2 } }; /* will be overridden */
+
+ reversi_init_board(board);
+
+ info->board = board;
+ info->tag = &tag;
+
+ info->cursor.r = 3;
+ info->cursor.c = 3;
+
+ if (mode == CHESS_MODE_WATCH)
+ setutmpmode(CHESSWATCHING);
+ else
+ setutmpmode(REVERSI);
+ currutmp->sig = SIG_REVERSI;
+
+ ChessPlay(info);
+
+ DeleteChessInfo(info);
+}
+
+int
+reversi_main(void)
+{
+ return ChessStartGame('r', SIG_REVERSI, "黑白棋");
+}
+
+int
+reversi_personal(void)
+{
+ reversi(0, CHESS_MODE_PERSONAL);
+ return 0;
+}
+
+int
+reversi_watch(void)
+{
+ return ChessWatchGame(&reversi, REVERSI, "黑白棋");
+}
+
+ChessInfo*
+reversi_replay(FILE* fp)
+{
+ ChessInfo *info;
+
+ info = NewChessInfo(&reversi_actions, &reversi_constants,
+ 0, CHESS_MODE_REPLAY);
+
+ if(!reversi_loadlog(fp, info)) {
+ DeleteChessInfo(info);
+ return NULL;
+ }
+
+ info->board = malloc(sizeof(board_t));
+ info->tag = malloc(sizeof(reversi_tag_t));
+
+ reversi_init_board(info->board);
+ /* tag will be initialized later */
+
+ return info;
+}