C++ 小游戏之推箱子

做完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;
}





            
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页