基于EasyX图形库的C/C++实战项目——西南大学大一C语言程序设计|课程设计《多功能应用平台》

 项目代码,使用教程:GitHub - Dusongg/program_lobby: 大一程序设计 C/C++:基于EasyX的图形化程序设计,实现通讯录、扫雷、迷宫游戏的应用平台(含实验报告与答辩ppt)

目录

一、扫雷

二、迷宫

三、通讯录

四、核心代码


一、扫雷

功能简介:1.   棋盘设置 2.    设置模式 3.   红旗标志 4.  递归展开(DFS)

扫雷

二、迷宫

功能介绍: 1.   二维矩阵设置 2.   键盘输入 3.   自动求解(回溯)

迷宫

三、通讯录

功能介绍:1.    设计模板 2.    添加、删除、查找 3.    文件操作 4.    用户栏信息展开 5.    排序与优化

可视化通讯录

四、核心代码

1. Interface.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#undef UNICODE
#undef _UNICODE

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <easyx.h>
#include <mmsystem.h>  
#pragma comment(lib,"winmm.lib")
#include <conio.h>
#include <stdlib.h>
#include <time.h>
#include <graphics.h>
#include <windows.h>
#include <unordered_map>
#include <string>
#include <stack>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

//1. Minesweeper
#define ROW 10
#define COL 10
#define ROWS ROW+2
#define COLS COL+2
#define MODE_EAZY 10
#define MODE_MID 15
#define MODE_HARD 20

void InitBoard(char Board[ROWS][COLS], int rows, int cols, int set);
void SetBoard(char Board[ROWS][COLS], int row, int col, int mode);
void StartToPlay(char inBoard[ROWS][COLS], char outBoard[ROWS][COLS], int row, int col, int mode, int* keep_playing);

//2. Mazegame
#define mROW 21
#define mCOL 29
void MazeGame();

//3. Contact
#define NAME_MAX 16
#define SEX_MAX 8
#define ADD_MAX 16
#define TELE_MAX 16
#define KEY_MAX 16
#define CON_MAX 32
#define AVA_MAX 32

typedef struct PeoInfo  //通过学号定位
{
	long long stu_number;  //学号
	char name[NAME_MAX];
	char key[KEY_MAX];  //密码
	int age;
	char sex[SEX_MAX];
	char address[ADD_MAX];
	char tele[TELE_MAX];
	char contact[CON_MAX];   //该学生的通讯录
	char avatar[AVA_MAX];   //头像
}IP;

void ContactInter(IP info, unordered_map<long long, IP> ID, long long* saveId);

//基于map的所有用户管理
void LoadStudentsID(unordered_map<long long, IP>& ID);
void SaveStudentsID(long long* id, IP* info);
void DelstudentsID(long long del);

2.Minesweeper.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Interface.h"

void InitBoard(char Board[ROWS][COLS], int rows, int cols, int set) {
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			Board[i][j] = set;
		}
	}
}

//踩雷之后展示整个棋盘
void DisPlayBoardGra(char Board[ROWS][COLS], int r, int c) {
	for (int i = 1; i < ROWS-1; i++) {
		for (int j = 1; j < COLS-1; j++) { 
			if (i == r && j == c) continue;

			if (Board[i][j] - '0' == 1) {
				
				IMAGE bomb;
				loadimage(&bomb, "image_minesweeper\\bomb.jpg");
				putimage(100 + (j - 1) * 40, 100 + (i - 1) * 40, &bomb);
			}
			else {
				IMAGE blank2;
				loadimage(&blank2, "image_minesweeper\\blank.jpg");
				putimage(100 + (j - 1) * 40, 100 + (i - 1) * 40, &blank2);
			}
			Sleep(75); 
		}
	}
}

//随机放置mode个数的雷到底层棋盘中
void SetBoard(char Board[ROWS][COLS], int row, int col, int mode){
	int count = mode;
	while (count){
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (Board[x][y] != '1'){
			Board[x][y] = '1';
			count--;
		}
	}
}

//计算周围雷的数量
int Count(char inBoard[ROWS][COLS], int x, int y){
	return (inBoard[x - 1][y - 1] + inBoard[x - 1][y] + inBoard[x][y - 1] +
		inBoard[x + 1][y - 1] + inBoard[x + 1][y] + inBoard[x + 1][y + 1] +
		inBoard[x][y + 1] + inBoard[x - 1][y + 1] - 8 * '0');
}

