c/c++公交管理系统(星穹轨道可视化EasyX)

  注:全代码由博主个人耗时两个星期开发,实现了基础的公交管理系统功能:1.前端用户查询站点以及查看所有站点。2.后端管理员权限,实现登录系统检验,添加路线以及删除路线。最重要的是使用EasyX实现了星穹轨道的启动以及抽卡界面以及音效

注意:代码可以直接拿,但是可视化部分的素材需收费(博主做可视化部分就做了一个多星期),需要的可以直接看文章结尾。

话不多说直接上代码:

#define  _CRT_SECURE_NO_WARNINGS
/*开发日志
* 1.明确公交管理系统分为后台和前台
* 2.前台的主要功能是给用户规划最佳路线
* 3.后台的主要功能是给管理员添加和删除路线使用
* 使用技术:
* 1.数据处理
*   1.1去重以及划分路线范围(路线范围后加)
*   1.2给每个站点进行编号便于处理
* 2.进行图的建立
* 3.使用Dijkstra算法给出最短路径
*
* 日志12-21 准备可视化开发
* 基本核心算法已经就绪
* 12-25开场动画准备完毕,开始进入第二阶段,查询功能判定
* 判断写好了接下来就是最后的输出了
* 遇到困难了,发现无法读取中文
* 好的困难解决将整个程序改为了unioncode字符集完成开发
* 又遇到难点了,怎么在文本框里换行输入啊,算了直接多设置几个控件就好了
* 好的管理员权限开发完毕
* 1-1开始开发使用者权限
* 1-2上午完成查询站点开发,使用了string转为wchar_t*的技术
* 
* 
* 与1-2晚上完成开发,完美收工!!!!!!
*/
#include<fstream>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<graphics.h>
#include<conio.h>
#include <Windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
using namespace std;

#define WHIDE 1870
#define HEIGHT 1000
//我们采用邻接表的方式储存数据
typedef struct First_Situation First_Situation;   //数组类型
typedef struct situation situation;  //小节点
typedef struct graph Graph;          //图
typedef struct Road  Road;           //线路

// 实现文本框控件
class EasyTextBox
{
private:
	int left = 0, top = 0, right = 0, bottom = 0;	// 控件坐标
	wchar_t* text = NULL;							// 控件内容
	size_t maxlen = 0;									// 文本框最大内容长度
public:
	void Create(int x1, int y1, int x2, int y2, int max)
	{
		maxlen = max;
		text = new wchar_t[maxlen];
		text[0] = 0;
		left = x1, top = y1, right = x2, bottom = y2;

		// 绘制用户界面
		Show();
	}

	~EasyTextBox()
	{
		if (text != NULL)
			delete[] text;
	}

	wchar_t* Text()
	{
		return text;
	}

	bool Check(int x, int y)
	{
		return (left <= x && x <= right && top <= y && y <= bottom);
	}

	// 绘制界面
	void Show()
	{
		// 备份环境值
		int oldlinecolor = getlinecolor();
		int oldbkcolor = getbkcolor();
		int oldfillcolor = getfillcolor();

		setlinecolor(LIGHTGRAY);		// 设置画线颜色
		setbkcolor(0xeeeeee);			// 设置背景颜色
		setfillcolor(0xeeeeee);			// 设置填充颜色
		fillrectangle(left, top, right, bottom);
		settextstyle(50, 0, L"黑体");
		outtextxy(left + 10, top + 5, text);

		// 恢复环境值
		setlinecolor(oldlinecolor);
		setbkcolor(oldbkcolor);
		setfillcolor(oldfillcolor);
	}

	void OnMessage()
	{
		// 备份环境值
		int oldlinecolor = getlinecolor();
		int oldbkcolor = getbkcolor();
		int oldfillcolor = getfillcolor();

		setlinecolor(BLACK);			// 设置画线颜色
		setbkcolor(WHITE);				// 设置背景颜色
		setfillcolor(WHITE);			// 设置填充颜色
		fillrectangle(left, top, right, bottom);
		outtextxy(left + 10, top + 5, text);

		int width = textwidth(text);	// 字符串总宽度
		int counter = 0;				// 光标闪烁计数器
		bool binput = true;				// 是否输入中

		ExMessage msg;
		while (binput)
		{
			while (binput && peekmessage(&msg, EX_MOUSE | EX_CHAR, false))	// 获取消息,但不从消息队列拿出
			{
				if (msg.message == WM_LBUTTONDOWN)
				{
					// 如果鼠标点击文本框外面,结束文本输入
					if (msg.x < left || msg.x > right || msg.y < top || msg.y > bottom)
					{
						binput = false;
						break;
					}
				}
				else if (msg.message == WM_CHAR)
				{
					size_t len = wcslen(text);
					switch (msg.ch)
					{
					case '\b':				// 用户按退格键,删掉一个字符
						if (len > 0)
						{
							text[len - 1] = 0;
							width = textwidth(text);
							counter = 0;
							clearrectangle(left + 10 + width, top + 1, right - 1, bottom - 1);
						}
						break;
					case '\r':				// 用户按回车键,结束文本输入
					case '\n':
						binput = false;
						break;
					default:				// 用户按其它键,接受文本输入
						if (len < maxlen - 1)
						{
							text[len++] = msg.ch;
							text[len] = 0;

							clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);	// 清除画的光标
							width = textwidth(text);				// 重新计算文本框宽度
							counter = 0;
							outtextxy(left + 10, top + 5, text);		// 输出新的字符串
						}
					}
				}
				peekmessage(NULL, EX_MOUSE | EX_CHAR);				// 从消息队列抛弃刚刚处理过的一个消息
			}

			// 绘制光标(光标闪烁周期为 20ms * 32)
			counter = (counter + 1) % 32;
			if (counter < 16) {
				line(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);				// 画光标
			}
			else
				clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);		// 擦光标

			// 延时 20ms
			Sleep(20);
		}

		clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);	// 擦光标

		// 恢复环境值
		setlinecolor(oldlinecolor);
		setbkcolor(oldbkcolor);
		setfillcolor(oldfillcolor);

		Show();
	}
};


struct situation {
	int number;
	situation* next;
};


struct First_Situation {
	string name;
	int number;
	situation* firstnode;
};


struct graph {
	First_Situation s[200];  //站点数组
	int sum;                //站点总数
};


struct Road {
	int sum;                //该线路上的站点总数
	int zhandian[100];      //用数组表示站点编号
	string name;            //也就是这条路叫什么
};


IMAGE sucai1;               //第一素材
IMAGE sucai2;               //第二素材
IMAGE sucai3;               //第三素材
IMAGE sucai4;               //第四素材
IMAGE sucai5;               //第五素材
IMAGE sucai6;               //第六素材
IMAGE sucai7;               //第七素材 
IMAGE sucai8;               //第八素材 
IMAGE first_donghua[150];   //第一动画   
IMAGE second_donghua[128];  //第二动画
IMAGE third_donghua[72];     //第三动画
IMAGE four_donghua[150];     //第四动画
Graph G;              //将图设计为全局变量
int Road_Sum;         //线路总数
map<string, int> m;   //这个是为了去重操作,并且直接找到站点对应的编号
vector<Road>  v;      //这个之所以用vector容器是为了之后便于删除路线
int parent[500];
int dist[500];
bool Visit[500];
int YS;              //用来判断账户密码是否是对的


//函数区
int CountRoads();     //统计有多少条路线
int CountLines();     //统计有多少行
void init();          //初始化
void Creat_Graph();   //创建图
bool next(int x, int y);          //判断两点是否相连
int findmindist(int dist[], bool visit[]);      //找出最小路径
void Dijkstra(int x, int dist[], int parent[], bool visit[]);  //开始计算
void printmy(int x, int y, int parent[], int dist[]);    //打印结果
void choice1();                                          //管理员权限
void choice2();                                          //用户权限
int Insert_Road();                                      //增加路线
int Delete_Road();                                      //删除路线
int check_in(char zhanghao[], char mima[]);                                     //检查登录函数

