summaryrefslogtreecommitdiffstats
path: root/console/othello.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/othello.c')
-rw-r--r--console/othello.c577
1 files changed, 577 insertions, 0 deletions
diff --git a/console/othello.c b/console/othello.c
new file mode 100644
index 00000000..99dd4794
--- /dev/null
+++ b/console/othello.c
@@ -0,0 +1,577 @@
+/* $Id$ */
+#include "bbs.h"
+
+#define LOGFILE "etc/othello.log"
+#define SECRET "etc/othello.secret"
+#define NR_TABLE 2
+
+#define true 1
+#define false 0
+#define STARTX 3
+#define STARTY 20
+#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 INVERT(COLOR) (((COLOR))==WHITE?BLACK:WHITE)
+
+struct OthelloData {
+ char nowx, nowy;
+ char number[2];
+
+ char pass;
+ char if_hint;
+ int think, which_table;
+
+ char nowboard[10][10];
+ char evaltable[NR_TABLE + 1][10][10];
+};
+
+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 char init_table[NR_TABLE + 1][5][5] = {
+ {{0, 0, 0, 0, 0},
+ {0, 30, -3, 2, 2},
+ {0, -3, -3, -1, -1},
+ {0, 2, -1, 1, 1},
+ {0, 2, -1, 1, 0}},
+
+ {{0, 0, 0, 0, 0},
+ {0, 70, 5, 20, 30},
+ {0, 5, -5, 3, 3},
+ {0, 20, 3, 5, 5},
+ {0, 30, 3, 5, 5}},
+
+ {{0, 0, 0, 0, 0},
+ {0, 5, 2, 2, 2},
+ {0, 2, 1, 1, 1},
+ {0, 2, 1, 1, 1},
+ {0, 2, 1, 1, 1}}
+};
+
+static void
+print_chess(struct OthelloData *od, int x, int y, char chess)
+{
+ move(STARTX - 1 + x * 2, STARTY - 2 + y * 4);
+ if (chess != HINT || od->if_hint == 1)
+ outs(CHESS_TYPE[(int)chess]);
+ else
+ outs(CHESS_TYPE[NONE]);
+ refresh();
+}
+
+static void
+printboard(struct OthelloData *od)
+{
+ int i;
+
+ move(STARTX, STARTY);
+ outs("┌─┬─┬─┬─┬─┬─┬─┬─┐");
+ for (i = 0; i < 7; i++) {
+ move(STARTX + 1 + i * 2, STARTY);
+ outs("│ │ │ │ │ │ │ │ │");
+ move(STARTX + 2 + i * 2, STARTY);
+ outs("├─┼─┼─┼─┼─┼─┼─┼─┤");
+ }
+ move(STARTX + 1 + i * 2, STARTY);
+ outs("│ │ │ │ │ │ │ │ │");
+ move(STARTX + 2 + i * 2, STARTY);
+ outs("└─┴─┴─┴─┴─┴─┴─┴─┘");
+ print_chess(od, 4, 4, WHITE);
+ print_chess(od, 5, 5, WHITE);
+ print_chess(od, 4, 5, BLACK);
+ print_chess(od, 5, 4, BLACK);
+ move(3, 56);
+ prints("(黑)%s", cuser.userid);
+ move(3, 72);
+ outs(": 02");
+ move(4, 56);
+ outs("(白)電腦 : 02");
+ move(6, 56);
+ outs("# 可以下之處");
+ move(7, 56);
+ outs("[q] 退出");
+ move(8, 56);
+ outs("[h] 開啟/關閉 提示");
+ move(9, 56);
+ outs("[Enter][Space] 下棋");
+ move(10, 56);
+ outs("上:↑, i");
+ move(11, 56);
+ outs("下:↓, k");
+ move(12, 56);
+ outs("左:←, j");
+ move(13, 56);
+ outs("右:→, l");
+}
+
+static int
+get_key(struct OthelloData *od, int x, int y)
+{
+ int ch;
+
+ move(STARTX - 1 + x * 2, STARTY - 1 + y * 4);
+ ch = igetch();
+ move(STARTX - 1 + x * 2, STARTY - 2 + y * 4);
+ if (od->nowboard[x][y] != HINT || od->if_hint == 1)
+ outs(CHESS_TYPE[(int)od->nowboard[x][y]]);
+ else
+ outs(CHESS_TYPE[NONE]);
+ return ch;
+}
+
+static int
+eatline(int i, int j, char color, int dir, char chessboard[][10])
+{
+ int tmpx, tmpy;
+ char tmpchess;
+
+ tmpx = i + DIRX[dir];
+ tmpy = j + DIRY[dir];
+ tmpchess = chessboard[tmpx][tmpy];
+ if (tmpchess == -1)
+ return false;
+ if (tmpchess != INVERT(color))
+ return false;
+
+ tmpx += DIRX[dir];
+ tmpy += DIRY[dir];
+ tmpchess = chessboard[tmpx][tmpy];
+ while (tmpchess != -1) {
+ if (tmpchess < BLACK)
+ return false;
+ if (tmpchess == color) {
+ while (i != tmpx || j != tmpy) {
+ chessboard[i][j] = color;
+ i += DIRX[dir];
+ j += DIRY[dir];
+ }
+ return true;
+ }
+ tmpx += DIRX[dir];
+ tmpy += DIRY[dir];
+ tmpchess = chessboard[tmpx][tmpy];
+ }
+ return false;
+}
+
+static int
+if_can_put(int x, int y, char color, char chessboard[][10])
+{
+ int i, temp, checkx, checky;
+
+ if (chessboard[x][y] < BLACK)
+ for (i = 0; i < 8; i++) {
+ checkx = x + DIRX[i];
+ checky = y + DIRY[i];
+ temp = chessboard[checkx][checky];
+ if (temp < BLACK)
+ continue;
+ if (temp != color)
+ while (chessboard[checkx += DIRX[i]][checky += DIRY[i]] > HINT)
+ if (chessboard[checkx][checky] == color)
+ return true;
+ }
+ return false;
+}
+
+static int
+get_hint(struct OthelloData *od, char color)
+{
+ int i, j, temp = 0;
+
+ for (i = 1; i <= 8; i++)
+ for (j = 1; j <= 8; j++) {
+ if (od->nowboard[i][j] == HINT)
+ od->nowboard[i][j] = NONE;
+ if (if_can_put(i, j, color, od->nowboard)) {
+ od->nowboard[i][j] = HINT;
+ temp++;
+ }
+ print_chess(od, i, j, od->nowboard[i][j]);
+ }
+ return temp;
+}
+
+static void
+eat(int x, int y, int color, char chessboard[][10])
+{
+ int k;
+
+ for (k = 0; k < 8; k++)
+ eatline(x, y, color, k, chessboard);
+}
+
+static void
+end_of_game(struct OthelloData *od, int quit)
+{
+ FILE *fp, *fp1;
+ char *opponent[] = {"", "CD-65", "", "嬰兒", "小孩", "", "大人", "專家"};
+
+ move(STARTX - 1, 30);
+ outs(" ");
+ move(22, 35);
+ fp = fopen(LOGFILE, "a");
+ if (!quit) {
+ fp1 = fopen(SECRET, "a");
+ if (fp1) {
+ fprintf(fp1, "%d,%d,%s,%02d,%02d\n", od->think, od->which_table,
+ cuser.userid, od->number[0], od->number[1]);
+ fclose(fp1);
+ }
+ }
+ if (quit) {
+ if (od->number[0] == 2 && od->number[1] == 2) {
+ if (fp)
+ fclose(fp);
+ return;
+ }
+ fprintf(fp, "在%s級中, %s臨陣脫逃\n", opponent[od->think], cuser.userid);
+ if (fp)
+ fclose(fp);
+ return;
+ }
+ if (od->number[0] > od->number[1]) {
+ prints("你贏了電腦%02d子", od->number[0] - od->number[1]);
+ if (od->think == 6 && od->number[0] - od->number[1] >= 50)
+ demoney(200);
+ if (od->think == 7 && od->number[0] - od->number[1] >= 40)
+ demoney(200);
+ if (fp)
+ fprintf(fp, "在%s級中, %s以 %02d:%02d 贏了電腦%02d子\n",
+ opponent[od->think], cuser.userid, od->number[0], od->number[1],
+ od->number[0] - od->number[1]);
+ } else if (od->number[1] > od->number[0]) {
+ prints("電腦贏了你%02d子", od->number[1] - od->number[0]);
+ if (fp) {
+ fprintf(fp, "在%s級中, ", opponent[od->think]);
+ if (od->number[1] - od->number[0] > 20)
+ fprintf(fp, "電腦以 %02d:%02d 慘電%s %02d子\n", od->number[1],
+ od->number[0], cuser.userid, od->number[1] - od->number[0]);
+ else
+ fprintf(fp, "電腦以 %02d:%02d 贏了%s %02d子\n", od->number[1],
+ od->number[0], cuser.userid, od->number[1] - od->number[0]);
+ }
+ } else {
+ outs("你和電腦打成平手!!");
+ if (fp)
+ fprintf(fp, "在%s級中, %s和電腦以 %02d:%02d 打成了平手\n",
+ opponent[od->think], cuser.userid, od->number[1], od->number[0]);
+ }
+ if (fp)
+ fclose(fp);
+ move(1, 1);
+ igetch();
+}
+
+static void
+othello_redraw(struct OthelloData *od)
+{
+ int i, j;
+
+ for (i = 1; i <= 8; i++)
+ for (j = 1; j <= 8; j++)
+ print_chess(od, i, j, od->nowboard[i][j]);
+}
+
+static int
+player(struct OthelloData *od, char color)
+{
+ int ch;
+
+ if (get_hint(od, color)) {
+ while (true) {
+ ch = get_key(od, od->nowx, od->nowy);
+ switch (ch) {
+ case 'J':
+ case 'j':
+ case KEY_LEFT:
+ od->nowy--;
+ break;
+ case 'L':
+ case 'l':
+ case KEY_RIGHT:
+ od->nowy++;
+ break;
+ case 'I':
+ case 'i':
+ case KEY_UP:
+ od->nowx--;
+ break;
+ case 'K':
+ case 'k':
+ case KEY_DOWN:
+ od->nowx++;
+ break;
+ case ' ':
+ case '\r':
+ if (od->nowboard[(int)od->nowx][(int)od->nowy] != HINT)
+ break;
+ od->pass = 0;
+ od->nowboard[(int)od->nowx][(int)od->nowy] = color;
+ eat(od->nowx, od->nowy, color, od->nowboard);
+ print_chess(od, od->nowx, od->nowy, color);
+ return true;
+ case 'q':
+ end_of_game(od, 1);
+ return false;
+ case 'H':
+ case 'h':
+ od->if_hint = od->if_hint ^ 1;
+ othello_redraw(od);
+ break;
+ }
+ if (od->nowx == 9)
+ od->nowx = 1;
+ if (od->nowx == 0)
+ od->nowx = 8;
+ if (od->nowy == 9)
+ od->nowy = 1;
+ if (od->nowy == 0)
+ od->nowy = 8;
+ }
+ } else {
+ od->pass++;
+ if (od->pass == 1) {
+ move(23, 34);
+ outs("你必需放棄這一步!!");
+ igetch();
+ move(28, 23);
+ outs(" ");
+ } else {
+ end_of_game(od,0);
+ return false;
+ }
+ }
+ return 0;
+}
+
+static void
+init(struct OthelloData *od)
+{
+ int i, j, i1, j1;
+
+ memset(od, 0, sizeof(struct OthelloData));
+ od->nowx = 4;
+ od->nowy = 4;
+ od->number[0] = od->number[1] = 2;
+ for (i = 1; i <= 8; i++)
+ for (j = 1; j <= 8; j++) {
+ i1 = 4.5 - abs(4.5 - i);
+ j1 = 4.5 - abs(4.5 - j);
+ od->evaltable[0][i][j] = init_table[0][i1][j1];
+ od->evaltable[1][i][j] = init_table[1][i1][j1];
+ }
+ memset(od->nowboard, NONE, sizeof(od->nowboard));
+ for(i=0;i<10;i++)
+ od->nowboard[i][0]=od->nowboard[0][i]=od->nowboard[i][9]=od->nowboard[9][i]=-1;
+ od->nowboard[4][4] = od->nowboard[5][5] = WHITE;
+ od->nowboard[4][5] = od->nowboard[5][4] = BLACK;
+}
+
+static void
+report(struct OthelloData *od)
+{
+ int i, j;
+
+ od->number[0] = od->number[1] = 0;
+ for (i = 1; i <= 8; i++)
+ for (j = 1; j <= 8; j++)
+ if (od->nowboard[i][j] == BLACK)
+ od->number[0]++;
+ else if (od->nowboard[i][j] == WHITE)
+ od->number[1]++;
+ move(3, 60);
+ outs(cuser.userid);
+ move(3, 72);
+ prints(": %02d", od->number[0]);
+ move(4, 60);
+ prints("電腦 : %02d", od->number[1]);
+}
+
+static int
+EVL(struct OthelloData *od, char chessboard[][10], int color, int table_number)
+{
+ int points = 0, a, b;
+ for (a = 1; a <= 8; a++)
+ for (b = 1; b <= 8; b++)
+ if (chessboard[a][b] > HINT) {
+ if (chessboard[a][b] == BLACK)
+ points += od->evaltable[table_number][a][b];
+ else
+ points -= od->evaltable[table_number][a][b];
+ }
+ return ((color == BLACK) ? points : -points);
+}
+
+static int
+alphabeta(struct OthelloData *od, int alpha, int beta, int level, char chessboard[][10],
+ int thinkstep, int color, int table)
+{
+ int i, j, k, flag = 1;
+ char tempboard[10][10];
+ if (level == thinkstep + 1)
+ return EVL(od, chessboard, (level & 1 ? color : ((color - 2) ^ 1) + 2),
+ table);
+ for (i = 1; i <= 8; i++) {
+ for (j = 1; j <= 8; j++) {
+ if (if_can_put(i, j, color, chessboard)) {
+ flag = 0;
+ memcpy(tempboard, chessboard, sizeof(char) * 100);
+ eat(i, j, color, tempboard);
+
+ k = alphabeta(od, alpha, beta, level + 1, tempboard, thinkstep,
+ ((color - 2) ^ 1) + 2, table);
+ if (((level & 1) && k > alpha))
+ alpha = k;
+ else if (!(level & 1) && k < beta)
+ beta = k;
+ if (alpha >= beta)
+ break;
+ }
+ }
+ }
+ if (flag)
+ return EVL(od, chessboard, color, table);
+ return ((level & 1) ? alpha : beta);
+}
+
+static int
+Computer(struct OthelloData *od, int thinkstep, int table)
+{
+ int i, j, maxi = 0, maxj = 0, level = 1;
+ char chessboard[10][10];
+ int alpha = -10000, k;
+ if ((od->number[0] + od->number[1]) > 44)
+ table = NR_TABLE;
+ for (i = 1; i <= 8; i++)
+ for (j = 1; j <= 8; j++) {
+ if (if_can_put(i, j, WHITE, od->nowboard)) {
+ memcpy(chessboard, od->nowboard, sizeof(char) * 100);
+ eat(i, j, WHITE, chessboard);
+ k = alphabeta(od, alpha, 10000, level + 1, chessboard, thinkstep,
+ BLACK, table);
+ if (k > alpha) {
+ alpha = k;
+ maxi = i;
+ maxj = j;
+ }
+ }
+ }
+ if (alpha != -10000) {
+ eat(maxi, maxj, WHITE, od->nowboard);
+ od->pass = 0;
+ od->nowx = maxi;
+ od->nowy = maxj;
+ } else {
+ move(23, 30);
+ outs("電腦放棄這一步棋!!");
+ od->pass++;
+ if (od->pass == 2) {
+ move(23, 24);
+ outs(" ");
+ end_of_game(od, 0);
+ return false;
+ }
+ igetch();
+ move(23, 24);
+ outs(" ");
+ }
+ return true;
+}
+
+static int
+choose(void)
+{
+ char thinkstep[2];
+
+ move(2, 0);
+ outs("請選擇難度:");
+ move(5, 0);
+ outs("[0] 離開\n");
+ outs("(1) CD-65\n");/* 想 1 步 */
+ outs("(2) 嬰兒\n"); /* 想 3 步 */
+ outs("(3) 小孩\n"); /* 想 4 步 */
+ do {
+ if (getdata(4, 0, "請選擇一個對象和您對打:(1~3)",
+ thinkstep, sizeof(thinkstep), LCECHO) == 0 ||
+ thinkstep[0] == '0')
+ return 0;
+
+ } while (thinkstep[0] < '1' || thinkstep[0] > '3');
+ clear();
+ switch (thinkstep[0]) {
+ case '2':
+ thinkstep[0] = '3';
+ break;
+ case '3':
+ thinkstep[0] = '4';
+ break;
+ default:
+ thinkstep[0] = '1';
+ break;
+ }
+ return atoi(thinkstep);
+}
+
+#define lockreturn0(unmode, state) if(lockutmpmode(unmode, state)) return 0
+
+int
+othello_main(void)
+{
+ struct OthelloData *od;
+
+ lockreturn0(OTHELLO, LOCK_MULTI);
+
+ od=(struct OthelloData*)malloc(sizeof(struct OthelloData));
+ if(od==NULL) {
+ unlockutmpmode();
+ return 0;
+ }
+
+ clear();
+ init(od);
+ od->think = choose();
+ if (!od->think)
+ {
+ unlockutmpmode();
+ free(od);
+ return 0;
+ }
+ showtitle("單人黑白棋", BBSName);
+ printboard(od);
+ od->which_table = random() % NR_TABLE;
+ while (true) {
+ move(STARTX - 1, 30);
+ outs("輪到你下了...");
+ if (!player(od, BLACK))
+ break;
+ report(od);
+ othello_redraw(od);
+ if (od->number[0] + od->number[1] == 64) {
+ end_of_game(od, 0);
+ break;
+ }
+ move(STARTX - 1, 30);
+ outs("電腦思考中...");
+ refresh();
+ if (!Computer(od, od->think, od->which_table))
+ break;
+ report(od);
+ othello_redraw(od);
+ if (od->number[0] + od->number[1] == 64) {
+ end_of_game(od, 0);
+ break;
+ }
+ }
+ more(LOGFILE, YEA);
+ unlockutmpmode();
+ free(od);
+ return 1;
+}