C++ 小遊戲之推箱子
阿新 • • 發佈:2019-01-31
做完C的貪吃蛇遊戲後,感覺還不錯,剛好記得在HDU上做過一道關於推箱子游戲的演算法題目,即雙BFS。
所以我決定來做做C++的小遊戲推箱子,由於剛學C++,對C++還是不很熟練,但是思路還是很清楚的,
編寫程式碼還是很舒服的。!
現在晒晒我的程式碼和詳細解釋,希望多交流~!
//******************************************************* //********************小小推箱子************************* //******************************************************* //**************版權所有***2011.9.24***鹹魚************** //******************************************************* //*****************如寫的不好,請見諒********************* //************每次執行程式的地圖是不同的***************** //*******************************************************
/* Sokoban.h: 類定義 Sokoban.c: 類成員函式實現 Use_Sokoban.c: 主函式 請用VC6(別編譯器的也行)先執行Use_Sokoban.c檔案,要編譯該檔案一下, 再點Project-> Add To Project-> Files 選擇Sokoban.c檔案, 即將Sokoban.c載入到工程裡,最後執行就OK拉。 */
//******************************************************* Sokoban.h //******************************************************* #ifndef SOKOBAN_H_ //防止檔案重複包含 #define SOKOBAN_H_ #include <queue> using std::queue; //每一步的資料型別 struct node { int bx, by; //箱子的座標 int px, py; //人的座標 }; //推箱子類 class Sokoban { private: enum {L = 15, H = 7}; char GameMap[H][L]; //地圖 int Pex, Pey; //人的位置 int Boxx, Boxy; //箱子的位置 int Succeed, Prove; //是否成功到目的地, 是否可玩性 int dx[4], dy[4]; //方向陣列 protected: char Empty; char People; char Box; char Block; char Target; int dir; //記錄按鍵方向 node s, e; public: Sokoban(); //構建函式 ~Sokoban() {} //解構函式,即為inline //地圖初始化函式 void Initial(); //箱子路勁驗證函式,引數為箱子座標(bx,by),人座標(px,py) void Box_Bfs(int bx, int by, int px, int py); //人路勁驗證函式,人所到的目的地(ex,ey) bool People_Bfs(int ex, int ey); //地圖重新整理函式 void Show(); //按鍵判斷函式 void Button(); //箱子人移動函式 void Move(); //驗證越界函式 bool Check(int x, int y); }; #endif
//******************************************************* Sokoban.cpp //******************************************************* #include "Sokoban.h" #include <cstring> #include <cstdlib> #include <ctime> #include <iostream> #include <conio.h> using std::cout; using std::endl; Sokoban::Sokoban() //構建函式即對變數初始化 { dir = -1; Succeed = Prove = 0; memset(GameMap, '.', sizeof(GameMap)); Empty = '.'; People = 'P'; Box = '#'; Block = '*'; Target = 'T'; //方向依次為上右下左 dx[0] = -1; dx[1] = 0; dx[2] = 1; dx[3] = 0; dy[0] = 0; dy[1] = 1; dy[2] = 0; dy[3] = -1; //隨機種子,使程式每次執行時所產生的隨機數不同 srand(time(0)); } //地圖初始化函式 void Sokoban::Initial() { int count = 0, x, y; //對地圖中隨機產生25個阻礙物 while(count != 25) { x = rand()%H; y = rand()%L; if(GameMap[x][y] == Empty) { GameMap[x][y] = Block; count++; } } while(true) //隨機產生人開始的位置 { x = rand()%H; y = rand()%L; if(GameMap[x][y] == Empty) { GameMap[x][y] = People; Pex = x; Pey = y; break; } } while(true) //隨機產生箱子開始的位置 { x = rand()%H; y = rand()%L; //不讓箱子在地圖的邊界處 if(GameMap[x][y] == Empty && x != 0 && y != 0 && x != H-1 && y != L-1) { GameMap[x][y] = Box; Boxx = x; Boxy = y; break; } } while(true) //隨機產生目標的位置 { x = rand()%H; y = rand()%L; if(GameMap[x][y] == Empty) { GameMap[x][y] = Target; break; } } //對遊戲地圖檢查是否可將箱子推到目的地,即判斷遊戲可玩性 Sokoban::Box_Bfs(Boxx, Boxy, Pex, Pey); //如遊戲不可玩,即再隨機產生地圖 if(!Prove) { memset(GameMap, '.', sizeof(GameMap)); Sokoban::Initial(); } else Sokoban::Show(); } //箱子路勁驗證函式 //用BFS演算法對箱子驗證是否可到目的地 void Sokoban::Box_Bfs(int bx, int by, int px, int py) { queue<node>_Box; //建立箱子佇列 //visit對上一步走到下一步的記錄,防止箱子走重複路勁 //visit[i][j][z][k]表示箱子從點(i,j)到點(z,k) //visit[][][][]為0時表示為走過,1時表示已走過 int visit[H][L][H][L]; memset(visit, 0, sizeof(visit)); //visit陣列初始化 s.bx = bx; s.by = by; //將起始的箱子、人位置放入佇列 s.px = px; s.py = py; _Box.push(s); int pe_x, pe_y; while(!_Box.empty()) //佇列為空時跳出 { s = _Box.front(); _Box.pop(); if(GameMap[s.bx][s.by] == Target) //到達目的地 { Prove = 1; break; } for(int i = 0; i < 4; i++) { e.bx = s.bx + dx[i]; e.by = s.by + dy[i]; switch(i) //人推箱子的位置 { case 0: pe_x = s.bx + dx[2]; pe_y = s.by + dy[2]; break; case 1: pe_x = s.bx + dx[3]; pe_y = s.by + dy[3]; break; case 2: pe_x = s.bx + dx[0]; pe_y = s.by + dy[0]; break; case 3: pe_x = s.bx + dx[1]; pe_y = s.by + dy[1]; break; } //驗證箱子和人的位置的合法性 if(!Check(e.bx, e.by) || !Check(pe_x, pe_y) || GameMap[e.bx][e.by] == Block || GameMap[pe_x][pe_y] == Block || visit[s.bx][s.by][e.bx][e.by] ) continue; //如人可推箱子即進入佇列 if(Sokoban::People_Bfs(pe_x, pe_y)) { //儲存人推箱子後的位置 e.px = pe_x; e.py = pe_y; _Box.push(e); visit[s.bx][s.by][e.bx][e.by] = 1; //箱子路勁的標記 } } } } //人路勁驗證函式 //用BFS演算法對人驗證是否可推箱子 bool Sokoban::People_Bfs(int ex, int ey) { queue<node>_People; node t, end; //visit陣列對人的路勁進行標記,0為未走過,1為走過 int visit[H][L]; //visit陣列初始化為0 memset(visit, 0, sizeof(visit)); t.px = s.px; t.py = s.py; //人初始位置進入佇列 _People.push(t); visit[t.px][t.py] = 1; while(!_People.empty()) //對立為空時跳出 { t = _People.front(); _People.pop(); if(t.px == ex && t.py == ey) //人可到達(ex,ey)該點 return 1; for(int i = 0; i < 4; i++) { end.px = t.px + dx[i]; end.py = t.py + dy[i]; //檢查人的位置合法性 if(!Check(end.px, end.py) || GameMap[end.px][end.py] == Block || GameMap[end.px][end.py] == Box || visit[end.px][end.py]) continue; //進入佇列 _People.push(end); visit[end.px][end.py] = 1; //記錄 } } return 0; } //地圖重新整理函式 void Sokoban::Show() { int i, j; while(true) { //每半秒重新整理一次地圖 clock_t s = clock(); while(clock() - s < CLOCKS_PER_SEC/2) ; //先判斷按鍵在移動 Sokoban::Button(); Sokoban::Move(); system("cls"); for(i = 0; i < H; i++) { for(j = 0; j < L; j++) cout << GameMap[i][j]; cout << endl; } cout << endl; cout << "\n**********************************" << endl; cout << "* 小小C++語言推箱子游戲 *" << endl; cout << "* 遊戲規則: *" << endl; cout << "* P: 人 #: 箱子 *" << endl; cout << "* *: 障礙物 T: 目的地 *" << endl; cout << "**********************************" << endl; cout << "* 每次遊戲地圖不一樣 *" << endl; cout << "* 人將箱子推到目的地即過關 *" << endl; cout << "*所給地圖,一定可過關,請慎重移箱子*" << endl; cout << "* 箱子無路可走時,機器不會提示 *" << endl; cout << "**********************************" << endl; //箱子成功到達目的地 if(Succeed) { cout << "\n ^_^ >_<" << endl; cout << "恭喜過關成功! 再來一盤吧" << endl; getchar(); break; } } } //按鍵判斷函式 void Sokoban::Button() { int key; if(kbhit() != 0) //檢查當前是否有鍵盤輸入,若有則返回一個非0值,否則返回0 { while(kbhit() != 0) //可能存在多個按鍵,要全部取完,以最後一個為主 key = getch(); //將按鍵從控制檯中取出並儲存到key中 switch(key) { //上 case 72: dir = 0; break; //右 case 77: dir = 1; break; //下 case 80: dir = 2; break; //左 case 75: dir = 3; break; } } } //人推箱子移動函式 void Sokoban::Move() { int x, y; //有按鍵時 if(dir != -1) { //人所推向的位置座標 x = Pex + dx[dir]; y = Pey + dy[dir]; //人所推位置為空,即走向該位置 if(Check(x, y) && GameMap[x][y] == '.') { GameMap[Pex][Pey] = '.'; //人的位置改變 GameMap[x][y] = 'P'; Pex = x; Pey = y; dir = -1; //按鍵記錄為無即-1 } else //人所推位置為箱子,即將箱子推向該方向的前面這點 if(Check(x, y) && GameMap[x][y] == '#' && Check(x+dx[dir], y+dy[dir]) && GameMap[ x+dx[dir] ][ y+dy[dir] ] == '.') { GameMap[Boxx][Boxy] = '.'; //箱子的位置改變 GameMap[x+dx[dir] ][ y+dy[dir] ] = '#'; Boxx = x + dx[dir]; Boxy = y + dy[dir]; GameMap[Pex][Pey] = '.'; //人的位置改變 GameMap[x][y] = 'P'; Pex = x; Pey = y; dir = -1; } else //將箱子推向該方向的前面這點為目的地 if(Check(x, y) && GameMap[x][y] == '#' && Check(x+dx[dir], y+dy[dir]) && GameMap[ x+dx[dir] ][ y+dy[dir] ] == 'T') { GameMap[Boxx][Boxy] = '.'; //箱子的位置改變 GameMap[x+dx[dir] ][ y+dy[dir] ] = '#'; Boxx = x + dx[dir]; Boxy = y + dy[dir]; GameMap[Pex][Pey] = '.'; //人的位置改變 GameMap[x][y] = 'P'; Pex = x; Pey = y; dir = -1; Succeed = 1; //記錄成功到達目的地 } } } //判斷越界情況 bool Sokoban::Check(int x, int y) { if(x < 0 || x >= H || y < 0 || y >= L) return 0; else return 1; }
//*************************************************
Use_Sokoban.cpp
//*************************************************
#include <iostream>
#include "Sokoban.h"
using namespace std;
int main()
{
Sokoban s;
s.Initial();
return 0;
}