void LookForRay(char inBoard[ROWS][COLS], char outBoard[ROWS][COLS], int x, int y, int* win){
	if (outBoard[x][y] == 'F') return;  //递归到红旗返回

	if (inBoard[x][y] == '0')
		*win -= 1;    //记录win
	char sum = Count(inBoard, x, y);   //计算周围雷的个数:越界问题?
	outBoard[x][y] = sum + '0';

	//显示该点周围雷的个数
	char sum_str[30];
	sprintf(sum_str, "image_minesweeper\\%d.jpg", sum);    // 用sprintf合并字符串,通过文件相对位置找到对应图片
	IMAGE number;
	loadimage(&number, sum_str);
	putimage(100 + (y - 1) * 40, 100 + (x - 1) * 40, &number);

	//该坐标周围没有雷,想周围递归
	if (outBoard[x][y] == '0'){
		IMAGE blank;
		loadimage(&blank, "image_minesweeper\\blank.jpg");
		putimage(100 + (y - 1) * 40, 100 + (x - 1) * 40, &blank);

		if (x - 1 >= 1 && x - 1 <= ROW && y - 1 >= 1 && y - 1 <= COL && outBoard[x - 1][y - 1] == '*')    //递归条件:递归坐标未被排点,
			LookForRay(inBoard, outBoard, x - 1, y - 1, win);    //win为地址

		if (x - 1 >= 1 && x - 1 <= ROW && y >= 1 && y <= COL && outBoard[x - 1][y] == '*')
			LookForRay(inBoard, outBoard, x - 1, y, win);

		if (x - 1 >= 1 && x - 1 <= ROW && y + 1 >= 1 && y + 1 <= COL && outBoard[x - 1][y + 1] == '*')
			LookForRay(inBoard, outBoard, x - 1, y + 1, win);

		if (x >= 1 && x <= ROW && y - 1 >= 1 && y - 1 <= COL && outBoard[x][y - 1] == '*')
			LookForRay(inBoard, outBoard, x, y - 1, win);

		if (x >= 1 && x <= ROW && y + 1 >= 1 && y + 1 <= COL && outBoard[x][y + 1] == '*')
			LookForRay(inBoard, outBoard, x, y + 1, win);

		if (x + 1 >= 1 && x + 1 <= ROW && y - 1 >= 1 && y - 1 <= COL && outBoard[x + 1][y - 1] == '*')
			LookForRay(inBoard, outBoard, x + 1, y - 1, win);

		if (x + 1 >= 1 && x + 1 <= ROW && y >= 1 && y <= COL && outBoard[x + 1][y] == '*')
			LookForRay(inBoard, outBoard, x + 1, y, win);

		if (x + 1 >= 1 && x + 1 <= ROW && y + 1 >= 1 && y + 1 <= COL && outBoard[x + 1][y + 1] == '*')
			LookForRay(inBoard, outBoard, x + 1, y + 1, win);
	}
}