//可视化函数
void init_background();                             //图像初始化
void bofang1();                                     //第一播放动画
char bofang2();                                     //第二播放动画
void bofang3();                                     //第三播放动画
void bofang4();                                     //第四播放动画
char* wideCharToMultiByte(wchar_t* pWCStrKey);     //转化函数
wchar_t* multiByteToWideChar(const string& pKey);  //转化函数



//用来将wchar_t*转为string
char* wideCharToMultiByte(wchar_t* pWCStrKey) {
	//第一次调用确认转换后单字节字符串的长度,用于开辟空间
	int pSize = WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), NULL, 0, NULL, NULL);
	char* pCStrKey = new char[pSize + 1];
	//第二次调用将双字节字符串转换成单字节字符串
	WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), pCStrKey, pSize, NULL, NULL);
	pCStrKey[pSize] = '\0';
	return pCStrKey;
}


//将string转为wchar_t*
wchar_t* multiByteToWideChar(const string& pKey)
{
	const char* pCStrKey = pKey.c_str();
	//第一次调用返回转换后的字符串长度,用于确认为wchar_t*开辟多大的内存空间
	int pSize = MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, NULL, 0);
	wchar_t* pWCStrKey = new wchar_t[pSize];
	//第二次调用将单字节字符串转换成双字节字符串
	MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, pWCStrKey, pSize);
	return pWCStrKey;
}

//具体实现
//统计文件行数,用来统计有多少个站点
int CountLines()
{
	ifstream ReadFile;
	int n = 0;
	string tmp;
	ReadFile.open("站点信息.txt", ios::in);//ios::in 表示以只读的方式读取文件
	if (ReadFile.fail())//文件打开失败:返回0
	{
		return 0;
	}
	else//文件存在
	{
		while (getline(ReadFile, tmp, '\n'))
		{
			n++;
		}
		ReadFile.close();
		return n;
	}
}



//统计有多少条路线
int CountRoads() {
	ifstream ReadFile;
	int n = 0;
	string tmp;
	ReadFile.open("路线信息.txt", ios::in);//ios::in 表示以只读的方式读取文件
	if (ReadFile.fail())//文件打开失败:返回0
	{
		return 0;
	}
	else//文件存在
	{
		while (getline(ReadFile, tmp, '\n')) {
			if (tmp.compare("end") == 0)
				n++;
		}
		ReadFile.close();
	}
	return n;
}


//初始化开头动画
void init_background() {
	initgraph(WHIDE, HEIGHT, EX_SHOWCONSOLE);//显示控制台
	//加载背景资源
	wchar_t name[64];
	for (int i = 0; i < 150; i++) {
		swprintf(name,L"开始背景/背景 (%d).jpg",i+1 );
		loadimage(&first_donghua[i],name);
	}
	for (int i = 0; i < 128; i++) {
		swprintf(name,L"第二背景/背景s (%d).jpg", i + 1);
		loadimage(&second_donghua[i], name);
	}
	for (int i = 0; i < 72; i++) {
		swprintf(name,L"第三背景/背景t (%d).jpg", i + 1);
		loadimage(&third_donghua[i], name);
	}
	for (int i = 0; i < 150; i++) {
		swprintf(name, L"第四背景/背景 (%d).jpg", i + 1);
		loadimage(&four_donghua[i], name);
	}
	loadimage(&sucai1, L"素材库/素材1.jpg");
	loadimage(&sucai2, L"素材库/素材2.png");
	loadimage(&sucai3, L"素材库/素材3.png");
	loadimage(&sucai4, L"素材库/素材4.png");
	loadimage(&sucai5, L"素材库/素材5.png"); 
	loadimage(&sucai6, L"素材库/素材6.png");
	loadimage(&sucai7, L"素材库/素材7.png");
	loadimage(&sucai8, L"素材库/素材8.png");
}




void bofang1() {
	mciSendString(L"play 素材库/背景1.mp3 repeat", 0, 0, 0);
	int i = 0;
	int towards = 1;                      //方向,控制图片移动
	setbkmode(TRANSPARENT);
	settextcolor(WHITE);
	settextstyle(30, 0, L"黑体");
	while (1) {
		BeginBatchDraw();
		putimage(0, 0, &first_donghua[i]);
		putimage(720, 875, &sucai1);
		outtextxy(825, 800, L"按任意键进入程序");
		EndBatchDraw();
		if (i < 150 && towards == 1)
			i++;
		else if (i == 150) {
			towards = 0;
			i = 149;
		}
		else if (towards == 0 && i > 0)
			i--;
		else if (towards == 0 && i == 0) {
			i = 1;
			towards = 1;
		}

		Sleep(60);
		if (kbhit()) {
			getch();
			break;
		}
	}
}


//在这里面我们需要多设一个变量来返回具体的选择
char bofang2() {
	char choice;
	int i = 0;
	int towards = 1;
	settextcolor(WHITE);
	int flag = 0;//开关1
	int flag2 = 1;//开关2
	int flag3 = 0;//开关3
	int flag4 = 0;//开关4
	int m = 0;   //开关登录检测
	int sum1 = 0;  //第一次读入了多少字符
	int sum2 = 0; //第二次读入了多少字符
	int a1 = 1;   //控制读取给哪个
	int a2 = 0;
	int k = 0;
	char text1[60] = { '\0' };  //账号
	char text2[60] = { '\0' };  //密码
	while (1) {
		settextstyle(50, 0, L"黑体");

		BeginBatchDraw();

		putimage(0, 0, &second_donghua[i]);

		if (i == 42)
			mciSendString(L"play 素材库/列车之启.mp3", 0, 0, 0);


		if (i > 70 && flag == 0) {
			outtextxy(100, 100, L"1.管理员权限");
			outtextxy(100, 300, L"2.使用者权限");
			settextstyle(30, 0,L"黑体");
			outtextxy(100, 500, L"按下1或2即可使用");
		}



		if (flag == 1) {
			settextstyle(35, 0, L"黑体");
			settextcolor(BLACK);
			putimage(550, 250, &sucai2);
			if (flag2 == 1) {
				outtextxy(610, 410, L"请在此输入您的账号");
			}
			if (flag3 == 1) {
				outtextxy(610, 490, L"请在此输入您的密码");
			}
			if (flag4 == 1) {
				settextstyle(20, 0, L"宋体");
				settextcolor(BLACK);
				outtextxy(610, 550, L"账号或着密码错误,请重试");
				outtextxy(610, 600, L"按Y重新开始");
				if (kbhit()) {
					if (getch() == 'Y') {
						break;
					}
				}
			}
			if (a1 == 1 && a2 == 0) {
				//这里我们需要循环打印之前的内容
				for (int j = 0; j < k; j++) {
					outtextxy(610 + j * 17, 410, text1[j]);
				}

				char ch;
				if (kbhit()) {
					flag2 = 0;
					ch = getch();
					if (ch == 13) {
						flag3 = 1;
						a1 = 0;
						a2 = 1;
						sum1 = k;
						k = 0;
					}
					else if (ch == 8) {
						if (k > 0) {
							text1[k] = '\0';
							k--;
						}
					}
					else if (ch >= 32 && ch <= 126) {
						if (k < 60) {
							text1[k] = ch;
							k++;
						}
					}
				}
			}


			else if (a1 == 0 && a2 == 1 && flag4 == 0) {
				for (int j = 0; j < sum1; j++) {
					outtextxy(610 + j * 17, 410, text1[j]);
				}
				for (int j = 0; j < k; j++) {
					outtextxy(610 + j * 17, 490, text2[j]);
				}

				char ch;
				if (kbhit()) {
					flag3 = 0;
					ch = getch();
					if (ch == 13) {
						m = 1;
					}
					else if (ch == 8) {
						if (k > 0) {
							text2[k] = '\0';
							k--;
						}
					}
					else if (ch >= 32 && ch <= 126) {
						if (k < 60) {
							text2[k] = ch;
							k++;
						}
					}
				}
			}
		}

		EndBatchDraw();


		//这一块是为了循环播放背景图片
		if (i < 127 && towards == 1)
			i++;
		else if (i == 127) {
			towards = 0;
			i = 126;
		}
		else if (towards == 0 && i > 75)
			i--;
		else if (towards == 0 && i == 75) {
			i = 76;
			towards = 1;
		}
		Sleep(60);


		if (m == 1) {
			if (check_in(text1, text2) == 1) {
				bofang3();
				YS = 1;
				return choice;
			}
			else {
				flag4 = 1;
				YS = 0;
			}
		}


		if (flag != 1) {
			if (kbhit()) {
				char ch = getch();
				choice = ch;     //返回值
				if (ch == '1')
					flag = 1;
				else if (ch == '2') {
					bofang3();
					return choice;
				}
			}
		}
	}
}




