/* $Id$ */ #include "bbs.h" #include #define BBLANK (-1) /* ªÅ¥Õ */ #define BWHITE (0) /* ¥Õ¤l, «á¤â */ #define BBLACK (1) /* ¶Â¤l, ¥ý¤â */ #define LWHITE (2) /* ¥ÕªÅ */ #define LBLACK (3) /* ¶ÂªÅ */ /* only used for communicating */ #define SETHAND (5) /* Åý¤l */ #define CLEAN (6) /* ²M°£¦º¤l */ #define UNCLEAN (7) /* ²M°£¿ù¤l¡A­«·s¨Ó¹L*/ #define CLEANDONE (8) /* ¶}©l­p¦a */ #define MAX_TIME (300) #define BOARD_LINE_ON_SCREEN(X) ((X) + 2) #define BRDSIZ (19) /* ´Ñ½L³æÃä¤j¤p */ static const char* turn_color[] = { ANSI_COLOR(37;43), ANSI_COLOR(30;43) }; static const rc_t SetHandPoints[] = { /* 1 */ { 0, 0}, /* 2 */ { 3, 3}, {15, 15}, /* 3 */ { 3, 3}, { 3, 15}, {15, 15}, /* 4 */ { 3, 3}, { 3, 15}, {15, 3}, {15, 15}, /* 5 */ { 3, 3}, { 3, 15}, { 9, 9}, {15, 3}, {15, 15}, /* 6 */ { 3, 3}, { 3, 15}, { 9, 3}, { 9, 15}, {15, 3}, {15, 15}, /* 7 */ { 3, 3}, { 3, 15}, { 9, 3}, { 9, 9}, { 9, 15}, {15, 3}, {15, 15}, /* 8 */ { 3, 3}, { 3, 9}, { 3, 15}, { 9, 3}, { 9, 15}, {15, 3}, {15, 9}, {15, 15}, /* 9 */ { 3, 3}, { 3, 9}, { 3, 15}, { 9, 3}, { 9, 9}, { 9, 15}, {15, 3}, {15, 9}, {15, 15}, }; typedef char board_t[BRDSIZ][BRDSIZ]; typedef char (*board_p)[BRDSIZ]; typedef struct { ChessStepType type; /* necessary one */ int color; rc_t loc; } go_step_t; #define RC_T_EQ(X,Y) ((X).r == (Y).r && (X).c == (Y).c) typedef struct { board_t backup_board; char game_end; char clean_end; /* bit 1 => I, bit 2 => he */ char need_redraw; float feed_back; /* ¶KÁÙ */ int eaten[2]; int backup_eaten[2]; rc_t forbidden[2]; /* ¥´§T¤§¸T¤â */ } go_tag_t; #define GET_TAG(INFO) ((go_tag_t*)(INFO)->tag) static char * const locE = "ABCDEFGHJKLMNOPQRST"; static void go_init_user(const userinfo_t* uinfo, ChessUser* user); static void go_init_user_userec(const userec_t* urec, ChessUser* user); static void go_init_board(board_t board); static void go_drawline(const ChessInfo* info, int line); static void go_movecur(int r, int c); static int go_prepare_play(ChessInfo* info); static int go_process_key(ChessInfo* info, int key, ChessGameResult* result); static int go_select(ChessInfo* info, rc_t location, ChessGameResult* result); static void go_prepare_step(ChessInfo* info, const go_step_t* step); static ChessGameResult go_apply_step(board_t board, const go_step_t* step); static void go_drawstep(ChessInfo* info, const go_step_t* step); static ChessGameResult go_post_game(ChessInfo* info); static void go_gameend(ChessInfo* info, ChessGameResult result); static void go_genlog(ChessInfo* info, FILE* fp, ChessGameResult result); static const ChessActions go_actions = { &go_init_user, &go_init_user_userec, (void (*)(void*)) &go_init_board, &go_drawline, &go_movecur, &go_prepare_play, &go_process_key, &go_select, (void (*)(ChessInfo*, const void*)) &go_prepare_step, (ChessGameResult (*)(void*, const void*)) &go_apply_step, (void (*)(ChessInfo*, const void*)) &go_drawstep, &go_post_game, &go_gameend, &go_genlog }; static const ChessConstants go_constants = { sizeof(go_step_t), MAX_TIME, BRDSIZ, BRDSIZ, 1, "³ò´Ñ", "photo_go", #ifdef BN_GOCHESS_LOG BN_GOCHESS_LOG, #else NULL, #endif { ANSI_COLOR(37;43), ANSI_COLOR(30;43) }, { "¥Õ´Ñ", "¶Â´Ñ" }, }; static void go_sethand(board_t board, int n) { if (n >= 2 && n <= 9) { const int lower = n * (n - 1) / 2; const int upper = lower + n; int i; for (i = lower; i < upper; ++i) board[SetHandPoints[i].r][SetHandPoints[i].c] = BBLACK; } } /* ­pºâ¬Y¤lªº®ð¼Æ, recursion part of go_countlib() */ static int go_count(board_t board, board_t mark, int x, int y, int color) { static const int diff[][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; int i; int total = 0; mark[x][y] = 0; for (i = 0; i < 4; ++i) { int xx = x + diff[i][0]; int yy = y + diff[i][1]; if (xx >= 0 && xx < BRDSIZ && yy >= 0 && yy < BRDSIZ) { if (board[xx][yy] == BBLANK && mark[xx][yy]) { ++total; mark[xx][yy] = 0; } else if (board[xx][yy] == color && mark[xx][yy]) total += go_count(board, mark, xx, yy, color); } } return total; } /* ­pºâ¬Y¤lªº®ð¼Æ */ static int go_countlib(board_t board, int x, int y, char color) { int i, j; board_t mark; for (i = 0; i < BRDSIZ; i++) for (j = 0; j < BRDSIZ; j++) mark[i][j] = 1; return go_count(board, mark, x, y, color); } /* ­pºâ½L­±¤W¨C­Ó¤lªº®ð¼Æ */ static void go_eval(board_t board, int lib[][BRDSIZ], char color) { int i, j; for (i = 0; i < 19; i++) for (j = 0; j < 19; j++) if (board[i][j] == color) lib[i][j] = go_countlib(board, i, j, color); } /* Àˬd¤@¨B¬O§_¦Xªk */ static int go_check(ChessInfo* info, const go_step_t* step) { board_p board = (board_p) info->board; int lib = go_countlib(board, step->loc.r, step->loc.c, step->color); if (lib == 0) { int i, j; int board_lib[BRDSIZ][BRDSIZ]; go_tag_t* tag = (go_tag_t*) info->tag; board[step->loc.r][step->loc.c] = step->color; go_eval(board, board_lib, !step->color); board[step->loc.r][step->loc.c] = BBLANK; /* restore to open */ lib = 0; for (i = 0; i < BRDSIZ; i++) for (j = 0; j < BRDSIZ; j++) if (board[i][j] == !step->color && !board_lib[i][j]) ++lib; if (lib == 0 || (lib == 1 && RC_T_EQ(step->loc, tag->forbidden[step->color]))) return 0; else return 1; } else return 1; } /* Clean up the dead chess of color `color,' summarize number of * eaten chesses and set the forbidden point. * * `info' might be NULL which means no forbidden point check is * needed and don't have to count the number of eaten chesses. * * Return: 1 if any chess of color `color' was eaten; 0 otherwise. */ static int go_examboard(board_t board, int color, ChessInfo* info) { int i, j, n; int lib[BRDSIZ][BRDSIZ]; rc_t dummy_rc; rc_t *forbidden; int dummy_eaten; int *eaten; if (info) { go_tag_t* tag = (go_tag_t*) info->tag; forbidden = &tag->forbidden[color]; eaten = &tag->eaten[!color]; } else { forbidden = &dummy_rc; eaten = &dummy_eaten; } go_eval(board, lib, color); forbidden->r = -1; forbidden->c = -1; n = 0; for (i = 0; i < BRDSIZ; i++) for (j = 0; j < BRDSIZ; j++) if (board[i][j] == color && lib[i][j] == 0) { board[i][j] = BBLANK; forbidden->r = i; forbidden->c = j; ++*eaten; ++n; } if ( n != 1 ) { /* No or more than one chess were eaten, * no forbidden points, then. */ forbidden->r = -1; forbidden->c = -1; } return (n > 0); } static int go_clean(board_t board, int mark[][BRDSIZ], int x, int y, int color) { static const int diff[][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; int i; int total = 1; mark[x][y] = 0; board[x][y] = BBLANK; for (i = 0; i < 4; ++i) { int xx = x + diff[i][0]; int yy = y + diff[i][1]; if (xx >= 0 && xx < BRDSIZ && yy >= 0 && yy < BRDSIZ) { if ((board[xx][yy] == color) && mark[xx][yy]) total += go_clean(board, mark, xx, yy, color); } } return total; } static int go_cleandead(board_t board, int x, int y) { int mark[BRDSIZ][BRDSIZ]; int i, j; if (board[x][y] == BBLANK) return 0; for (i = 0; i < BRDSIZ; i++) for (j = 0; j < BRDSIZ; j++) mark[i][j] = 1; return go_clean(board, mark, x, y, board[x][y]); } static int go_findcolor(board_p board, int x, int y) { int k, result = 0, color[4]; if (board[x][y] != BBLANK) return BBLANK; if (x > 0) { k = x; do --k; while ((board[k][y] == BBLANK) && (k > 0)); color[0] = board[k][y]; } else color[0] = board[x][y]; if (x < 18) { k = x; do ++k; while ((board[k][y] == BBLANK) && (k < 18)); color[1] = board[k][y]; } else color[1] = board[x][y]; if (y > 0) { k = y; do --k; while ((board[x][k] == BBLANK) && (k > 0)); color[2] = board[x][k]; } else color[2] = board[x][y]; if (y < 18) { k = y; do ++k; while ((board[x][k] == BBLANK) && (k < 18)); color[3] = board[x][k]; } else color[3] = board[x][y]; for (k = 0; k < 4; k++) { if (color[k] == BBLANK) continue; else { result = color[k]; break; } } if (k == 4) return BBLANK; for (k = 0; k < 4; k++) { if ((color[k] != BBLANK) && (color[k] != result)) return BBLANK; } return result; } static int go_result(ChessInfo* info) { int i, j; int count[2]; board_p board = (board_p) info->board; go_tag_t *tag = (go_tag_t*) info->tag; board_t result_board; memcpy(result_board, board, sizeof(result_board)); count[0] = count[1] = 0; for (i = 0; i < 19; i++) for (j = 0; j < 19; j++) if (board[i][j] == BBLANK) { int result = go_findcolor(board, i, j); if (result != BBLANK) { count[result]++; /* BWHITE => LWHITE, BBLACK => LBLACK */ result_board[i][j] = result + 2; } } else count[(int) board[i][j]]++; memcpy(board, result_board, sizeof(result_board)); /* ¦º¤l¦^¶ñ */ count[0] -= tag->eaten[1]; count[1] -= tag->eaten[0]; tag->eaten[0] = count[0]; tag->eaten[1] = count[1]; if (tag->feed_back < 0.01 && tag->eaten[0] == tag->eaten[1]) return BBLANK; /* tie */ else return tag->eaten[0] + tag->feed_back > tag->eaten[1] ? BWHITE : BBLACK; } static char* go_getstep(const go_step_t* step, char buf[]) { static const char* const ColName = "¢Ï¢Ð¢Ñ¢Ò¢Ó¢Ô¢Õ¢Ö¢Ø¢Ù¢Ú¢Û¢Ü¢Ý¢Þ¢ß¢à¢á¢â"; static const char* const RawName = "19181716151413121110¢¸¢·¢¶¢µ¢´¢³¢²¢±¢°"; static const int ansi_length = sizeof(ANSI_COLOR(30;43)) - 1; strcpy(buf, turn_color[step->color]); buf[ansi_length ] = ColName[step->loc.c * 2]; buf[ansi_length + 1] = ColName[step->loc.c * 2 + 1]; buf[ansi_length + 2] = RawName[step->loc.r * 2]; buf[ansi_length + 3] = RawName[step->loc.r * 2 + 1]; strcpy(buf + ansi_length + 4, ANSI_RESET " "); return buf; } static void go_init_tag(go_tag_t* tag) { tag->game_end = 0; tag->need_redraw = 0; tag->feed_back = 5.5; tag->eaten[0] = 0; tag->eaten[1] = 0; tag->forbidden[0].r = -1; tag->forbidden[0].c = -1; tag->forbidden[1].r = -1; tag->forbidden[1].c = -1; } static void go_init_user(const userinfo_t* uinfo, ChessUser* user) { strlcpy(user->userid, uinfo->userid, sizeof(user->userid)); user->win = uinfo->go_win; user->lose = uinfo->go_lose; user->tie = uinfo->go_tie; } static void go_init_user_userec(const userec_t* urec, ChessUser* user) { strlcpy(user->userid, urec->userid, sizeof(user->userid)); user->win = urec->go_win; user->lose = urec->go_lose; user->tie = urec->go_tie; } static void go_init_board(board_t board) { memset(board, BBLANK, sizeof(board_t)); } static void go_drawline(const ChessInfo* info, int line) { static const char* const BoardPic[] = { "ùÝ", "ùç", "ùç", "ùß", "ùò", "¢q", "¢q", "ùô", "ùò", "¢q", "¡Ï", "ùô", "ùã", "ùí", "ùí", "ùå", }; static const int BoardPicIndex[] = { 0, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 3 }; board_p board = (board_p) info->board; go_tag_t* tag = (go_tag_t*) info->tag; 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 == 1) { outs(" A B C D E F G H J K L M N O P Q R S T"); } else if (line >= 2 && line <= 20) { const int board_line = line - 2; const char* const* const pics = &BoardPic[BoardPicIndex[board_line] * 4]; int i; prints("%2d" ANSI_COLOR(30;43), 21 - line); for (i = 0; i < BRDSIZ; ++i) if (board[board_line][i] == BBLANK) outs(pics[BoardPicIndex[i]]); else outs(bw_chess[(int) board[board_line][i]]); outs(ANSI_RESET); } else if (line >= 21 && line < b_lines) prints("%40s", ""); else if (line == b_lines) { if (info->mode == CHESS_MODE_VERSUS || info->mode == CHESS_MODE_PERSONAL) { if (tag->game_end) outs(ANSI_COLOR(31;47) "(w)" ANSI_COLOR(30) "­p¦a" ANSI_RESET); else if (info->history.used == 0 && (info->myturn == BWHITE || info->mode == CHESS_MODE_PERSONAL)) outs(ANSI_COLOR(31;47) "(x)" ANSI_COLOR(30) "±Â¤l" ANSI_RESET); } } if (line == 1 || line == 2) { int color = line - 1; /* BWHITE or BBLACK */ if (tag->game_end && tag->clean_end == 3) prints(" " ANSI_COLOR(30;43) "%s" ANSI_RESET " ¤è¤lªÅ¡G%3.1f", bw_chess[color], tag->eaten[color] + (color == BWHITE ? tag->feed_back : 0.0)); else prints(" " ANSI_COLOR(30;43) "%s" ANSI_RESET " ¤è´£¤l¼Æ¡G%3d", bw_chess[color], tag->eaten[color]); } else ChessDrawExtraInfo(info, line, 3); } static void go_movecur(int r, int c) { move(r + 2, c * 2 + 3); } static int go_prepare_play(ChessInfo* info) { if (((go_tag_t*) info->tag)->game_end) { strlcpy(info->warnmsg, "½Ð²M°£¦º¤l¡A¥H«K­pºâ³Ó­t", sizeof(info->warnmsg)); if (info->last_movestr[0] != ' ') strcpy(info->last_movestr, " "); } if (info->history.used == 1) ChessDrawLine(info, b_lines); /* clear the 'x' instruction */ return 0; } static int go_process_key(ChessInfo* info, int key, ChessGameResult* result) { go_tag_t* tag = (go_tag_t*) info->tag; if (tag->game_end) { if (key == 'w') { if (!(tag->clean_end & 1)) { go_step_t step = { .type = CHESS_STEP_SPECIAL, .color = CLEANDONE }; ChessStepSend(info, &step); tag->clean_end |= 1; } if (tag->clean_end & 2 || info->mode == CHESS_MODE_PERSONAL) { /* both sides agree */ int winner = go_result(info); tag->clean_end = 3; if (winner == BBLANK) *result = CHESS_RESULT_TIE; else *result = (winner == info->myturn ? CHESS_RESULT_WIN : CHESS_RESULT_LOST); ChessRedraw(info); return 1; } } else if (key == 'u') { char buf[4]; getdata(b_lines, 0, "¬O§_¯uªº­n­«·sÂI¦º¤l? (y/N)", buf, sizeof(buf), DOECHO); ChessDrawLine(info, b_lines); if (buf[0] == 'y' || buf[0] == 'Y') { go_step_t step = { .type = CHESS_STEP_SPECIAL, .color = UNCLEAN }; ChessStepSend(info, &step); memcpy(info->board, tag->backup_board, sizeof(tag->backup_board)); tag->eaten[0] = tag->backup_eaten[0]; tag->eaten[1] = tag->backup_eaten[1]; } } } else if (key == 'x' && info->history.used == 0 && ((info->mode == CHESS_MODE_VERSUS && info->myturn == BWHITE) || info->mode == CHESS_MODE_PERSONAL)) { char buf[4]; int n; getdata(22, 43, "­n±Â¦h¤Ö¤l©O(2 - 9)¡H ", buf, sizeof(buf), DOECHO); n = atoi(buf); if (n >= 2 && n <= 9) { go_step_t step = { CHESS_STEP_NORMAL, SETHAND, {n, 0} }; ChessStepSend(info, &step); ChessHistoryAppend(info, &step); go_sethand(info->board, n); ((go_tag_t*)info->tag)->feed_back = 0.0; snprintf(info->last_movestr, sizeof(info->last_movestr), ANSI_COLOR(1) "±Â %d ¤l" ANSI_RESET, n); ChessRedraw(info); return 1; } else ChessDrawLine(info, 22); } return 0; } static int go_select(ChessInfo* info, rc_t location, ChessGameResult* result GCC_UNUSED) { board_p board = (board_p) info->board; if (GET_TAG(info)->game_end) { go_step_t step = { CHESS_STEP_SPECIAL, CLEAN, location }; if (board[location.r][location.c] == BBLANK) return 0; GET_TAG(info)->eaten[!board[location.r][location.c]] += go_cleandead(board, location.r, location.c); ChessStepSend(info, &step); ChessRedraw(info); return 0; /* don't have to return from ChessPlayFuncMy() */ } else { go_step_t step = { CHESS_STEP_NORMAL, info->turn, location }; if (board[location.r][location.c] != BBLANK) return 0; if (go_check(info, &step)) { board[location.r][location.c] = info->turn; ChessStepSend(info, &step); ChessHistoryAppend(info, &step); go_getstep(&step, info->last_movestr); if (go_examboard(board, !info->myturn, info)) ChessRedraw(info); else ChessDrawLine(info, BOARD_LINE_ON_SCREEN(location.r)); return 1; } else return 0; } } static void go_prepare_step(ChessInfo* info, const go_step_t* step) { go_tag_t* tag = GET_TAG(info); if (tag->game_end) { /* some actions need tag so are done here */ if (step->color == CLEAN) { board_p board = (board_p) info->board; tag->eaten[!board[step->loc.r][step->loc.c]] += go_cleandead(board, step->loc.r, step->loc.c); } else if (step->color == UNCLEAN) { memcpy(info->board, tag->backup_board, sizeof(tag->backup_board)); tag->eaten[0] = tag->backup_eaten[0]; tag->eaten[1] = tag->backup_eaten[1]; } else if (step->color == CLEANDONE) { if (tag->clean_end & 1) { /* both sides agree */ int winner = go_result(info); tag->clean_end = 3; if (winner == BBLANK) ((go_step_t*)step)->loc.r = (int) CHESS_RESULT_TIE; else ((go_step_t*)step)->loc.r = (int) (winner == info->myturn ? CHESS_RESULT_WIN : CHESS_RESULT_LOST); ChessRedraw(info); } else { ((go_step_t*)step)->color = BBLANK; /* tricks apply */ tag->clean_end |= 2; } } } else if (step->type == CHESS_STEP_NORMAL) { if (step->color != SETHAND) { go_getstep(step, info->last_movestr); memcpy(tag->backup_board, info->board, sizeof(board_t)); tag->backup_board[step->loc.r][step->loc.c] = step->color; /* if any chess was eaten, wholely redraw is needed */ tag->need_redraw = go_examboard(tag->backup_board, !step->color, info); } else { snprintf(info->last_movestr, sizeof(info->last_movestr), ANSI_COLOR(1) "±Â %d ¤l" ANSI_RESET, step->loc.r); tag->need_redraw = 1; ((go_tag_t*)info->tag)->feed_back = 0.0; } } else if (step->type == CHESS_STEP_PASS) strcpy(info->last_movestr, "µê¤â"); } static ChessGameResult go_apply_step(board_t board, const go_step_t* step) { if (step->type != CHESS_STEP_NORMAL) return CHESS_RESULT_CONTINUE; switch (step->color) { case BWHITE: case BBLACK: board[step->loc.r][step->loc.c] = step->color; go_examboard(board, !step->color, NULL); break; case SETHAND: go_sethand(board, step->loc.r); break; case CLEAN: go_cleandead(board, step->loc.r, step->loc.c); break; case CLEANDONE: /* should be agreed by both sides, [see go_prepare_step()] */ return (ChessGameResult) step->loc.r; } return CHESS_RESULT_CONTINUE; } static void go_drawstep(ChessInfo* info, const go_step_t* step) { go_tag_t* tag = GET_TAG(info); if (tag->game_end || tag->need_redraw) ChessRedraw(info); else ChessDrawLine(info, BOARD_LINE_ON_SCREEN(step->loc.r)); } static ChessGameResult go_post_game(ChessInfo* info) { extern ChessGameResult ChessPlayFuncMy(ChessInfo* info); go_tag_t *tag = (go_tag_t*) info->tag; ChessTimeLimit *orig_limit = info->timelimit; ChessGameResult result; info->timelimit = NULL; info->turn = info->myturn; strcpy(info->warnmsg, "½ÐÂI°£¦º¤l"); ChessDrawLine(info, CHESS_DRAWING_WARN_ROW); memcpy(tag->backup_board, info->board, sizeof(tag->backup_board)); tag->game_end = 1; tag->clean_end = 0; tag->backup_eaten[0] = tag->eaten[0]; tag->backup_eaten[1] = tag->eaten[1]; ChessDrawLine(info, b_lines); /* 'w' instruction */ while ((result = ChessPlayFuncMy(info)) == CHESS_RESULT_CONTINUE); ChessRedraw(info); info->timelimit = orig_limit; return result; } static void go_usr_put(userec_t* userec, const ChessUser* user) { userec->go_win = user->win; userec->go_lose = user->lose; userec->go_tie = user->tie; } static void go_gameend(ChessInfo* info, ChessGameResult result) { if (info->mode == CHESS_MODE_VERSUS) { ChessUser* const user1 = &info->user1; /* ChessUser* const user2 = &info->user2; */ user1->lose--; if (result == CHESS_RESULT_WIN) { user1->win++; currutmp->go_win++; } else if (result == CHESS_RESULT_LOST) { user1->lose++; currutmp->go_lose++; } else { user1->tie++; currutmp->go_tie++; } go_usr_put(&cuser, user1); passwd_sync_update(usernum, &cuser); } else if (info->mode == CHESS_MODE_REPLAY) { free(info->board); free(info->tag); } } static void go_genlog(ChessInfo* info, FILE* fp, ChessGameResult result GCC_UNUSED) { static const char ColName[] = "ABCDEFGHJKLMNOPQRST"; const int nStep = info->history.used; char buf[ANSILINELEN] = ""; int i, sethand = 0; VREFCUR cur; if (nStep > 0) { const go_step_t* const step = (const go_step_t*) ChessHistoryRetrieve(info, 0); if (step->color == SETHAND) sethand = step->loc.r; } cur = vcur_save(); for (i = 1; i <= 22; i++) { move(i, 0); inansistr(buf, sizeof(buf)-1); fprintf(fp, "%s\n", buf); } vcur_restore(cur); fprintf(fp, "\n"); fprintf(fp, "«ö z ¥i¶i¤J¥´ÃмҦ¡\n"); fprintf(fp, "\n"); if (sethand) { fprintf(fp, "[ 1] ±Â %d ¤l\n ", sethand); i = 1; } else i = 0; for (; i < nStep; ++i) { const go_step_t* const step = (const go_step_t*) ChessHistoryRetrieve(info, i); if (step->type == CHESS_STEP_NORMAL) fprintf(fp, "[%3d]%s => %c%-4d", i + 1, bw_chess[step->color], ColName[step->loc.c], 19 - step->loc.r); else if (step->type == CHESS_STEP_PASS) fprintf(fp, "[%3d]%s => µê¤â ", i + 1, bw_chess[(i + 1) % 2]); else break; if (i % 5 == 4) fputc('\n', fp); } fprintf(fp, "\n\n¡m¥H¤U¬° sgf ®æ¦¡´ÑÃСn\n\n(;GM[1]" "GN[%s-%s(W) Ptt]\n" "SZ[19]HA[%d]PB[%s]PW[%s]\n" "PC[FPG BBS/Ptt BBS: ptt.cc]\n", info->user1.userid, info->user2.userid, sethand, info->user1.userid, info->user2.userid); if (sethand) { const int lower = sethand * (sethand - 1) / 2; const int upper = lower + sethand; int j; fputs("AB", fp); for (j = lower; j < upper; ++j) fprintf(fp, "[%c%c]", SetHandPoints[j].c + 'a', SetHandPoints[j].r + 'a'); fputc('\n', fp); } for (i = (sethand ? 1 : 0); i < nStep; ++i) { const go_step_t* const step = (const go_step_t*) ChessHistoryRetrieve(info, i); if (step->type == CHESS_STEP_NORMAL) fprintf(fp, ";%c[%c%c]", step->color == BWHITE ? 'W' : 'B', step->loc.c + 'a', step->loc.r + 'a'); else if (step->type == CHESS_STEP_PASS) fprintf(fp, ";%c[] ", i % 2 ? 'W' : 'B'); else break; if (i % 10 == 9) fputc('\n', fp); } fprintf(fp, ";)\n\n\n"); } void gochess(int s, ChessGameMode mode) { ChessInfo* info = NewChessInfo(&go_actions, &go_constants, s, mode); board_t board; go_tag_t tag; go_init_board(board); go_init_tag(&tag); info->board = board; info->tag = &tag; info->cursor.r = 9; info->cursor.c = 9; // copied from gomo.c if (info->mode == CHESS_MODE_VERSUS) { /* Assume that info->user1 is me. */ info->user1.lose++; passwd_sync_query(usernum, &cuser); go_usr_put(&cuser, &info->user1); passwd_sync_update(usernum, &cuser); } if (mode == CHESS_MODE_WATCH) setutmpmode(CHESSWATCHING); else setutmpmode(UMODE_GO); currutmp->sig = SIG_GO; ChessPlay(info); DeleteChessInfo(info); } int gochess_main(void) { return ChessStartGame('g', SIG_GO, "³ò´Ñ"); } int gochess_personal(void) { gochess(0, CHESS_MODE_PERSONAL); return 0; } int gochess_watch(void) { return ChessWatchGame(&gochess, UMODE_GO, "³ò´Ñ"); } static int mygetc(FILE* fp, char* buf, int* idx, int len) { for (;;) { while (buf[*idx] && isspace(buf[*idx])) ++*idx; if (buf[*idx]) { ++*idx; return buf[*idx - 1]; } if (fgets(buf, len, fp) == NULL) return EOF; if (strcmp(buf, "\n") == 0) return EOF; *idx = 0; } } ChessInfo* gochess_replay(FILE* fp) { ChessInfo *info; int ch; char userid[2][IDLEN + 1] = { "", "" }; char sethand_str[4] = ""; char *recording = NULL; char *record_end = NULL; go_step_t step; /* for mygetc */ char buf[512] = ""; int idx = 0; #define GETC() mygetc(fp, buf, &idx, sizeof(buf)) /* sgf file started with "(;" */ if (GETC() != '(' || GETC() != ';') return NULL; /* header info */ while ((ch = GETC()) != EOF && ch != ';') { if (ch == '[') { if (recording) { while ((ch = GETC()) != EOF && ch != ']') if (recording < record_end) *recording++ = ch; *recording = 0; recording = NULL; } else while ((ch = GETC()) != EOF && ch != ']') continue; if (ch == EOF) break; } else if (ch == ';') /* next stage */ break; else { int ch2 = GETC(); if (ch2 == EOF) { ch = EOF; break; } if (ch == 'P') { if (ch2 == 'B') { recording = userid[BBLACK]; record_end = userid[BBLACK] + IDLEN; } else if (ch2 == 'W') { recording = userid[BWHITE]; record_end = userid[BWHITE] + IDLEN; } } else if (ch == 'H') { if (ch2 == 'A') { recording = sethand_str; record_end = sethand_str + sizeof(sethand_str) - 1; } } } } if (ch == EOF) return NULL; info = NewChessInfo(&go_actions, &go_constants, 0, CHESS_MODE_REPLAY); /* filling header information to info */ if (userid[BBLANK][0]) { userec_t rec; if (getuser(userid[BBLANK], &rec)) go_init_user_userec(&rec, &info->user1); } if (userid[BWHITE][0]) { userec_t rec; if (getuser(userid[BWHITE], &rec)) go_init_user_userec(&rec, &info->user2); } if (sethand_str[0]) { int sethand = atoi(sethand_str); if (sethand >= 2 && sethand <= 9) { step.type = CHESS_STEP_NORMAL; step.color = SETHAND; step.loc.r = sethand; ChessHistoryAppend(info, &step); } } /* steps, ends with ")" */ while ((ch = GETC()) != EOF && ch != ')') { if (ch == ';') ChessHistoryAppend(info, &step); else if (ch == 'B') step.color = BBLACK; else if (ch == 'W') step.color = BWHITE; else if (ch == '[') { ch = GETC(); if (ch == EOF) break; else if (ch == ']') { step.type = CHESS_STEP_PASS; continue; } else step.loc.c = ch - 'a'; ch = GETC(); if (ch == EOF) break; else if (ch == ']') { step.type = CHESS_STEP_PASS; continue; } else step.loc.r = ch - 'a'; while ((ch = GETC()) != EOF && ch != ']'); if (step.loc.r < 0 || step.loc.r >= BRDSIZ || step.loc.c < 0 || step.loc.c >= BRDSIZ) step.type = CHESS_STEP_PASS; else step.type = CHESS_STEP_NORMAL; } } info->board = malloc(sizeof(board_t)); info->tag = malloc(sizeof(go_tag_t)); go_init_board(info->board); go_init_tag(info->tag); return info; #undef GETC }