void StartToPlay(char inBoard[ROWS][COLS], char outBoard[ROWS][COLS], int row, int col, int mode, int* keep_playing){
	int r = 0;
	int c = 0;
	int win = row * col - mode;   //当win等于0时,即剩下的格子全是雷时,判断胜利
	bool die = false;
	//🚩数量
	int flag = mode;
	char flag_str[5];
	//防止一开始就踩雷
	int deathfree = 0;
	char deathfree_str[20];
	if (mode == MODE_EAZY) deathfree = 1;
	if (mode == MODE_MID) deathfree = 2;
	if (mode == MODE_HARD) deathfree = 3;

	//判断循环条件
	while (win && !die){
		if (MouseHit()) {
			MOUSEMSG msg = GetMouseMsg();
			switch (msg.uMsg) {
				case WM_LBUTTONDOWN: {
					r = ((msg.y - 100) / 40) + 1;   // 5位棋盘格一小格的宽度 10 * 50 = 500
					c = ((msg.x - 100) / 40) + 1;
					if (msg.x > 830 && msg.x < 970 && msg.y > 525 && msg.y < 580) {
						*keep_playing = 0;  //退出游戏,返回扫雷界面
						return;
					}

					if (r >= 1 && r <= row && c >= 1 && c <= col) {
						if (outBoard[r][c] == 'F') break;   //点击已插旗的格子

						//一开始踩到雷的情况 :将该点插入红旗,提醒该点有雷,放置一开始就点到雷
						if (inBoard[r][c] == '1' && deathfree > 0) {
							Beep(494, 200);
							//减少红旗并插入红旗
							flag--;  
							outBoard[r][c] = 'F';
							IMAGE FLAG;
							loadimage(&FLAG, "image_minesweeper\\flag.jpg");
							putimage(100 + (c - 1) * 40, 100 + (r - 1) * 40, &FLAG);
							IMAGE flag_board;
							loadimage(&flag_board, "image_minesweeper\\flag_board.jpg");
							putimage(700, 40, &flag_board);
							sprintf(flag_str, "%d", flag);
							outtextxy(850, 40, (flag_str));
							deathfree--;
							sprintf(deathfree_str, "剩余防弹衣:%d", deathfree);
							HWND hWnd = GetHWnd();
							MessageBox(hWnd, deathfree_str, "! ! ! ! ! ! !", MB_OKCANCEL);
							break;
						}
						if (inBoard[r][c] == '1' && deathfree == 0) {
							mciSendString("open bomb.mp3", 0, 0, 0);
							mciSendString("play bomb.mp3", 0, 0, 0);
							IMAGE red_bomb;
							loadimage(&red_bomb, "image_minesweeper\\red_bomb.jpg");
							putimage(100 + (c - 1) * 40, 100 + (r - 1) * 40, &red_bomb);
							DisPlayBoardGra(inBoard, r, c);   //展示整个棋盘
							die = true;   //将die置为true跳出外层的while循环
							break;
						}
						if (outBoard[r][c] != '*') {
							break;
						}
						LookForRay(inBoard, outBoard, r, c, &win);   //排雷:DFS -->  54

						if (deathfree > 0)  //最开始的前几此排雷对deathfree--;
							deathfree--;
					}
					break;
				}
				//右键插入红旗 : 该坐标有旗与无旗? 剩余红旗?
				case WM_RBUTTONDOWN: {
					r = ((msg.y - 100) / 40) + 1;  
					c = ((msg.x - 100) / 40) + 1;
					if (r > 0 && r < 11 && c > 0 && c < 11) {
						if (outBoard[r][c] == '*' && outBoard[r][c] != 'F') {
							outBoard[r][c] = 'F';
							if (flag == 0) {
								Beep(494, 200);//震动提示
								HWND hWnd = GetHWnd();
								MessageBox(hWnd, "无剩余红旗可用!", "无剩余红旗可用!", MB_OKCANCEL);
								break;
							}
							flag--;

							IMAGE flag_board;
							loadimage(&flag_board, "image_minesweeper\\flag_board.jpg");
							putimage(700, 40, &flag_board);
							sprintf(flag_str, "%d", flag);
							outtextxy(850, 40, flag_str);

							//放置🚩
							IMAGE FLAG;
							loadimage(&FLAG, "image_minesweeper\\flag.jpg");
							putimage(100 + (c - 1) * 40, 100 + (r - 1) * 40, &FLAG);
						}
						else if (outBoard[r][c] == 'F') {
							outBoard[r][c] = '*';
							//回收🚩
							IMAGE back;
							loadimage(&back, "image_minesweeper\\back.jpg");
							putimage(100 + (c - 1) * 40, 100 + (r - 1) * 40, &back);

							flag++;
							IMAGE flag_board;
							loadimage(&flag_board, "image_minesweeper\\flag_board.jpg");
							putimage(700, 40, &flag_board);
							sprintf(flag_str, "%d", flag);
							outtextxy(850, 40, flag_str);
						}
					}
					break;
				}
			}
		}
	}
	
	IMAGE end;
	loadimage(&end, "image_minesweeper\\end.jpg");
	putimage(200, 150, &end);
	if (win == 0) {
		settextcolor(RGB(250, 51, 57));
		outtextxy(280, 100, "!!!WIN!!!");
	}
	while (1) {
		if (MouseHit()) {
			MOUSEMSG msg = GetMouseMsg();
			switch (msg.uMsg) {
			case WM_LBUTTONDOWN: {
				if (msg.x > 200 && msg.x < 800 && msg.y > 150 && msg.y < 300) {
					return;     //keep_playing未改变,继续玩
				}
				if (msg.x > 200 && msg.x < 800 && msg.y > 300 && msg.y < 450) {
					*keep_playing = 0;   //返回扫雷界面
					return;
				}
			}
			}
		}
	}
}

3.Mazegame.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Interface.h"

vector<char> path;
bool BackTracking(int maze[mROW][mCOL], int r, int c, int move[4][2], char moveS[4]) {
	if (r == 19 && c == 27) return true;   //找到出口

	if (maze[r - 1][c] == 1 && maze[r + 1][c] == 1 && maze[r][c + 1] == 1 && maze[r][c - 1] == 1) {
		return false;    //四面均被堵,返回false,回溯,另外求解
	}

	for (int i = 0; i < 4; i++) {
		if (maze[r + move[i][0]][c + move[i][1]] == 1) continue;
		path.push_back(moveS[i]);
		maze[r + move[i][0]][c + move[i][1]] = 1;    //将来路封锁,防止无限递归

		if (BackTracking(maze, r + move[i + 4][0], c + move[i + 4][1], move, moveS)) return true;   //找到出口连续返回true
		else {
			path.pop_back();    //弹出path中的走向
			maze[r + move[i][0]][c + move[i][1]] = 0;     //将该去路封锁 :表示该路找不到答案
		}
	}
	return false;    //四路都无答案,返回false(可有可无)
}

