1 引言
在电影《生死关头》中,James Bond作为世界上最著名的特工,遭遇了一场惊心动魄的逃脱戏码。在这个被湖中心的小岛和湖中的鳄鱼包围的困境中,他需要快速做出决策,以成功逃脱险恶的环境。
在这个场景中,湖的大小为100米乘以100米,湖的中心坐标为(0,0),而东北角的坐标则是(50,50)。湖中的小岛位于湖中心,其圆心为(0,0),岛的直径为15米。此外,湖中还分布着一些凶猛的鳄鱼,它们的分布位置和James Bond可以跳跃的最大距离已知。
在这样险恶的环境下,James Bond需要依靠我们的路径规划系统来寻找最短的逃脱路径。这个路径规划系统是专门为他提供帮助的,帮助他在小岛和湖中的鳄鱼包围中找到最短的路径,以尽可能快地脱离险境。
这个路径规划系统是通过广度优先搜索算法实现的。广度优先搜索算法是一种经典的图搜索算法,它能够按照距离的远近逐一搜索每个可能的路径,并从中选择最短的一条。这种算法可以确保所推荐的路径是最短的、可行的,能够提供最短的逃脱路径给James Bond。
2 需求分析
根据用户需求分析,设计一个名为“拯救007游戏系统”的应用,以满足以下功能要求:
1. 输入数据读取:从文件中读取输入数据,包括鳄鱼数量、最大跳跃距离以及各个鳄鱼的位置坐标。
2. 路径规划:采用广度优先搜索算法,计算从中心小岛到湖岸的最短路径,并将结果输出至文件。若无法找到007逃离的路径,则在文件中写入“007不存在可逃离的路径”。
3. 路径坐标按跳跃顺序输出:按照007跳跃的顺序排列,输出路径上鳄鱼的坐标,每行输出一个坐标。
4. 数据处理:支持对输入数据的处理,数据之间相互独立,可分别计算路径的长度。
5. 输出排序:按照长度排序,输出所有可行的路径。
6. 图形化展示:提供图形化界面,以图的方式展示游戏的最短路径。
7. 用户界面友好:提供简单易用的操作界面,便于用户进行输入和输出操作。
3 系统设计
3.1系统功能设计
本系统的功能模块图如图1所示。
图1 系统功能模块图
3.2 开发与运行环境
硬件:Windows11
软件:Visual Studio 2022
4.测试
4.1文件功能测试
(1)输入数据界面
图2 输入数据界面
图3 输入数据界面(无路径输出)
图4 输入数据界面(一步到位)
(2)运行结果界面
图5 运行结果界面
图6 运行结果界面(无路径输出)
图7 运行结果界面(一步到位)
4.2显示功能测试
(1)结果路径图界面:
图8 路径图界面
图9 路径图界面(无路径输出)
图10 路径图界面(一步到位)
5.另外提醒
input.txt和output.txt的位置:
图11 文件存放结构图
6.源代码
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <cstring>
#include <graphics.h>
#include <algorithm>
using namespace std;
typedef struct Node
{
double x, y;
} Node;
int N, M; // N是鳄鱼数量,M是最大步长
Node node[1000];
int vis[1000];//记录结点是否被访问过
vector<vector<int>> results; // 存储所有可能的路径
vector<int> path; // 当前路径,存储从起点到当前节点经过的所有鳄鱼
Node circle_boundary; // 圆心坐标
Node box_boundary; // 方框边界的终点坐标
// 从岛跳到第 i 号鳄鱼是否可行
int first(int i, int M)
{
double p1 = pow(node[i].x, 2);
double p2 = pow(node[i].y, 2);
double m = p1 + p2;
double q = sqrt(m);
if (q - 7.5 <= M && m >= 56.25) //起始点
return 1;
return 0;
}
// 求第 i 号和第 j 号鳄鱼之间的距离,并判断
int jump(int i, int j)
{
double p1 = (node[i].x - node[j].x) * (node[i].x - node[j].x);
double p2 = (node[i].y - node[j].y) * (node[i].y - node[j].y);
// 圆的公式
if (p1 + p2 <= pow(M, 2))
return 1;
return 0;
}
// 求当前位置能否直接跳到鳄鱼池边缘
int key(int k)
{
if (abs(node[k].x) + M >= 50 || abs(node[k].y) + M >= 50) {
return 1;
}
return 0;
}
void draw_alligators()
{
// 绘制鳄鱼
setfillcolor(YELLOW);
for (int i = 0; i < N; i++)
{
solidcircle(node[i].x, node[i].y, 1);
}
}
// 绘制路径
void draw_path(const vector<int>& path)
{
// 保存圆点坐标
circle_boundary.x = 0;
circle_boundary.y = 0;
// 保存方框边界的终点坐标
box_boundary.x = 0;
box_boundary.y = 0;
if (path.size() < 1) return;
setlinecolor(BLACK);
//画圆心到第一个点的图
moveto(node[path[0]].x, node[path[0]].y);
lineto(circle_boundary.x, circle_boundary.y);
//更新box
box_boundary.x = node[path[0]].x;
box_boundary.y = node[path[0]].y;
for (int i = 1; i < path.size(); i++)
{
moveto(node[path[i - 1]].x, node[path[i - 1]].y);
lineto(node[path[i]].x, node[path[i]].y);
//更新box
box_boundary.x = node[path[i]].x;
box_boundary.y = node[path[i]].y;
}
//判断box的位置
if (abs(box_boundary.x) >= abs(box_boundary.y))
{
if (box_boundary.x <= 0)
{
moveto(box_boundary.x, box_boundary.y);
lineto(-50, box_boundary.y);
}
else
{
moveto(box_boundary.x, box_boundary.y);
lineto(50, box_boundary.y);
}
}
else {
if (box_boundary.y <= 0)
{
moveto(box_boundary.x, box_boundary.y);
lineto(box_boundary.x, -50);
}
else
{
moveto(box_boundary.x, box_boundary.y);
lineto(box_boundary.x, 50);
}
}
}
// 计算路径长度
double get_path_length(const vector<int>& path)
{
double length = 0.0;
double p = 0.0;//存放最后一个点到边的距离
double t = 0.0;//存放圆边缘到第一个点的距离
//判断t,p
//假如只有一个点
if (path.size() == 1)
{
t = sqrt((pow(node[path[0]].x, 2) + pow(node[path[0]].y, 2))) - 7.5;
box_boundary.x = node[path[0]].x;
box_boundary.y = node[path[0]].y;
}
else {
for (int i = 1; i < path.size(); i++)
{
int j = path[i - 1];
int k = path[i];
if (i == 1)//圆的边缘到第一个点的距离
{
t = sqrt((pow(node[path[i-1]].x, 2) + pow(node[path[i-1]].y, 2))) - 7.5;
box_boundary.x = node[j].x;
box_boundary.y = node[j].y;
length += sqrt(pow(node[k].x - node[j].x, 2) + pow(node[k].y - node[j].y, 2));
}
else {
length += sqrt(pow(node[k].x - node[j].x, 2) + pow(node[k].y - node[j].y, 2));
box_boundary.x = node[k].x;
box_boundary.y = node[k].y;
}
}
}
//判断box的位置
if (abs(box_boundary.x) >= abs(box_boundary.y))
{
p = abs(50 - abs(box_boundary.x));
}
else {
p = abs(50 - abs(box_boundary.y));
}
return length + p + t;
}
// 求所有路径中的最短路径
vector<int> get_shortest_path(const vector<vector<int>>& results)
{
double shortest_length = 100000.0; // 设一个较大的值作为初始值
vector<int> shortest_path;
for (const auto& path : results)
{
double length = get_path_length(path);
if (length <= shortest_length)
{
shortest_length = length;
shortest_path = path;
}
}
return shortest_path;
}
void bfs(int s)
{
queue<int> q;
vector<int> path(N, -1); // 用于记录路径
q.push(s);
vis[s] = 1;
while (!q.empty())
{
int u = q.front();
q.pop();
if (sqrt(pow(node[u].x, 2) + pow(node[u].y, 2)) <= 7.5)
{
continue; // 当前节点在圆心岛的范围内,直接跳过
}
if (key(u))
{
vector<int> tmp;
int p = u;
while (p != s)
{
tmp.push_back(p);
p = path[p];
}
tmp.push_back(s); // 加入起点
reverse(tmp.begin(), tmp.end()); // 反转路径
results.push_back(tmp); // 将当前路径加入结果集合
continue; // 找到一条路径后,继续搜索
}
for (int i = 0; i < N; i++)
{
if (!vis[i] && jump(u, i))
{
q.push(i);
vis[i] = 1;
path[i] = u;
}
}
}
}
int main()
{
initgraph(1000, 1000, EW_SHOWCONSOLE);// 设置窗口坐标系的原点和比例系数
setorigin(500, 500);
setaspectratio(5, 5); // 200个像素对应1米
// 绘制鳄鱼池和池心岛
setfillcolor(RGB(142, 150, 255));
fillrectangle(-50, -50, 50, 50); // 鳄鱼池边长为100米
setfillcolor(GREEN);
solidcircle(0, 0, 7.5); // 池心岛直径为15米
// 保存圆点坐标
circle_boundary.x = 0;
circle_boundary.y = 0;
ifstream input_file("input.txt");
ofstream output_file("output.txt");
// 检查输入文件是否打开成功
if (!input_file.is_open())
{
cout << "文件打开失败!" << endl;
return 1;
}
// 检查输出文件是否打开成功
if (!output_file.is_open())
{
cout << "文件打开失败!" << endl;
return 1;
}
// 读取鳄鱼数量和最大步长
input_file >> N >> M;
// 读取每只鳄鱼的位置坐标
for (int i = 0; i < N; i++)
{
input_file >> node[i].x >> node[i].y;
}
draw_alligators(); //画鳄鱼
//若可以一步跳出边界这里单独判断
if (M < 42.5) {
// 枚举起点
for (int i = 0; i < N; i++)
{
if (first(i, M))
{
memset(vis, 0, sizeof(vis));
bfs(i);
}
}
// 输出所有的路径(保存在文件中)
output_file << "所有可能的路径:" << endl;
for (int i = 0; i < results.size(); i++)
{
output_file << "第" << i + 1 << "组:" << endl;
output_file << "需要" << results[i].size() + 1 << "步:" << endl;
for (int j = 0; j < results[i].size(); j++)
{
output_file << "(" << node[results[i][j]].x << "," << node[results[i][j]].y << ") " << endl; // 输出路径的坐标
}
output_file << "路径长度:" << get_path_length(results[i]) << endl;
output_file << endl;
}
output_file << endl;
//若没有路径
if (results.size() == 0)
output_file << "007不存在可以逃出去的路径" << endl;
// 输出最短路径(图的形式)
if (!results.empty())
{
vector<int> shortest_path = get_shortest_path(results);
output_file << "最短路径: " << endl;
output_file << "需要" << shortest_path.size() + 1 << "步:" << endl;
for (int i = 0; i < shortest_path.size(); i++)
{
output_file << "(" << node[shortest_path[i]].x << "," << node[shortest_path[i]].y << ")" << endl; // 输出最短路径的坐标
}
double shortest_length = 1000000; //设置一个大点的值
shortest_length = get_path_length(shortest_path);
output_file << endl << "最短路径长度: " << shortest_length << endl;
draw_path(shortest_path);
}
input_file.close();
output_file.close();
}
else {
output_file << "所有可能的路径:" << endl;
output_file << "由于步长M:" << M << "大于等于可以从湖心岛一步跳跃到边界的距离:" << "42.5" << "这里路径/最短路径任意一条可行" << endl;
output_file << "需要" << 1 << "步" << endl;
output_file << endl << "最短路径长度: " << 42.5 << endl;
setlinecolor(RED);
moveto(box_boundary.x, box_boundary.y);
lineto(box_boundary.x, -50);
input_file.close();
output_file.close();
}
system("pause");
return 0;
}
7.结语
这个项目是楼主很早之前写的,之前没注意到一步跳出的情况,这次修改了想着就发出来,大家一起进步了。我看到csdn相关的这个项目还挺多的,我也贡献我的一份绵薄之力了。希望看到这篇文章的小伙伴,如果对你有帮助,给一个小小的赞吧!