程序实践:命令行之连连看
Posted jhcelue
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了程序实践:命令行之连连看相关的知识,希望对你有一定的参考价值。
命令行之连连看
程序实践周课题,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("\n\n *********************************\n"); SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED); printf(" ***** 欢迎进入 连连看 *****\n"); SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN); printf(" ***** C - 选择游戏等级 *****\n"); printf(" ***** T - 有时间限定 *****\n"); printf(" ***** N - 无时间限定 *****\n"); printf(" ***** R - 玩家排名 *****\n"); printf(" ***** E - 游戏结束 *****\n"); printf(" *********************************\n\n"); if(level == 0) printf(" ***** 游戏等级 : 未选 *****\n"); else if(level == 4) printf(" ***** 游戏等级 : 低级 *****\n"); else if(level == 6) printf(" ***** 游戏等级 : 中级 *****\n"); else if(level == 8) printf(" ***** 游戏等级 : 高级 *****\n"); else if(level == 10) printf(" ***** 游戏等级 : 特高 *****\n"); printf("\n输入操作 : "); scanf("%s", &choose); //printf("choose = %c\n", choose); //system("PAUSE"); system("CLS"); //清屏函数 //清屏后显示菜单 } void ChooseMenu() { int tmp; //设4个等级。通过选择游戏难度初始化level printf("\n\n ********************************\n"); printf(" ***** 选择等级 *****\n"); printf(" ***** 1 - 低级 *****\n"); // 4 * 4 printf(" ***** 2 - 中级 *****\n"); // 6 * 6 printf(" ***** 3 - 高级 *****\n"); // 8 * 8 printf(" ***** 4 - 特高 *****\n"); // 10 * 10 printf(" ********************************\n"); printf(" \n游戏等级 : "); scanf("%d", &tmp); while(tmp != 1 && tmp != 2 && tmp != 3 && tmp != 4) { printf(" 请选择正确的游戏等级 1 , 2 , 3 , 4\n"); printf(" \n游戏等级 : "); 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("\n\n\n"); 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("\n\n"); 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 - 提示\n\n"); else if (i == 2) printf(" R - 洗牌\n\n"); else if (i == 3) printf(" C - 继续\n\n"); else printf("\n\n"); } 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("\n 输入的两点坐标同样,请保证两点的坐标不同\n\n"); return false; } if(map[x1][y1] == ‘ ‘ || map[x2][y2] == ‘ ‘) { printf("\n 两点之中有没有图案的点,请保证两点都有图案\n\n"); return false; } if(map[x1][y1] != map[x2][y2]) { printf("\n 两点图案不同。请保证两点图案同样\n\n"); return false; } if(DFS(x1, y1, x2, y2, 0, -1)) { printf("\n 这两点能够消除\n\n"); return true; } else { printf("这两点不能消除。请重试\n\n"); 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\n", 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\n", 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("\n 提示 :\n x1 = %d, y1 = %d\n x2 = %d, y2 = %d\n", 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("\n\n"); for (int i = 0; i < 4; i++) { if (i == 0) { printf(" 低级:\n"); printf(" 排名 账号 成绩(秒)\n"); for (int j = 0; j < data[i].n; j++) printf(" %d %s %d\n", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000); printf("\n"); } if (i == 1) { printf(" 中级:\n"); printf(" 排名 账号 成绩(秒)\n"); for (int j = 0; j < data[i].n; j++) printf(" %d %s %d\n", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000); printf("\n"); } if (i == 2) { printf(" 高级:\n"); printf(" 排名 账号 成绩(秒)\n"); for (int j = 0; j < data[i].n; j++) printf(" %d %s %d\n", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000); printf("\n"); } if (i == 3) { printf(" 特高:\n"); printf(" 排名 账号 成绩(秒)\n"); for (int j = 0; j < data[i].n; j++) printf(" %d %s %d\n", data[i].player[j].rank, data[i].player[j].name, data[i].player[j].score / 1000); printf("\n"); } } system("PAUSE"); } int GameMenuChoose() { printf("\n 输入操作 : "); 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("\n 请输入两点坐标\n"); printf("\n 一 : "); scanf("%d %d", &x1, &y1); printf("\n 二 : "); 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!\n"); 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("\n\n 请先选择游戏等级\n"); ChooseMenu(); system("PAUSE"); if (level != 0) { system("CLS"); break; } } goto Loop; } if (choose[0] == ‘N‘) { Start(); goto Loop; } if (choose[0] == ‘T‘) { printf("请输入username\n"); 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("非法输入,请看清菜单选项\n"); system("PAUSE"); system("CLS"); goto Loop; } return 0; }
以上是关于程序实践:命令行之连连看的主要内容,如果未能解决你的问题,请参考以下文章