void AutomaticSolving(int maze[mROW][mCOL], int startrow, int startcol) {
	char moveS[4] = { 's', 'w', 'd', 'a' };
	int move[8][2] = { {1,0},{-1, 0},{0, 1},{0, -1},{2,0},{-2, 0},{0, 2},{0, -2} };    //前四个为行走方向,后四个为行走步长
	BackTracking(maze, startrow, startcol, move, moveS);    从目前[startrow, startcol]位置开始寻找答案:回溯->获得解决方案  -> 5

	int i = startrow;   
	int j = startcol;
	int x = 4 + (j / 2) * 50;
	int y = 4 + (i / 2) * 50;
	settextcolor(RED);
	IMAGE mazeimage;
	loadimage(&mazeimage, "image_mazegame\\迷宫.jpg");
	IMAGE boger;
	loadimage(&boger, "image_mazegame\\博哥.jpg", 45, 45);

	for (char move : path) {    //读出path中的行走路径
		cleardevice();
		putimage(0, 0, &mazeimage);
		putimage(x, y, &boger);

		if (move == 'w') {
			i -= 2;
			y -= 50;
		} else if (move == 's') {
			i += 2;
			y += 50;
		} else if (move == 'a') {
			j -= 2;
			x -= 50;
		} else {
			j += 2;
			x += 50;
		}
		Sleep(100);
	}
}

void MazeGame(){
	int maze[mROW][mCOL] = { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1},
		{1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1},{1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,1},{1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1},
		{1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1},{1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1},
		{1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,1},{1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1},
		{1,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1},{1,0,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,1,1},
		{1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1},{1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1},
		{1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1},{1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1},
		{1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1},{1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1},
		{1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1},{1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1},
		{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1},{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} };
	while (1) {
		int i = 1;  //行  (初始坐标)
		int j = 1; //列
		int learnPoint = 90;
		char lPointstr[20];

		settextcolor(RED);
		IMAGE mazeimage;
		loadimage(&mazeimage, "image_mazegame\\迷宫.jpg");
		IMAGE boger;
		loadimage(&boger, "image_mazegame\\博哥.jpg", 45, 45);

		int x = 4;
		int y = 4;
		int Armor = 3;   //护甲,撞墙时-1
		char armorStr[20];
		while (i != 19 || j != 27) {

			FlushBatchDraw();
			cleardevice();
			putimage(0, 0, &mazeimage);
			putimage(x, y, &boger);
			sprintf(lPointstr, "所剩学分:%d", learnPoint);
			outtextxy(700, 300, lPointstr);
			EndBatchDraw();


			if (MouseHit()) {
				MOUSEMSG msg = GetMouseMsg();
				switch (msg.uMsg) {
				case WM_LBUTTONDOWN:
					if (msg.x > 700 && msg.x < 800 && msg.y > 357 && msg.y < 400) {
						AutomaticSolving(maze, i, j);   //自动求解  ->  26
						i = 19; j = 27;   //将横纵坐标设置未终点位置,跳出while之后,判断为游戏胜利
						break;
					}
					if (msg.x > 700 && msg.x < 800 && msg.y > 10 && msg.y < 52) {
						return;
					}
				}
			}

			if (GetAsyncKeyState(VK_UP))
			{
				if (maze[i - 1][j] == 1) {
					if (Armor == 0)
						break;
					else {
						Beep(494, 200);
						Armor--;
						sprintf(armorStr, "剩余护甲值:%d", Armor);
						HWND hWnd = GetHWnd();
						MessageBox(hWnd, armorStr, "! ! ! ! ! ! !", MB_OKCANCEL);
						continue;
					}
				}
				i -= 2;
				y -= 50;
				learnPoint--; 
				Sleep(300);
			}
			else if (GetAsyncKeyState(VK_DOWN))
			{
				if (maze[i + 1][j] == 1) {
					if (Armor == 0)
						break;
					else {
						Beep(494, 200);
						Armor--;
						sprintf(armorStr, "剩余护甲值:%d", Armor);
						HWND hWnd = GetHWnd();
						MessageBox(hWnd, armorStr, "! ! ! ! ! ! !", MB_OKCANCEL);
						continue;
					}
				}
				i += 2;
				y += 50;
				learnPoint--;
				Sleep(300);
			}
			else if (GetAsyncKeyState(VK_LEFT))
			{
				if (maze[i][j - 1] == 1) {
					if (Armor == 0)
						break;
					else {
						Beep(494, 200);
						Armor--;
						sprintf(armorStr, "剩余护甲值:%d", Armor);
						HWND hWnd = GetHWnd();
						MessageBox(hWnd, armorStr, "! ! ! ! ! ! !", MB_OKCANCEL);
						continue;
					}
				}
				j -= 2;
				x -= 50;
				learnPoint--;
				Sleep(300);
			}
			else if (GetAsyncKeyState(VK_RIGHT))
			{
				if (maze[i][j + 1] == 1) {
					if (Armor == 0)
						break;
					else {
						Beep(494, 200);
						Armor--;
						sprintf(armorStr, "剩余护甲值:%d", Armor);
						HWND hWnd = GetHWnd();
						MessageBox(hWnd, armorStr, "! ! ! ! ! ! !", MB_OKCANCEL);
						continue;
					}
				}
				j += 2;
				x += 50;
				learnPoint--;
				Sleep(300);
			}
			if (learnPoint < 0) break;
		} 

		//三种结束情况
		if (learnPoint < 0) {
			int lod = -500;
			IMAGE game_over_point;
			loadimage(&game_over_point, "image_mazegame\\game_over_point.jpg");
			while (lod <= 0) {
				BeginBatchDraw();
				cleardevice();
				putimage(0, lod, &game_over_point);
				EndBatchDraw();
				lod++;
				Sleep(1);
			}
		}
		else if (i == 19 && j == 27) {
			int lod = 500;
			IMAGE game_over_win;
			loadimage(&game_over_win, "image_mazegame\\game_over_win.jpg");
			while (lod >= 0) {
				BeginBatchDraw();
				cleardevice();
				putimage(0, lod, &game_over_win);
				EndBatchDraw();
				lod--;
				Sleep(1);
			}
		}
		else if (maze[i - 1][j] == 1 || maze[i + 1][j] == 1 || maze[i][j - 1] == 1 || maze[i][j + 1] == 1) {
			int lod = 800;
			IMAGE game_over_die;
			loadimage(&game_over_die, "image_mazegame\\game_over_die.jpg");
			while (lod >= 0) {
				BeginBatchDraw();
				cleardevice();
				putimage(lod, 0, &game_over_die);
				EndBatchDraw();
				lod--;
				Sleep(1);
			}
		}

		while (1) {
			if (MouseHit()) {
				MOUSEMSG msg = GetMouseMsg();
				switch (msg.uMsg) {
				case WM_LBUTTONDOWN: 
					if (msg.x > 228 && msg.x < 571 && msg.y > 200 && msg.y < 300) {
						goto AGAIN;   //返回到最外层的while循环
					}
					if (msg.x > 228 && msg.x < 571 && msg.y > 300 && msg.y < 400) {
						return;
					}
				}
			}
		}
	AGAIN:
		printf("keeping playing!\n");
	}

}

