[M3] 俄羅斯方塊(雙人版) tetris2.c

看板Maple (BBS架站)作者時間22年前 (2003/01/02 12:47), 編輯推噓0(001)
留言1則, 1人參與, 最新討論串1/12 (看更多)
::: 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
文章代碼(AID): #-4yHL00 (Maple)
討論串 (同標題文章)
文章代碼(AID): #-4yHL00 (Maple)