diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2009-08-26 18:47:08 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2009-08-26 18:47:08 +0800 |
commit | 93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc (patch) | |
tree | a30f06e2eff15b42ca47f942923bde375b5c1d6d /mbbsd/ch_reversi.c | |
parent | fd70a4e6e7fc66edaf02664751270576cab74c73 (diff) | |
download | pttbbs-93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc.tar pttbbs-93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc.tar.gz pttbbs-93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc.tar.bz2 pttbbs-93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc.tar.lz pttbbs-93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc.tar.xz pttbbs-93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc.tar.zst pttbbs-93d4cfc9955a16f6dc42ac3f7de55ec398e91bdc.zip |
* change chess source file names to be prefixed with ch_*.
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@4778 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd/ch_reversi.c')
-rw-r--r-- | mbbsd/ch_reversi.c | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/mbbsd/ch_reversi.c b/mbbsd/ch_reversi.c new file mode 100644 index 00000000..1a4a6d1f --- /dev/null +++ b/mbbsd/ch_reversi.c @@ -0,0 +1,514 @@ +/* $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 +}; + +static const ChessConstants reversi_constants = { + sizeof(reversi_step_t), + MAX_TIME, + BRDSIZ, + BRDSIZ, + 0, + "黑白棋", + "photo_reversi", +#ifdef BN_REVERSI_LOG + BN_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(" (" BLACK_CHESS ") %-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(" (" WHITE_CHESS ") %-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 = { .type = CHESS_STEP_SPECIAL, .color = 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 GCC_UNUSED) +{ + 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 GCC_UNUSED) +{ + 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 GCC_UNUSED, ChessGameResult result GCC_UNUSED) +{ + /* nothing to do now + * TODO game record */ +} + +static void +reversi_genlog(ChessInfo* info, FILE* fp, ChessGameResult result GCC_UNUSED) +{ + char buf[ANSILINELEN] = ""; + const int nStep = info->history.used; + int i; + VREFCUR cur; + + cur = vcur_save(); + for (i = 2; i <= 21; i++) + { + move(i, 0); + inansistr(buf, sizeof(buf)-1); + fprintf(fp, "%s\n", buf); + } + vcur_restore(cur); + + 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 = { .type = 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; +} |