/* $Id$ */ #include "bbs.h" #define LOGFILE "etc/othello.log" #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; char *opponent[] = {"", "CD-65", "", "嬰兒", "小孩", "", "大人", "專家"}; move(STARTX - 1, 30); outs(" "); move(22, 35); fp = fopen(LOGFILE, "a"); 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 KEY_ENTER: 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; }