[M3] 俄羅斯方塊(雙人版) tetris2.c
::: src/so/tetris2.c :::
說明:由於本程序調用了數學函數 pow() 所以必須在編譯參數加上 -lm
在相關 Makefile 裏
// .o.so: ; ld -s -G $*.o -o $*.so -L../lib -ldao
// 在最後加上 -lm (用到了數學函數 pow)
-------------以下是程序正文---------------
/*-------------------------------------------------------*/
/* tetris2.c ( MapleBBS Ver 3.10 ) */
/*-------------------------------------------------------*/
/* author : hightman.bbs@bbs.hightman.net */
/* target : double tetris fight dynamic link module */
/* create : 02/12/29 */
/* update : / / */
/*-------------------------------------------------------*/
#include "bbs.h"
#ifdef HAVE_TETRIS
#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];
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, iquit 5, unknow resone 0: no 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(y, x)
int y, x;
{
move(y + 2, 2 * x);
}
/* 根據 (m: 0-我方 1-對方) 秀出畫麵 */
static void map_show(m)
int m;
{
int y, x;
for (y = 0; y < 21; y++)
{
map_move(y, m * 28);
for (x = 0; x < 12; x++)
{
if (ts[m].map[y][x] == 8)
outs("▓");
else if (ts[m].map[y][x])
outs(BLOCK_CHAR);
else
outs(" ");
}
}
}
/* 秀出或清除一個方塊組, f: 1-顯示 0-清除 */
static void show_block(f, m)
int f, m;
{
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(y + style_y[s][d][i], x + b + style_x[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 (y = 0; y < 20; y++)
{
for (x = 1; x < 11; x++)
{
ts[k].map[y][x] = ts[k].map[y+n][x];
}
}
ts[k].ypos -= n;
if (ts[k].ypos < 0) ts[k].ypos = 0;
do
{
for (x = 1; x < 11; x++)
{
y = 20 - n;
ts[k].map[y][x] = ((y * ts[m].score) >> (int)(x / 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_y[s][0][i], 12 + b + style_x[s][0][i]);
outs(BLOCK_CHAR);
}
move(1, 0);
}
/* 方塊組的移動 */
static void move_block(m)
int m;
{
show_block(0, m);
ts[m].last_y = ts[m].ypos;
ts[m].last_x = ts[m].xpos;
ts[m].last_d = ts[m].direction;
show_block(1, m);
}
/* 判斷是否撞到了 :p */
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[y + style_y[s][d][i]][x + style_x[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);
// 上升吧 haha :-)
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;
}
#include <math.h>
static void save_record()
{
double f;
int i;
TetrisRecord tr[2];
// 讓勝的人計算就可以嘍
if (GameOver == GAME_FAIL || GameOver == GAME_IQUIT) return;
if (attr_get(cuser.userid, ATTR_TETRIS_REC, &tr[0]) < 0)
{
tr[0].exp = 1000;
tr[0].win = tr[0].total = tr[0].maxlines = tr[0].maxscore = 0;
}
if (attr_get(cutmp->mateid, ATTR_TETRIS_REC, &tr[1]) < 0)
{
tr[1].exp = 1000;
tr[1].win = tr[1].total = tr[1].maxlines = tr[1].maxscore = 0;
}
f = (tr[1].exp - tr[0].exp) / 400;
f = pow(10, f);
f = 1 - 1/(1+f);
i = 40 * f;
tr[0].exp += i;
tr[1].exp -= i;
if (GameOver != GAME_WIN)
tr[1].exp -= 20; // 惡意離線,再扣 20點
tr[0].win ++;
for (i = 0; i < 2; i++)
{
tr[i].total++;
if (ts[i].lines > tr[i].maxlines)
tr[i].maxlines = ts[i].lines;
if (ts[i].score > tr[i].maxscore)
tr[i].maxscore = ts[i].score;
}
attr_put(cuser.userid, ATTR_TETRIS_REC, &tr[0]);
attr_put(cutmp->mateid, ATTR_TETRIS_REC, &tr[1]);
}
static void show_record()
{
TetrisRecord tr;
if (attr_get(cuser.userid, ATTR_TETRIS_REC, &tr) < 0)
{
tr.exp = 1000;
tr.win = tr.total = tr.maxlines = tr.maxscore = 0;
}
move(1, 0);
clrtoeol();
refresh();
prints("★%s [%d戰%d勝 %d點%d級]", cuser.userid, tr.total, tr.win, tr.exp, tr.exp / 100);
move(1, 38);
outs("V.S.");
if (attr_get(cutmp->mateid, ATTR_TETRIS_REC, &tr) < 0)
{
tr.exp = 1000;
tr.win = tr.total = tr.maxlines = tr.maxscore = 0;
}
move(1, 44);
prints("☆%s [%d戰%d勝 %d點%d級]", cutmp->mateid, tr.total, tr.win, tr.exp, tr.exp / 100);
}
/* 游戲結束原因 */
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()
{
save_record();
move(2, 0);
clrtobot();
refresh();
prints("\n ┌─────────────────────────┐\n");
prints(" │ 俄羅斯方塊對戰結果 第%3d局 │\n", GameTimes);
prints(" │ ========================== │\n");
prints(" ├─────┬─────────┬─────────┤\n");
prints(" │ 用戶名稱 │ [%-13s] │ (%-13s) │\n", cutmp->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;32m回車\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;
update_foot();
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) // 有數據進來
{
//if (GameOver == GAME_START) // 衹讀一個字節
ch = recv(cfd, data, 1, 0);
// else
//ch = recv(cfd, data, 79, 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[b_lines + 1];
char c;
cfd = sock;
vs_save(sl);
if(!later)
{
c = vans("您確定要和對方單挑俄羅斯方塊嗎? [Y/n]");
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;
utmp_mode(M_TETRIS2);
sock = va_arg(pvar, int);
later = va_arg(pvar, int);
return Tetris2(sock, later);
}
#endif /* HAVE_TETRIS */
--
※ 來源:.溫馨小屋 bbs.feeling.smth.org.[FROM: lib.zju.edu.cn]
→
05/16 19:23, , 1F
05/16 19:23, 1F
討論串 (同標題文章)
完整討論串 (本文為第 1 之 12 篇):
Maple 近期熱門文章
PTT數位生活區 即時熱門文章