我目前正在尝试实现递归蛇路径查找算法。我试图做点什么,但是我发现很难解决问题。问题是我的程序只搜索一次路径,并且由于未知原因,它不会违反目的地,而是像那样前进一个位置:
*紫色是食物和绿蛇。 Aftar,它继续向右,然后离开控制台。
" 网格"是一个二维布尔数组,它与控制台一样大,如果在控制台上有类似蛇的一部分,则值为true。
方向 是一个包含Up,Down,Left,Right值的枚举。 位置是一个带有两个整数的结构,称为X和Y。
ScheduledDirections 是一个列表,其中包含将来用于蛇在控制台上绘图的路线。 < / p>
我想要做的是快速添加一个可用路径到该列表。我知道像A *这样的寻路算法,但我发现它太复杂而且难以实现。
源代码:
private static void FindAvailablePath(Position currentPosition, Stack<Position> previousPositions, Direction currentDirection, Stack<Direction> previousDirections)
{
// break if the snake's path search has ended or it went out of the console
if (currentPosition.X < 0 || currentPosition.X >= Console.WindowWidth ||
currentPosition.Y < 0 || currentPosition.Y >= Console.WindowHeight ||
AIController.isReady)
{
return;
}
// break if there is something that is blocking the snake's path
if (Snake.Grid[currentPosition.X, currentPosition.Y])
{
return;
}
// break if the snake has reached its destination
if (currentPosition.Equals(AIController.Destination))
{
if (AIController.scheduledDirections == null || AIController.scheduledDirections.Count > previousDirections.Count + 1)
{
AIController.scheduledDirections = previousDirections.ToList();
AIController.scheduledDirections.Add(currentDirection);
}
return;
}
// Break if previously visited
if (previousPositions.Contains(currentPosition))
{
return;
}
// if the current path is available, adds it to the collection and checks for the next one
if (!Snake.Grid[currentPosition.X, currentPosition.Y])
{
AIController.scheduledDirections.Add(currentDirection);
previousPositions.Push(currentPosition);
previousDirections.Push(currentDirection);
if (AIController.Destination.X > currentPosition.X && !Snake.Grid[currentPosition.X + 1, currentPosition.Y])
{
FindAvailablePath(new Position(currentPosition.X + 1, currentPosition.Y), previousPositions, Direction.Right, previousDirections); // right
}
else if (AIController.Destination.Y < currentPosition.Y && !Snake.Grid[currentPosition.X, currentPosition.Y - 1])
{
FindAvailablePath(new Position(currentPosition.X, currentPosition.Y - 1), previousPositions, Direction.Up, previousDirections); // up
}
else if (AIController.Destination.X < currentPosition.X && !Snake.Grid[currentPosition.X - 1, currentPosition.Y])
{
FindAvailablePath(new Position(currentPosition.X - 1, currentPosition.Y), previousPositions, Direction.Left, previousDirections); // left
}
else if (AIController.Destination.Y > currentPosition.Y + 1 && !Snake.Grid[currentPosition.X, currentPosition.Y + 1])
{
FindAvailablePath(new Position(currentPosition.X, currentPosition.Y + 1), previousPositions, Direction.Down, previousDirections); // down
}
previousPositions.Pop();
previousDirections.Pop();
}
}
谢谢!如果有人有任何更好的建议,我很乐意听到他们的意见!
答案 0 :(得分:0)
如果我理解正确,你需要在迷宫中找到一条蛇的路径。
让我们假设它有无限长度。然后,任务是在从蛇的初始位置到最终
的图形中找到路径使用dfs这样做是个坏主意,因为它具有O(N!)复杂性。 dfs只是按照字典顺序遍历所有路径。在许多情况下,它可以通过启发式方法进行改进,例如转到最接近目的地点,但无论如何在此任务中耗费时间。
另一方面,bfs与O(N)一起使用。
c ++中有一个代码,它实现了递归算法和bfs。 c#和c ++有相似的语法。
//<Just a code template>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stdlib.h>
#include<set>
#include<map>
#include<utility>
#include<time.h>
#include<queue>
#include<limits.h>
#include<bitset>
#include<deque>
using namespace std;
typedef vector<int>::iterator vit;
typedef string::iterator sit;
typedef vector<int>::reverse_iterator rvit;
typedef long long ll;
typedef long double ld;
//</Just a code template>
const int N=100;
bool Grid[N][N];
//Emulating your classes
struct TConsole
{
int WindowWidth,WindowHeight;
TConsole()
{
WindowWidth=WindowHeight=N;
}
};
TConsole console;
struct Position
{
int X,Y;
bool Equals(Position other)
{
return X==other.X&&Y==other.Y;
}
Position(int _X=0, int _Y=0)
{
X=_X;
Y=_Y;
}
};
const int UP=0,DOWN=1,LEFT=2,RIGHT=3;
struct Direction
{
int cnt;
Direction(int _cnt=0)
{
cnt=_cnt;
}
bool operator==(Direction other)
{
return cnt==other.cnt;
}
bool operator==(int other)
{
return cnt==other;
}
};
Direction R=Direction(RIGHT);
Direction L=Direction(LEFT);
Direction U=Direction(UP);
Direction D=Direction(DOWN);
struct AI
{
bool isReady;//What is it for?
Position Destination;
vector<Direction> scheduledDirections;
AI()
{
isReady=false;
}
};
AI AIController;
Direction prevdir[N][N];//Bfs stores for each cell destination with which it is entered
bool was[N][N];//if was[i][j] is 1, then the snake has visited position (i,j) in this branch of recursion in dfs case or just visited (bfs)
vector<Direction> path; // inversed path from start position to finish
//function prototypes. To explain the compiler that their definition is somwhere below
bool dfs(Position pos, Direction dir);
void bfs(Position pos, Direction dir);
void calculate_schedules()
{
//Restore the path from reversed and write it down
reverse(path.begin(),path.end());
for(vector<Direction>::iterator i=path.begin();i!=path.end();++i)
{
AIController.scheduledDirections.push_back(*i);
printf("%d\n",*i);
}
}
Position position_by_destination(Position initial, Direction d)//Returns a position to which snake comes, if it goes from position 'initial' in direction d
{
if(d==U)
{
return Position(initial.X-1,initial.Y);
}
else if(d==D)
{
return Position(initial.X+1,initial.Y);
}
else if(d==R)
{
return Position(initial.X,initial.Y+1);
}
return Position(initial.X,initial.Y-1);
}
//Returns opposite to d direction
Direction get_inverse(Direction d)
{
if(d==D)
return U;
else if(d==U)
return D;
else if(d==R)
return L;
return R;
}
//It starts from destination point and in each iteration 1)pushes the direction with which a current state was reached. 2)Goes to previous state
void bfs_path_restore(Position target,Position player)
{
Position cpos=target;
while(!(cpos.X==player.X&&cpos.Y==player.Y))
{
path.push_back(prevdir[cpos.X][cpos.Y]);
cpos=position_by_destination(cpos,get_inverse(*(path.end()-1)));
}
}
void FindAvailablePathBFS(Position currentPosition, Direction currentDirection)
{
//The snake hasn't been anywhere
memset(was,0,sizeof(was));
path.clear();
//Find path.
bfs(currentPosition,currentDirection);
bfs_path_restore(AIController.Destination,currentPosition);
printf("By BFS:\n");
calculate_schedules();
}
void FindAvailablePathDFS(Position currentPosition, Direction currentDirection)
{
//The snake doesn't know anything about the path, and hasn't visited anything but the starting point
memset(was,0,sizeof(was));
was[currentPosition.X][currentPosition.Y]=1;
path.clear();
//Find path.
dfs(currentPosition,currentDirection);
printf("By DFS:\n");
calculate_schedules();
}
int mabs(int x)// Abs function. The standard one tends to return double in some cases
{
return x<0?-x:x;
}
//Positions' evaluation and comparsion function for their sorting based on it
int appraise_position(Position p)
{
return mabs(p.X-AIController.Destination.X)+mabs(p.Y-AIController.Destination.Y);
}
bool heuristic_smaller(Position f, Position s)
{
return appraise_position(f)<appraise_position(s);
}
//Returns direction of a snake when it goes from 'from' to 'to'.
Direction get_dir(Position from, Position to)
{
if(to.X>from.X)
return D;
if(to.X<from.X)
return U;
if(to.Y>from.Y)
return R;
if(to.Y<from.Y)
return L;
}
//Position pos is not in the console
bool out_of_bounds(Position pos)
{
return pos.X<0||pos.Y<0||pos.X>console.WindowWidth||pos.Y>console.WindowHeight;
}
bool dfs(Position pos, Direction dir)//It returns true if managed to find position to the target with a state of was created by recursion
{
if(out_of_bounds(pos))
return false;
//An obstacle
if(Grid[pos.X][pos.Y])
return false;
//Got it
if (pos.Equals(AIController.Destination))
return true;
//v contains all possible positions for the next step.
vector<Position> v;
v.push_back(Position(pos.X+1,pos.Y));
v.push_back(Position(pos.X-1,pos.Y));
v.push_back(Position(pos.X,pos.Y+1));
v.push_back(Position(pos.X,pos.Y-1));
//Evaluate them and process according to a result
sort(v.begin(),v.end(),heuristic_smaller);
for(vector<Position>::iterator i=v.begin();i!=v.end();++i)
{
//The snake was there in this branch
if(was[i->X][i->Y])
continue;
Direction cdir=get_dir(pos,*i);
if(cdir==get_inverse(dir))//This prevents the snake from turning inside out
{
continue;
}
//Go to next position and tell child branches that the snake was there
was[i->X][i->Y]=1;
if(dfs(*i,cdir))
{
path.push_back(cdir);
return true;
}
//Return from position
was[i->X][i->Y]=0;
}
return false;
}
//Processes a cell, finds closest ones to it and ads them to queue of procession.
void bfs(Position pos, Direction dir)
{
//Pathfinding starts from initial snake position
queue<Position> qp;
queue<Direction> qd;
qp.push(pos);
qd.push(dir);
while(!qp.empty())
{
//Get current cell
Position curpos=qp.front();
Direction curdir=qd.front();
//BFS processes cells in order of increasing distance from the first. Hence, if destination cell is found, it cannot be reached with less steps
if(curpos.Equals(AIController.Destination))
{
qp=queue<Position>();
qd=queue<Direction>();
return;
}
//Find all neighbours
vector<Position> v;
v.push_back(Position(curpos.X+1,curpos.Y));
v.push_back(Position(curpos.X-1,curpos.Y));
v.push_back(Position(curpos.X,curpos.Y+1));
v.push_back(Position(curpos.X,curpos.Y-1));
for(vector<Position>::iterator i=v.begin();i!=v.end();++i)
{
//Check if a neighbour is acceptable
if(out_of_bounds(*i))
continue;
if(was[i->X][i->Y])
continue;
if(Grid[i->X][i->Y])
continue;
Direction cdir=get_dir(curpos,*i);
if(cdir==get_inverse(curdir))
continue;
//State that the way to the neighbour cell is found. Write destination with which the snake goes there to the corresponding prevdir. Add the cell to the queue
was[i->X][i->Y]=1;
prevdir[i->X][i->Y]=cdir;
qp.push(*i);
qd.push(cdir);
}
qp.pop();
qd.pop();
}
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
char a[N][N];
Position player,target;
for(int i=0;i<N;++i)//Never do like this
{
for(int j=0;j<N;++j)
{
Grid[i][j]=1;
}
}
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
scanf("%c",&a[i][j]);
if(a[i][j]=='\n')
{
--j;
continue;
}
if(a[i][j]=='@')
player=Position(i,j);
else if(a[i][j]=='x')
target=Position(i,j);
if(a[i][j]!='*')
Grid[i][j]=0;
}
}
AIController.Destination=target;
FindAvailablePathDFS(player,D);
FindAvailablePathBFS(player,D);
return 0;
}
请注意,它是在假设存在路径的情况下进行的。此源从test.in文件获取级别映射,其中@表示蛇的初始位置,x - 目标点和* - 障碍物前两个数字应该是字段的宽度和高度。 字段示例:
7 7
.......
.......
..***.*
..*@*.*
..*...*
..*****
......x
让我们回到有限的蛇。有限的蛇可以访问同一个地方X两次,如果它的尾巴已经不存在了。
有两种情况:
a)在X的第一次访问期间,蛇可能会移向目的地。然后没有必要这样做。
b)蛇无法做到。然后,为了向目标点移动,它必须相对于头部速度返回。然后蛇应该尝试找到最长的尾巴(转向并从X开始),如果不可能的话,到它的头部(在一个可以访问的网格的一部分中转身),这是{{ 3}}已经很晚了。我可能错过了一些东西/