summaryrefslogtreecommitdiffstats
path: root/console/go.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/go.c')
-rw-r--r--console/go.c1118
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) /* ¶}©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);
+
+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«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 = { 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
+}