俄羅斯方塊----Ubuntu終端遊戲
阿新 • • 發佈:2019-01-25
我最近在看C++和linux,為了能夠更加牢靠的掌握自己的學到的知識,所以採用寫小遊戲的方法來幫助自己鞏固學習。在這篇程式碼中我用到的C++和linux知識有:
C++知識:
1. 類的建立
2. 行內函數
3. pthread執行緒
linux知識:
1. 進入root模式
2. 給檔案增加許可權
3. 檢視和使用系統的外設
有三點要特別說明:
1. 本程式碼不適用於所有linux系統,如果照搬的話可能會執行失敗。如果要保證此程式碼編譯的程式能在另一臺機器上執行,需要先檢視該機器的鍵盤是對應哪一個event檔案,並將程式碼中的開啟event檔案的名稱改掉。
2. 因為開啟event檔案需要很高的許可權,所以需要在root模式下執行
3. 要編譯此程式需要在連結是加入pthread庫,即編譯時需要g++ -lpthread *
具體程式碼如下:
RussiaBlock.h
#ifndef RUSSIA_BLOCK_H
#define RUSSIA_BLOCK_H
#include <iostream>
#include <ctime>
#include <stdexcept>
#include <unistd.h>
#include <pthread.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <errno.h>
#include <linux/input.h>
using namespace std;
typedef struct Pos
{
unsigned char x;
unsigned char y;
} Pos;
typedef struct Block
{
Pos pos[4];
bool Droping;
short shape;
} Block;
class RussiaBlock
{
private :
enum {TIAN_BLOCK=0,L_BLOCK,T_BLOCK,FL_BLOCK,I_BLOCK,HI_BLOCK,Z_BLOCK,FZ_BLOCK};
bool gameFail;
bool dropToEnd;
bool Rotating;
unsigned int score;
unsigned char screenWidth;
unsigned char screenHeight;
Block currentBlock;
Block nextBlock;
unsigned char map[100][100];
public:
void GameStart(void); //開始遊戲
RussiaBlock(unsigned char, unsigned char = 80);
private:
Block TianBlock(void); //田型方塊
void FailNotice(void); //遊戲失敗提示
Block LBlock(void); //L型方塊
void KeyOperate(void); //按鍵操作
void ShowScreen(void); //顯示遊戲區域
Block CreatNewBlock(void); //建立一個新的方塊
void ShowNextBlock(void); //顯示下一個方塊提示
private:
Block TBlock(); //T型方塊
Block FLBlock(); //反L型方塊
Block IBlock(); //I型方塊
Block HIBlock(); //橫I型方塊
Block ZBlock(); //Z型方塊
Block FZBlock(); //反Z型方塊
void AutoDrop(void *); //自動下降執行緒
static void *Thread_func(void *);
static void *Thread_Key(void *);
void MoveRight(); //右移
void MoveLeft(); //左移
void DropEnd(); //下降到底
void RotateBlock(); //旋轉方塊
void DeleteLines(); //清除遊戲區域內多行內容
bool DropOneLine(); //下降一行
void DeleteOneLine(); //刪除一行
bool AtTheEnd(); //判斷是否在可降落的最低點
inline bool ThisLineIsFull(int index)
{
for(int i=0;i<screenWidth;i++)
{
if(map[i][index] == 0)
return false;
}
return true;
}
inline void DeleteOneLine(int index)
{
for(int i=index;i>0;i--)
{
for(int j=0;j<screenWidth;j++)
{
map[j][i] = map[j][i-1];
}
}
for(int j=0;j<screenWidth;j++)
{
map[j][0] = 0;
}
}
};
#endif
RussiaBlock.cpp
#include "RussiaBlock.h"
#include <iostream>
using namespace std;
RussiaBlock::RussiaBlock(unsigned char width, unsigned char height)
{
gameFail = false;
screenWidth = width;
screenHeight= height;
score = 0;
for(int i=0;i != 100;i++)
for(int j=0;j!=100;j++)
{
map[i][j] = 0;
}
srand(time(0));
nextBlock = CreatNewBlock();
Rotating = false;
}
Block RussiaBlock::CreatNewBlock(void)
{
short blockShape = 0;
blockShape = (short)(rand()%8);
Block newBlock;
switch(blockShape)
{
case 0: newBlock = TianBlock();break;
case 1: newBlock = LBlock();break;
case 2: newBlock = TBlock();break;
case 3: newBlock = FLBlock();break;
case 4: newBlock = IBlock();break;
case 5: newBlock = HIBlock();break;
case 6: newBlock = ZBlock();break;
case 7: newBlock = FZBlock();break;
}
newBlock.shape = blockShape;
return newBlock;
}
void RussiaBlock::ShowScreen()
{
system("clear");
for(int i=0;i<screenWidth+2;i++)
cout << "_";
cout <<endl;
for(int i(0);i<screenHeight;i++)
{
cout << "|";
for(int j(0);j<screenWidth;j++)
{
if(map[j][i] == 0) cout<<" ";
else if(map[j][i] == 1 || map[j][i] == 2) cout<<"#";
else cout<<map[j][i];
}
cout << "|";
cout<<endl;
}
for(int i=0;i<screenWidth+2;i++)
cout << "-" ;
cout << endl;
ShowNextBlock();
cout << "score:" << score<<endl;
}
void RussiaBlock::ShowNextBlock(void)
{
bool have = false;
for(int r=0;r<4;r++)
{
for(int c=0;c<4;c++)
{
have = false;
for(int blk=0;blk<4;blk++)
{
if(nextBlock.pos[blk].x == c&& nextBlock.pos[blk].y == r)
{
have = true;
cout << "#";
}
}
if(have==false) cout<<" ";
}
cout << endl;
}
}
void RussiaBlock::GameStart(void)
{
pthread_t id_auto,id_key;
try
{
int ret = pthread_create(&id_auto,NULL,&Thread_func,this);
if(ret != 0) throw runtime_error("start game fail");
ret = pthread_create(&id_key,NULL,&Thread_Key,this);
if(ret != 0) throw runtime_error("start game fail");
}
catch(runtime_error err)
{
cout << err.what() <<endl;
return;
}
gameFail = false;
while(!gameFail)
{
dropToEnd = false;
currentBlock = nextBlock;
nextBlock = CreatNewBlock();
for(int i=0;i<4;i++)
{
currentBlock.pos[i].x += (screenWidth/2);
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 1;
}
while(!dropToEnd)
{
if(AtTheEnd())
{
dropToEnd = true;
for(int i=0;i<4;i++)
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 2;
DeleteLines();
}
}
for(int i=0;i<screenWidth;i++)
{
if(map[i][0] == 2)
{
gameFail = true;
break;
}
}
}
pthread_join(id_auto,NULL);
pthread_join(id_key,NULL);
FailNotice();
}
bool RussiaBlock::AtTheEnd(void)
{
for(int i=0;i<4;i++)
{
if(map[currentBlock.pos[i].x][currentBlock.pos[i].y + 1] == 2|| currentBlock.pos[i].y ==screenHeight - 1)
return true;
}
return false;
}
void RussiaBlock::FailNotice(void)
{
string failStr("Game Over!");
for(int i=0;i!=failStr.size();i++)
{
map[i][screenHeight/2] = failStr[i];
}
ShowScreen();
}
void RussiaBlock::KeyOperate(void)
{
struct input_event ev_key;
int btn_fd = open("/dev/input/event3",O_RDWR);
if(btn_fd < 0)
{
cerr << btn_fd <<endl;
cerr << "Game initial fail!" << endl;
return;
}
while(!gameFail)
{
int count = read(btn_fd,&ev_key,sizeof(struct input_event));
//for(int i=0;i<(int)count/sizeof(struct input_event);i++)
if(EV_KEY == ev_key.type && ev_key.value == 1)
{
switch(ev_key.code)
{
case KEY_A:MoveLeft();break;
case KEY_S:DropEnd();break;
case KEY_D:MoveRight();break;
case KEY_W:RotateBlock();break;
}
}
ShowScreen();
}
close(btn_fd);
}
void RussiaBlock::DeleteLines(void)
{
for(int i=0;i<screenHeight;i++)
{
if(ThisLineIsFull(i))
{
DeleteOneLine(i);
score++;
}
}
}
void RussiaBlock::DropEnd(void)
{
bool getEnd = false;
while(1)
{
for(int i=0;i<4;i++)
{
if(map[currentBlock.pos[i].x][currentBlock.pos[i].y + 1] == 2|| currentBlock.pos[i].y ==screenHeight - 1)
{
getEnd = true;
break;
}
}
if(!getEnd)
{
for(int i=0;i<4;i++)
{
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 0;
currentBlock.pos[i].y++;
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 1;
}
}
else break;
}
}
void RussiaBlock::MoveRight(void)
{
for(int i=0;i<4;i++)
{
if(currentBlock.pos[i].x + 1 == screenWidth || map[currentBlock.pos[i].x+1][currentBlock.pos[i].y] == 2)
{
return ;
}
}
for(int i=0;i<4;i++)
{
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 0;
currentBlock.pos[i].x++;
}
for(int i=0;i<4;i++)
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 1;
}
void RussiaBlock::MoveLeft(void)
{
for(int i=0;i<4;i++)
{
if(currentBlock.pos[i].x - 1 < 0 || map[currentBlock.pos[i].x-1][currentBlock.pos[i].y] == 2)
{
return ;
}
}
for(int i=0;i<4;i++)
{
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 0;
currentBlock.pos[i].x--;
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 1;
}
}
void RussiaBlock::RotateBlock(void)
{
short blockShape = currentBlock.shape;
if(blockShape == TIAN_BLOCK) return;
if(blockShape == I_BLOCK)
{
Pos pos[4] = {0};
for(int i=0;i<4;i++)
{
pos[i].y = currentBlock.pos[2].y;
pos[i].x = currentBlock.pos[2].x + i-1;
}
for(int i=0;i<4;i++)
{
if(map[pos[i].x][pos[i].y] == 2 || pos[i].y >= screenHeight || pos[i].y < 0 || pos[i].x >= screenWidth || pos[i].x < 0) return ;
}
for(int i=0;i<4;i++)
{
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 0;
currentBlock.pos[i] = pos[i];
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 1;
}
currentBlock.shape = HI_BLOCK;
return ;
}
if(blockShape == HI_BLOCK)
{
Pos pos[4] = {0};
for(int i=0;i<4;i++)
{
pos[i].x = currentBlock.pos[1].x;
pos[i].y = currentBlock.pos[1].y + i-1;
}
for(int i=0;i<4;i++)
{
if(map[pos[i].x][pos[i].y] == 2 || pos[i].y >= screenHeight || pos[i].y < 0 || pos[i].x >= screenWidth || pos[i].x < 0) return ;
}
for(int i=0;i<4;i++)
{
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 0;
currentBlock.pos[i] = pos[i];
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 1;
}
currentBlock.shape = I_BLOCK;
return ;
}
unsigned char maxX = currentBlock.pos[0].x,maxY = currentBlock.pos[0].y,minX = currentBlock.pos[0].x, minY = currentBlock.pos[0].y;
for(int i=1;i<4;i++)
{
if(maxX < currentBlock.pos[i].x) maxX = currentBlock.pos[i].x;
if(minX > currentBlock.pos[i].x) minX = currentBlock.pos[i].x;
if(maxY < currentBlock.pos[i].y) maxY = currentBlock.pos[i].y;
if(minY > currentBlock.pos[i].y) minY = currentBlock.pos[i].y;
}
unsigned char X = ((maxX-minX) == 2)?maxX-1:maxX;
unsigned char Y = ((maxY-minY) == 2)?maxY-1:maxY;
unsigned char arr[3][3] = {0};
short arrX=0,arrY=0;
for(int i=Y-1;i<=Y+1;i++)
{
arrX = 0;
for(int j=X-1;j<=X+1;j++)
{
arr[arrX++][arrY] = map[j][i];
if(map[j][i] == 2) return ;
}
arrY++;
}
//轉置並換行
for(int i=0;i<3;i++)
{
for(int j = 0;j<=i;j++)
{
unsigned char temp = arr[j][i];
arr[j][i] = arr[i][j];
arr[i][j] = temp;
}
}
for(int j=0;j<3;j++)
{
unsigned char temp = arr[j][0];
arr[j][0] = arr[j][2];
arr[j][2] = temp;
}
arrY = 0;
int index = 0;
for(int i=Y-1;i<=Y+1;i++)
{
arrX = 0;
for(int j=X-1;j<=X+1;j++)
{
map[j][i] = arr[arrX++][arrY];
if(map[j][i] == 1)
{
currentBlock.pos[index].x = j;
currentBlock.pos[index++].y = i;
}
}
arrY++;
}
}
Block RussiaBlock::TianBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 0;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 1;
NewBlock.pos[1].y = 0;
NewBlock.pos[2].x = 0;
NewBlock.pos[2].y = 1;
NewBlock.pos[3].x = 1;
NewBlock.pos[3].y = 1;
return NewBlock;
}
Block RussiaBlock::LBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 0;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 0;
NewBlock.pos[1].y = 1;
NewBlock.pos[2].x = 0;
NewBlock.pos[2].y = 2;
NewBlock.pos[3].x = 1;
NewBlock.pos[3].y = 2;
return NewBlock;
}
Block RussiaBlock::TBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 0;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 1;
NewBlock.pos[1].y = 0;
NewBlock.pos[2].x = 2;
NewBlock.pos[2].y = 0;
NewBlock.pos[3].x = 1;
NewBlock.pos[3].y = 1;
return NewBlock;
}
Block RussiaBlock::FLBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 0;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 1;
NewBlock.pos[1].y = 0;
NewBlock.pos[2].x = 2;
NewBlock.pos[2].y = 0;
NewBlock.pos[3].x = 2;
NewBlock.pos[3].y = 1;
return NewBlock;
}
Block RussiaBlock::IBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 0;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 0;
NewBlock.pos[1].y = 1;
NewBlock.pos[2].x = 0;
NewBlock.pos[2].y = 2;
NewBlock.pos[3].x = 0;
NewBlock.pos[3].y = 3;
return NewBlock;
}
Block RussiaBlock::HIBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 0;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 1;
NewBlock.pos[1].y = 0;
NewBlock.pos[2].x = 2;
NewBlock.pos[2].y = 0;
NewBlock.pos[3].x = 3;
NewBlock.pos[3].y = 0;
return NewBlock;
}
Block RussiaBlock::ZBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 0;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 1;
NewBlock.pos[1].y = 0;
NewBlock.pos[2].x = 1;
NewBlock.pos[2].y = 1;
NewBlock.pos[3].x = 2;
NewBlock.pos[3].y = 1;
return NewBlock;
}
Block RussiaBlock::FZBlock(void)
{
Block NewBlock = {0};
NewBlock.pos[0].x = 1;
NewBlock.pos[0].y = 0;
NewBlock.pos[1].x = 2;
NewBlock.pos[1].y = 0;
NewBlock.pos[2].x = 0;
NewBlock.pos[2].y = 1;
NewBlock.pos[3].x = 1;
NewBlock.pos[3].y = 1;
return NewBlock;
}
void *RussiaBlock::Thread_func(void *param)
{
RussiaBlock *p = (RussiaBlock *)param;
p->AutoDrop(NULL);
}
void RussiaBlock::AutoDrop(void *ptr)
{
while(!gameFail)
{
int tim = time(0);
while(tim == time(0)) ;
DropOneLine();
ShowScreen();
}
}
bool RussiaBlock::DropOneLine(void)
{
for(int i=0;i<4;i++)
{
if(map[currentBlock.pos[i].x][currentBlock.pos[i].y + 1] ==2 ||currentBlock.pos[i].y == screenHeight -1)
{
return false;
}
}
for(int i=0;i<4;i++)
{
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 0;
currentBlock.pos[i].y++;
}
for(int i=0;i<4;i++)
map[currentBlock.pos[i].x][currentBlock.pos[i].y] = 1;
return true;
}
void *RussiaBlock::Thread_Key(void *param)
{
RussiaBlock *p = (RussiaBlock *)param;
p->KeyOperate();
}
main.cpp
#include <iostream>
#include "RussiaBlock.h"
using namespace std;
int main()
{
RussiaBlock blk(10,10);
blk.GameStart();
return 0;
}
makefile
RussiaBlock: main.o RussiaBlock.o
g++ -lpthread main.o RussiaBlock.o -o RussiaBlock
main.o: main.cpp RussiaBlock.h
g++ -c main.cpp
RussiaBlock.o: RussiaBlock.cpp
g++ -c RussiaBlock.cpp