void bofang3() {
	int i = 0;
	while (i < 72) {
		putimage(0, 0, &third_donghua[i]);
		Sleep(60);
		if (i == 8) {
			mciSendString(L"play 素材库/开动.mp3", 0, 0, 0);
		}
		i++;
	}
	mciSendString(L"stop 素材库/背景1.mp3", 0, 0, 0);
}



void bofang4() {
	mciSendString(L"play 素材库/抽卡.mp3", 0, 0, 0);
	for (int i = 0; i < 150; i++) {
		putimage(0, 0, &four_donghua[i]);

		//这里我们加一个一键跳过的功能
		if (kbhit()) {
			char ch = getch();
			if (ch == ' ') {
				mciSendString(L"stop 素材库/抽卡.mp3", 0, 0, 0);
				break;
			}
		}
		Sleep(100);
	}
}

//初始化
void init() {
	G.sum = CountLines();
	Road_Sum = CountRoads();
	ifstream  fin;
	fin.open("站点信息.txt", ios::in);
	if (!fin)
	{
		std::cerr << "cannot open the file";
	}
	for (int i = 0; i < G.sum; i++) {
		int a;
		string s;
		fin >> a >> s;
		m.insert(pair<string, int>(s, a));
		G.s[i].number = a;
		G.s[i].name = s;
	}
	fin.close();
}

//创建图
void Creat_Graph() {
	ifstream  fin;
	fin.open("路线信息.txt", ios::in);
	if (!fin)
	{
		std::cerr << "cannot open the file";
	}
	for (int i = 0; i < Road_Sum; i++) {
		int sum = 0;
		Road r;
		r.sum = 0;
		fin >> r.name;
		string t;
		fin >> t;
		while (t.compare("end") != 0) {
			map<string, int>::iterator iter;
			iter = m.find(t);
			if (iter != m.end()) {
				r.zhandian[sum++] = iter->second;
				fin >> t;
			}
		}
		r.sum = sum;
		//结束之后直接开始点之间相连
		for (int j = 0; j < r.sum - 1; j++) {
			int a1 = r.zhandian[j];
			int a2 = r.zhandian[j + 1];
			situation* s1 = (situation*)malloc(sizeof(situation));
			situation* s2 = (situation*)malloc(sizeof(situation));
			s2->number = G.s[a2].number;
			s1->number = G.s[a1].number;
			s2->next = G.s[a1].firstnode;
			G.s[a1].firstnode = s2;
			s1->next = G.s[a2].firstnode;
			G.s[a2].firstnode = s1;
		}
		v.push_back(r); //将路线添加到容器里
	}
	fin.close();
}

bool next(int x, int y) {   //为了判断这两个点是否相连
	situation* p;
	for (p = G.s[x].firstnode; p; p = p->next) {
		if (p->number == y)
			return true;
	}
	return false;
}

int findmindist(int dist[], bool visit[]) {    //找最小的路径
	int minv, v;
	int mindist = 999;
	for (v = 0; v < G.sum; v++) {
		if (visit[v] == false && dist[v] < mindist) {
			mindist = dist[v];
			minv = v;
		}
	}
	if (mindist < 999) {
		return minv;
	}

	else
		return -1;//表示不存在
}


void Dijkstra(int x, int dist[], int parent[], bool visit[])    //Dijkstra算法,传入源顶点
{
	int v, w;
	situation* m;
	//初始化
	for (int i = 0; i < G.sum; i++) {
		dist[i] = 999;
		parent[i] = -1;
		visit[i] = false;
	}
	for (m = G.s[x].firstnode; m; m = m->next) {  //只找相邻点
		dist[m->number] = 1;
		parent[m->number] = x;
	}
	//先将起始点收入集合
	dist[x] = 0;
	visit[x] = true;

	while (findmindist(dist, visit) != -1) {
		v = findmindist(dist, visit);
		visit[v] = true;
		for (w = 0; w < G.sum; w++) {
			if (visit[w] == false && next(v, w) == true) {
				if (dist[w] > dist[v] + 1) {
					dist[w] = dist[v] + 1;
					parent[w] = v;
				}
			}
		}
	}
}

void printmy(int x, int y, int parent[], int dist[]) {
	putimage(0, 0, &sucai7);
	string sresult="";                        //用来可视化
	int x1 = 0;
	int y1 = 100;
	int s = 0;
	int nums[100];
	int k = 0;
	int t = y;
	//这里犯了个致命的错误,如果两个点之间没有相连就会一直死循环
	if (dist[t] == 999) {
		settextcolor(WHITE);
		settextstyle(50, 0, L"黑体");
		outtextxy(900, 500, L"不在已知路线中,请联系管理员");
		cout << "不在已知路线中,请联系管理员" << endl;
		return;
	}
	while (parent[y] != x) {
		nums[k++] = parent[y];
		y = parent[y];
	}
	sresult = sresult.append("从");
	sresult = sresult.append(G.s[x].name);
	sresult = sresult.append("到");
	sresult = sresult.append(G.s[t].name);
	sresult = sresult.append("需要");
	sresult = sresult.append(to_string(dist[t]));
	sresult = sresult.append("站");
	cout << "从" << G.s[x].name << "到" << G.s[t].name << "需要" << dist[t] << "站" << endl;


	settextcolor(WHITE);
	settextstyle(50, 0, L"黑体");
	outtextxy(0, 50, multiByteToWideChar(sresult));
	sresult = "";

	//这里我们做一个小小的改进,也就是我们希望得到啊具体的路线信息
	//也就是说两个以上相邻的站点才有可能是一路
	//我们开辟一个新的数组来储存线路过程
	//重要算法
	int result[100];
	result[0] = x;
	for (int i = 1; i <= k; i++) {
		result[i] = nums[k - i];
	}
	result[k + 1] = t;
	int towards = 0;//表示方向,0表示向后查找,1表示向前查找
	int i = 0;
	while (1) {
		for (int j = 0; j < v.size(); j++) {             //线路容器
			int m = 0;   //开关
			int num = 0;
			//先要找到首个站点的所在容器
			int n = 0;
			//这里注意如果从开头查找就要进行第二次判断
			while (n < v[j].sum) {
				if (v[j].zhandian[n] == result[i] && i == 0) {     //先找到首个站点在容器中的位置n
					i++;
					num++;
					m = 2;
					break;
				}
				//就是上一次路线断了之后查找第一个站点位置,他又可能往回坐车,所以或着也需要判断
				else if (v[j].zhandian[n - 1] == result[i - 1] && n > 0 && v[j].zhandian[n] == result[i] && i > 0) {
					cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
					sresult = sresult.append("乘坐");
					sresult = sresult.append(v[j].name);
					sresult = sresult.append(" ");
					sresult = sresult.append(G.s[result[i - 1]].name);
					sresult = sresult.append("->");
					sresult = sresult.append(G.s[result[i]].name);

					i++;
					num = num + 2;
					m = 1;
					break;
				}

				else if (v[j].zhandian[n - 1] == result[i] && n > 0 && v[j].zhandian[n] == result[i - 1] && i > 0) {
					cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
					sresult = sresult.append("乘坐");
					sresult = sresult.append(v[j].name);
					sresult = sresult.append(" ");
					sresult = sresult.append(G.s[result[i - 1]].name);
					sresult = sresult.append("->");
					sresult = sresult.append(G.s[result[i]].name);


					i++;
					n--;
					num = num + 2;
					m = 1;
					break;
				}
				else {
					n++;
				}
			}

			if (n >= v[j].sum)
				continue;

			//这里之所有不n++是因为可能是往前找的
			if (result[i] == v[j].zhandian[n + 1] && m == 1) {
				n++;
				cout << "->" << G.s[result[i]].name;
				sresult=sresult.append("->");
				sresult=sresult.append(G.s[result[i]].name);

				towards = 0;
				num++;
				i++;
				n++;
				while (n < v[j].sum) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n++;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}
			else if (result[i] == v[j].zhandian[n + 1] && m == 2) {
				n++;
				cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
				sresult = sresult.append("乘坐");
				sresult = sresult.append(v[j].name);
				sresult = sresult.append(" ");
				sresult = sresult.append(G.s[result[i - 1]].name);
				sresult = sresult.append("->");
				sresult = sresult.append(G.s[result[i]].name);

				towards = 0;
				num++;
				i++;
				n++;
				while (n < v[j].sum) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n++;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}


			//向前查找
			if (v[j].zhandian[n - 1] == result[i] && m == 1) {
				n--;
				cout << "->" << G.s[result[i]].name;
				sresult = sresult.append("->");
				sresult = sresult.append(G.s[result[i]].name);

				towards = 1;
				num++;
				i++;
				n--;
				while (n < v[j].sum && n >= 0) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n--;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}
			else if (v[j].zhandian[n - 1] == result[i] && m == 2) {
				n--;
				cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
				sresult = sresult.append("乘坐");
				sresult = sresult.append(v[j].name);
				sresult = sresult.append(" ");
				sresult = sresult.append(G.s[result[i - 1]].name);
				sresult = sresult.append("->");
				sresult = sresult.append(G.s[result[i]].name);


				towards = 1;
				num++;
				i++;
				n--;
				while (n < v[j].sum && n >= 0) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n--;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}
			if (num >= 2) {
				settextcolor(WHITE);
				settextstyle(30, 0, L"黑体");
				cout << endl;
				if (10 + y1 * s >= 950) {
					x1 = x1 + 600;
					s = 0;
				}
				outtextxy(x1,200+ y1 * s, multiByteToWideChar(sresult));
				s++;
				sresult = "";                  //一次路线结束清空
				break;
			}
		}
		if (i > k + 1) {
			break;
		}
	}
}


