[M3] 俄羅斯方塊(單人版) tetris.c
::: src/so/tetris.c :::
說明:程序中的 HAVE_MONEY_ISM 部分是本站用到的錢幣
處理方法,大家可略過或刪除。
-------------以下是程序正文---------------
/* so/tetris.c */
/* 俄羅斯方塊在線游戲 */
/* 原作者: zhch.bbs@bbs.nju.edu.cn */
/* 改寫者: hightman.bbs@bbs.hightman.net */
#include "bbs.h"
#ifdef HAVE_TETRIS
#define HAVE_RECORD // 把最高條數和得分記錄在 attr lib中
#define MAX_MAP_WIDTH 10
#define MAX_MAP_HEIGHT 20
#define FN_TETRIS_REC "game/tetris.top"
#define BLOCK_CHAR "■"
static int last_ypos, last_xpos, last_style, last_dir;
static int game_times, max_lines, max_scores;
#ifdef HAVE_MONEY_ISM
static int win_money;
#endif
static int map[MAX_MAP_HEIGHT + 1][MAX_MAP_WIDTH + 2] = {
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
};
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 my_style, direction, ypos, xpos, number;
static int new_style, my_lines, delay, level, start_time, my_scores;
typedef struct {
char userid[IDLEN + 1];
int lines;
int scores;
} TOP_LIST;
static TOP_LIST top_list[MAX_MAP_HEIGHT];
// --------------------------------------- //
// function start
// --------------------------------------- //
static void
move_map(y, x)
int y, x;
{
move(y + 2, 2 * x);
}
static int
top_cmp(a, b)
TOP_LIST *a, *b;
{
int retval;
retval = b->scores - a->scores;
if (retval == 0)
retval = b->lines - a->lines;
return retval;
}
static void
top_init()
{
int fd, i = 0;
memset(top_list, 0, sizeof(TOP_LIST) * MAX_MAP_HEIGHT);
fd = open(FN_TETRIS_REC, O_RDONLY);
if (fd >= 0)
{
while (read(fd, &top_list[i], sizeof(TOP_LIST)) == sizeof(TOP_LIST))
i++;
close(fd);
}
}
static int
save_top()
{
int fd, wflag, i;
i = 0;
wflag = 1;
memset(top_list, 0, sizeof(TOP_LIST) * MAX_MAP_HEIGHT);
if ((fd = open(FN_TETRIS_REC, O_RDWR | O_CREAT, 0600)) >= 0)
{
f_exlock(fd);
while (read(fd, &top_list[i], sizeof(TOP_LIST)) == sizeof(TOP_LIST))
{
if (!str_cmp(top_list[i].userid, cuser.userid))
{
wflag = 0;
if (top_list[i].scores < max_scores)
{
top_list[i].scores = max_scores;
wflag = 2;
}
if (top_list[i].lines < max_lines)
{
top_list[i].lines = max_lines;
wflag = 2;
}
}
i++;
}
if (wflag == 1)
{
if (i < MAX_MAP_HEIGHT)
{
strcpy(top_list[i].userid, cuser.userid);
top_list[i].scores = max_scores;
top_list[i].lines = max_lines;
i++;
}
else {
if (top_list[i-1].scores < max_scores)
{
strcpy(top_list[i-1].userid, cuser.userid);
top_list[i-1].scores = max_scores;
top_list[i-1].lines = max_lines;
}
else
wflag = 0;
}
}
if (wflag)
{
xsort(top_list, i, sizeof(TOP_LIST), top_cmp);
lseek(fd, 0, SEEK_SET);
wflag = write(fd, top_list, sizeof(TOP_LIST) * i);
ftruncate(fd, sizeof(TOP_LIST) * i);
}
f_unlock(fd);
close(fd);
}
return wflag;
}
#ifdef HAVE_RECORD
static void save_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;
}
if (max_lines > tr.maxlines)
tr.maxlines = max_lines;
if (max_scores > tr.maxscore)
tr.maxscore = max_scores;
attr_put(cuser.userid, ATTR_TETRIS_REC, &tr);
}
#endif
static void
show_welcome()
{
move(2, 0);
prints("歡迎光臨 %s!\n\n", cuser.userid);
prints("鍵盤設置如下: \n");
prints("左: '\033[1;32ma\033[m', '\033[1;32m←\033[m';\n");
prints("右: '\033[1;32md\033[m', '\033[1;32m→\033[m';\n");
prints("下: '\033[1;32ms\033[m', '\033[1;32m↓\033[m';\n");
prints("順時針轉動: '\033[1;32mk\033[m', <\033[1;32m回車\033[m>;");
prints("逆時針轉動: '\033[1;32mh\033[m', '\033[1;32m↑\033[m'; 180度轉動: '\033
[1;32mj\033[m';\n");
prints("快降: '<空格>', 暫停: <\033[1;32m ^S \033[m>.\n");
prints("退出: '\033[1;32m^C ^D\033[m', '\033[1;32m^D\033[m'.\n\n");
prints("每消 \033[1;33m30\033[m 行升一級, 消1行得1分、行3分、三行7分、四行15分!\n");
prints("退出游戲後方能刷新排行榜!! 進入排行榜者有□金挀喔~~^0^");
vmsg(NULL);
}
static void
tetris_init(lines)
int lines;
{
/* init screen */
game_times++;
/* get the rand style and set other parameter */
start_time = time(0);
srand(start_time);
level = lines;
lines = MAX_MAP_HEIGHT - lines;
for (ypos = 0; ypos < MAX_MAP_HEIGHT; ypos++)
{
for (xpos = 1; xpos <= MAX_MAP_WIDTH; xpos++)
{
if (ypos >= lines)
map[ypos][xpos] = rand() % 3;
else
map[ypos][xpos] = 0;
}
}
new_style = rand() % 7;
my_lines = my_scores = 0;
delay = 40;
do
{
delay *= 0.9;
}
while (lines++ < MAX_MAP_HEIGHT);
}
static void
show_map()
{
int y, x;
for (y = 0; y <= MAX_MAP_HEIGHT; y++)
{
move_map(y, 0);
clrtoeol();
for(x = 0; x <= MAX_MAP_WIDTH + 1; x++)
{
if (map[y][x] == 8)
outs("▓");
else if (map[y][x])
outs(BLOCK_CHAR);
else
outs(" ");
}
if (y == 0)
prints(" \033[1;33;44m == %s 本周俄羅斯方塊高手榜 ==\033[m",
str_site);
else if(top_list[y-1].userid[0])
prints(" %2d %-14s %6d 分 %6d 條",
y, top_list[y-1].userid, top_list[y-1].scores, top_list[y-1].lines);
}
}
/* ypost, xpos, style, direction, flag */
static void
show_block(y, x, k, d, f)
int y, x, k, d, f;
{
if (d == -1) return;
for (number = 0; number <= 3; number++)
{
move_map(y + style_y[k][d][number], x + style_x[k][d][number]);
if (f)
outs(BLOCK_CHAR);
else
outs(" ");
}
move(1, 0);
}
static void
move_block()
{
if (last_ypos == ypos && last_xpos == xpos && last_style == my_style && last_dir ==
direction) return;
show_block(last_ypos, last_xpos, last_style, last_dir, 0);
last_ypos = ypos;
last_xpos = xpos;
last_style = my_style;
last_dir = direction;
show_block(last_ypos, last_xpos, last_style, last_dir, 1);
}
/* ypost, xpos, style, direction */
static int
crash(x, y, k, d)
int x, y, k, d;
{
for (number = 0; number <= 3; number++)
if (map[y + style_y[k][d][number]][x + style_x[k][d][number]])
return 1;
return 0;
}
static void
check_lines()
{
int y, x, s = 1;
for (y = 0; y < MAX_MAP_HEIGHT; y++)
{
for (x = 1; x <= MAX_MAP_WIDTH; x++)
{
if (map[y][x] == 0) break;
}
if (x == (MAX_MAP_WIDTH + 1))
{
int y2;
s *= 2;
/* 消去行上部全部下移! */
for (y2 = y; y2 > 0; y2--)
{
for (x = 1; x <= MAX_MAP_WIDTH; x++)
map[y2][x] = map[y2-1][x];
}
for (x = 1; x <= MAX_MAP_WIDTH; x++)
map[0][x] = 0;
if (++my_lines % 30 == 0)
{
delay *= 0.9;
level ++;
bell();
}
}
}
s -= 1;
if (s > 0)
{
s = s * (10 + level) / 10;
my_scores += s;
show_map();
move(b_lines, 0);
clrtoeol();
prints("本局成績: 總消去條數: \033[1;32m%d\033[m , 總得分 \033[1;32m%d\033[m, 等級:
\033[1;32m%d\033[m",
my_lines, my_scores, level);
}
}
static void
arrived()
{
int y, x;
for (number = 0; number <= 3; number++)
{
y = ypos + style_y[my_style][direction][number];
x = xpos + style_x[my_style][direction][number];
map[y][x] = my_style + 1;
}
check_lines();
last_dir = -1;
}
int
p_tetris()
{
int ch, clock;
clear();
utmp_mode(M_TETRIS);
vs_bar("俄羅斯方塊 - 單人版");
show_welcome();
game_times = max_lines = max_scores = 0;
#ifdef HAVE_MONEY_ISM
win_money = 0;
#endif
top_init();
start:
last_dir = -1;
ch = vans("從第幾級別開始玩(0-9)? [0]");
if (ch < '0') ch = 0;
else if (ch > '9') ch = 9;
else ch = ch - 48;
tetris_init(ch);
move(1, 0);
clrtobot();
refresh();
prints("第 \033[1;32m%d\033[m 局", game_times);
show_map();
vmsg("游戲開始!");
move(b_lines, 0);
clrtoeol();
prints("本局成績: 總消去條數: \033[1;32m0\033[m, 總得分: \033[1;32m0\033[m , 等級:
\033[1;32m%d\033[m", level);
add_io(0, 0);
while (1)
{
my_style = new_style;
#if 1
if (!str_cmp(cuser.userid, str_sysop))
new_style = rand() % 2;
else
new_style = rand() % 7;
#else
new_style = rand() % 7;
#endif
direction = 0;
xpos = 3;
ypos = 0;
move_map(0, MAX_MAP_WIDTH + 2);
outs(" ");
move_map(1, MAX_MAP_WIDTH + 2);
outs(" ");
show_block(0, MAX_MAP_WIDTH + 2, new_style, direction, new_style + 1);
move_block();
if (crash(xpos, ypos, my_style, direction)) /* game over */
break;
clock = times(0);
while(1)
{
ch = vkey();
switch(ch) {
case Ctrl('L') :
break;
case Ctrl('S') :
add_io(0, 60);
vmsg("游戲暫停!");
move(b_lines, 0);
clrtoeol();
prints("本局成績: 總消去條數: \033[1;32m%d\033[m, 總得分 \033[1;32m%
d\033[m, 等級: \033[1;32m%d\033[m",
my_lines, my_scores, level);
add_io(0, 0);
break;
case 'A' :
case 'a' :
case KEY_LEFT :
if (!crash(xpos - 1, ypos, my_style, direction))
{
xpos --;
move_block();
}
break;
case 'D' :
case 'd' :
case KEY_RIGHT :
if (!crash(xpos + 1, ypos, my_style, direction))
{
xpos++;
move_block();
}
break;
case 'H' :
case 'h' :
case KEY_UP :
if (!crash(xpos, ypos, my_style, (direction + 3) % 4))
{
direction = (direction + 3) % 4;
move_block();
}
break;
case 'K' :
case 'k' :
case KEY_ENTER :
if (!crash(xpos, ypos, my_style, (direction + 1) % 4))
{
direction = (direction + 1) % 4;
move_block();
}
break;
case 'J' :
case 'j' :
if (!crash(xpos, ypos, my_style, (direction + 2) % 4))
{
direction = (direction + 2) % 4;
move_block();
}
break;
case 'S' :
case 's' :
case 'Z' :
case 'z' :
case KEY_DOWN :
clock = times(0);
if (crash(xpos, ypos + 1, my_style, direction))
{
arrived();
ch = '#';
}
else
{
ypos++;
move_block();
}
break;
case ' ' :
while (!crash(xpos, ypos + 1, my_style, direction)) ypos++;
move_block();
arrived();
ch = '#';
break;
default :
break;
}
if (ch == Ctrl('C') || ch == Ctrl('D') || ch == '#')
break;
/* down auto */
if ((times(0) - clock > delay))
{
clock = times(0);
if (crash(xpos, ypos + 1, my_style, direction))
{
arrived();
break;
}
else {
ypos++;
move_block();
}
}
}
if (ch == Ctrl('C') || ch == Ctrl('D'))
{
break;
}
}
add_io(0, 60);
if (my_scores > max_scores) max_scores = my_scores;
if (my_lines > max_lines) max_lines = my_lines;
#ifdef HAVE_MONEY_ISM
win_money += my_scores * 20;
#endif
ch = vans("本局結束!您還要繼續玩嗎? [Y/n]");
if (ch != 'n' & ch != 'N')
goto start;
#ifdef HAVE_RECORD
save_record();
#endif
#undef HAVE_RECORD
ch = save_top();
if (ch > 0)
{
#ifdef HAVE_MONEY_ISM
win_money <<= 1;
#endif
vmsg("游戲結束,您刷新了您的記錄!");
}
else
vmsg("游戲結束,您未能刷新高手榜!");
#ifdef HAVE_MONEY_ISM
if (win_money > 1000)
{
char buf[128];
ACCT acct;
sprintf(buf, "恭喜!您獲得□金 %d 元。", win_money);
if (acct_load(&acct, cuser.userid) >= 0)
{
acct.money += win_money;
cuser.money = acct.money;
acct_save(&acct);
}
vmsg(buf);
}
#endif
return 0;
}
#endif /* _HAVE_TETRIS_ */
--
※ 來源:.溫馨小屋 bbs.feeling.smth.org.[FROM: lib.zju.edu.cn]
討論串 (同標題文章)
Maple 近期熱門文章
PTT數位生活區 即時熱門文章