/* $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;
}