1. 程式人生 > >俄羅斯方塊----Ubuntu終端遊戲

俄羅斯方塊----Ubuntu終端遊戲

我最近在看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 *

.cpp

具體程式碼如下:

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

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述