作者andrew771027 (老柏~)
看板C_and_CPP
標題[問題] 貪食蛇遇到阻斷式getch問題
時間Thu Aug 13 21:39:58 2015
開發平台(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: 謝謝你 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: 然後 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
→ 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