这些代码我用了一个队列和两个栈,先用队列入所有的路径,直到目的地,第二个栈用来过滤无用的路径,第三个栈用来存储最短路径…
箱子和目的地的对数可以任意设置多对,只需要改地图Map二维数组的中的数字即可, 有足够的注释, 完全可以看得懂
地图的形状和大小都支持任意修改:
注意: #define Row 和 #define Col 已经规定了地图的行列数 修改时注意即可
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <Windows.h>
#include <queue>
#include <stack>
#define Row 7
#define Col 8
using namespace std;
//节点坐标
struct node{
int X;
int Y;
};
typedef node *Node;
//定义小人
struct People{
int ROW; //行
int COL; //列
};
struct People Person={0,0}; //小人的初始定义
stack<Node> Stack; //定义栈对象
stack<Node> History;//定义栈对象存储历史路径
queue<Node> Que; //定义队列对象
int P[Row][Col]; //P作为背景
int Success=3; //箱子是否全部到达目的地的 全部到达,Success=0
Node RecordPath[30]; //记录BFS遍历的历史路径
Node BoxStart; //只是一个箱子的坐标
Node BoxEnd; //只是一个目的地的坐标
//BFS遍历时的方向
int Direction[4][2]={
{0,1},{1,0},{0,-1},{-1,0}
};
//地图 0表示路, 1表示墙, 2表示人, 3表示箱子, 4表示箱子目的地
int Map_1[Row][Col] = {
{1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,1},
{1,0,4,3,2,0,0,1},
{1,0,3,0,0,0,0,1},
{1,0,0,0,1,0,0,1},
{1,0,0,4,1,0,0,1},
{1,1,1,1,1,1,1,1},
};
int Map_2[Row][Col] = {
{1,1,1,1,1,1,1,1},
{1,0,0,0,1,0,0,1},
{1,0,4,0,0,0,0,1},
{1,0,3,0,0,3,2,1},
{1,0,0,0,1,0,0,1},
{1,0,4,0,1,0,0,1},
{1,1,1,1,1,1,1,1},
};
//节点初始化
Node inital(){
Node N;
N=(Node)malloc(sizeof(struct node));
N->X=0;
N->Y=0;
return N;
}
//背景初始化
void BackGround(int Map[Row][Col]){
int i,j;
for(i=0;i<Row;i++){ //背景初始化
for(j=0;j<Col;j++){
if(Map[i][j]==0 || Map[i][j]==4 || Map[i][j]==1){ //显示的路径 目的地 墙
P[i][j]=Map[i][j];
}
if(Map[i][j]==2 ){ //背景
P[i][j]=0;
}
}
}
}
//展示地图
void ShowMap(int Map[Row][Col]){
for(int i=0;i<Row;i++){
for(int j=0;j<Col;j++){
if(Map[i][j]==1){
printf("■");
}
else if(Map[i][j]==0 || Map[i][j]==-1){
printf(" ");
}
else if(Map[i][j]==2){
printf("◆");
}
else if(Map[i][j]==3){
printf("☆");
}
else if(Map[i][j]==4){
printf("ⅹ");
}
else if(Map[i][j]==5){//箱子已到达目的地
printf("★");
}
}
printf("\n");
}
}
//得到人的地址
void GetDirection(int Map[Row][Col]){
int i,j;
Success=0; //初始化
for(i=0;i<Row;i++){
for(j=0;j<Col;j++){
if(Map[i][j]== 2 ){//得到人的地址
Person.ROW=i;
Person.COL=j;
}
if(Map[i][j]== 3){ //箱子全部到达目的地时,应该不存在箱子了,K=0,
Success++;
}
}
}
}
//得到一个箱子及 一个目的地, 已扫描的路径初始化
void GetBoxDirection(int Map[Row][Col]){
//初始化
BoxStart=inital();
BoxEnd=inital();
int i,j;
for(i=0;i<Row;i++){ //背景初始化
for(j=0;j<Col;j++){
if(Map[i][j]==3){ //就只找一个箱子
BoxStart->X=i;
BoxStart->Y=j;
}
if(Map[i][j]==4){ //就只找一个目的地
BoxEnd->X=i;
BoxEnd->Y=j;
}
if(P[i][j]==-1){ //已扫描的路径初始化
P[i][j]=0;
}
}
}
}
//地图背景刷新
void Refresh(int Map[Row][Col], int PersonROW, int PersonCOL){
//当人站在目的地的位置时, 认定目的地的值不变
if( P[PersonROW][PersonCOL] == 4){ //背景刷新
Map[PersonROW][PersonCOL]=4; //小人当前位置设定
}else{
Map[PersonROW][PersonCOL] = 0;
}
}
//广度遍历
void BFS(int Map[Row][Col], Node BoxStart, Node BoxEnd){
if(BoxEnd->X!=0 && BoxEnd->Y!=0 && BoxStart->X!=0 && BoxStart->Y!=0){ //当所有箱子都退到目的地时,就不再进行遍历
P[BoxStart->X][BoxStart->Y]=-1; //表示已经过
Node N = inital(); //定义一个节点
N->X=BoxStart->X; //一个箱子的坐标
N->Y=BoxStart->Y;
Que.push(N);
while(Map[BoxEnd->X][BoxEnd->Y]!=Map[Que.back()->X][Que.back()->Y]){
for(int i=0;i<4; i++){ //上下左右四个方向入队列
int tx = Que.front()->X+Direction[i][0];
int ty = Que.front()->Y+Direction[i][1];
//不能超过边界值
if(tx<0||tx>Row ||ty<0||ty>Col)
continue;
if(Map[tx][ty]==0 && P[tx][ty]==0){ //将路径入队列
P[tx][ty]=-1;
Node N = inital(); //申请一个节点
N->X=tx;
N->Y=ty;
Que.push(N); //入队列
}
else if(Map[tx][ty]==4) //找到终点后
{
Node N = inital(); //申请一个节点
N->X=tx;
N->Y=ty;
Que.push(N);
break;
}
}
Stack.push(Que.front()); //队列出一个元素,栈就存队列出的元素
Que.pop(); //一个元素出队列
}
//清空队列
while (!Que.empty())
{
Que.pop();
}
//栈的元素开始出栈,回溯到起点
//取出栈的第一个和第二个元素
Node node_1=Stack.top();
Node node_2=Stack.top(); //默认给一个初始值,以避免当箱子邻近目的地时node_2出现内容为空的情况
Stack.pop();
int i=1;
while(!Stack.empty()){
node_2=Stack.top();
//找上一个节点位置
if(node_1->Y==node_2->Y ){
if((node_1->X-1)==node_2->X || (node_1->X+1)==node_2->X ){ //上边, 下边
History.push(node_1); //存入栈中
node_1=node_2;
Stack.pop();
}else{
Stack.pop();
}
}
else if(node_1->X==node_2->X){
if((node_1->Y-1)==node_2->Y || (node_1->Y+1)==node_2->Y){ //左边, 右边
History.push(node_1); //存入栈中
node_1=node_2;
Stack.pop();
}else{
Stack.pop();
}
}
else{ //不是该节点相邻节点的话,就直接弹出来,再找下一个
Stack.pop();
}
}
//当node_1和node_2都回到起点的时候, 结束
if( node_1->X!=0 || node_1->Y!=0)
if(node_1->X==node_2->X && node_1->Y==node_2->Y){
printf(" (%d,%d)",node_1->X,node_1->Y);
}
//打印历史路径
while(!History.empty()){
printf(" (%d,%d)",History.top()->X,History.top()->Y);
History.pop();
}
printf("\n");//换行 使地图在历史路径下边
}else{
printf("\n");
}
}
//小人,箱子移动
void Move(int X,int Y,int Map[Row][Col]){
GetDirection(Map); //得到人的当前位置
//小人的下一个坐标
int nextPersonRow = Person.ROW + X;
int nextPersonCol = Person.COL + Y;
//箱子的下一个位置
int nextBoxRow=nextPersonRow + X;
int nextBoxCol=nextPersonCol + Y;
//如果小人的下一个坐标是路径 目的地
if(Map[nextPersonRow][nextPersonCol]==0 || Map[nextPersonRow][nextPersonCol] == 4 ){
Map[nextPersonRow][nextPersonCol] = 2;
Refresh(Map,Person.ROW,Person.COL); //背景刷新
Person.ROW = nextPersonRow;
Person.COL = nextPersonCol;
}
//如果小人的下一个坐标是箱子的位置,且箱子被推动后的下一个位置不是墙,另一个箱子,已被占的目的地
if(Map[nextPersonRow][nextPersonCol]==3 && Map[nextBoxRow][nextBoxCol]!=1 && Map[nextBoxRow][nextBoxCol]!=3 &&Map[nextBoxRow][nextBoxCol]!=5){
//箱子移动到目的地的情况
if(Map[nextBoxRow][nextBoxCol]==4){
Map[nextBoxRow][nextBoxCol]=5;
}
else{ //没移动到目的地
Map[nextBoxRow][nextBoxCol]=3;
}
Refresh(Map,Person.ROW,Person.COL); //背景刷新
Map[nextPersonRow][nextPersonCol] = 2; //小人下一个位置值
Person.ROW = nextPersonRow;
Person.COL = nextPersonCol;
}
//如果小人的下一个坐标是箱子已放到目的地的坐标时 条件是小人移动箱子后,箱子的下一个位置不能是墙和另一个箱子
if(Map[nextPersonRow][nextPersonCol] == 5 && (Map[nextBoxRow][nextBoxCol]!=1 && Map[nextBoxRow][nextBoxCol]!=3)){
Map[nextPersonRow][nextPersonCol] = 2;
Map[nextBoxRow][nextBoxCol]=3;
Refresh(Map,Person.ROW,Person.COL); //背景刷新
Person.ROW = nextPersonRow;
Person.COL = nextPersonCol;
}
//如果小人的下一个坐标是墙或是另一个箱子
if(Map[nextPersonRow][nextPersonCol]==1 || Map[nextPersonRow][nextPersonCol]==3 ){
//什么也不做
}
}
//控制移动
void ControlMove(int Map[Row][Col]){
while(1){
//清屏
system("CLS");
//定位一个箱子和目的地
GetBoxDirection(Map);
//展示地图
ShowMap(Map);
//由上面的定位开始找最短路径, 并且打印
BFS(Map,BoxStart,BoxEnd);
//接收小人要走的方向
rewind(stdin);
//输入下一步
char dir = getch();
switch(dir){
//小人向上移动
case 'w':
case 'W':
Move(-1,0,Map);
break;
//小人向下移动
case 's':
case 'S':
Move(1,0,Map);
break;
//小人向右移动
case 'd':
case 'D':
Move(0,1,Map);
break;
//小人向左移动
case 'a':
case 'A':
Move(0,-1,Map);
break;
}
//判断是否全部箱子已经推到目的地
if(Success==0){
printf("闯关成功!");
break;
}
}
}
//主函数
int main(){
//第一关
BackGround(Map_1);
ControlMove(Map_1);
//第二关
BackGround(Map_2);
ControlMove(Map_2);
system("pause");
}
有一个致命的缺点是:
只能显示坐标值最大的星星箱子到坐标值最大的目的地"X"的最短路径, 当推完第一个箱子后,自动显示下一个箱子到目的地的最短路径
(目前没发现bug, 若发现有bug希望能私信我)