5.Contact.cpp 

#define _CRT_SECURE_NO_WARNINGS 1
#include "Interface.h"

//判断通讯录中是否已经有这个人
bool IsAdd(vector<IP*> user, long long stu_number) {
	for (IP* contact : user) {
		if (contact->stu_number == stu_number) {
			return true;  //如果已添加此人,则返回true
		}
	}
	return false;
}

//用于AddContact() 与 SearchContact() 函数的相互添加功能(考虑当添加-删除-添加后,如何防止对方用户重复添加)    
void InterAdd(long long stu_number, unordered_map<long long, IP> ID, IP info) {   
	//判断之前对方是否已经添加过登录用户
	FILE* pfr = fopen(ID[stu_number].contact, "rb");   //ID[stu_number].contact ---> 访问对方通讯录
	IP* ret = (IP*)malloc(sizeof(IP));
	while (fread(ret, sizeof(IP), 1, pfr)) {
		if (ret->stu_number == info.stu_number) {
			fclose(pfr);
			return;
		}
	}
	fclose(pfr);
	//若无重复则添加
	FILE* pf = fopen(ID[stu_number].contact, "ab");
	fwrite(&info, sizeof(IP), 1, pf);
	fclose(pf);
}

//bug:map传地址,否则添加到vector的指针是出函数会被摧毁
void AddContact(vector<IP*>& user, unordered_map<long long, IP>& ID, IP info) {    
	while (1) {
		long long stu_number;
		printf("学号:>");
		scanf("%lld", &stu_number);
		//考虑三种情况: 已添加、 添加自己、 添加用户不存在
		if (!ID.count(stu_number)) {
			printf("查无此人,请重新输入!!\n");
			continue;
		}
		else if (stu_number == info.stu_number) {
			printf("无法添加自己,请重新输入!!\n");
			continue;
		}
		else if (IsAdd(user, stu_number)) {
			printf("已添加此人,请重新输入\n");
		}
		else {
			user.push_back(&ID[stu_number]);
			InterAdd(stu_number, ID, info);
			return;
		}
	}
}

