此程序配合M3写作,在两人聊天时用功能键调用此函数。
所以这里没有关于 socket 的东西。
这版基本上是FB的东西,只是顺便贴出来供有兴趣人士
参考。:p
游戏规则参考联众写法。
其它更多文件请参看 czz 温新小屋(cn-bbs.org) 的maplePlan版
::: 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 */
--
FROM lib.zju.edu.cn