summaryrefslogtreecommitdiffstats
path: root/mbbsd/othello.c
diff options
context:
space:
mode:
Diffstat (limited to 'mbbsd/othello.c')
-rw-r--r--mbbsd/othello.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/mbbsd/othello.c b/mbbsd/othello.c
new file mode 100644
index 00000000..47b8cef3
--- /dev/null
+++ b/mbbsd/othello.c
@@ -0,0 +1,541 @@
+/* $Id: othello.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern char *BBSName;
+
+#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)
+
+static char nowx = 3, nowy = 3;
+static char *CHESS_TYPE[] = {NONE_CHESS, HINT_CHESS, BLACK_CHESS, WHITE_CHESS};
+static char DIRX[] = {-1,-1,-1, 0, 1, 1, 1, 0};
+static char DIRY[] = {-1, 0, 1, 1, 1, 0,-1,-1};
+static char number[2];
+
+static char pass = 0;
+static char if_hint = 0;
+static int think, which_table;
+
+static char nowboard[10][10]=
+{{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, -1},
+ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
+static 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 char table[NR_TABLE + 1][10][10];
+static void print_chess(int x, int y, char chess) {
+ move(STARTX - 1 + x * 2, STARTY - 2 + y * 4);
+ if(chess != HINT || if_hint == 1)
+ prints(CHESS_TYPE[(int)chess]);
+ else
+ prints(CHESS_TYPE[NONE]);
+ refresh();
+}
+
+extern userec_t cuser;
+
+static void printboard() {
+ int i;
+
+ move(STARTX, STARTY);
+ prints("┌─┬─┬─┬─┬─┬─┬─┬─┐");
+ for(i = 0; i < 7; i++) {
+ move(STARTX + 1 + i * 2, STARTY);
+ prints ("│ │ │ │ │ │ │ │ │");
+ move(STARTX + 2 + i * 2, STARTY);
+ prints ("├─┼─┼─┼─┼─┼─┼─┼─┤");
+ }
+ move(STARTX + 1 + i * 2, STARTY);
+ prints("│ │ │ │ │ │ │ │ │");
+ move(STARTX + 2 + i * 2, STARTY);
+ prints("└─┴─┴─┴─┴─┴─┴─┴─┘");
+ print_chess(4, 4, WHITE);
+ print_chess(5, 5, WHITE);
+ print_chess(4, 5, BLACK);
+ print_chess(5, 4, BLACK);
+ move(3, 56);
+ prints("(黑)%s",cuser.userid);
+ move(3, 72);
+ prints(": 02");
+ move(4, 56);
+ prints("(白)電腦 : 02");
+ move(6, 56);
+ prints("# 可以下之處");
+ move(7, 56);
+ prints("[q] 退出");
+ move(8, 56);
+ prints("[h] 開啟/關閉 提示");
+ move(9,56);
+ prints("[Enter][Space] 下棋");
+ move(10, 56);
+ prints("上:↑, i");
+ move(11, 56);
+ prints("下:↓, k");
+ move(12, 56);
+ prints("左:←, j");
+ move(13, 56);
+ prints("右:→, l");
+}
+
+static int get_key(char nowx, char nowy) {
+ int ch;
+
+ move(STARTX - 1 + nowx * 2, STARTY - 1 + nowy * 4);
+ ch = igetkey();
+ move(STARTX - 1 + nowx * 2, STARTY - 2 + nowy * 4);
+ if(nowboard[(int)nowx][(int)nowy] != HINT || if_hint==1)
+ outs(CHESS_TYPE[(int)nowboard[(int)nowx][(int)nowy]]);
+ 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(char color) {
+ int i, j, temp = 0;
+
+ for(i = 1; i <= 8; i++)
+ for(j = 1; j <= 8; j++) {
+ if(nowboard[i][j] == HINT)
+ nowboard[i][j] = NONE;
+ if(if_can_put(i, j, color, nowboard)) {
+ nowboard[i][j] = HINT;
+ temp++;
+ }
+ print_chess(i, j, 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(int quit) {
+ FILE *fp,*fp1;
+ char *opponent[] = {"","CD-65","","嬰兒","小孩","","大人","專家"};
+
+ move(STARTX - 1, 30);
+ prints (" ");
+ move(22, 35);
+ fp = fopen(LOGFILE, "a");
+ if(!quit) {
+ fp1 = fopen(SECRET, "a");
+ if(fp1) {
+ fprintf(fp1, "%d,%d,%s,%02d,%02d\n", think, which_table,
+ cuser.userid, number[0], number[1]);
+ fclose(fp1);
+ }
+ }
+
+ if(quit) {
+ if(number[0] == 2 && number[1] == 2) {
+ if(fp)
+ fclose(fp);
+ return;
+ }
+ fprintf(fp, "在%s級中, %s臨陣脫逃\n", opponent[think], cuser.userid);
+ if(fp)
+ fclose(fp);
+ return;
+ }
+ if(number[0] > number[1]) {
+ prints("你贏了電腦%02d子", number[0] - number[1]);
+ if(think == 6 && number[0] - number[1] >= 50)
+ demoney(200);
+ if(think == 7 && number[0] - number[1] >= 40)
+ demoney(200);
+ if(fp)
+ fprintf(fp, "在%s級中, %s以 %02d:%02d 贏了電腦%02d子\n",
+ opponent[think], cuser.userid, number[0], number[1],
+ number[0] - number[1]);
+ } else if(number[1] > number[0]) {
+ prints("電腦贏了你%02d子", number[1] - number[0]);
+ if(fp) {
+ fprintf(fp, "在%s級中, ", opponent[think]);
+ if(number[1] - number[0] > 20)
+ fprintf(fp, "電腦以 %02d:%02d 慘電%s %02d子\n", number[1],
+ number[0], cuser.userid, number[1] - number[0]);
+ else
+ fprintf(fp, "電腦以 %02d:%02d 贏了%s %02d子\n", number[1],
+ number[0], cuser.userid, number[1] - number[0]);
+ }
+ } else {
+ prints("你和電腦打成平手!!");
+ if(fp)
+ fprintf(fp, "在%s級中, %s和電腦以 %02d:%02d 打成了平手\n",
+ opponent[think], cuser.userid, number[1], number[0]);
+ }
+ if(fp)
+ fclose(fp);
+ move(1,1);
+ igetkey();
+}
+
+static void othello_redraw() {
+ int i, j;
+
+ for(i = 1; i <= 8; i++)
+ for(j = 1; j <= 8; j++)
+ print_chess(i, j, nowboard[i][j]);
+}
+
+static int player(char color) {
+ int ch;
+
+ if(get_hint(color)) {
+ while(true) {
+ ch = get_key(nowx,nowy);
+ switch(ch) {
+ case 'J':
+ case 'j':
+ case KEY_LEFT:
+ nowy--;
+ break;
+ case 'L':
+ case 'l':
+ case KEY_RIGHT:
+ nowy++;
+ break;
+ case 'I':
+ case 'i':
+ case KEY_UP:
+ nowx--;
+ break;
+ case 'K':
+ case 'k':
+ case KEY_DOWN:
+ nowx++;
+ break;
+ case ' ':
+ case '\r':
+ if(nowboard[(int)nowx][(int)nowy] != HINT)
+ break;
+ pass = 0;
+ nowboard[(int)nowx][(int)nowy] = color;
+ eat(nowx, nowy, color, nowboard);
+ print_chess(nowx, nowy, color);
+ return true;
+ case 'q':
+ end_of_game(1);
+ return false;
+ case 'H':
+ case 'h':
+ if_hint = if_hint^1;
+ othello_redraw();
+ break;
+ }
+ if(nowx == 9)
+ nowx=1;
+ if(nowx == 0)
+ nowx=8;
+ if(nowy == 9)
+ nowy=1;
+ if(nowy == 0)
+ nowy=8;
+ }
+ } else {
+ pass++;
+ if(pass == 1) {
+ move(23, 34);
+ prints("你必需放棄這一步!!");
+ igetch();
+ move(28,23);
+ prints(" ");
+ } else {
+ end_of_game(0);
+ return false;
+ }
+ }
+ return 0;
+}
+
+static void init() {
+ int i, j, i1, j1;
+
+ nowx = 4;
+ nowy = 4;
+ number[0] = 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);
+ table[0][i][j] = init_table[0][i1][j1];
+ table[1][i][j] = init_table[1][i1][j1];
+ }
+ for(i = 1; i <= 8; i++)
+ for(j = 1; j <= 8; j++)
+ nowboard[i][j] = NONE;
+ nowboard[4][4] = nowboard[5][5] = WHITE;
+ nowboard[4][5] = nowboard[5][4] = BLACK;
+}
+
+static void report() {
+ int i, j;
+
+ number[0] = number[1] = 0;
+ for(i = 1; i <= 8; i++)
+ for(j = 1; j <= 8; j++)
+ if(nowboard[i][j] == BLACK)
+ number[0]++;
+ else if(nowboard[i][j] == WHITE)
+ number[1]++;
+ move(3, 60);
+ prints("%s", cuser.userid);
+ move(3, 72);
+ prints(": %02d", number[0]);
+ move(4, 60);
+ prints("電腦 : %02d", number[1]);
+}
+
+static int EVL(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 += table[table_number][a][b];
+ else
+ points -= table[table_number][a][b];
+ }
+ return ((color == BLACK) ? points : -points);
+}
+
+static int alphabeta(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(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(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(chessboard, color, table);
+ return ((level & 1) ? alpha : beta);
+}
+
+static int Computer(int thinkstep, int table) {
+ int i, j, maxi = 0, maxj = 0, level = 1;
+ char chessboard[10][10];
+ int alpha = -10000, k;
+ if((number[0] + 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,nowboard)) {
+ memcpy(chessboard, nowboard, sizeof(char) * 100);
+ eat(i, j, WHITE, chessboard);
+ k = alphabeta(alpha, 10000, level + 1, chessboard, thinkstep,
+ BLACK, table);
+ if(k > alpha) {
+ alpha = k;
+ maxi = i;
+ maxj = j;
+ }
+ }
+ }
+ if(alpha != -10000) {
+ eat(maxi, maxj, WHITE, nowboard);
+ pass = 0;
+ nowx = maxi;
+ nowy = maxj;
+ } else {
+ move(23, 30);
+ prints("電腦放棄這一步棋!!");
+ pass++;
+ if(pass == 2) {
+ move(23, 24);
+ prints(" ");
+ end_of_game(0);
+ return false;
+ }
+ igetch();
+ move(23, 24);
+ prints(" ");
+ }
+ return true;
+}
+
+static int choose() {
+ char thinkstep[2];
+
+ move(2, 0);
+ prints("請選擇難度:");
+ move(5, 0);
+ prints("(1) CD-65\n"); /* 想 1 步 */
+ prints("(2) 嬰兒\n"); /* 想 3 步 */
+ prints("(3) 小孩\n"); /* 想 4 步 */
+ do {
+ getdata(4, 0, "請選擇一個對象和您對打:(1~5)", thinkstep, 2, LCECHO);
+ } 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() {
+ lockreturn0(OTHELLO, LOCK_MULTI);
+ clear();
+ init();
+ think = choose();
+ showtitle("黑白棋", BBSName);
+ printboard();
+ which_table = rand() % NR_TABLE;
+ while(true) {
+ move(STARTX - 1, 30);
+ prints("輪到你下了...");
+ if(!player(BLACK))
+ break;
+ report();
+ othello_redraw();
+ if(number[0] + number[1] == 64) {
+ end_of_game(0);
+ break;
+ }
+ move(STARTX - 1, 30);
+ prints("電腦思考中...");
+ refresh();
+ if(!Computer(think, which_table))
+ break;
+ report();
+ othello_redraw();
+ if(number[0] + number[1] == 64) {
+ end_of_game(0);
+ break;
+ }
+ }
+ more(LOGFILE, YEA);
+ unlockutmpmode();
+ return 1;
+}