diff options
Diffstat (limited to 'console/go.c')
-rw-r--r-- | console/go.c | 1118 |
1 files changed, 1118 insertions, 0 deletions
diff --git a/console/go.c b/console/go.c new file mode 100644 index 00000000..44cf9cc8 --- /dev/null +++ b/console/go.c @@ -0,0 +1,1118 @@ +/* $Id$ */ + +#include "bbs.h" +#include <sys/socket.h> + +#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) /* ¶}©lp¦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); + +const static 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 +}; + +const static ChessConstants go_constants = { + sizeof(go_step_t), + MAX_TIME, + BRDSIZ, + BRDSIZ, + 1, + "³ò´Ñ", + "photo_go", +#ifdef GLOBAL_GOCHESS_LOG + GLOBAL_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) +{ + const static 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) +{ + const static 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[]) +{ + const static char* const ColName = "¢Ï¢Ð¢Ñ¢Ò¢Ó¢Ô¢Õ¢Ö¢Ø¢Ù¢Ú¢Û¢Ü¢Ý¢Þ¢ß¢à¢á¢â"; + const static char* const RawName = "19181716151413121110¢¸¢·¢¶¢µ¢´¢³¢²¢±¢°"; + const static 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) +{ + const static char* const BoardPic[] = { + "ùÝ", "ùç", "ùç", "ùß", + "ùò", "¢q", "¢q", "ùô", + "ùò", "¢q", "¡Ï", "ùô", + "ùã", "ùí", "ùí", "ùå", + }; + const static 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«Kpºâ³Ó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 = { CHESS_STEP_SPECIAL, 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 = { CHESS_STEP_SPECIAL, 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) +{ + 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_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++; + } + + cuser.go_win = user1->win; + cuser.go_lose = user1->lose; + cuser.go_tie = user1->tie; + + passwd_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) +{ + const static char ColName[] = "ABCDEFGHJKLMNOPQRST"; + const int nStep = info->history.used; + char buf[ANSILINELEN] = ""; + int i, x, y; + int sethand = 0; + + if (nStep > 0) { + const go_step_t* const step = + (const go_step_t*) ChessHistoryRetrieve(info, 0); + if (step->color == SETHAND) + sethand = step->loc.r; + } + + getyx(&y, &x); + for (i = 1; i <= 22; i++) + { + move(i, 0); + inansistr(buf, sizeof(buf)-1); + fprintf(fp, "%s\n", buf); + } + move(y, x); + + 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<golog>\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<golog>\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; + + 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, "<golog>\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 +} |