看板 C_and_CPP 關於我們 聯絡資訊
開發平台(Platform): (Ex: VC++, GCC, Linux, ...) Mac OS (g++) 額外使用到的函數庫(Library Used): (Ex: OpenGL, ...) ncurses.h 問題(Question): 目前寫的貪食蛇練習題,可以分別讓蛇自動前進及按上下左右鍵盤前進 但嘗試想把兩個功能合併,卻疑似發生阻斷,已經爬文過但還不知道怎麼解 在下面的程式碼有highlight,求解 餵入的資料(Input): 預期的正確結果(Expected Output): 錯誤結果(Wrong Output): 程式碼(Code):(請善用置底文網頁, 記得排版) #include <iostream> #include <ncurses.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <termios.h> #include <fcntl.h> using namespace std; //int move(int y , int x); #define Initial_Length 5 #define width 50 #define hieght 20 typedef struct snack{ int x; int y; }Snack; Snack Sbody[1000]; Snack Newbody; Snack Food; void Show_Bulletinboard() { mvaddstr(2,55,"Press Enter or Space to start."); mvaddstr(6,55,"Press P or p to pause."); mvaddstr(10,55,"Press to arrow key to move."); //加一個狀態欄位 //mvaddch(10,55,'\x18\x19\x1a\x1b'); mvaddstr(14,55,"Grade:"); mvaddstr(18,55,"Speed:"); } void Initial_Snack_Position() { for (int i = 0; i < Initial_Length ; i++) { Sbody[i].y = 2; Sbody[i].x = Initial_Length + 1 - (i); } } void Print_Snack(int *Length) { for (int i = 0; i < *Length ; i++) { if (i == 0) { //改變頭的顏色 mvaddstr(Sbody[i].y, Sbody[i].x , "@"); //printf("\033[33m@"); } else { mvaddstr(Sbody[i].y, Sbody[i].x , "@"); } } } void Print_Dot(int Pos_y, int Pos_x, char ch ) { mvaddch(Pos_y, Pos_x , ch); //printf("\033[33m@"); } void Initial_GameZone() { for (int j = 1 ; j <= hieght ; j++) { for (int i = 1; i <= width ; i++) { if (j == 1 || j == hieght) { Print_Dot(j,i,'#'); } else if(i == 1 || i == width) { Print_Dot(j,i,'#'); } } } } void Change_Snack_Position(int * Length) { for(int i = *Length -1 ; i >= 0 ; i--) { if (i == 0) //移動頭 { Sbody[i].x = Newbody.x; Sbody[i].y = Newbody.y; Print_Dot(Sbody[i].y , Sbody[i].x , '@'); } else if (i == *Length -1) //清除尾巴 { Print_Dot(Sbody[i].y, Sbody[i].x, ' '); Sbody[i].x = Sbody[i-1].x; Sbody[i].y = Sbody[i-1].y; } else{ //移動身體 Sbody[i].x = Sbody[i-1].x; Sbody[i].y = Sbody[i-1].y; Print_Dot(Sbody[i].y , Sbody[i].x, '@'); } } } void Create_Food(int * Length) //產生食物的位置 { int count = 0; srand(time(NULL)); do { Food.x = (rand() % (width-2) ) +2; Food.y = (rand() % (hieght-2)) +2; for (int i = 0; i < *Length ; i++) { if (Sbody[i].x != Food.x && Sbody[i].y != Food.y) { count = count + 1; } } }while ( count == *Length); Print_Dot(Food.y , Food.x , '*'); } void isEaten(int *Length) //判斷是否吃到食物 { if (Sbody[0].x == Food.x && Sbody[0].y == Food.y) { *Length = *Length + 1; //當吃到新的果實,長出一節,位置在食物的位置 Sbody[*Length].x = Food.x; Sbody[*Length].y = Food.y; Print_Dot(Sbody[*Length].y,Sbody[*Length].x , '@'); Create_Food( Length ); } } bool isGameOver(int * Length) { bool flag = true; for (int i = 1 ; i < *Length ; i++) //頭碰到身體 { if (Sbody[0].x == Sbody[i].x && Sbody[0].y == Sbody[i].y) { flag = false; } } for (int n = 1 ; n <= hieght ; n++) //頭碰到牆 { for (int m = 1; m <= width ; m++) { if (n == 1 || n == hieght) { if (Sbody[0].x == m && Sbody[0].y == n) { flag = false; } } else if(m == 1 || m == width) { if (Sbody[0].x == m && Sbody[0].y == n) { flag = false; } } } } return flag; } int kbhit(void) { struct termios oldt, newt; int ch; int oldf; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF) { ungetc(ch, stdin); return 1; } return 0; } int main(void) { WINDOW * w; //產生視窗 w=initscr(); keypad(stdscr,TRUE); //啟動鍵盤特殊按鍵功能 int GameKey =1; noecho(); int Length = Initial_Length; Initial_GameZone(); //產生遊戲邊 筐 Show_Bulletinboard(); Initial_Snack_Position(); Print_Snack(&Length); Create_Food(&Length); while (GameKey != 27) { if ( kbhit() ) { GameKey = getch(); //根據鍵盤上下左右間決定移動方向 switch(GameKey) { case KEY_UP: Newbody.y = Sbody[0].y -1; Newbody.x = Sbody[0].x; setbuf(stdin,NULL); break; case KEY_DOWN: Newbody.y = Sbody[0].y +1; Newbody.x = Sbody[0].x; setbuf(stdin,NULL); break; case KEY_LEFT: Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x -1; setbuf(stdin,NULL); break; case KEY_RIGHT: Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x +1; setbuf(stdin,NULL); break; default:; } } else //依據原本的方向前進 { if((Sbody[0].x - Sbody[1].x) == -1) //自動想左移動 { Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x -1; } else if ((Sbody[0].x - Sbody[1].x) == 1) //自動向右移動 { Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x +1; } else if ((Sbody[0].y-Sbody[0].y)== -1) //自動向上移動 { Newbody.y = Sbody[0].y -1; Newbody.x = Sbody[0].x; } else if ((Sbody[0].y - Sbody[1].y) == 1) //自動向下移動 { Newbody.y = Sbody[0].y +1; Newbody.x = Sbody[0].x; } else { } sleep(1); refresh(); } Change_Snack_Position(&Length); isEaten (&Length); //GameKey = 1; if ( isGameOver(&Length) == false) //判斷是否GameOver 吃到自己或是撞牆 { break; } } //getchar(); delwin(w); return 0; } http://codepad.org/XP42S3rE 補充說明(Supplement): -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 59.115.156.66 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1439473205.A.511.html
Feis: 你是不是連按會連走? 方向鍵控制方向, 他不會走. 08/13 21:43
andrew771027: 對 08/13 21:57
andrew771027: 兩個功能分開是OK的,但是合在一起就不行 08/13 21:58
Feis: 所以你是希望連按連走 ? 不懂. "方向鍵控制方向. 不要走" 08/13 22:05
Feis: 然後看你要 multithread 還是要把 sleep 換掉 08/13 22:06
andrew771027: 應該是我按一次上下左右 蛇就會轉向 08/13 22:08
andrew771027: 不按的時候就照著原方向移動 08/13 22:09
Feis: 正常的貪吃蛇在你按上下左右的時候是不會轉的 08/13 22:09
Feis: 他只是改變方向. 等下一個時間點移動你才看得到轉 08/13 22:10
Feis: "方向鍵控制方向. 不要移動" 08/13 22:10
Feis: 剩下的問題就是你怎麼控制時間點. 你用 sleep 的話 08/13 22:10
Feis: 一旦睡了要怎麼收到鍵盤資料? 08/13 22:11
andrew771027: 是的,我現在可以單獨控制上下左右 讓他轉向 08/13 22:11
andrew771027: 也可以單獨施作,當沒有碰觸上下左右的時候 08/13 22:12
andrew771027: 蛇會往前跑 08/13 22:12
andrew771027: 但是這兩個功能和不起來 08/13 22:12
Feis: 好吧. 我放棄. 簡單講就是你上下左右實作是錯的 08/13 22:13
Feis: 你按方向就會動本身是不對的. 其他的我就不跳針了 08/13 22:13
andrew771027: 我原本是把sleep放在外面 08/13 22:15
andrew771027: http://codepad.org/jI1i5ln9 08/13 22:15
andrew771027: 謝謝你 08/13 22:15
Feis: 那有沒有想過是因為不能用 sleep ? 08/13 22:16
Feis: 你在睡的時候要怎麼收到鍵盤資料? 08/13 22:16
andrew771027: 我在思考看看 “方向鍵控制方向,不要移動” 08/13 22:17
Feis: 如果你把 sleep 放在後面, 邏輯上應該就是我要的 08/13 22:42
Feis: 但是我研究了一下你的 code. 感覺 stdin 被弄爛了 08/13 22:42
Feis: 看起來意圖比較像是用 stdin 的 buffer 來避免 multithread 08/13 22:43
Feis: 看看這樣有沒有比較好? http://codepad.org/gi1cVofH 08/13 23:10
Feis: 然後 refresh 跟 sleep 要對調. 不然會 lag.. 08/13 23:16
EdisonX: 為什麼我覺得最大的問題是在 getch() @@ 08/13 23:22
EdisonX: 不知道 linux 有沒有像 windows 類似 GetKeyState 的東西 08/13 23:22
Feis: 看你怎麼想. getch 感覺沒甚麼錯啊. 只是他亂用 08/13 23:25
EdisonX: @Feis : 但 getch 不是 Blocking 嗎 ? 08/13 23:26
Feis: nodelay 08/13 23:27
EdisonX: 試了一下, 原來是我誤會了 @@ 08/13 23:35
Feis: 不過我發現原 po 沒加, 也許這才是他想問的.. Orz 08/13 23:41
ctrlbreak: 看看能不能讓kbhit改成直接返回按鍵訊息, 不要再去 08/13 23:43
ctrlbreak: getch第2次. 08/13 23:44
andrew771027: 我自己覺得是和c大一樣,被kbhit卡住 08/14 08:34
Feis: 我給的版本沒有 kbhit 阿 08/14 08:37
Feis: 我發現因為中文所以貼爛了: http://codepad.org/lyZQziS9 08/14 08:42
andrew771027: F大,不是指你的code(我還在研究),我原本已為我 08/14 13:30
andrew771027: 的code是在kbhit卡住 08/14 13:31
Feis: 你的 kbhit 是自己寫的嗎? 08/14 15:14
andrew771027: 不是,從網路上找到的,因為linux不支援 08/14 20:08
andrew771027: 我原本有想要直接用全域變數處理kbhit裡的問題 08/14 20:08
andrew771027: 但我也不確定我覺得的問題是不是真正的問題@@ 08/14 20:09
andrew771027: 謝謝F大解惑 08/14 20:09