//删除个人账户中的联系人(不删除对方用户的通讯录中自己的信息)
void DelectContact(vector<IP*>& user) {
	if (user.empty()) {
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[NAME_MAX];
	printf("请输入你要删除的名字:>");
	scanf("%s", name);
	IP* search = nullptr;
	for (int i = 0; i < user.size(); i++) {
		if (!strcmp(name, user[i]->name)) {
			search = user[i];
			break;
		}
	}
	if (!search) return;   

	auto del = find(user.begin(), user.end(), search);    //find函数返回迭代器,指向search,配合erase删除search
	if (del == user.end()) {
		printf("没有你要查找的人\n");
		return;
	}
	user.erase(del);
	printf("删除成功!\n");
	//删除后SaveContact() 保存txt文档
}

//查找ID里的用户  --- 返回true表示查找后添加 , 返回false表示查找后没有添加
bool SearchContact(vector<IP*>& user, unordered_map<long long, IP>& ID, IP info) {
	printf("请输入学号:>");
	long long stu_number;
	scanf("%lld", &stu_number);
	if (!ID.count(stu_number)) {
		printf("查无此人!\n");
		return false;
	}
	else {
		//展示该用户信息
		IP people = ID[stu_number];
		IMAGE search;
		loadimage(&search, "image_contact\\search.jpg");
		putimage(200, 200, &search);
		settextstyle(22, 0, "黑体");
		outtextxy(278, 224, people.name);

		char str_number[20] = { '\0' };
		sprintf(str_number, "%lld", people.stu_number);
		outtextxy(486, 224, str_number);

		char str_age[20] = { '\0' };
		sprintf(str_age, "%d", people.age);
		outtextxy(278, 290, str_age);

		outtextxy(500, 290, people.sex);
		outtextxy(278, 354, people.tele);
		outtextxy(544, 354, people.address);

		while (1) {
			if (MouseHit()) {
				MOUSEMSG msg = GetMouseMsg();
				switch (msg.uMsg) {
				case WM_LBUTTONDOWN: 
					//添加此人
					if (msg.x > 250 && msg.x < 400 && msg.y > 400 && msg.y < 450) {
						if (stu_number == info.stu_number) {
							HWND hWnd = GetHWnd();
							MessageBox(hWnd, "无法添加自己", "! ! ! ! ! ! !", MB_OKCANCEL);
							continue;
						}
						else if (IsAdd(user, stu_number)) {    //在链表中查找是否已经添加了该用户, 如果已添加返回true
							HWND hWnd = GetHWnd();
							MessageBox(hWnd, "已添加该用户", "! ! ! ! ! ! !", MB_OKCANCEL);
							continue;
						}
						else {
							user.push_back(&ID[stu_number]); 
							InterAdd(stu_number, ID, info);
							return true;   //若添加返回true, 保存txt文档
						}
					}
					//返回
					else if (msg.x > 500 && msg.x < 650 && msg.y > 400 && msg.y < 450) {
						return false;   //没添加则无需保存
					}
				}
			}
		}
	}
}

//注销学生账户(从map中将该学生信息销毁)
void DelstudentsID(long long del) {
	FILE* pf1 = fopen("studentsID.txt", "rb");  //读数据   
	FILE* pf2 = fopen("del_tmp.txt", "wb");    //写入临时文件
	
	long long* stu_number1 = (long long*)malloc(sizeof(long long));
	IP* ip1 = (IP*)malloc(sizeof(IP));
	//将除开要删除的学生的信息读取到临时文件
	while (fread(stu_number1, sizeof(long long), 1, pf1) && fread(ip1, sizeof(IP), 1, pf1)) {	
		if (*stu_number1 != del) {
			fwrite(stu_number1, sizeof(long long), 1, pf2);
			fwrite(ip1, sizeof(IP), 1, pf2);
		}
	}
	fclose(pf1);
	fclose(pf2);

	FILE* pf3 = fopen("studentsID.txt", "wb");
	FILE* pf4 = fopen("del_tmp.txt", "rb");
	long long* stu_number2 = (long long*)malloc(sizeof(long long));
	IP* ip2 = (IP*)malloc(sizeof(IP));

	//再将临时文件的信息读回到studentsID.txt文件中,以此完成对文档的信息删除
	while (fread(stu_number2, sizeof(long long), 1, pf4) && fread(ip2, sizeof(IP), 1, pf4)) {
		fwrite(stu_number2, sizeof(long long), 1, pf3);
		fwrite(ip2, sizeof(IP), 1, pf3);
	}
	fclose(pf3);
	fclose(pf4);
	printf("注销完成\n");

}

//加载info.contact文本中的数据到user中,并在ID中检查是否有注销的用户
void LoadContact(vector<IP*>& user, IP info, unordered_map<long long, IP> ID) {
	//检查该用户的txt文档中有没有被注销的账户
	FILE* pf1 = fopen(info.contact, "rb");  //读数据   
	FILE* pf2 = fopen("del_tmp.txt", "wb");    //写入临时文件

	IP* ip1 = (IP*)malloc(sizeof(IP));
	bool flag = false;  //判断是否有被注销的账户
	while (fread(ip1, sizeof(IP), 1, pf1)) {
		if (ID.count(ip1->stu_number)) {  //在map中存在则写入临时文本
			fwrite(ip1, sizeof(IP), 1, pf2);
		}
		else {
			flag = true;   //位于用户文档的信息在map中不存在, 此时flag置为true, 表示需要对临时文件读数据到info.contact中(参考DelstudentsID())
		}
	}
	fclose(pf1);
	fclose(pf2);

	if (flag) {
		FILE* pf3 = fopen(info.contact, "wb");
		FILE* pf4 = fopen("del_tmp.txt", "rb");
		IP* ip2 = (IP*)malloc(sizeof(IP));
		while (fread(ip2, sizeof(IP), 1, pf4)) {
			fwrite(ip2, sizeof(IP), 1, pf3);
		}
		fclose(pf3);
		fclose(pf4);
	}

	FILE* pf = fopen(info.contact, "rb");
	int n = 0;
	IP* tmp = (IP*)malloc(sizeof(IP));
	while (fread(tmp, sizeof(IP), 1, pf)) {
		n++;
	}
	rewind(pf);
	while (n--) {
		IP* tmp = (IP*)malloc(sizeof(IP));
		fread(tmp, sizeof(IP), 1, pf);
		user.push_back(tmp);
	}
	fclose(pf);
	pf = NULL;
}

void SaveContact(vector<IP*>& user, IP info) {   //将数组所有节点的数据写入到文档中
	FILE* pf = fopen(info.contact, "wb");
	if (pf == NULL) {
		exit(EXIT_FAILURE);
		return;
	}
	for (int i = 0; i < user.size(); i++) {
		fwrite(user[i], sizeof(IP), 1, pf);
	}
	fclose(pf);
	pf = NULL;
}

//加载所有账户信息
void LoadStudentsID(unordered_map<long long, IP>& ID) {
	FILE* pf = fopen("studentsID.txt", "rb");

	int n = 0;
	long long* tmp1 = (long long*)malloc(sizeof(long long));
	IP* tmp2 = (IP*)malloc(sizeof(IP));
	while (fread(tmp1, sizeof(long long), 1, pf) && fread(tmp2, sizeof(IP), 1, pf)) {
		n++;
	}
	rewind(pf);
	while (n--) {
		long long* stu_number = (long long*)malloc(sizeof(long long));
		IP* ip = (IP*)malloc(sizeof(IP));
		fread(stu_number, sizeof(long long), 1, pf);
		fread(ip, sizeof(IP), 1, pf);
		ID[*stu_number] = *ip;
	}
	fclose(pf);
	pf = NULL;
}

//注册一个用户
void SaveStudentsID(long long* stu_number, IP* info) {
	FILE* pf = fopen("studentsID.txt", "ab");

	fwrite(stu_number, sizeof(long long), 1, pf);
	fwrite(info, sizeof(IP), 1, pf);

	fclose(pf);
	pf = NULL;
}

//展示用户信息,考虑是否有展开的信息
void DisplayContact(vector<IP*> user, int y, vector<bool> isunfold) {   //用isunfold数组,记录某一下标的信息是否已被展开
	settextcolor(RGB(40, 40, 40));
	setbkmode(TRANSPARENT);
	settextstyle(25, 15, "微软雅黑");

	int index = 0;
	for (IP* contact : user) {    //遍历user数组中的所有联系人
		if (isunfold[index] == false) {   
			IMAGE doc;
			loadimage(&doc, "image_contact\\doc.jpg");
			putimage(508, y, &doc);
			outtextxy(528, (y + 5), contact->name);
			y += 50;
		}
		else if (isunfold[index] == true) {
			IMAGE Unfold;
			loadimage(&Unfold, "image_contact\\Unfold.jpg");
			putimage(500, y, &Unfold);
			settextstyle(30, 0, "黑体");
			outtextxy(523, y + 11, contact->name);

			settextstyle(26, 0, "黑体");
			char str_number[20] = { '\0' };
			sprintf(str_number, "%lld", contact->stu_number);
			outtextxy(795, y + 11, str_number);

			char str_age[20] = { '\0' };
			sprintf(str_age, "%d", contact->age);
			outtextxy(582, y + 58, str_age);

			outtextxy(795, y + 58, contact->sex);
			outtextxy(582, y + 105, contact->tele);
			outtextxy(845, y + 105, contact->address);
			y += 150;
		}
		index++;
	}
}

//展示登录用户的信息
void UserInfo(IP info) {
	settextcolor(RGB(40, 40, 40));
	setbkmode(TRANSPARENT);
	settextstyle(40, 0, "黑体");
	outtextxy(675, 12, info.name);
	settextstyle(17, 0, "黑体");
	char str_number[20] = { '\0' };
	sprintf(str_number, "%lld", info.stu_number);
	outtextxy(736, 105, str_number);
}

//将鼠标点击的纵坐标传入函数,通过不同的展开与否对y减去相应的值,当y < 0时即找到点击的用户栏位置,并更改isunfold数组的值
void GetIndex(int n, vector<bool>& isunfold, int y) {
	int index = 0;
	for (int i = 0; i < n; i++) {
		if (isunfold[index] == false) {
			if (y - 50 <= 0) {
				isunfold[index] = true;
				return;
			}
			else y -= 50;  //未展开的图形高度为50
		}
		else if (isunfold[index] == true) {
			if (y - 150 <= 0) {
				isunfold[index] = false;
				return;
			}
			else y -= 150;   //已展开的图形高度为150
		}
		index++;
	}
}

//用于ContactInter函数中多次刷新界面
void RefreshInterface(IMAGE avatar, IMAGE chatInter, IMAGE desktop, IP info) {
	cleardevice();
	putimage(0, 0, &desktop);
	putimage(500, 0, &chatInter);
	putimage(520, 10, &avatar);
	UserInfo(info);
}

//重载
bool CompareName(IP* cont1, IP* cont2) {  
	return cont1->name < cont2->name;
}

void ContactInter(IP info, unordered_map<long long, IP> ID, long long* saveUser) {
	vector<IP*> user;
	LoadContact(user, info, ID);    
	
	IMAGE chatInter;  //界面
	IMAGE desktop;    //桌面
	IMAGE avatar;     //头像
	loadimage(&desktop, "desktop.jpg");
	loadimage(&chatInter, "image_contact\\chatInter.jpg");
	loadimage(&avatar, info.avatar, 130, 130);

	int enter = 1000; 
	while (enter >= 500) {
		BeginBatchDraw();
		cleardevice();
		putimage(0, 0, &desktop);
		putimage(enter, 0, &chatInter);
		putimage(enter + 20, 10, &avatar);
		EndBatchDraw();
		enter -= 10;
		Sleep(2);
	}

	vector<bool> isunfold(100, false);
	UserInfo(info);    //用户信息
	DisplayContact(user, 155, isunfold);  //联系人信息——140

	while (1) {
		if (MouseHit()) {
			MOUSEMSG msg = GetMouseMsg();
			switch (msg.uMsg) {
			case WM_LBUTTONDOWN: {
				//添加
				if (msg.x > 906 && msg.x < 929 && msg.y >123 && msg.y < 145) {
					HWND hWnd = GetHWnd();
					MessageBox(hWnd, "请在命令行输入添加用户的学号!!","!", MB_OKCANCEL);
					AddContact(user, ID, info);   //添加的用户必须是ID里存在的用户 
					//保存、刷新(覆盖图片)、展示   更改信息后必要的三步骤
					SaveContact(user, info); 
					RefreshInterface(avatar, chatInter, desktop, info);
					DisplayContact(user, 155, isunfold);
				}
				//删除
				else if (msg.x > 939 && msg.x < 961 && msg.y > 123 && msg.y < 145) {
					HWND hWnd = GetHWnd();
					MessageBox(hWnd, "请在命令行输入你要删除的人", "! ! ! ! ! ! !", MB_OKCANCEL);
					DelectContact(user);    

					SaveContact(user, info);
					RefreshInterface(avatar, chatInter, desktop, info);
					DisplayContact(user, 155, isunfold);
				}
				//通过GetIndex改变isunfold数组相应位置的值,再通过DisplayContact函数展示所有用户
				else if (msg.x > 508 && msg.x < 992 && msg.y > 155 && msg.y < 600) {
					GetIndex(user.size(),isunfold, msg.y - 155);
					RefreshInterface(avatar, chatInter, desktop, info);
					DisplayContact(user, 155, isunfold);
					
				}
				//查找 -> 展示搜索出的用户的信息 -> 选择是否添加
				else if (msg.x > 972 && msg.x < 995 && msg.y > 123 && msg.y < 145) {
					HWND hWnd = GetHWnd();
					MessageBox(hWnd, "请在命令行输入你要查找的人", "! ! ! ! ! ! !", MB_OKCANCEL);
					
					if (SearchContact(user, ID, info)) {   //返回true表示添加联系人,此时保存联系人 
						SaveContact(user, info);
					}
					RefreshInterface(avatar, chatInter, desktop, info);
					DisplayContact(user, 155, isunfold);
					
				}
				//排序
				else if (msg.x > 873 && msg.x < 897 && msg.y > 123 && msg.y < 145) {
					sort(user.begin(), user.end(), CompareName);    //->421
					RefreshInterface(avatar, chatInter, desktop, info);
					DisplayContact(user, 155, isunfold);
				}
				//退出登录
				else if (msg.x > 872 && msg.x < 995 && msg.y > 4.6 && msg.y < 24.6) {
					*saveUser = 0;
					int exit = 500;
					while (exit <= 1000) {
						BeginBatchDraw();
						cleardevice();
						putimage(0, 0, &desktop);
						putimage(exit, 0, &chatInter);
						EndBatchDraw();
						exit += 10;
						Sleep(2);
					}
					return;
				}
				//返回桌面, 保持登录
				else if (msg.x > 0 && msg.x < 500 && msg.y > 0 && msg.y < 600) {
					int exit = 500;
					while (exit <= 1000) {
						BeginBatchDraw();
						cleardevice();
						putimage(0, 0, &desktop);
						putimage(exit, 0, &chatInter);
						EndBatchDraw();
						exit += 10;
						Sleep(2);
					}
					return;
				}
			}
			}
		}
	}
}

拷贝代码仓库到本地:

gitee:Program lobby: C/C++程序设计 + easyx (gitee.com)

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dusong_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值