//添加路线
//这边有一个点我们需要知道已有的站点中是否存在该站点
//如果有就不做操作
//如果没有就需要写入新的站点
//对于我们所使用的算法即使重复添加路线也并不会产生影响
//在读取的同时还要进行追加写入
int Insert_Road() {
	putimage(0, 0, &sucai4);
	settextcolor(BLACK);
	EasyTextBox roadname;
	EasyTextBox roadnumber;
	EasyTextBox roadguocheng1;
	EasyTextBox roadguocheng2;
	EasyTextBox roadguocheng3;
	EasyTextBox roadguocheng4;
	roadname.Create(1230, 284, 1580,360 ,10);						// 创建用户名文本框控件
	roadnumber.Create(1620, 284,1740, 360, 10);						// 创建用户名文本框控件
	roadguocheng1.Create(1230, 500, 1778, 577, 50);						// 创建用户名文本框控件
	roadguocheng2.Create(1230, 577, 1778, 654, 50);						// 创建用户名文本框控件
	roadguocheng3.Create(1230, 654, 1778, 731, 50);						// 创建用户名文本框控件
	roadguocheng4.Create(1230, 731, 1778, 808, 50);						// 创建用户名文本框控件
	Road_Sum++;
	fstream  fin;     //写入格式
	fin.open("路线信息.txt", ios::out | ios::app);
	Road r;
	int n;
	int flag = 0;        //用于开关,也就是判断是否有该站点,1表示有
	cout << "请输入您添加路线的名称" << endl;
	ExMessage msg;
	while (true)
	{
		msg = getmessage(EX_MOUSE);			// 获取消息输入

		if (msg.message == WM_LBUTTONDOWN)
		{
			// 判断控件
			if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();

			// 判断控件
			if (roadnumber.Check(msg.x, msg.y))	roadnumber.OnMessage();

			// 判断控件
			if (roadguocheng1.Check(msg.x, msg.y))	roadguocheng1.OnMessage();
			if (roadguocheng2.Check(msg.x, msg.y))	roadguocheng2.OnMessage();
			if (roadguocheng3.Check(msg.x, msg.y))	roadguocheng3.OnMessage();
			if (roadguocheng4.Check(msg.x, msg.y))	roadguocheng4.OnMessage();
		}
		if (kbhit()) {
			char ch = getch();
			if (ch == 13)
				break;
		}
	}
	string Road_name;
	string Road_number;
	string Road_guocheng1;
	string Road_guocheng2;
	string Road_guocheng3;
	string Road_guocheng4;
	Road_name = wideCharToMultiByte(roadname.Text());
	Road_number= wideCharToMultiByte(roadnumber.Text());
	Road_guocheng1= wideCharToMultiByte(roadguocheng1.Text());
	Road_guocheng2 = wideCharToMultiByte(roadguocheng2.Text());
	Road_guocheng3 = wideCharToMultiByte(roadguocheng3.Text());
	Road_guocheng4 = wideCharToMultiByte(roadguocheng4.Text());

	//这边最好加一个判断,如果添加的线路已经存在就直接退出
	for (int i = 0; i < v.size(); i++) {
		if (Road_name.compare(v[i].name) == 0) {
			cout << "线路已存在,请重新输入" << endl;
			return 0;
		}
	}
	fin << Road_name << endl;
	r.name = Road_name;
	cout << "请输入您添加路线的站点数量" << endl;
	
	//如果输入不是数字会报错
	n = stoi(Road_number);
	r.sum = n;
	cout << "请逐个输入您的路线" << endl;
	string station_name[100];
	int i = 0;

	for (int j = 0; j< Road_guocheng1.length(); j++) {
		if (Road_guocheng1[j] == ' ' && Road_guocheng1[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng1[i] == ' ' && Road_guocheng1[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng1[j];                      //将分割好的字符串放到K数组里
	}
	i++;

	for (int j = 0; j < Road_guocheng2.length(); j++) {
		if (Road_guocheng2[j] == ' ' && Road_guocheng2[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng2[i] == ' ' && Road_guocheng2[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng2[j];                      //将分割好的字符串放到K数组里
	}
	i++;

	for (int j = 0; j < Road_guocheng3.length(); j++) {
		if (Road_guocheng3[j] == ' ' && Road_guocheng3[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng3[i] == ' ' && Road_guocheng3[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng3[j];                      //将分割好的字符串放到K数组里
	}
	i++;

	for (int j = 0; j < Road_guocheng4.length(); j++) {
		if (Road_guocheng4[j] == ' ' && Road_guocheng4[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng4[i] == ' ' && Road_guocheng4[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng4[j];                      //将分割好的字符串放到K数组里
	}
	for (int j = 0; j < n; j++) {
		fin << station_name[j] << endl;
	}
	//结束后不要忘记加end
	fin << "end" << endl;
	fin.close();
	fin.open("站点信息.txt", ios::out | ios::app);
	for (int i = 0; i < n; i++) {
		flag = 0;
		for (int j = 0; j < G.sum; j++) {
			if (station_name[i].compare(G.s[j].name) == 0) {
				flag = 1;
				break;
			}
		}
		if (flag == 0) {
			fin << G.sum << " " << station_name[i] << endl;
			G.s[G.sum].number = G.sum;
			G.s[G.sum].name = station_name[i];
			m.insert(pair<string, int>(station_name[i], G.sum++));
		}
	}
	fin.close();
	for (int i = 0; i < n; i++) {
		map<string, int>::iterator iter;
		iter = m.find(station_name[i]);
		r.zhandian[i] = iter->second;
	}
	v.push_back(r);

	//接下来直接插入线路
	for (int j = 0; j < r.sum - 1; j++) {
		int a1 = r.zhandian[j];
		int a2 = r.zhandian[j + 1];
		situation* s1 = (situation*)malloc(sizeof(situation));
		situation* s2 = (situation*)malloc(sizeof(situation));
		s2->number = G.s[a2].number;
		s1->number = G.s[a1].number;
		s2->next = G.s[a1].firstnode;
		G.s[a1].firstnode = s2;
		s1->next = G.s[a2].firstnode;
		G.s[a2].firstnode = s1;
	}
	cout << "添加成功" << endl;
	return 1;
}




//首先我们要明确,在删除路线的时候站点不需要发生改变
//我们这里的需求是删除某一条线路,线路名字已知
//这里就体现出了重复建立节点的好处,即使删除了两点之间的节点也不会导致其他有相同节点的路线发生变化
int Delete_Road() {
	putimage(0, 0, &sucai5);
	settextcolor(BLACK);
	EasyTextBox roadname;
	roadname.Create(1250, 360, 1755, 455, 10);						// 创建用户名文本框控件
	ExMessage msg;
	while (true)
	{
		msg = getmessage(EX_MOUSE);			// 获取消息输入

		if (msg.message == WM_LBUTTONDOWN)
		{
			// 判断控件
			if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();
		}
		if (kbhit()) {
			char ch = getch();
			if (ch == 13)
				break;
		}
	}


	int flag = 0;          //开关,用来判断是否存在该路线
	Road r;
	Road_Sum--;
	cout << "请输入你要删除的路线" << endl;
	string tmp;
	tmp= wideCharToMultiByte(roadname.Text());
	auto it = v.begin();
	while (it != v.end()) {
		if (it->name.compare(tmp) == 0) {
			r = *it;
			v.erase(it);     //删除这个路径
			flag = 1;
			break;
		}
		it++;
	}
	if (flag == 0) {
		cout << "抱歉,不存在该路线" << endl;
		return 0;
	}
	//这里先写从图中删除数据,明确是两两之间进行删除
	//要从某站点中开始找,即遍历链表找到与之对应的站点,进行节点删除
	//我们要知道一个节点的前驱节点
	else {
		for (int i = 0; i < r.sum - 1; i++) {
			int a1 = r.zhandian[i];
			int a2 = r.zhandian[i + 1];
			situation* tmp1 = G.s[a1].firstnode;    //前驱节点
			situation* tmp2 = tmp1->next;           //后驱节点
			//开头就等于
			if (tmp1->number == a2) {
				G.s[a1].firstnode = tmp1->next;
				free(tmp1);//释放空间
				continue;
			}
			else {
				while (tmp2 != NULL) {
					if (tmp2->number == a2) {
						tmp1->next = tmp2->next;
						free(tmp2);
						break;
					}
					else {
						tmp1 = tmp1->next;
						tmp2 = tmp2->next;
					}
				}
			}
			//此时第一个节点已经完成删除
			//开始第二个节点
			tmp1 = G.s[a2].firstnode;
			tmp2 = tmp1->next;
			if (tmp1->number == a1) {
				G.s[a2].firstnode = tmp1->next;
				free(tmp1);//释放空间
				continue;
			}
			else {
				while (tmp2 != NULL) {
					if (tmp2->number == a2) {
						tmp1->next = tmp2->next;
						free(tmp2);
						break;
					}
					else {
						tmp1 = tmp1->next;
						tmp2 = tmp2->next;
					}
				}
			}
		}


		//接下来我们需要进行文件的删除
		//原理很简单,就是我们找出需要删除的路线,将其他路线全部读取到tmp中间文件中,然后再拷贝回来即可
		string s;
		ifstream fin;
		fstream Fin;
		Fin.open("tmp.txt", ios::out | ios::app);
		fin.open("路线信息.txt", ios::in);
		while (getline(fin, s, '\n')) {
			if (s.compare(tmp) != 0) {
				Fin << s << endl;
			}
			else {
				while (getline(fin, s, '\n')) {
					if (s.compare("end") == 0)
						break;
				}
			}
		}
		Fin.close();
		fin.close();
		ofstream file_writer1("路线信息.txt", ios_base::out);//清空文本文件
		Fin.open("路线信息.txt", ios::out | ios::app);
		fin.open("tmp.txt", ios::in);
		//重新写入
		while (getline(fin, s, '\n')) {
			Fin << s << endl;
		}
		//清空tmp文件
		ofstream file_writer2("tmp.txt", ios_base::out);//清空文本文件
		Fin.close();
		fin.close();
		cout << "删除成功" << endl;
		return 1;
	}
}

 

int check_in(char zhanghao[], char mima[]) {
	char check_zhanghao[60] = { 'Z','H','Z','S','S','B' };
	char check_mima[60] = { 'Y','H','W','Z','N','B' };
	if (strcmp(zhanghao, check_zhanghao) == 0 && strcmp(mima, check_mima) == 0) {
		cout << "登录成功" << endl;
		return 1;
	}
	else {
		cout << "账号或密码错误" << endl;
		return 0;
	}
}



void choice1() {
	while (1) {
		putimage(0, 0, &sucai3);
		char choice;
		choice = getch();
		if (choice == '1') {
			int flag = Insert_Road();
			settextcolor(WHITE);
			settextstyle(30, 0, L"黑体");
			if (flag == 0)
				outtextxy(1250, 400, L"线路已存在,请重新输入");
			else
				outtextxy(1250, 825, L"添加成功");
		}
		else if (choice == '2') {
			int flag = Delete_Road();
			settextcolor(WHITE);
			settextstyle(30, 0, L"黑体");
			if (flag == 0)
				outtextxy(1250, 825, L"抱歉,不存在该路线");
			else
				outtextxy(1250, 825, L"删除成功");
		}
		else if (choice == '3') {
			break;
		}
		outtextxy(1250, 900, L"是否退出管理员权限,按Y确认,按N继续");
		cout << "是否退出管理员权限,按Y确认" << endl;
		char ys;
		int yss = 0;
		while (1) {
			ys = getch();
			if (ys == 'Y') {
				yss = 0;
				break;
			}
			else if (ys == 'N') {
				yss = 1;
				break;
			}
		}
		if (yss == 0) {
			break;
		}
		else
			continue;
	}
}



void choice2() {    //也就是用户权限
	string start;
	string end;
	while (1) {
		putimage(0, 0, &sucai6);
		cout << "请选择您所需要的服务" << endl;
		cout << "1.查询最短路线" << endl;
		cout << "2.查看所有站点" << endl;
		char ch = getch();


		if (ch == '1') {
			putimage(0, 0, &sucai8);
			settextcolor(BLACK);
			settextstyle(40, 0, L"黑体");
			EasyTextBox startstation;
			EasyTextBox endstation;
			startstation.Create(1310, 450, 1750, 540, 10);						// 创建用户名文本框控件
			endstation.Create(1310, 640, 1750, 730, 10);						// 创建用户名文本框控件
			cout << "请输入您的起始点和终点,例如:博物馆 桔园" << endl;
			ExMessage msg;
			while (true)
			{
				msg = getmessage(EX_MOUSE);			// 获取消息输入

				if (msg.message == WM_LBUTTONDOWN)
				{
					// 判断控件
					if (startstation.Check(msg.x, msg.y))	startstation.OnMessage();
					if (endstation.Check(msg.x, msg.y))	endstation.OnMessage();
				}
				if (kbhit()) {
					char ch = getch();
					if (ch == 13)
						break;
				}
			}
			start= wideCharToMultiByte(startstation.Text());
			end= wideCharToMultiByte(endstation.Text());
			map<string, int>::iterator iter1;
			map<string, int>::iterator iter2;
			iter1 = m.find(start);
			iter2 = m.find(end);
				if (iter1 != m.end() && iter2 != m.end()) {
					Dijkstra(iter1->second, dist, parent, Visit);
					bofang4();
					printmy(iter1->second, iter2->second, parent, dist);
					}
				else {
					settextcolor(WHITE);
					settextstyle(30, 0, L"黑体");
					outtextxy(1310, 830, L"输入的地址有问题,请重新输入");
					cout << "输入的地址有问题,请重新输入" << endl;
					}
				}


		else if (ch == '2') {
			bofang4();
			putimage(0, 0, &sucai7);
			settextcolor(WHITE);
			settextstyle(30, 0, L"黑体");
			int x = 200;
			int y = 30;
			int k = 0;
			for (int i = 0; i < G.sum; i++) {
				if (10 + y * k >= 950) {
					x = x + 300;
					k = 0;
				}
				outtextxy(x, 10 + y * k, multiByteToWideChar(G.s[i].name));
				k++;
				cout << G.s[i].name << endl;
			}
		}


		else {
			settextcolor(WHITE);
			settextstyle(30, 0, L"黑体");
			outtextxy(1265, 800, L"选择有误,请重新选择");
		}
		   
		    settextcolor(WHITE);
		    settextstyle(30, 0, L"黑体");
			outtextxy(1265, 900, L"是否退出? 按Y确认,按N则继续查询");
			cout << "是否退出? 按Y确认,按任意键则继续查询" << endl;
			char ys;
			int yss = 0;
			while (1) {
				ys = getch();
				if (ys == 'Y') {
					yss = 0;
					break;
				}
				else if (ys == 'N') {
					yss = 1;
					break;
				}
			}
			if (yss == 0) {
				break;
			}
			else
			 continue;
	}
}


//主函数
int main() {
	init();          //初始化
	Creat_Graph();   //创建图
	init_background();  //初始化图片
	bofang1();
	int f = 0;      //总声音开关,就是经过一次循环后这个会变成1,然后再while循环开始时播放音乐
	while (1) {
		if(f==1)
			mciSendString(L"play 素材库/背景1.mp3 repeat", 0, 0, 0);
		char choice = bofang2();
		if (choice == '2') {
			choice2();
			f = 1;
		}
		else if (choice == '1') {
			if (YS == 1)
				choice1();
			f = 1;
		}
	}
}

 

 接下来讲解一下大概的算法思想(不想看的可以跳过):

1.首先是文件的读取和写入

  文件读取这一方法在这个程序中占有不可取代的地位,首先需要知道c++中的文件读取方式,ifstream是读取流,fstream是输入流,具体的使用方法可以去网上搜索,这边介绍两种最简单的使用方法

以下是写入方式,该写入方法会覆盖原本的文件,相当于从头开始写入,并不建议

fstream  fin;     //写入格式
fin.open("路线信息.txt", ios::out);

该方法属于写入方法的追加写入,也是比较常用的方法 

fstream  fin;     //写入格式
fin.open("路线信息.txt", ios::out | ios::app);

 以下是读取方式,比较简单

ifstream ReadFile;
ReadFile.open("路线信息.txt", ios::in);//ios::in 表示以只读的方式读取文件

2.初始化图

  文章中的初始化函数包括初始化数据以及创建图

//初始化
void init() {
	G.sum = CountLines();
	Road_Sum = CountRoads();
	ifstream  fin;
	fin.open("站点信息.txt", ios::in);
	if (!fin)
	{
		std::cerr << "cannot open the file";
	}
	for (int i = 0; i < G.sum; i++) {
		int a;
		string s;
		fin >> a >> s;
		m.insert(pair<string, int>(s, a));
		G.s[i].number = a;
		G.s[i].name = s;
	}
	fin.close();
}

//创建图
void Creat_Graph() {
	ifstream  fin;
	fin.open("路线信息.txt", ios::in);
	if (!fin)
	{
		std::cerr << "cannot open the file";
	}
	for (int i = 0; i < Road_Sum; i++) {
		int sum = 0;
		Road r;
		r.sum = 0;
		fin >> r.name;
		string t;
		fin >> t;
		while (t.compare("end") != 0) {
			map<string, int>::iterator iter;
			iter = m.find(t);
			if (iter != m.end()) {
				r.zhandian[sum++] = iter->second;
				fin >> t;
			}
		}
		r.sum = sum;
		//结束之后直接开始点之间相连
		for (int j = 0; j < r.sum - 1; j++) {
			int a1 = r.zhandian[j];
			int a2 = r.zhandian[j + 1];
			situation* s1 = (situation*)malloc(sizeof(situation));
			situation* s2 = (situation*)malloc(sizeof(situation));
			s2->number = G.s[a2].number;
			s1->number = G.s[a1].number;
			s2->next = G.s[a1].firstnode;
			G.s[a1].firstnode = s2;
			s1->next = G.s[a2].firstnode;
			G.s[a2].firstnode = s1;
		}
		v.push_back(r); //将路线添加到容器里
	}
	fin.close();
}

其实这些学过数据结构的同学应该都是会的,但是这边我要讲一下为什么这个项目中采取了邻接表的形式而不是邻接矩阵,我认为主要有以下两点考虑:

1.公交管理系统中的数据大概率是一个稀疏矩阵,如果使用矩阵的形式储存就会浪费很多空间。

2.在后续的添加和删除路线时,矩阵会直接切断两个站点之间的连接,可能导致其他路线无法正常进行,而使用邻接表时就会重复添加某个站点(这个站点存在于多条路线中),注意,重复添加站点并不会导致程序出现问题,相反,在删除站点时,由于站点多次创建,删除一个依旧会有该站点存在,就不会导致bug出现,这也是一个核心思想。

3.Dijkstra算法

整个算法由三个部分组成

bool next(int x, int y) {   //为了判断这两个点是否相连
	situation* p;
	for (p = G.s[x].firstnode; p; p = p->next) {
		if (p->number == y)
			return true;
	}
	return false;
}

int findmindist(int dist[], bool visit[]) {    //找最小的路径
	int minv, v;
	int mindist = 999;
	for (v = 0; v < G.sum; v++) {
		if (visit[v] == false && dist[v] < mindist) {
			mindist = dist[v];
			minv = v;
		}
	}
	if (mindist < 999) {
		return minv;
	}

	else
		return -1;//表示不存在
}


void Dijkstra(int x, int dist[], int parent[], bool visit[])    //Dijkstra算法,传入源顶点
{
	int v, w;
	situation* m;
	//初始化
	for (int i = 0; i < G.sum; i++) {
		dist[i] = 999;
		parent[i] = -1;
		visit[i] = false;
	}
	for (m = G.s[x].firstnode; m; m = m->next) {  //只找相邻点
		dist[m->number] = 1;
		parent[m->number] = x;
	}
	//先将起始点收入集合
	dist[x] = 0;
	visit[x] = true;

	while (findmindist(dist, visit) != -1) {
		v = findmindist(dist, visit);
		visit[v] = true;
		for (w = 0; w < G.sum; w++) {
			if (visit[w] == false && next(v, w) == true) {
				if (dist[w] > dist[v] + 1) {
					dist[w] = dist[v] + 1;
					parent[w] = v;
				}
			}
		}
	}
}

以上是基于邻接表的迪杰斯特拉算法,函数结束后,dist中储存着源节点到每个节点的最短距离,这边不建议采用弗洛伊德算法大家懂得都懂。

4.找出需要经过的路线并打印

这部分算法是一个难点,你需要沿着某一条路线一直走,然后在某个点下车换成另一辆车,详细代码有点屎山,注释都写了有需要的小伙伴可以研究一下(忽略可视化部分)

void printmy(int x, int y, int parent[], int dist[]) {
	putimage(0, 0, &sucai7);
	string sresult="";                        //用来可视化
	int x1 = 0;
	int y1 = 100;
	int s = 0;
	int nums[100];
	int k = 0;
	int t = y;
	//这里犯了个致命的错误,如果两个点之间没有相连就会一直死循环
	if (dist[t] == 999) {
		settextcolor(WHITE);
		settextstyle(50, 0, L"黑体");
		outtextxy(900, 500, L"不在已知路线中,请联系管理员");
		cout << "不在已知路线中,请联系管理员" << endl;
		return;
	}
	while (parent[y] != x) {
		nums[k++] = parent[y];
		y = parent[y];
	}
	sresult = sresult.append("从");
	sresult = sresult.append(G.s[x].name);
	sresult = sresult.append("到");
	sresult = sresult.append(G.s[t].name);
	sresult = sresult.append("需要");
	sresult = sresult.append(to_string(dist[t]));
	sresult = sresult.append("站");
	cout << "从" << G.s[x].name << "到" << G.s[t].name << "需要" << dist[t] << "站" << endl;


	settextcolor(WHITE);
	settextstyle(50, 0, L"黑体");
	outtextxy(0, 50, multiByteToWideChar(sresult));
	sresult = "";

	//这里我们做一个小小的改进,也就是我们希望得到啊具体的路线信息
	//也就是说两个以上相邻的站点才有可能是一路
	//我们开辟一个新的数组来储存线路过程
	//重要算法
	int result[100];
	result[0] = x;
	for (int i = 1; i <= k; i++) {
		result[i] = nums[k - i];
	}
	result[k + 1] = t;
	int towards = 0;//表示方向,0表示向后查找,1表示向前查找
	int i = 0;
	while (1) {
		for (int j = 0; j < v.size(); j++) {             //线路容器
			int m = 0;   //开关
			int num = 0;
			//先要找到首个站点的所在容器
			int n = 0;
			//这里注意如果从开头查找就要进行第二次判断
			while (n < v[j].sum) {
				if (v[j].zhandian[n] == result[i] && i == 0) {     //先找到首个站点在容器中的位置n
					i++;
					num++;
					m = 2;
					break;
				}
				//就是上一次路线断了之后查找第一个站点位置,他又可能往回坐车,所以或着也需要判断
				else if (v[j].zhandian[n - 1] == result[i - 1] && n > 0 && v[j].zhandian[n] == result[i] && i > 0) {
					cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
					sresult = sresult.append("乘坐");
					sresult = sresult.append(v[j].name);
					sresult = sresult.append(" ");
					sresult = sresult.append(G.s[result[i - 1]].name);
					sresult = sresult.append("->");
					sresult = sresult.append(G.s[result[i]].name);

					i++;
					num = num + 2;
					m = 1;
					break;
				}

				else if (v[j].zhandian[n - 1] == result[i] && n > 0 && v[j].zhandian[n] == result[i - 1] && i > 0) {
					cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
					sresult = sresult.append("乘坐");
					sresult = sresult.append(v[j].name);
					sresult = sresult.append(" ");
					sresult = sresult.append(G.s[result[i - 1]].name);
					sresult = sresult.append("->");
					sresult = sresult.append(G.s[result[i]].name);


					i++;
					n--;
					num = num + 2;
					m = 1;
					break;
				}
				else {
					n++;
				}
			}

			if (n >= v[j].sum)
				continue;

			//这里之所有不n++是因为可能是往前找的
			if (result[i] == v[j].zhandian[n + 1] && m == 1) {
				n++;
				cout << "->" << G.s[result[i]].name;
				sresult=sresult.append("->");
				sresult=sresult.append(G.s[result[i]].name);

				towards = 0;
				num++;
				i++;
				n++;
				while (n < v[j].sum) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n++;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}
			else if (result[i] == v[j].zhandian[n + 1] && m == 2) {
				n++;
				cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
				sresult = sresult.append("乘坐");
				sresult = sresult.append(v[j].name);
				sresult = sresult.append(" ");
				sresult = sresult.append(G.s[result[i - 1]].name);
				sresult = sresult.append("->");
				sresult = sresult.append(G.s[result[i]].name);

				towards = 0;
				num++;
				i++;
				n++;
				while (n < v[j].sum) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n++;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}


			//向前查找
			if (v[j].zhandian[n - 1] == result[i] && m == 1) {
				n--;
				cout << "->" << G.s[result[i]].name;
				sresult = sresult.append("->");
				sresult = sresult.append(G.s[result[i]].name);

				towards = 1;
				num++;
				i++;
				n--;
				while (n < v[j].sum && n >= 0) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n--;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}
			else if (v[j].zhandian[n - 1] == result[i] && m == 2) {
				n--;
				cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;
				sresult = sresult.append("乘坐");
				sresult = sresult.append(v[j].name);
				sresult = sresult.append(" ");
				sresult = sresult.append(G.s[result[i - 1]].name);
				sresult = sresult.append("->");
				sresult = sresult.append(G.s[result[i]].name);


				towards = 1;
				num++;
				i++;
				n--;
				while (n < v[j].sum && n >= 0) {
					if (result[i] == v[j].zhandian[n]) {
						cout << "->" << G.s[result[i]].name;
						sresult = sresult.append("->");
						sresult = sresult.append(G.s[result[i]].name);

						n--;
						i++;
						num++;
					}
					else {
						break;
					}
				}
			}
			if (num >= 2) {
				settextcolor(WHITE);
				settextstyle(30, 0, L"黑体");
				cout << endl;
				if (10 + y1 * s >= 950) {
					x1 = x1 + 600;
					s = 0;
				}
				outtextxy(x1,200+ y1 * s, multiByteToWideChar(sresult));
				s++;
				sresult = "";                  //一次路线结束清空
				break;
			}
		}
		if (i > k + 1) {
			break;
		}
	}
}

5.添加删除路线

这个部分是最简单的没有什么特别好说的,就是找到对应的站点,然后删除或者添加即可。

//添加路线
//这边有一个点我们需要知道已有的站点中是否存在该站点
//如果有就不做操作
//如果没有就需要写入新的站点
//对于我们所使用的算法即使重复添加路线也并不会产生影响
//在读取的同时还要进行追加写入
int Insert_Road() {
	putimage(0, 0, &sucai4);
	settextcolor(BLACK);
	EasyTextBox roadname;
	EasyTextBox roadnumber;
	EasyTextBox roadguocheng1;
	EasyTextBox roadguocheng2;
	EasyTextBox roadguocheng3;
	EasyTextBox roadguocheng4;
	roadname.Create(1230, 284, 1580,360 ,10);						// 创建用户名文本框控件
	roadnumber.Create(1620, 284,1740, 360, 10);						// 创建用户名文本框控件
	roadguocheng1.Create(1230, 500, 1778, 577, 50);						// 创建用户名文本框控件
	roadguocheng2.Create(1230, 577, 1778, 654, 50);						// 创建用户名文本框控件
	roadguocheng3.Create(1230, 654, 1778, 731, 50);						// 创建用户名文本框控件
	roadguocheng4.Create(1230, 731, 1778, 808, 50);						// 创建用户名文本框控件
	Road_Sum++;
	fstream  fin;     //写入格式
	fin.open("路线信息.txt", ios::out | ios::app);
	Road r;
	int n;
	int flag = 0;        //用于开关,也就是判断是否有该站点,1表示有
	cout << "请输入您添加路线的名称" << endl;
	ExMessage msg;
	while (true)
	{
		msg = getmessage(EX_MOUSE);			// 获取消息输入

		if (msg.message == WM_LBUTTONDOWN)
		{
			// 判断控件
			if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();

			// 判断控件
			if (roadnumber.Check(msg.x, msg.y))	roadnumber.OnMessage();

			// 判断控件
			if (roadguocheng1.Check(msg.x, msg.y))	roadguocheng1.OnMessage();
			if (roadguocheng2.Check(msg.x, msg.y))	roadguocheng2.OnMessage();
			if (roadguocheng3.Check(msg.x, msg.y))	roadguocheng3.OnMessage();
			if (roadguocheng4.Check(msg.x, msg.y))	roadguocheng4.OnMessage();
		}
		if (kbhit()) {
			char ch = getch();
			if (ch == 13)
				break;
		}
	}
	string Road_name;
	string Road_number;
	string Road_guocheng1;
	string Road_guocheng2;
	string Road_guocheng3;
	string Road_guocheng4;
	Road_name = wideCharToMultiByte(roadname.Text());
	Road_number= wideCharToMultiByte(roadnumber.Text());
	Road_guocheng1= wideCharToMultiByte(roadguocheng1.Text());
	Road_guocheng2 = wideCharToMultiByte(roadguocheng2.Text());
	Road_guocheng3 = wideCharToMultiByte(roadguocheng3.Text());
	Road_guocheng4 = wideCharToMultiByte(roadguocheng4.Text());

	//这边最好加一个判断,如果添加的线路已经存在就直接退出
	for (int i = 0; i < v.size(); i++) {
		if (Road_name.compare(v[i].name) == 0) {
			cout << "线路已存在,请重新输入" << endl;
			return 0;
		}
	}
	fin << Road_name << endl;
	r.name = Road_name;
	cout << "请输入您添加路线的站点数量" << endl;
	
	//如果输入不是数字会报错
	n = stoi(Road_number);
	r.sum = n;
	cout << "请逐个输入您的路线" << endl;
	string station_name[100];
	int i = 0;

	for (int j = 0; j< Road_guocheng1.length(); j++) {
		if (Road_guocheng1[j] == ' ' && Road_guocheng1[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng1[i] == ' ' && Road_guocheng1[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng1[j];                      //将分割好的字符串放到K数组里
	}
	i++;

	for (int j = 0; j < Road_guocheng2.length(); j++) {
		if (Road_guocheng2[j] == ' ' && Road_guocheng2[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng2[i] == ' ' && Road_guocheng2[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng2[j];                      //将分割好的字符串放到K数组里
	}
	i++;

	for (int j = 0; j < Road_guocheng3.length(); j++) {
		if (Road_guocheng3[j] == ' ' && Road_guocheng3[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng3[i] == ' ' && Road_guocheng3[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng3[j];                      //将分割好的字符串放到K数组里
	}
	i++;

	for (int j = 0; j < Road_guocheng4.length(); j++) {
		if (Road_guocheng4[j] == ' ' && Road_guocheng4[j + 1] != ' ') {                 //以空格作为分隔符
			i++;
			continue;
		}
		else if (Road_guocheng4[i] == ' ' && Road_guocheng4[i + 1] == ' ')
			continue;
		station_name[i] += Road_guocheng4[j];                      //将分割好的字符串放到K数组里
	}
	for (int j = 0; j < n; j++) {
		fin << station_name[j] << endl;
	}
	//结束后不要忘记加end
	fin << "end" << endl;
	fin.close();
	fin.open("站点信息.txt", ios::out | ios::app);
	for (int i = 0; i < n; i++) {
		flag = 0;
		for (int j = 0; j < G.sum; j++) {
			if (station_name[i].compare(G.s[j].name) == 0) {
				flag = 1;
				break;
			}
		}
		if (flag == 0) {
			fin << G.sum << " " << station_name[i] << endl;
			G.s[G.sum].number = G.sum;
			G.s[G.sum].name = station_name[i];
			m.insert(pair<string, int>(station_name[i], G.sum++));
		}
	}
	fin.close();
	for (int i = 0; i < n; i++) {
		map<string, int>::iterator iter;
		iter = m.find(station_name[i]);
		r.zhandian[i] = iter->second;
	}
	v.push_back(r);

	//接下来直接插入线路
	for (int j = 0; j < r.sum - 1; j++) {
		int a1 = r.zhandian[j];
		int a2 = r.zhandian[j + 1];
		situation* s1 = (situation*)malloc(sizeof(situation));
		situation* s2 = (situation*)malloc(sizeof(situation));
		s2->number = G.s[a2].number;
		s1->number = G.s[a1].number;
		s2->next = G.s[a1].firstnode;
		G.s[a1].firstnode = s2;
		s1->next = G.s[a2].firstnode;
		G.s[a2].firstnode = s1;
	}
	cout << "添加成功" << endl;
	return 1;
}

 

 这边需要特别提示:在我们删除文件中的某一段时,由于没有直接的方法删除,我们需要设置一个中间文本,从原文本中一直读取到需要删除部分的内容然后跳过这段内容,将其他部分的内容都存入这个中间文本中,然后清空原文本,最后将中间文本中的内容再重新输入到原文本中即可,注意中间文本在使用完毕之后需要清空。

//首先我们要明确,在删除路线的时候站点不需要发生改变
//我们这里的需求是删除某一条线路,线路名字已知
//这里就体现出了重复建立节点的好处,即使删除了两点之间的节点也不会导致其他有相同节点的路线发生变化
int Delete_Road() {
	putimage(0, 0, &sucai5);
	settextcolor(BLACK);
	EasyTextBox roadname;
	roadname.Create(1250, 360, 1755, 455, 10);						// 创建用户名文本框控件
	ExMessage msg;
	while (true)
	{
		msg = getmessage(EX_MOUSE);			// 获取消息输入

		if (msg.message == WM_LBUTTONDOWN)
		{
			// 判断控件
			if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();
		}
		if (kbhit()) {
			char ch = getch();
			if (ch == 13)
				break;
		}
	}


	int flag = 0;          //开关,用来判断是否存在该路线
	Road r;
	Road_Sum--;
	cout << "请输入你要删除的路线" << endl;
	string tmp;
	tmp= wideCharToMultiByte(roadname.Text());
	auto it = v.begin();
	while (it != v.end()) {
		if (it->name.compare(tmp) == 0) {
			r = *it;
			v.erase(it);     //删除这个路径
			flag = 1;
			break;
		}
		it++;
	}
	if (flag == 0) {
		cout << "抱歉,不存在该路线" << endl;
		return 0;
	}
	//这里先写从图中删除数据,明确是两两之间进行删除
	//要从某站点中开始找,即遍历链表找到与之对应的站点,进行节点删除
	//我们要知道一个节点的前驱节点
	else {
		for (int i = 0; i < r.sum - 1; i++) {
			int a1 = r.zhandian[i];
			int a2 = r.zhandian[i + 1];
			situation* tmp1 = G.s[a1].firstnode;    //前驱节点
			situation* tmp2 = tmp1->next;           //后驱节点
			//开头就等于
			if (tmp1->number == a2) {
				G.s[a1].firstnode = tmp1->next;
				free(tmp1);//释放空间
				continue;
			}
			else {
				while (tmp2 != NULL) {
					if (tmp2->number == a2) {
						tmp1->next = tmp2->next;
						free(tmp2);
						break;
					}
					else {
						tmp1 = tmp1->next;
						tmp2 = tmp2->next;
					}
				}
			}
			//此时第一个节点已经完成删除
			//开始第二个节点
			tmp1 = G.s[a2].firstnode;
			tmp2 = tmp1->next;
			if (tmp1->number == a1) {
				G.s[a2].firstnode = tmp1->next;
				free(tmp1);//释放空间
				continue;
			}
			else {
				while (tmp2 != NULL) {
					if (tmp2->number == a2) {
						tmp1->next = tmp2->next;
						free(tmp2);
						break;
					}
					else {
						tmp1 = tmp1->next;
						tmp2 = tmp2->next;
					}
				}
			}
		}


		//接下来我们需要进行文件的删除
		//原理很简单,就是我们找出需要删除的路线,将其他路线全部读取到tmp中间文件中,然后再拷贝回来即可
		string s;
		ifstream fin;
		fstream Fin;
		Fin.open("tmp.txt", ios::out | ios::app);
		fin.open("路线信息.txt", ios::in);
		while (getline(fin, s, '\n')) {
			if (s.compare(tmp) != 0) {
				Fin << s << endl;
			}
			else {
				while (getline(fin, s, '\n')) {
					if (s.compare("end") == 0)
						break;
				}
			}
		}
		Fin.close();
		fin.close();
		ofstream file_writer1("路线信息.txt", ios_base::out);//清空文本文件
		Fin.open("路线信息.txt", ios::out | ios::app);
		fin.open("tmp.txt", ios::in);
		//重新写入
		while (getline(fin, s, '\n')) {
			Fin << s << endl;
		}
		//清空tmp文件
		ofstream file_writer2("tmp.txt", ios_base::out);//清空文本文件
		Fin.close();
		fin.close();
		cout << "删除成功" << endl;
		return 1;
	}
}

 最后提一点小小的建议:

1.我们需要在学习完一门语言之后进行一定简易项目的实战有利于我们巩固该语言的使用。

2.你在开发一个全新的项目过程中可能会困难重重,就像博主在开发可视化部分的时候经理了3,4次的代码大改(一般来说这是大忌),同时也遇到了许多不可抗拒的困难,由于没有人的帮助,博主只能靠自身的水平和网上搜来的结果一步一步试验,还有就是上一个项目的经验,大家可以看我的上一个项目c语言仿天天酷跑小游戏

 如果代码写的烂不要喷我,额算了想喷就喷吧,希望大家指出我的不足

 整合包(内涵源代码文件以及素材)百度网盘:https://pan.baidu.com/s/1kf5iIhQXSr_1MPSF-BQCRg

 有需要的小伙伴可以加博主微信woyuxiuxian123私聊(白嫖怪勿扰).

 

程序截图:

 

 

 由于图片过多就不一一演示了,里面的登录界面以及抽卡界面全都是动态的,以及配有音效,没有可以来真实博主,跟游戏里面几乎一摸一样。

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值