diff options
Diffstat (limited to 'game/km.c')
-rw-r--r-- | game/km.c | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/game/km.c b/game/km.c new file mode 100644 index 0000000..0014c99 --- /dev/null +++ b/game/km.c @@ -0,0 +1,532 @@ +/*-------------------------------------------------------*/ +/* km.c ( NTHU CS MapleBBS Ver 3.10 ) */ +/*-------------------------------------------------------*/ +/* target : KongMing Chess routines */ +/* create : 01/02/08 */ +/* update : 01/05/09 */ +/* author : einstein@bbs.tnfsh.tn.edu.tw */ +/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */ +/*-------------------------------------------------------*/ + + +#include "bbs.h" + + +#ifdef HAVE_GAME + +#define LOG_KM /* 是否提供記錄棋譜的功能 */ +#define RETRACT_CHESS /* 是否提供悔棋功能 */ + + +#if 0 + +棋盤在 etc/game/km 格式如下: + +第一行放總共有幾盤棋盤(三位數),從第三行開始則是一盤一盤的棋譜。 + +TILE_NOUSE 0 表示不能移動的格子 +TILE_BLANK 1 表示空格 +TILE_CHESS 2 表示棋子 + +#123混一宇內 +0 0 2 2 2 0 0 +0 0 2 2 2 0 0 +2 2 2 2 2 2 2 +2 2 2 1 2 2 2 +2 2 2 2 2 2 2 +0 0 2 2 2 0 0 +0 0 2 2 2 0 0 + +#endif + + +enum +{ + KM_XPOS = 5, + KM_YPOS = 5, + MAX_X = 7, /* 要是奇數 */ + MAX_Y = 7, /* 要是奇數 */ + + /* 用 bitwise operators & 來取代 == */ + TILE_NOUSE = 0, /* 不能移動的格子 */ + TILE_BLANK = 1, /* 空格 */ + TILE_CHESS = 2 /* 棋子 */ +}; + + +static int board[MAX_X][MAX_Y]; +static int cx, cy; +static int stage, NUM_TABLE; +static char piece[4][3] = {" ", "○", "●", "☆"}; +static char title[20]; /* 棋譜名稱 */ + +#ifdef RETRACT_CHESS +static int route[MAX_X * MAX_Y][4]; /* 記錄 (fx, fy) -> (tx, ty),悔棋步數不可能超過棋盤大小 */ +static int step; +#endif + + +static void +out_song() +{ + /* itoc.註解: 每句話都弄成一樣長度,就不用 clrtoeol() :p */ + uschar *msg[8] = + { + "您太強了,就是這樣!", + "您怎麼可能想到這一步", + "這真是太神奇了,傑克", + "我不知道該說些什麼了", + "這一著真是天人手筆呀", + "太佩服您了,這樣也行", + "快完成了!加油加油!", + "好的棋譜要告訴站長喔" + }; + move(21, 0); + prints("\033[1;3%dm%s\033[m", time(0) % 7, msg[time(0) % 8]); +} + + +static void +show_board() +{ + int i, j; + + vs_bar("孔明棋"); + move(2, KM_YPOS + MAX_Y - 6); /* 置中顯示棋譜名稱 */ + outs(title); + + for (i = 0; i < MAX_X; i++) + { + for (j = 0; j < MAX_Y; j++) + { + move(KM_XPOS + i, KM_YPOS + j * 2); + outs(piece[board[i][j]]); + } + } + + move(3, 40); + outs("↑↓←→ 方向鍵"); + move(5, 40); + outs("[Enter] 選取/反選取"); + move(7, 40); + outs("Q/q 離開"); + move(9, 40); + outs("h 讀取棋譜範例"); + +#ifdef RETRACT_CHESS + move(11, 40); + outs("r 悔棋"); +#endif + + move(13, 40); + outs("○ 空位"); + move(14, 40); + outs("● 棋子"); + move(15, 40); + outs("☆ 選取"); + + out_song(); + move(KM_XPOS + MAX_X / 2, KM_YPOS + MAX_Y / 2 * 2 + 1); /* 一開始將游標置中 */ +} + + +static int +read_board() +{ + int i, j, count; + FILE *fp; + char buf[40], ans[4]; + + if (!(fp = fopen("etc/game/km", "r"))) + return 0; + + if (stage < 0) /* 第一次進入遊戲 */ + { + fgets(buf, 4, fp); + NUM_TABLE = atoi(buf); /* etc/game/km 第一行記錄棋譜數 */ + sprintf(buf, "請選擇編號 [1-%d],[0] 隨機出題,或按 [Q] 離開:", NUM_TABLE); + if (vget(b_lines, 0, buf, ans, 4, DOECHO) == 'q') + { + fclose(fp); + return 0; + } + + stage = atoi(ans) - 1; + if (stage < 0 || stage >= NUM_TABLE) /* 隨機出題 */ + stage = time(0) % NUM_TABLE; + } + + fseek(fp, 4 + stage * (2 * MAX_X * MAX_Y + 14), SEEK_SET); + /* 4: 第一行的三位數棋譜數目\n 14: \n#999棋譜名稱\n */ + + fscanf(fp, "%s", &title); /* 棋譜名稱 */ + + count = 0; + for (i = 0; i < MAX_X; i++) + { + for (j = 0; j < MAX_Y; j++) + { + fscanf(fp, "%d", &board[i][j]); + if (board[i][j] & TILE_CHESS) + { + count++; + } + } + } + fclose(fp); + return count; +} + + +static inline int +valid_pos(x, y) + int x, y; +{ + if (x < 0 || x >= MAX_X || y < 0 || y >= MAX_Y || + board[x][y] == TILE_NOUSE) /* TILE_NOUSE = 0 不能用 & operation */ + { + return 0; + } + return 1; +} + + +static void +get_pos(x, y) + int *x, *y; +{ + int ch; + while (1) + { + ch = vkey(); + if (ch == KEY_UP && valid_pos(cx - 1, cy)) + { + cx--; + move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1); + } + else if (ch == KEY_DOWN && valid_pos(cx + 1, cy)) + { + cx++; + move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1); + } + else if (ch == KEY_LEFT && valid_pos(cx, cy - 1)) + { + cy--; + move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1); + } + else if (ch == KEY_RIGHT && valid_pos(cx, cy + 1)) + { + cy++; + move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1); + } + else if (ch == 'h') + { + more("etc/game/km.hlp", NULL); + show_board(); + move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1); + } + else if (ch == 'q' || ch == 'Q') + { + vmsg(MSG_QUITGAME); + *x = -1; + break; + } + else if (ch == '\n') + { + *x = cx; + *y = cy; + break; + } +#ifdef RETRACT_CHESS + else if (ch == 'r') + { + *x = -2; + break; + } +#endif + } +} + + +static inline void +jump(fx, fy, tx, ty) + int fx, fy, tx, ty; /* From (fx, fy) To (tx, ty) */ +{ + out_song(); + + board[fx][fy] = TILE_BLANK; + move(KM_XPOS + fx, KM_YPOS + fy * 2); + outs(piece[1]); + + board[(fx + tx) / 2][(fy + ty) / 2] = TILE_BLANK; + move(KM_XPOS + (fx + tx) / 2, KM_YPOS + (fy + ty)); + outs(piece[1]); + + board[tx][ty] = TILE_CHESS; + move(KM_XPOS + tx, KM_YPOS + ty * 2); + outs(piece[2]); + move(KM_XPOS + tx, KM_YPOS + ty * 2 + 1); + +#ifdef RETRACT_CHESS + route[step][0] = fx; + route[step][1] = fy; + route[step][2] = tx; + route[step][3] = ty; + step++; +#endif +} + + +#ifdef RETRACT_CHESS +static inline void +retract() +{ + int fx, fy, tx, ty; + + out_song(); + + step--; + ty = route[step][3]; + tx = route[step][2]; + fy = route[step][1]; + fx = route[step][0]; + + board[tx][ty] = TILE_BLANK; + move(KM_XPOS + tx, KM_YPOS + ty * 2); + outs(piece[1]); + + board[(fx + tx) / 2][(fy + ty) / 2] = TILE_CHESS; + move(KM_XPOS + (fx + tx) / 2, KM_YPOS + (fy + ty)); + outs(piece[2]); + + board[fx][fy] = TILE_CHESS; + move(KM_XPOS + fx, KM_YPOS + fy * 2); + outs(piece[2]); + move(KM_XPOS + fx, KM_YPOS + fy * 2); + cx = fx; + cy = fy; +} +#endif + + +static inline int +check(fx, fy, tx, ty) + int fx, fy, tx, ty; +{ + if ((board[(fx + tx) / 2][(fy + ty) / 2] & TILE_CHESS) && + ((abs(fx - tx) == 2 && fy == ty) || (fx == tx && abs(fy - ty) == 2))) + { + return 1; + } + return 0; +} + + +static inline int +live() +{ + int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1}; + int i, j, k, nx, ny, nx2, ny2; + for (i = 0; i < MAX_X; i++) + { + for (j = 0; j < MAX_Y; j++) + { + for (k = 0; k < 4; k++) + { + nx = i + dir[k][0]; + ny = j + dir[k][1]; + nx2 = nx + dir[k][0]; + ny2 = ny + dir[k][1]; + if (valid_pos(nx2, ny2) && (board[i][j] & TILE_CHESS) && + (board[nx][ny] & TILE_CHESS) && (board[nx2][ny2] & TILE_BLANK)) + { + return 1; + } + } + } + } + return 0; +} + + +#ifdef LOG_KM +static void +log_km(fp) + FILE *fp; +{ + int i, j; + + for (i = 0; i < MAX_X; i++) + { + for (j = 0; j < MAX_Y; j++) + { + fprintf(fp, "%s", piece[board[i][j]]); + } + fprintf(fp, "\n"); + } + fprintf(fp, "\n"); +} +#endif + + +int +main_km() +{ + int fx, fy, tx, ty, count; + +#ifdef LOG_KM + char fpath[64]; + FILE *fp; +#endif + + stage = -1; + +start_game: + + if (!(count = read_board())) + return 0; + +#ifdef LOG_KM + usr_fpath(fpath, cuser.userid, "km.log"); + fp = fopen(fpath, "w"); + fprintf(fp, "%s %s (%s)\n", str_author1, cuser.userid, cuser.username); + fprintf(fp, "標題: 孔明棋譜 %s 破解過程\n時間: %s\n\n", title, Now()); + fprintf(fp, "%s\n\n", title); + log_km(fp); +#endif + +#ifdef RETRACT_CHESS + step = 0; +#endif + + show_board(); + cx = MAX_X / 2; + cy = MAX_Y / 2; + + while (1) + { + if (count == 1 && board[MAX_X / 2][MAX_Y / 2] & TILE_CHESS) + { /* 最後一子要在正中間 */ + vmsg("恭喜您成功\了"); + +#ifdef LOG_KM + ve_banner(fp, 0); + fclose(fp); + + if (vans("您是否要把完成的棋譜保存在信箱中(Y/N)?[Y] ") != 'n') + { + char buf[60]; + + sprintf(buf, "孔明棋譜 %s 破解過程", title); + mail_self(fpath, cuser.userid, buf, MAIL_READ); + } + unlink(fpath); +#endif + + switch (vans("請選擇 (1)繼續下一關 (2)重新挑戰此關 (Q)離開 ?[1] ")) + { + case 'q': + goto abort_game; + case '2': + stage--; + default: + if (++stage >= NUM_TABLE) + stage = 0; + goto start_game; + } + } + if (!live()) + { +#ifdef LOG_KM + unlink(fpath); +#endif + + vmsg("糟糕...沒棋了...@@"); + + switch (vans("請選擇 (1)繼續下一關 (2)重新挑戰此關 (Q)離開 ?[2] ")) + { + case 'q': + goto abort_game; + case '1': + if (++stage >= NUM_TABLE) + stage = 0; + default: + goto start_game; + } + } + + while (1) /* 第一次 */ + { + get_pos(&fx, &fy); + if (fx < 0) + { +#ifdef RETRACT_CHESS + if (fx == -2) + { + if (step) /* 一步都還沒走,不能悔棋 */ + { + retract(); + count++; +#ifdef LOG_KM + fprintf(fp, "悔棋,回到上一步\n"); + log_km(fp); +#endif + } + continue; + } +#endif + goto abort_game; + } + if (!(board[fx][fy] & TILE_CHESS)) + { + continue; + } + else /* 選子 */ + { + move(KM_XPOS + fx, KM_YPOS + fy * 2); + outs(piece[3]); + move(KM_XPOS + fx, KM_YPOS + fy * 2 + 1); + break; + } + } + + while (1) /* 第二次 */ + { + get_pos(&tx, &ty); + if (tx < 0) + { +#ifdef RETRACT_CHESS + if (tx == -2) + { + continue; /* 要取消選子才能悔棋 */ + } +#endif + goto abort_game; + } + if (fx == tx && fy == ty) /* 放棄選子 */ + { + move(KM_XPOS + tx, KM_YPOS + ty * 2); + outs(piece[2]); + move(KM_XPOS + tx, KM_YPOS + ty * 2 + 1); + break; + } + else if (!(board[tx][ty] & TILE_BLANK) || !check(fx, fy, tx, ty)) /* 選跳的地方不能跳 */ + { + continue; + } + else /* 跳到該地方 */ + { + jump(fx, fy, tx, ty); + count--; +#ifdef LOG_KM + log_km(fp); +#endif + break; + } + } + } +abort_game: + return 0; +} + +#endif /* HAVE_GAME */ |