作者hightman@feeling-NOsmthSPAM-org (xtt), 看板: plan
標題[M3] 俄羅斯方塊(雙人版) tetris2.c
時間溫馨小屋 (Thu Jan 2 12:30:36 2003) Updated: 2005/03/20
: talk.c:talk_speak()
#ifdef HAVE_GAME
else if (ch == Ctrl('O'))
{ /* Thor.990219: 呼叫外掛棋盤 */
data[0] = ch;
if (send(fd, data, 1, 0) != 1)
break;
if (DL_func("bin/bwboard.so:vaBWboard", fd, 0) == -2)
break;
}
+ else if (ch == Ctrl('L')) /* Hightman.021229: 俄羅斯方塊對戰 */
+ {
+ data[0] = ch;
+ if (send(fd, data, 1, 0) != 1)
+ break;
+ if (DL_func("bin/tetris2.so:vaTetris", fd, 0) == -2)
+ break;
+ }
#endif
: src/game/Makefile
SO = .... tetris2.so
: src/game/tetris2.c 新增此程式
/*-------------------------------------------------------*/
/* tetris2.c ( NTHU CS MapleBBS Ver 3.10 ) */
/*-------------------------------------------------------*/
/* target : 雙人俄羅斯方塊 */
/* create : 02/12/29 */
/* update : / / */
/* author : hightman.bbs@bbs.hightman.net */
/*-------------------------------------------------------*/
#include "bbs.h"
#ifdef HAVE_GAME
#define BLOCK_CHAR "■"
static int cfd; /* socket number */
typedef struct
{
int ypos;
int xpos;
int style;
int direction;
int last_y;
int last_x;
int last_d;
int newstyle;
int level;
int score;
int lines;
int lines2; /* 送出行數 */
int map[21][12];
} TetrisScreen;
static TetrisScreen ts[2];
/* 每種方塊都是 4*4 的,然後可以轉 4 個方向 */
static int style_x[7][4][4] =
{
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 2, 3, 1, 1, 1, 1, 0, 1, 2, 3, 1, 1, 1, 1,
0, 1, 1, 2, 1, 0, 1, 0, 0, 1, 1, 2, 1, 0, 1, 0,
1, 2, 0, 1, 0, 0, 1, 1, 1, 2, 0, 1, 0, 0, 1, 1,
0, 1, 2, 0, 0, 1, 1, 1, 2, 0, 1, 2, 0, 0, 0, 1,
0, 1, 2, 2, 1, 1, 0, 1, 0, 0, 1, 2, 0, 1, 0, 0,
0, 1, 2, 1, 1, 0, 1, 1, 1, 0, 1, 2, 0, 0, 1, 0
};
static int style_y[7][4][4] =
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
1, 1, 1, 1, 0, 1, 2, 3, 1, 1, 1, 1, 0, 1, 2, 3,
0, 0, 1, 1, 0, 1, 1, 2, 0, 0, 1, 1, 0, 1, 1, 2,
0, 0, 1, 1, 0, 1, 1, 2, 0, 0, 1, 1, 0, 1, 1, 2,
0, 0, 0, 1, 0, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 2,
0, 0, 0, 1, 0, 1, 2, 2, 0, 1, 1, 1, 0, 0, 1, 2,
0, 0, 0, 1, 0, 1, 1, 2, 0, 1, 1, 1, 0, 1, 1, 2
};
static int GameOver; /* 1:I-win 2:I-fail 3:He-quit 4:I-quit 5:unknow-reson 0:not-over */
static int GameTimes; /* 第幾局了? */
static int clocker, delay, gmode;
#define GAME_OVER -5
#define GAME_WAIT -4
#define GAME_START -3
#define GAME_PREP -2
#define GAME_REFRESH -1
#define GAME_ON 0
#define GAME_WIN 1
#define GAME_FAIL 2
#define GAME_IQUIT 3
#define GAME_HQUIT 4
#define GAME_DISCONN 5
static void
map_move(x, y) /* 在遊戲畫面上的相對移動 */
int x, y;
{
move(x + 2, 2 * y);
}
static void
map_show(m) /* 根據 (m: 0-我方 1-對方) 秀出畫面 */
int m;
{
int x, y;
for (x = 0; x < 21; x++)
{
map_move(x, m * 28);
for (y = 0; y < 12; y++)
{
if (ts[m].map[x][y] == 8)
outs("▓");
else if (ts[m].map[x][y])
outs(BLOCK_CHAR);
else
outs(" ");
}
}
}
static void
show_block(f, m) /* 秀出或清除一個方塊組 */
int f, m; /* f=1:秀出 f=0:清除 */
{
int i, b, x, y, d, s;
b = m * 28;
d = ts[m].last_d;
y = ts[m].last_y;
x = ts[m].last_x;
s = ts[m].style;
if (d == -1)
return;
for (i = 0; i < 4; i++)
{
map_move(x + style_x[s][d][i], y + b + style_y[s][d][i]);
if (f)
outs(BLOCK_CHAR);
else
outs(" ");
}
move(1, 0);
}
static void
map_raise(n, m) /* 對方迫使我方地圖上升 */
int n, m;
{
int x, y, k;
if (n < 1)
return;
k = (m + 1) % 2;
for (x = 0; x < 20; x++)
{
for (y = 1; y < 11; y++)
{
ts[k].map[x][y] = ts[k].map[x + n][y];
}
}
ts[k].ypos -= n;
if (ts[k].ypos < 0)
ts[k].ypos = 0;
do
{
for (y = 1; y < 11; y++)
{
x = 20 - n;
ts[k].map[x][y] = ((x * ts[m].score) >> (int)(y / n)) % 3;
}
}
while (--n > 0);
map_show(k);
show_block(1, k);
}
static void
new_block(m) /* 顯示一個新方塊組 */
int m;
{
int i, b, s;
b = m * 12;
s = ts[m].newstyle;
map_move(0, b + 12);
outs(" ");
map_move(1, b + 12);
outs(" ");
for (i = 0; i < 4; i++)
{
map_move(style_x[s][0][i], 12 + b + style_y[s][0][i]);
outs(BLOCK_CHAR);
}
move(1, 0);
}
static void
move_block(m) /* 方塊組的移動 */
int m;
{
show_block(0, m);
ts[m].last_x = ts[m].xpos;
ts[m].last_y = ts[m].ypos;
ts[m].last_d = ts[m].direction;
show_block(1, m);
}
static int
crash(x, y, s, d, m) /* 判斷是否撞到了 */
int x, y, s, d, m;
{
int i;
for (i = 0; i < 4; i++)
{
if (ts[m].map[x + style_x[s][d][i]][y + style_y[s][d][i]])
return 1;
}
return 0;
}
/* 檢查是否有需要消去的 */
static void
check_lines(m)
int m;
{
int y, x, s = 0;
for (y = 0; y < 20; y++)
{
for (x = 1; x < 11; x++)
{
if (ts[m].map[y][x] == 0)
break;
}
if (x == 11)
{
int y2;
/* 消去行上部全部下移 */
for (y2 = y; y2 > 0; y2--)
{
for (x = 1; x < 11; x++)
ts[m].map[y2][x] = ts[m].map[y2 - 1][x];
}
for (x = 1; x < 11; x++)
ts[m].map[0][x] = 0;
ts[m].lines++;
s++;
if (ts[m].lines % 30 == 0)
{
if (m == 0)
{
delay *= 0.9;
bell();
}
ts[m].level++;
}
}
}
y = s;
s = (1 << y) - 1;
if (s > 0)
{
s = s * (10 + ts[m].level) / 10;
ts[m].score += s;
map_show(m);
/* 上升吧 */
if (y > 1)
map_raise(y - 1, m);
/* update lines, score, lines2 */
ts[m].lines2 += (y - 1);
move(9, 27 + m * 24);
prints("%d", ts[m].lines);
move(11, 27 + m * 24);
prints("%d", ts[m].lines2);
move(13, 27 + m * 24);
prints("%d", ts[m].score);
move(15, 27 + m * 24);
prints("%d", ts[m].level);
move(1, 0);
}
}
/* 撞到後的處理 */
static void
arrived(m)
int m;
{
int y, x, i, s, d;
s = ts[m].style;
d = ts[m].direction;
for (i = 0; i < 4; i++)
{
y = ts[m].ypos + style_y[s][d][i];
x = ts[m].xpos + style_x[s][d][i];
ts[m].map[y][x] = s + 1;
}
check_lines(m);
if (m == 0)
GameOver = GAME_REFRESH;
}
static void
play_count(userid, total, win) /* 回傳 userid 的總場數、勝場數 */
char *userid;
int *total, *win;
{
char fpath[64];
int fd;
int grade[32]; /* 保留 32 個 int */
*total = 0;
*win = 0;
usr_fpath(fpath, userid, "bwboard");
if ((fd = open(fpath, O_RDONLY)) >= 0)
{
read(fd, grade, sizeof(grade));
close(fd);
*total = grade[10];
*win = grade[11];
}
}
static void
play_add(win) /* 我的總/勝場數 +1 */
int win;
{
char fpath[64];
int fd;
int grade[32]; /* 保留 32 個 int */
usr_fpath(fpath, cuser.userid, "bwboard");
if ((fd = open(fpath, O_RDWR | O_CREAT, 0600)) >= 0)
{
if (read(fd, grade, sizeof(grade)) != sizeof(grade))
memset(grade, 0, sizeof(grade));
grade[10]++;
if (win)
grade[11]++;
lseek(fd, (off_t) 0, SEEK_SET);
write(fd, grade, sizeof(grade));
close(fd);
}
}
static void
show_record()
{
int myTotal, yourTotal, myWin, yourWin;
/* 取得戰績 */
play_count(cuser.userid, &myTotal, &myWin);
play_count(cutmp->mateid, &yourTotal, &yourWin);
move(1, 0);
clrtoeol();
prints("★%s [%d戰%d勝]", cuser.userid, myTotal, myWin);
move(1, 38);
outs("V.S.");
move(1, 44);
prints("☆%s [%d戰%d勝]", cutmp->mateid, yourTotal, yourWin);
}
/* 遊戲結束原因 */
static char *
over_reason(over)
int over;
{
if (over == GAME_WIN)
return "◆ 我方獲勝!";
else if (over == GAME_FAIL)
return "◆ 我方失敗!";
else if (over == GAME_HQUIT)
return "◆ 對方中斷!";
else if (over == GAME_IQUIT)
return "◆ 我方中斷!";
else
return "◆ 不明原因!(網路問題?)";
}
/* 遊戲結果 */
static void
show_result()
{
/* 記錄戰積,分數多者為勝者 */
play_add(ts[0].score > ts[1].score);
move(2, 0);
clrtobot();
refresh();
prints(" ┌─────────────────────────┐\n");
prints(" │ 俄羅斯方塊對戰結果 第%3d局 │\n", GameTimes);
prints(" │ ========================== │\n");
prints(" ├─────┬─────────┬─────────┤\n");
prints(" │ 用戶名稱 │ [%-13s] │ (%-13s) │\n", cuser.userid, cutmp->mateid);
prints(" ├─────┼─────────┼─────────┤\n");
prints(" │ 總消行數 │ %-13d │ %-13d │\n", ts[0].lines, ts[1].lines);
prints(" ├─────┼─────────┼─────────┤\n");
prints(" │ 遣送行數 │ %-13d │ %-13d │\n", ts[0].lines2, ts[1].lines2);
prints(" ├─────┼─────────┼─────────┤\n");
prints(" │ 本局得分 │ %-13d │ %-13d │\n", ts[0].score, ts[1].score);
prints(" ├─────┼─────────┴─────────┤\n");
prints(" │ 結束原因 │ %-37s│\n", over_reason(GameOver));
prints(" └─────┴───────────────────┘\n");
prints(" 按 <\033[1;32mEnter\033[m> 繼續遊戲, <\033[1;32m^C ^D\033[m> 退出遊戲. ");
delay = GameOver; /* 借用 delay 保存真正 over 原因,以免斷線了仍然要重開 */
GameOver = GAME_WAIT;
add_io(cfd, 60);
}
/* 幫助畫面 */
static void
show_help()
{
move(4, 33);
prints(" 第 %4d 局 ", ++GameTimes);
move(5, 33);
outs("╭─────╮");
move(6, 33);
outs("│ 計 分 牌 │");
move(7, 33);
outs("╰─────╯");
move(8, 26);
outs("---------== 行數 ==---------");
move(9, 26);
outs(" 0 0");
move(10, 26);
outs("---------== 送出 ==---------");
move(11, 26);
outs(" 0 0");
move(12, 26);
outs("---------== 得分 ==---------");
move(13, 26);
outs(" 0 0");
move(14, 26);
outs("---------== 級別 ==---------");
move(15, 26);
outs(" 0 0");
move(16, 26);
outs("---------== 幫助 ==---------");
move(17, 26); /* 12+16 = 28 */
outs("左移 a, ← | 右移 d, →");
move(18, 26);
outs("旋轉 h, ↑ | 下降 s, ↓");
move(19, 26);
outs("快降 x,' ' | 認輸 ^F");
move(20, 26);
outs("中斷 ^C ^D 會扣分的 ..");
move(21, 26);
outs("切換對方屏幕即時模式 ^G");
}
/* 可傳送給對方的指令 */
static int
send_cmd(cmd)
int cmd;
{
int i;
char valid[23] =
{
'a', 'A', 'd', 'D', 'h', 'H', 'k', 'K', ' ', 'n', 'N', 'y', 'Y',
'j', 'J', 's', 'S', 'z', 'Z', 'x', 'X', KEY_ENTER, Ctrl('F')
};
if (cmd == KEY_UP)
cmd = 'h';
else if (cmd == KEY_DOWN)
cmd = 's';
else if (cmd == KEY_LEFT)
cmd = 'a';
else if (cmd == KEY_RIGHT)
cmd = 'd';
for (i = 0; i < 23; i++)
{
if (valid[i] == cmd)
{
char c;
c = cmd;
if (send(cfd, &c, 1, 0) != 1)
{
GameOver = GAME_DISCONN;
return 0;
}
return 1;
}
}
return 0;
}
static int tetris_start();
/* 操作指令集 m為0時若返回0才送出指令給對方 */
static int
tetris_cmd(cmd, m)
int cmd, m;
{
int x, y, s, d;
if (cmd >= '0' && cmd < '7')
{
if (!m)
return -1;
if (GameOver == GAME_START)
{
ts[1].newstyle = cmd - '0';
GameOver = GAME_REFRESH;
return -1;
}
else
{
ts[1].style = ts[1].newstyle;
ts[1].xpos = 3;
ts[1].ypos = 0;
ts[1].direction = 0;
ts[1].newstyle = cmd - '0';
ts[1].last_d = -1;
new_block(1);
move_block(1);
return 0;
}
}
/* game wait to over */
if (GameOver == GAME_WAIT)
{
if (delay >= 0)
{
if (cmd == KEY_ENTER)
{
move(b_lines - 1, 0);
if (m)
outs("對方要求再來一盤,您衕意嗎?[Y/n]");
else
outs("等待對方應答中,請稍候 ... ^c ^d 退出");
delay = -1 - m; /* 等的: -1 回答的: -2 */
}
return 0;
}
if ((delay - m) != -2)
return 0; /* 這時 key 就不算 */
if (cmd == 'n' || cmd == 'N')
delay = 1;
else
delay = 0;
/* disconn 的不會執行到這步 */
GameOver = GAME_OVER;
return -1;
}
if (GameOver != GAME_ON)
return -1;
x = ts[m].xpos;
y = ts[m].ypos;
s = ts[m].style;
d = ts[m].direction;
switch (cmd)
{
case Ctrl('F'):
GameOver = GAME_FAIL - m;
return -1;
case Ctrl('G'):
gmode ^= 1; /* 即時模式切換 */
break;
case 'A':
case 'a':
case KEY_LEFT:
if (!crash(x - 1, y, s, d, m))
{
ts[m].xpos--;
if (!gmode || !m)
move_block(m);
}
break;
case 'D':
case 'd':
case KEY_RIGHT:
if (!crash(x + 1, y, s, d, m))
{
ts[m].xpos++;
if (!gmode || !m)
move_block(m);
}
break;
case 'H':
case 'h':
case KEY_UP:
if (!crash(x, y, s, (d + 3) % 4, m))
{
ts[m].direction = (d + 3) % 4;
if (!gmode || !m)
move_block(m);
}
break;
case 'K':
case 'k':
if (!crash(x, y, s, (d + 1) % 4, m))
{
ts[m].direction = (d + 1) % 4;
if (!gmode || !m)
move_block(m);
}
break;
case 'J':
case 'j':
if (!crash(x, y, s, (d + 2) % 4, m))
{
ts[m].direction = (d + 2) % 4;
if (!gmode || !m)
move_block(m);
}
break;
case 'S':
case 's':
case 'Z':
case 'z':
case KEY_DOWN:
if (m == 0)
clocker = times(0);
if (crash(x, y + 1, s, d, m))
{
if (gmode && m)
move_block(m);
arrived(m);
break;
}
else
{
ts[m].ypos++;
if (!gmode || !m)
move_block(m);
}
break;
case 'x':
case 'X':
case ' ':
while (!crash(x, ts[m].ypos + 1, s, d, m))
ts[m].ypos++;
move_block(m);
arrived(m);
break;
default:
break;
}
return 0;
}
/* 每場比賽的初始化 */
static int
tetris_init()
{
int i;
/* Tetris Screen initilize ... */
memset(ts, 0, sizeof(ts));
for (i = 0; i < 20; i++)
{
ts[0].map[i][0] = 8;
ts[1].map[i][0] = 8;
ts[0].map[i][11] = 8;
ts[1].map[i][11] = 8;
}
for (i = 0; i < 12; i++)
{
ts[0].map[20][i] = 8;
ts[1].map[20][i] = 8;
}
GameOver = GAME_PREP;
delay = 40;
clear();
vs_bar("俄羅斯方塊 - 雙人版");
map_show(0);
map_show(1);
show_record();
show_help();
return 0;
}
/* 一個字符一個字符的送過去 */
static int
tetris_start()
{
uschar data[80];
int i, ch;
tetris_init();
clocker = times(0);
srand(time(0));
add_io(cfd, 0);
for (;;)
{
if (GameOver == GAME_PREP)
{
ts[0].newstyle = rand() % 7;
data[0] = ts[0].newstyle + '0';
if (send(cfd, data, 1, 0) != 1)
{
GameOver = GAME_DISCONN;
continue;
}
GameOver = GAME_START;
outz("對方還在準備中 ... 請稍後 \033[5m ... \033[m");
refresh();
continue;
}
if (GameOver == GAME_REFRESH) /* need new block */
{
ts[0].style = ts[0].newstyle;
ts[0].xpos = 3;
ts[0].ypos = 0;
ts[0].direction = 0;
ts[0].newstyle = rand() % 7;
ts[0].last_d = -1;
data[0] = ts[0].newstyle + '0';
if (send(cfd, data, 1, 0) != 1)
{
GameOver = GAME_DISCONN;
continue;
}
move_block(0);
new_block(0);
if (crash(ts[0].xpos, ts[0].ypos, ts[0].style, ts[0].direction, 0)) /* game over */
{
data[0] = Ctrl('F'); /* 相當於認輸 */
if (send(cfd, data, 1, 0) != 1)
GameOver = GAME_DISCONN; /* 網絡斷線? */
else
GameOver = GAME_FAIL; /* 我方先敗! */
continue;
}
GameOver = GAME_ON; /* 遊戲繼續. */
}
if (GameOver == GAME_OVER)
break; /* 遊戲結束2 */
if (GameOver > 0) /* 遊戲結束1 */
show_result(); /* 此步將 GameOver 設為 GAME_WAIT */
ch = vkey();
/* 斷線了還做其它乾嘛 */
if (GameOver == GAME_WAIT && delay == GAME_DISCONN)
break;
if (ch == I_OTHERDATA) /* 有數據進來 */
{
ch = recv(cfd, data, 1, 0);
if (ch <= 0)
{
GameOver = GAME_DISCONN;
continue;
}
for (i = 0; i < ch; i++)
{
if (data[i] == Ctrl('C') || data[i] == Ctrl('D'))
{
if (GameOver == GAME_WAIT)
GameOver = GAME_OVER;
else
GameOver = GAME_HQUIT;
break;
}
else if (tetris_cmd(data[i], 1) != 0)
break;
}
}
if (ch == Ctrl('C') || ch == Ctrl('D'))
{
/* ^c ^d 是肯定要傳出去的 */
data[0] = ch;
if (send(cfd, data, 1, 0) != 1)
{
GameOver = GAME_DISCONN;/* 網絡斷線? */
continue;
}
if (GameOver == GAME_WAIT)
GameOver = GAME_OVER;
else
GameOver = GAME_IQUIT;
continue;
}
if (GameOver == GAME_START)
continue;
/* 送出所有能送出的鍵 */
/* 送出失敗本地也無需執行了 */
if (send_cmd(ch))
tetris_cmd(ch, 0);
if (GameOver != GAME_ON)
continue;
/* Delay timeout auto down */
if ((times(0) - clocker) > delay)
{
/* 通知對方, 相當於 's' */
data[0] = 's';
if (send(cfd, data, 1, 0) != 1)
{
GameOver = GAME_DISCONN;
continue;
}
clocker = times(0);
if (crash(ts[0].xpos, ts[0].ypos + 1, ts[0].style, ts[0].direction, 0))
arrived(0);
else
{
ts[0].ypos++;
move_block(0);
}
}
}
return GAME_OVER;
}
/* Tetris2 Main */
static int
Tetris2(sock, later)
int sock;
int later;
{
screenline sl[T_LINES];
char c;
cfd = sock;
vs_save(sl);
if (!later)
{
c = vans("您確定要和對方單挑俄羅斯方塊嗎(Y/N)?[Y] ");
if (send(cfd, &c, 1, 0) != 1)
return -2;
vs_restore(sl);
}
else
{
outz("對方要求進入俄羅斯方塊對戰模式, 選擇中請稍候.....");
refresh();
if (recv(cfd, &c, 1, 0) != 1)
return -2;
vs_restore(sl);
}
if (c == 'n')
return -1;
GameTimes = 0;
delay = 0; /* 借用 delay 決定程序是否終止 */
gmode = 0; /* 即時傳輸 */
do
{
tetris_start();
}
while (!delay);
add_io(cfd, 60);
vs_restore(sl);
return 0;
}
#include <stdarg.h>
int
vaTetris(va_list pvar)
{
int sock, later;
sock = va_arg(pvar, int);
later = va_arg(pvar, int);
return Tetris2(sock, later);
}
#endif /* HAVE_GAME */
□ 本文章由 itoc 從 itoc.Dorm-GD2.NCTU.edu.tw 發表