• 程序实践:命令行之连连看


    命令行之连连看

    程序实践周课题,VC++6.0上可编译执行


    游戏截图:




    #include <cstdio>
    
    #include <cstring>
    #include <iostream>
    #include <windows.h> 
    #include <time.h>
    #include <algorithm>
    using namespace std;
    int dirx[4] = {1,-1,0,0};  
    int diry[4] = {0,0,1,-1};  //四个方向,DFS函数中要使用
    bool vis[15][15];	//标记该点是否已经被訪问,DFS中使用
    char map[15][15];	//二维字符数组表示连连看矩阵
    bool Hash[105];  //hash数组用来标记,去重
    int num, level;		//num游戏记录人数。 level游戏等级
    char choose[100];		//提取对应操作的字符
    struct Players  //结构体存游戏者的帐号,得分,排名
    {
    	char name[50];
    	int score;
    	int rank;
    };
    struct Data
    {
    	int n;
    	struct Players player[100];
    }data[4];
    
    
    bool cmp(Players a, Players b)
    {
    	return a.score < b.score;
    }
    
    void set_rank(int l)
    {
    	int tmp;
    	if (l == 4)
    		tmp = 0;
    	else if (l == 6)
    		tmp = 1;
    	else if (l == 8)
    		tmp = 2;
    	else if (l == 10)
    		tmp = 3;
    	for(int i = 0; i < data[tmp].n; i++)
    	{
    		data[tmp].player[i].rank = i + 1;
    		if(data[tmp].player[i].score == data[tmp].player[i + 1].score)
    		{
    			data[tmp].player[i + 1].rank = data[tmp].player[i].rank;
    			i++;
    		}
    	}
    }
    
    void menu()  //菜单
    {
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    	SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
    	printf("
    
    			*********************************
    ");
    	SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
    	printf("			*****    欢迎进入 连连看    *****
    ");
    	SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
    	printf("			*****    C - 选择游戏等级   *****
    ");
    	printf("			*****    T - 有时间限定     *****
    ");
    	printf("			*****    N - 无时间限定     *****
    ");
    	printf("			*****    R - 玩家排名       *****
    ");
    	printf("			*****    E - 游戏结束       *****
    ");
    	printf("			*********************************
    
    ");
    	if(level == 0)
    		printf("			*****    游戏等级 : 未选    *****
    ");
    	else if(level == 4)
    		printf("			*****    游戏等级 : 低级    *****
    ");
    	else if(level == 6)
    		printf("			*****    游戏等级 : 中级    *****
    ");
    	else if(level == 8)
    		printf("			*****    游戏等级 : 高级    *****
    ");
    	else if(level == 10)
    		printf("			*****    游戏等级 : 特高    *****
    ");
    	printf("
    输入操作 : ");
    	scanf("%s", &choose);
    	//printf("choose = %c
    ", choose);
    	//system("PAUSE");
    	system("CLS"); //清屏函数
    	//清屏后显示菜单
    
    }
    
    void ChooseMenu()
    {
    	int tmp;
    	//设4个等级。通过选择游戏难度初始化level
    	printf("
    
    			********************************
    ");
    	printf("			*****        选择等级      *****
    ");
    	printf("			*****        1 - 低级      *****
    "); // 4 * 4
    	printf("			*****        2 - 中级      *****
    "); // 6 * 6
    	printf("			*****        3 - 高级      *****
    "); // 8 * 8
    	printf("			*****        4 - 特高      *****
    "); // 10 * 10
    	printf("			********************************
    ");
    	printf("		  
    游戏等级 : ");
    	scanf("%d", &tmp);
    	while(tmp != 1 && tmp != 2 && tmp != 3 && tmp != 4)
    	{
    		printf("			请选择正确的游戏等级 1 , 2 , 3 , 4
    ");
    		printf("		  
    游戏等级 : ");
    		scanf("%d", &tmp);
    	}
    	if (tmp == 1)
    		level = 4;
    	else if (tmp == 2)
    		level = 6;
    	else if (tmp == 3)
    		level = 8;
    	else if (tmp == 4)
    		level = 10;
    	getchar();
    }
    
    void init_map()  //初始化游戏矩阵
    {
    	int i, j;
    	char get[105];	 //get数组用来得到随机字符
    	memset(Hash, false, sizeof(Hash)); //初始化hash数组
    	srand((unsigned)time(NULL));	//设置随机数种子
    	//随机生成一半的字符。由于要保证两两配对
    	for(i = 0; i < (level * level) / 2; i++) 
    	{
    		get[i] = 65 + rand() % 26;   //随机生成前一半
    		get[i + (level * level) / 2] = get[i]; //将前一半的值赋给后一半
    	}
    	int index;  //在get中随机产生的数组下标
    	bool flag;	//标记是否找到一个新的字符
    	srand((unsigned)time(NULL)); //设置随机数种子
    	for(i = 1; i <= level; i++)
    	{
    		for(j = 1; j <= level; j++)
    		{
    			flag = false;   //flag每次先赋值为false
    			while(true)		//死循环
    			{
    				index = 0 + rand() % (level * level); //随机生成get数组中的下标
    				if(!Hash[index])	//	假设该点没有被使用
    				{	
    					map[i][j] = get[index];  //将它的值赋给map
    					Hash[index] = true;		 //将它标记为已使用
    					flag = true;			 //找到一个字符flag设为true
    				}
    				if(flag)  //假设找到则退出for循环
    					break;
    			}
    		}
    	}
    	//在连连看矩阵周围加一圈空格,用来消除边界元素
    	for(i = 0; i <= level + 1; i++)
    		map[0][i] = map[level + 1][i] = map[i][0] = map[i][level +1] = ' ';
    }
    
    void show_map()   //显示矩阵
    {
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    	printf("
    
    
    ");
    	int i, j;
    	SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
    	printf("	");
    	for(i = 1; i <= level; i++)
    		printf("%4d", i);
    	SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
    	printf("	游戏选择");
    	printf("
    
    ");
    	for(i = 1; i <= level; i++)
    	{
    		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
    		printf("	");
    		for(j = 1; j <= level; j++)
    			printf("%4c", map[i][j]);
    		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
    		printf("%4d", i);
    		SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
    		if (i == 1)
    			printf("	T - 提示
    
    ");
    		else if (i == 2)
    			printf("	R - 洗牌
    
    ");
    		else if (i == 3)
    			printf("	C - 继续
    
    ");
    		else
    			printf("
    
    ");
    	}
    	SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
    }
    
    
    bool IsWin()  //推断是否结束
    {
    	int i, j;
    	for(i = 1; i <= level; i++)
    		for(j = 1; j <= level; j++)
    			if(map[i][j] != ' ') //假设矩阵中有一个不是空格则说明没有结束
    				return false;
    	return true;
    }
    
    bool DFS(int x1, int y1, int x2, int y2, int turn, int dir)
    {
        //转向次数大于2则返回false。这里大于3是由于dir初始为-1也就是第一次选方向时
        //就已经让turn加1了,
        if(turn > 3)
            return false;
        //转向两次后不能沿某一方向到终点,返回false
        if(turn == 3 && (x2 != x1 && y2 != y1))
            return false;
        //到达终点,返回true
        if(x1 == x2 && y1 == y2)
            return true;
        for(int i = 0; i < 4; i++) //四个方向遍历
        {
            int xx = x1 + dirx[i];
            int yy = y1 + diry[i];
            //若该点有字符且不是终点则换方向
            if(map[xx][yy] != ' ' && !(xx == x2 && yy == y2))
                continue;
    		//推断是否出界,是否已经訪问
            if(xx < 0 || yy < 0 || xx > level + 1 || yy > level + 1 || vis[xx][yy])
                continue;   
            //将该点设为已訪问
            vis[xx][yy] = true;
            //若当前方向与之前方向不同,转向次数+1,继续搜索
            if(DFS(xx, yy, x2, y2, turn + (dir != i), i))
                return true;
            //若从该点的dfs不成功,将该点设为未訪问
            vis[xx][yy] = false;
        }
        return false;
    }
    
    
    bool judge(int x1, int y1, int x2, int y2)
    {
    	memset(vis, false, sizeof(vis)); //初始化vis数组用于DFS
    	vis[x1][y1] = true;	//将起点标记为已訪问
    	//特判几种不能消除的情况
    	if(x1 == x2 && y1 == y2)
    	{
    		printf("
    	输入的两点坐标同样,请保证两点的坐标不同
    
    ");
    		return false;
    	}
    	if(map[x1][y1] == ' ' || map[x2][y2] == ' ')
    	{
    		printf("
    	两点之中有没有图案的点,请保证两点都有图案
    
    ");
    		return false;
    	}
    	if(map[x1][y1] != map[x2][y2])
    	{
    		printf("
    	两点图案不同。请保证两点图案同样
    
    ");
    		return false;
    	}
    	if(DFS(x1, y1, x2, y2, 0, -1))
    	{
    		printf("
    	这两点能够消除
    
    ");
    		return true;
    	}
    	else
    	{
    		printf("这两点不能消除。请重试
    
    ");
    		return false;
    	}
    } 
    
    
    //该函数在total_judge()中被调用。功能与judge相似
    //不输出中文提示
    bool judge2(int x1, int y1, int x2, int y2) 
    {
    	memset(vis, false, sizeof(vis));
    	vis[x1][y1] = true;
    	if(x1 == x2 && y1 == y2)
    		return false;
    	if(map[x1][y1] == ' ' || map[x2][y2] == ' ')
    		return false;
    	if(map[x1][y1] != map[x2][y2])
    		return false;
    	if(DFS(x1, y1, x2, y2, 0, -1))
    		return true;
    	return false;
    }
    
    
    bool total_judge()  //全盘判定,用于推断是否还有可行解
    {
    	int i, j, ii, jj;
    	bool vis2[15][15];   //标记该点是否被訪问
    	memset(vis2, false, sizeof(vis2)); //初始化vis2
    	int x1, y1, x2, y2;  //记录两个点的坐标
    	for(i = 1; i <= level; i++)
    	{
    		for(j = 1; j <= level; j++)
    		{
    			//假设该点不是空格,而且没有被訪问
    			if(map[i][j] != ' ' && !vis2[i][j])
    			{
    				vis2[i][j] = true; //将其设为已訪问
    				//找到一个点A赋值
    				x1 = i;		
    				y1 = j;
    				//printf("x1 = %d  y1 = %d
    ", x1, y1);
    				//枚举点找到与A点字符同样的一个点
    				for(ii = 1; ii <= level; ii++)
    				{
    					for(jj = 1; jj <= level; jj++)
    					{
    						if(map[ii][jj] == map[x1][y1] && !vis2[ii][jj])
    						{
    							//假设找到一个点与A点不是同一点而且图案同样
    							//将其标记为已訪问
    							vis2[ii][jj] = true;
    							//记为B点并赋值
    							x2 = ii;
    							y2 = jj;
    							//printf("x2 = %d  y2 = %d
    ", x2, y2);
    							//推断A,B是否能被消除
    							//找到一个可行解则说明游戏能够继续不用restart
    							if(judge2(x1, y1, x2, y2))
    								return true;
    						}
    					}
    				}
    			}
    		}
    	}
    	//假设没找到可行解。则须要restart
    	return false;
    }
    
    
    bool Tip()  //全盘判定,用于推断是否还有可行解
    {
    	int i, j, ii, jj;
    	bool vis2[15][15];   //标记该点是否被訪问
    	memset(vis2, false, sizeof(vis2)); //初始化vis2
    	int x1, y1, x2, y2;  //记录两个点的坐标
    	for(i = 1; i <= level; i++)
    	{
    		for(j = 1; j <= level; j++)
    		{
    			//假设该点不是空格,而且没有被訪问
    			if(map[i][j] != ' ' && !vis2[i][j])
    			{
    				vis2[i][j] = true; //将其设为已訪问
    				//找到一个点A赋值
    				x1 = i;		
    				y1 = j;
    				//枚举点找到与A点字符同样的一个点
    				for(ii = 1; ii <= level; ii++)
    				{
    					for(jj = 1; jj <= level; jj++)
    					{
    						if(map[ii][jj] == map[x1][y1] && !vis2[ii][jj])
    						{
    							//假设找到一个点与A点不是同一点而且图案同样
    							//将其标记为已訪问
    							vis2[ii][jj] = true;
    							//记为B点并赋值
    							x2 = ii;
    							y2 = jj;
    							//推断A,B是否能被消除
    							//找到一个可行解则说明游戏能够继续不用restart
    							if(judge2(x1, y1, x2, y2))
    							{
    								//输出可行解
    								printf("
    	提示 :
    	x1 = %d, y1 = %d
    	x2 = %d, y2 = %d
    ", x1, y1, x2, y2);
    								return true;
    							}
    						}
    					}
    				}
    			}
    		}
    	}
    	return false;
    }
    
    void Restart() //洗牌函数
    {
    	int i, j;
    	bool hash2[105], flag;
    	char re[105];
    	int cnt = 0, index2;
    	memset(re, 0, sizeof(re));
    	memset(hash2, false, sizeof(hash2));
    	for(i = 1; i <= level; i++)
    		for(j = 1; j <= level; j++)
    			if(map[i][j] != ' ') 
    				re[cnt++] = map[i][j]; //提取实用字符
    	
    	srand((unsigned)time(NULL));
    	for(i = 1; i <= level; i++)
    	{
    		for(j = 1; j <= level; j++)
    		{
    			if (map[i][j] != ' ')
    			{
    				flag = false;
    				while (true)
    				{
    					index2 = 0 + rand() % cnt;
    					if (!hash2[index2])
    					{
    						hash2[index2] = true;
    						map[i][j] = re[index2];
    						flag = true;
    					}
    					if (flag)
    						break;
    				}
    			}
    		}
    	}
    }
    
    
    void Rank()
    {
    	printf("
    
    ");
    	for (int i = 0; i < 4; i++)
    	{
    		if (i == 0)
    		{
    			printf("		低级:
    ");
    			printf("		排名		账号		成绩(秒)
    ");
    			for (int j = 0; j < data[i].n; j++)
    				printf("		%d		%s		%d
    ", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000);
    			printf("
    ");
    		}
    		if (i == 1)
    		{
    			printf("		中级:
    ");
    			printf("		排名		账号		成绩(秒)
    ");
    			for (int j = 0; j < data[i].n; j++)
    				printf("		%d		%s		%d
    ", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000);
    			printf("
    ");
    		}
    		if (i == 2)
    		{
    			printf("		高级:
    ");
    			printf("		排名		账号		成绩(秒)
    ");
    			for (int j = 0; j < data[i].n; j++)
    				printf("		%d		%s		%d
    ", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000);
    			printf("
    ");
    		}
    		if (i == 3)
    		{
    			printf("		特高:
    ");
    			printf("		排名		账号		成绩(秒)
    ");
    			for (int j = 0; j < data[i].n; j++)
    				printf("		%d		%s		%d
    ", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000);
    			printf("
    ");
    		}
    	}
    	system("PAUSE");
    }
    
    
    int GameMenuChoose()
    {
    	printf("
    	输入操作 : ");
    	char tmp;
    	getchar();
    	scanf("%c", &tmp);
    	if (tmp == 'T')
    	{
    		Tip();
    		return 1;
    	}
    	if (tmp == 'R')
    	{
    		system("CLS");
    		Restart();
    		show_map();
    		return 1;
    	}
    	if (tmp == 'C')
    		return 0;
    	return 0;
    }
    
    void play() //游戏函数
    {
    	int x1, y1, x2, y2;
    	while (GameMenuChoose());
    	printf("
    	请输入两点坐标
    ");
    	printf("
    	一 : ");
    	scanf("%d %d", &x1, &y1);
    	printf("
    	二 : ");
    	scanf("%d %d", &x2, &y2);
    	if (judge(x1, y1, x2, y2))  //假设能够消除,把两点的值设为空格
    	{
    		map[x1][y1] = ' ';
    		map[x2][y2] = ' ';
    	}
    }
    
    
    void Start()
    {
    	system("CLS");
    	init_map();  //初始化游戏界面
    	while (!IsWin())  //假设游戏没有结束则一直play
    	{
    		while (!total_judge())	//若没有可行解,则一直洗牌直到有可行解
    		{
    			Restart();
    			show_map();
    		}
    		show_map();
    		play();
    		system("PAUSE");
    		system("CLS");
    	}
    	system("CLS");
    	printf("Good Job!
    ");
    	system("PAUSE");
    	system("CLS");
    }
    
    void ReadInMemory(Data *u)
    {
    	FILE *fp;
    	fp = fopen("data.txt", "rb");
    	if (fp == NULL)
    		return;
    	fread(u, sizeof(struct Data), 1, fp);
    	fclose(fp);
    }
    
    void WriteToFile(Data * u)
    {
    	FILE *fp;
    	fp = fopen("data.txt", "wb");
    	if (fp == NULL)
    		return;
    	fwrite(u, sizeof(struct Data), 1, fp);
    	fclose(fp);
    }
    
    void SetData(int l,int e, int s, char *a)
    {
    	if (l == 4)
    	{
    		data[0].n++;
    		data[0].player[data[0].n].score = (int)e - s;
    		strcpy(data[0].player[data[0].n].name, a);
    		sort(data[0].player, data[0].player + data[0].n, cmp);
    	}
    	else if (l == 6)
    	{
    		data[1].n++;
    		data[1].player[data[1].n].score = (int)e - s;
    		strcpy(data[1].player[data[1].n].name, a);
    		sort(data[1].player, data[1].player + data[1].n, cmp);
    	}
    	else if (l == 8)
    	{
    		data[2].n++;
    		data[2].player[data[2].n].score = (int)e - s;
    		strcpy(data[2].player[data[2].n].name, a);
    		sort(data[2].player, data[2].player + data[2].n, cmp);
    	}
    	else if (l == 10)
    	{
    		data[3].n++;
    		data[3].player[data[3].n].score = (int)e - s;
    		strcpy(data[3].player[data[3].n].name, a);
    		sort(data[3].player, data[3].player + data[3].n, cmp);
    	}
    }
    
    
    int main()
    {
    	//system("title 连连看");
    	level = 0;
    Loop:
    	char tmp_name[20];
    	ReadInMemory(data);
    	menu();
    	if(choose[0] == 'E')
    		return 0;
    	if(choose[0] == 'R')
    	{
    		Rank();
    		system("CLS");
    		goto Loop;
    	}
    	if(choose[0] == 'C')
    	{
    		system("CLS");
    		ChooseMenu();
    		system("PAUSE");
    		system("CLS");
    		goto Loop;
    	}
    	if (level == 0 && (choose[0] == 'N' || choose[0] == 'T'))
    	{
    		while (choose[0] != 'C')
    		{
    			system("CLS");
    			printf("
    
    				请先选择游戏等级
    ");
    			ChooseMenu();
    			system("PAUSE");
    			if (level != 0)
    			{
    				system("CLS");
    				break;
    			}
    		}
    		goto Loop;
    	}
    	if (choose[0] == 'N')
    	{
    		Start();
    		goto Loop;
    	}
    	if (choose[0] == 'T')
    	{
    		printf("请输入username
    ");
    		cin >> tmp_name;
    		clock_t st = clock();
    		Start();
    		clock_t ed = clock();
    		SetData(level, st, ed, tmp_name);
    		set_rank(level);
    		WriteToFile(data);
    		goto Loop;
    	}
    	if(strlen(choose) != 1 || choose[0] != 'E' || choose[0] != 'R' || choose[0] != 'C' || choose[0] != 'N' || choose[0] != 'T')
    	{
    		printf("非法输入,请看清菜单选项
    ");
    		system("PAUSE");
    		system("CLS");
    		goto Loop;
    	}
    	return 0;
    }
    
    


  • 相关阅读:
    Apollo配置中心环境搭建(Linux)
    SpringBoot整合Swagger2
    DevExpress ASP.NET v18.2新功能详解(四)
    UI控件Telerik UI for WinForms发布R1 2019|附下载
    DevExpress WinForms使用教程:Data Grid
    开发框架DevExtreme全新发布v18.2.6|附下载
    DevExpress ASP.NET v18.2新功能详解(三)
    .NET界面控件DevExpress全新发布v18.2.6|附下载
    DevExpress WinForms使用教程:图表控件
    VCL界面控件DevExpress VCL Controls发布v18.2.4|附下载
  • 原文地址:https://www.cnblogs.com/jhcelue/p/6930223.html
Copyright © 2020-2023  润新知