diff --git a/MineSweeper Run 1.1.c b/MineSweeper Run 1.1.c new file mode 100644 index 0000000..d620448 --- /dev/null +++ b/MineSweeper Run 1.1.c @@ -0,0 +1,2708 @@ +//#define _CRT_SECURE_NO_WARNINGS//vs专属(doge) +#include +#include +#include //时间戳作种子生成随机数 +/** + * 扫雷 MineSweeper Run + * + * by Ltabsyy & Jws + **/ +#define LimHeight 128//最大高度,限制行数 +#define LimWidth 93//最大宽度,限制列数 +#define LimDictionary 1024//字典大小 +//内存占用至少为 53*H*W+128*D Byte,默认约0.7MB + +// 地图生成和显示 +void SummonBoard(int r0,int c0); +int Place(int n); +void ShowBoard(int mode); + +// 后台计算 +void OpenZeroChain(int r0,int c0); +void SummonZeroChain(int r0,int c0); +int RealRemainder(); +int NumberOfNotShown(); +int NumberOfSign(); +int NumberOfNotShownAround(int r0,int c0); +int NumberOfSignAround(int r0,int c0); +int IsAroundZeroChain(int r0,int c0); + +// 方案思考 +void LookMap(); +void translateMap(int rs0,int cs0); +void Solve(); +int Think(); +void SummonMineRateForNotShown(); +int NumberOfNumberAround(int r0,int c0); +void ShowAnswer(int isFoundOpen); +void ShownModeBak(int mode); + +// 全局矩阵 +int isMine[LimHeight][LimWidth]={0}; +int numberOfMineAround[LimHeight][LimWidth]={0}; +int board[LimHeight][LimWidth]={0}; +int isShown[LimHeight][LimWidth]={0}; +int zeroChain[LimHeight][LimWidth]={0}; + +// 方案矩阵 +char mapShown[LimHeight+2][2*LimWidth+5]={0}; +char map[LimHeight][LimWidth]={0}; +int isShownBak[LimHeight][LimWidth]={0};//备份显示方式矩阵 +int numberShown[LimHeight][LimWidth]={0}; +int solution[LimHeight][LimWidth]={0}; +int isThought[LimHeight][LimWidth]={0}; +int numberTeam[LimHeight][LimWidth]={0}; +int thinkChain[LimHeight][LimWidth]={0}; +int possibility[32]={0}; +int numberCheck[LimHeight][LimWidth]={0}; +int dictionary[LimDictionary][32]={0}; +float isMineRate[LimHeight][LimWidth]={0}; + +// Bench统计 +int numberOfAbandonedThinkChain = 0; +int isWinningOfSeed[LimDictionary]={0}; + +// 全局变量及初始化 +int numberOfMine = 10;//雷数量 +int heightOfBoard = 10;//界面高度 +int widthOfBoard = 10;//界面宽度 +int lengthOfThinkChain = 19;//未知链长度 +int summonCheckMode = 2;//0不校验,1非雷,2必空 +int solveMode = 1;//1游戏模式,2分析模式 +int isBench = 0; + +// 是否调试(0:关 1:开,显示部分 2:开,显示全部) +int debug = 0; + +int main() +{ + int choiceMode;//游戏功能的选择 + int seed, r0, c0, seedMin, seedMax;//地图生成 + char operateMode;//游戏操作和设置的字符读取 + int r, c, remainder, isOpenMine, ra, ca;//通用坐标和游戏胜利与连锁反应判断 + int showTime = 0, t0, t1;//显示用时 + int set, temp;//设置 + int difficulty = 0;//当前难度显示,实际没有必要定义(doge) + float density =(float) numberOfMine / heightOfBoard / widthOfBoard;//雷密度 + int heightOfMapShown, widthOfMapShown, rs0, cs0;//地图求解输入翻译 + int countOfWin, countOfSolution, countOfStep;//Bench统计 + //很好,我已经看不清我定义些啥了(doge) + while(1)//main内循环防止变量重复定义 + { + system("cls");//清屏 + if(debug == 2) printf("\nHello! Administrator Ltabsyy or Jws!\n"); + /*printf("*******************************\n" + "**新游戏(1)**设置(2)**退出(3)**\n" + "*******************************\n");*/ + printf("*******************************\n"//宽31 + "(1)新游戏\n" + "(2)地图求解\n" + "(3)设置\n" + "(4)Bench\n" + "(5)退出\n" + "*******************************\n"); + printf(">"); + scanf("%d", &choiceMode); + /*--新游戏--*/ + if(choiceMode == 1) + { + /*重置*/ + system("cls"); + remainder = numberOfMine; + isOpenMine = 0; + for(r=0; r@ "); + scanf("%d%d", &r0, &c0);//通过键盘获取第一次选择的坐标 + seed = time(0);//当前时间戳作种子生成随机数 + if(debug == 2) + { + set = 0; + printf("[Debug]seed=%d,%d,%d\n", seed, r0, c0); + printf("[0:生成/1:更改/2:搜索]\n>"); + scanf("%d", &set); + if(set == 1) + { + printf("[seed] [r0] [c0]\n>"); + scanf("%d%d%d", &seed, &r0, &c0); + } + else if(set == 2) + { + printf("[seedMin] [seedMax] [r0] [c0]\n>"); + scanf("%d%d%d%d", &seedMin, &seedMax, &r0, &c0); + debug = 0;//搜索不显示多余信息 + for(seed=seedMin; seed<=seedMax; seed++) + { + srand(seed); + SummonBoard(r0,c0); + printf("Map:%d*%d-%d\n", heightOfBoard, widthOfBoard, numberOfMine); + printf("seed=%d,%d,%d\n", seed, r0, c0); + ShowBoard(1); + } + debug = 2; + system("pause"); + continue; + } + } + srand(seed); + r = r0; + c = c0; + SummonBoard(r,c); + isShown[r][c] = 1; + t0 = time(0); + /*游戏循环*/ + while(1) + { + /*计算*/ + if(isShown[r][c] == 1)//对翻开操作做出反应 + { + if(board[r][c] == 0)//翻开0连锁翻开 + { + OpenZeroChain(r,c); + } + else if(board[r][c] == 9)//寄 + { + system("cls"); + ShowBoard(1); + printf(":(\nGame Over!\n"); + t1 = time(0); + if(showTime == 1) printf("用时:%d\n", t1-t0); + system("pause"); + break; + } + } + if(RealRemainder() == 0//正确标记所有雷则胜利 + ||NumberOfNotShown() == remainder)//未翻开的都是雷则胜利 + { + system("cls"); + ShowBoard(1); + printf(":)\nYou Win!\n"); + t1 = time(0); + if(showTime == 1) printf("用时:%d\n", t1-t0); + system("pause"); + break; + } + /*显示*/ + system("cls"); + if(debug == 1 || debug == 2) + { + printf("\n"); + printf("Map:%d*%d-%d\n", heightOfBoard, widthOfBoard, numberOfMine); + printf("seed=%d,%d,%d\n", seed, r0, c0); + ShowBoard(1); + printf("RealRemainder=%d\n", RealRemainder()); + printf("[Debug]显示方式矩阵:\n"); + for(r=0; r"); + getchar();//吸收回车防止显示未选择操作模式 + scanf("%c%d%d", &operateMode, &r, &c); + if(operateMode == '@') + { + if(isShown[r][c] == 1)//翻开数字则尝试翻开周围 + { + if(NumberOfSignAround(r,c) == numberOfMineAround[r][c]) + { + for(ra=r-1; ra<=r+1; ra++) + { + for(ca=c-1; ca<=c+1; ca++) + { + if(ra>=0 && ra=0 && ca\nWelcome! Administrator Ltabsyy or Jws!\n"); + } + system("pause"); + break; + } + } + else + { + printf(":(\n未选择操作模式!\n"); + } + } + } + } + /*--地图求解--*/ + else if(choiceMode == 2) + { + system("cls");//清屏 + //printf("MineSweeper Solution\n"); + if(debug == 1) + { + printf("*******************************\n"//宽31 + "使用方法:\n" + "从含坐标地图左上角到右下角整个选中,\n" + "复制粘贴并回车。\n" + "可以把所有方案批量复制到游戏处粘贴,\n" + "最后一个方案如未执行别忘了按下回车。\n" + "按 ` 返回菜单。\n" + "*******************************\n"); + } + else if(debug == 2) + { + printf("*******************************\n"//宽31 + "使用方法:\n" + "没有必要,你已是开发者。(doge)\n" + "*******************************\n"); + } + else + { + printf("*******************************\n"//宽31 + "使用方法:\n" + "从含坐标地图左上角到右下角整个选中,\n" + "复制粘贴并回车。\n" + "按 ` 返回菜单。\n" + "*******************************\n"); + } + printf("当前雷数:%d|当前界面大小:%d*%d\n", numberOfMine, heightOfBoard, widthOfBoard); + rs0 = Place(widthOfBoard-1);//行起始指针,为列坐标显示占用行数 + cs0 = Place(heightOfBoard-1)+1;//列起始指针,为行坐标显示占用列数 + heightOfMapShown = heightOfBoard + rs0;//计算包含坐标的行数 + widthOfMapShown = 2*widthOfBoard + cs0 + 1;//计算包含坐标的每行字符数加回车 + srand(time(0)); + if(debug == 2) + { + printf("\n"); + printf("pointer ->(%d,%d)\n", rs0, cs0); + printf("heightOfMapShown=%d\n", heightOfMapShown); + printf("widthOfMapShown=%d\n", widthOfMapShown); + } + /*扫雷*/ + getchar();//吃回车 + while(1) + { + /*输入*/ + printf("请粘贴最新地图:\n"); + for(r=0; r"); + scanf("%d", &set); + if(set == 1)//设置游戏难度 + { + printf("*******************************\n"//宽31 + "(0)默认:10*10 - 10\n" + "(1)初级: 9*9 - 10\n" + "(2)中级:16*16 - 40\n" + "(3)高级:16*30 - 99\n" + "(4)自定义**********\n" + "*******************************\n"); + printf("/set difficulty "); + scanf("%d", &difficulty); + if(difficulty == 0)//默认为10*10-10,比初级更简单(doge) + { + numberOfMine = 10; + heightOfBoard = 10; + widthOfBoard = 10; + } + else if(difficulty == 1) + { + numberOfMine = 10; + heightOfBoard = 9; + widthOfBoard = 9; + } + else if(difficulty == 2) + { + numberOfMine = 40; + heightOfBoard = 16; + widthOfBoard = 16; + } + else if(difficulty == 3) + { + numberOfMine = 99; + heightOfBoard = 16; + widthOfBoard = 30; + } + else + { + /*设置雷数*/ + printf("请输入雷数. . . \n>"); + scanf("%d", &temp); + if(temp < 0) + { + numberOfMine = 0; + } + else if(temp > LimHeight * LimWidth) + { + numberOfMine = LimHeight * LimWidth; + } + else + { + numberOfMine = temp; + } + printf("/set numberOfMine %d\n", numberOfMine); + /*设置界面高度*/ + printf("请输入界面高度/行数(最大行坐标+1). . . \n>"); + scanf("%d", &temp); + if(temp < 1) + { + heightOfBoard = 1; + } + else if(temp > LimHeight) + { + heightOfBoard = LimHeight; + } + else + { + heightOfBoard = temp; + } + printf("/set heightOfBoard %d\n", heightOfBoard); + /*设置界面宽度*/ + printf("请输入界面宽度/列数(最大列坐标+1). . . \n>"); + scanf("%d", &temp); + if(temp < 1) + { + widthOfBoard = 1; + } + else if(temp > LimWidth) + { + widthOfBoard = LimWidth; + } + else + { + widthOfBoard = temp; + } + printf("/set widthOfBoard %d\n", widthOfBoard); + /*界面校正*/ + if(widthOfBoard == LimWidth && heightOfBoard > 100) + { + printf(":(\n界面不足以容纳行坐标轴!\n"); + printf("(1)维持当前界面高度,界面宽度-1\n"); + printf("(2)维持当前界面宽度,界面高度设为100\n"); + printf("(3)坚持设置!\n"); + printf(">"); + scanf("%d", &temp); + if(temp == 1) + { + widthOfBoard--; + } + else if(temp == 2) + { + heightOfBoard = 100; + } + } + /*雷数校正*/ + if(heightOfBoard * widthOfBoard < numberOfMine) + { + printf(":(\n界面不足以容纳当前雷数!\n"); + numberOfMine = heightOfBoard * widthOfBoard; + printf("/set numberOfMine %d\n", numberOfMine); + } + /*更新密度*/ + density =(float) numberOfMine / heightOfBoard / widthOfBoard; + if(density > 0.72) + { + printf(":(\n当前密度%.2f,难以生成地图\n", density); + } + if(density > 0.24) + { + printf(":(\n当前密度%.2f,难度可能过高!\n", density); + printf("(1)更改雷数\n"); + printf("(2)更改密度\n"); + printf("(3)坚持挑战!\n");//坚持访问!(doge) + printf(">"); + scanf("%d", &temp); + if(temp == 1) + { + printf("/set numberOfMine "); + scanf("%d", &numberOfMine); + } + else if(temp == 2) + { + printf(">"); + scanf("%f", &density); + numberOfMine = density * heightOfBoard * widthOfBoard; + printf("/set numberOfMine %d\n", numberOfMine); + } + } + } + } + else if(set == 2)//调试选项 + { + printf("*******************************\n"//宽31 + "调试选项仅提供给专业玩家和开发者使用。\n" + "除非你知道你在做什么,否则请退出设置。\n" + "(1)退出\n" + "(2)设置求解模式\n" + "(3)以密度设置雷数\n" + "(4)设置地图生成校验\n" + "(5)更改游戏结束是否显示用时(当前:%d)\n" + "*******************************\n", showTime); + getchar(); + printf(">"); + operateMode=getchar(); + if(operateMode == '$')//调试 + { + scanf("%d", &set); + if(set == debug) + { + scanf("%d", &debug); + if(debug == 2) + { + printf("\nWelcome! Administrator Ltabsyy or Jws!\n"); + } + system("pause"); + } + } + else if(operateMode == '2')//设置求解模式 + { + printf("*******************************\n"//宽31 + "(1)设置模式:游戏模式/分析模式\n" + "(2)自定义设置枚举限制\n" + "(3)退出\n" + "*******************************\n"); + printf("当前模式:%d|当前枚举限制:%d\n", solveMode, lengthOfThinkChain); + printf(">"); + scanf("%d", &set); + if(set == 1) + { + printf("*******************************\n"//宽31 + "(1)游戏模式:先不枚举,无解则枚举\n" + "(2)分析模式:始终以设置的限制枚举\n" + "*******************************\n"); + printf("/set solveMode "); + scanf("%d", &set); + if(set==1 || set==2) solveMode=set; + } + else if(set == 2) + { + if(solveMode == 2) + { + printf("*******************************\n"//宽31 + "** 0 ---- 17 ------------ 30 **\n" + "** 关 效率 最大 **\n" + "*******************************\n"); + } + if(solveMode == 1) + { + printf("*******************************\n"//宽31 + "** 0 ------ 19 ---------- 30 **\n" + "** 关 效率 最大 **\n" + "*******************************\n"); + } + printf("/set lengthOfThinkChain "); + scanf("%d", &set); + if(set>=0 && set<=30) lengthOfThinkChain=set; + } + } + else if(operateMode == '3')//以密度设置雷数 + { + printf(">"); + scanf("%f", &density); + numberOfMine = density * heightOfBoard * widthOfBoard; + printf("/set numberOfMine %d\n", numberOfMine); + system("pause"); + } + else if(operateMode == '4')//设置地图生成校验 + { + printf("*******************************\n"//宽31 + "(0)关闭校验\n" + "(1)起始点必非雷\n" + "(2)起始点必为空\n" + "*******************************\n"); + printf("当前:%d\n", summonCheckMode); + printf("/set summonCheckMode "); + scanf("%d", &summonCheckMode); + } + else if(operateMode == '5')//更改游戏结束是否显示用时 + { + if(showTime == 0) + { + showTime = 1; + } + else + { + showTime = 0; + } + } + } + else//返回菜单 + { + break; + } + } + } + /*--Bench--*/ + else if(choiceMode == 4) + { + system("cls");//清屏 + /*初始化*/ + isBench=1; + r0 = heightOfBoard/2; + c0 = widthOfBoard/2; + countOfWin=0; + countOfSolution=0; + countOfStep=0; + seedMin=0; + seedMax=99; + for(seed=seedMin; seed<=seedMax; seed++) + { + isWinningOfSeed[seed]=0; + } + if(summonCheckMode != 2)//锁定校验 + { + summonCheckMode=2; + printf("/set summonCheckMode 2\n"); + system("pause"); + } + t0=time(0);//计时 + /*开跑!*/ + for(seed=seedMin; seed<=seedMax; seed++) + { + srand(seed); + for(r=0; r\n"); + printf("Map:%d*%d-%d\n", heightOfBoard, widthOfBoard, numberOfMine); + printf("seed=%d,%d,%d\n", seed, r0, c0); + t1=time(0);//显示Ave FPS(步数/时间) + if(t1 != t0) printf("Ave FPS %d\n", countOfStep/(t1-t0)); + //ShowBoard(1); + } + /*全局已翻开点检查*/ + for(r=0; r 0) continue;//第1次就爆则循环 + /*--生成雷周围数字--*/ + for(r=0; r0) + { + i++; + n /= 10; + } + return i; +} + +void ShowBoard(int mode) +{ + int r, c, i, j, n; + for(i=Place(widthOfBoard-1); i>0; i--)//列坐标 + { + for(j=0; j=0 && r=0 && c=0 && r=0 && c=0 && r=0 && c='1' && map[r][c]<='9') + { + numberShown[r][c] = map[r][c]-'1'+1;//转为数字 + } + } + } + } + if(debug == 2) + { + printf("[Debug]已获取已知数字:\n"); + for(r=0; r#", r, c); + //标记周围未知方块 + for(ra=r-1; ra<=r+1; ra++) + { + for(ca=c-1; ca<=c+1; ca++) + { + if(ra>=0 && ra=0 && ca@", r, c); + //翻开周围未知方块 + for(ra=r-1; ra<=r+1; ra++) + { + for(ca=c-1; ca<=c+1; ca++) + { + if(ra>=0 && ra=0 && ca=0 && r2=0 && c2=0 && r=0 && c=0 && r=0 && c=0 && r=0 && c@(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + if(numberOfMine2 - numberOfMine1 == numberOfNotShown1)//已知雷数之差等于数对之一独占区未知量 + { + //标记数对之一独占区未知方块 + for(r=r1-1; r<=r1+1; r++) + { + for(c=c1-1; c<=c1+1; c++) + { + if(r>=0 && r=0 && c#(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + } + else//不相等数对,34类数对 + { + if(numberShown[r2][c2] - numberShown[r1][c1] == numberOfMine2 - numberOfMine1)//数差等于已知雷数差 + { + //翻开数对之一独占区未知方块 + for(r=r1-1; r<=r1+1; r++) + { + for(c=c1-1; c<=c1+1; c++) + { + if(r>=0 && r=0 && c@(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + if(numberShown[r1][c1] - numberShown[r2][c2] == numberOfMine1 - numberOfMine2 + numberOfNotShown1)//数差等于已知雷数差加数对之一独占区未知量 + { + //标记数对之一独占区未知方块 + for(r=r1-1; r<=r1+1; r++) + { + for(c=c1-1; c<=c1+1; c++) + { + if(r>=0 && r=0 && c#(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + } + } + else//数对之二独占区不全已知,5678类数对 + { + if(numberShown[r1][c1] == numberShown[r2][c2])//相等数对,56类数对 + { + if(numberOfMine1 - numberOfMine2 == numberOfNotShown2)//数对之一和数对之二独占区已知雷数之差等于数对之二独占区未知量 + { + //翻开数对之一独占区未知方块 + for(r=r1-1; r<=r1+1; r++) + { + for(c=c1-1; c<=c1+1; c++) + { + if(r>=0 && r=0 && c@(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + if(numberOfMine2 - numberOfMine1 == numberOfNotShown1)//数对之二和数对之一独占区已知雷数之差等于数对之一独占区未知量 + { + //标记数对之一独占区未知方块 + for(r=r1-1; r<=r1+1; r++) + { + for(c=c1-1; c<=c1+1; c++) + { + if(r>=0 && r=0 && c#(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + } + else//不相等数对,78类数对,实际是所有数对的通式(doge) + { + if(numberShown[r2][c2] - numberShown[r1][c1] == numberOfMine2 - numberOfMine1 + numberOfNotShown2)//数对之二与数对之一之差等于已知雷数差加数对之二独占区未知量 + { + //翻开数对之一独占区未知方块 + for(r=r1-1; r<=r1+1; r++) + { + for(c=c1-1; c<=c1+1; c++) + { + if(r>=0 && r=0 && c@(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + if(numberShown[r1][c1] - numberShown[r2][c2] == numberOfMine1 - numberOfMine2 + numberOfNotShown1)//数对之一与数对之二之差等于已知雷数差加数对之一独占区未知量 + { + //标记数对之一独占区未知方块 + for(r=r1-1; r<=r1+1; r++) + { + for(c=c1-1; c<=c1+1; c++) + { + if(r>=0 && r=0 && c#(%d,%d)\n", r1, c1, r2, c2, r, c); + return 1; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + /*是否枚举*/ + if(solveMode == 1) + { + isFound=0; + for(r=0; r=0 && r=0 && c=0 && r=0 && c lengthOfThinkChain) + //int上限2147483647=2^31-1,最多支持长度30的未知链 + //0关 17效率 30最大 + { + if(debug == 2) printf("[Debug]该未知链过长,已放弃\n"); + for(r=0; r 262144 && (i+1)%131072==0) + { + printf("\r[Loading...]%d/%d", (i+1)/131072, numberOfPossibility/131072); + }//2^19起等待时间较长,每2^17刷新一次,最多2^30,刷新8192次,对性能影响小 + /*重置*/ + for(c=0; c0) + { + possibility[c] = r%2; + r /= 2; + c++; + } + /*根据雷数排除*/ + r=0; + for(c=0; c remainedMine//未知链中雷数大于剩余雷数 + || remainedNotShown < remainedMine - r)//不含未知链的剩余%数小于剩余雷数减未知链中雷数 + { + /*if(numberOfPossibility > 262144)//进度条加速 + { + printf(">>>"); + }*/ + continue; + } + /*绘制地图数字*/ + c=0; + for(r1=0; r1=0 && r2=0 && c2=0 && r2=0 && c2 262144)//进度条 + { + printf("\n"); + } + if(realNumberOfPossibility == 1)//输出唯一可能 + { + for(c=0; c0) + { + possibility[c] = r%2; + r /= 2; + c++; + } + if(debug == 2) + { + printf("[Debug]已找到唯一可能性%d:", temp); + for(c=0; c LimDictionary) + { + if(debug == 1 || debug == 2) printf("[Debug]字典大小不足%d!\n", realNumberOfPossibility); + realNumberOfPossibility = LimDictionary; + } + if(debug == 2) + { + printf("[Debug]已生成字典,共%d种可能\n", realNumberOfPossibility); + for(i=0; i\n"); + printf("aveRemainedMine=%.2f\n", aveRemainedMine); + printf("realNumberOfNotShown=%d\n", realNumberOfNotShown); + printf("isMineRateOfNotShown=%.2f\n", isMineRateOfNotShown); + } + /*生成*/ + for(r=0; r=0 && r=0 && c@ %d %d\n", rc, cc); + } + } + else if(solution[rc][cc] == 2 && isBench == 0) + { + printf(":)\n># %d %d\n", rc, cc); + } +} + +void ShownModeBak(int mode)//1备份,0恢复 +{ + int r, c; + for(r=0; r