伪C++开发连连看(补充)

没想到这么快就又有时间了,好久不见,正好没什么事做把上一篇提到的想要实现的功能给实现了,顺便也修复了上篇文章提到的一个小bug,就作为补充吧。

目录

修复

 优化

新增

暂停

刷新

无法消除刷新 

关卡模式 

难度参数

关卡切换

关卡动画

效果图 


修复

首先是修复问题,在之前老的项目上跑,最大的问题就是会出现一种奇怪的画线,也就是跑过头了,总之就是画线不对,但是判定两个能否消除却又从来没有错过,初步的判断是因为入栈的问题,逐步调试之后终于找到了问题所在。

这是在dfs里的一句代码,比较长,大概就是在确定了下一步之后要判定能不能这么走,后面几个条件是判定越界同时不能走到有数字的点除非是终点,没有问题,问题就出现在第一个条件,也就是限制不能往来的方向的反向方向走,除非是起点,当时我写的是下面这个

if (i == now + 2 && x != qdx && y != qdy || xx < 0 || yy < 0 || xx == N || yy == N ||
			mat[yy][xx] && !(xx == zdx && yy == zdy))//不合法,最后一个代表已经被占据并且不是终点
			continue;

对比就会发现第一个条件不一样,实际上第一个条件是为了显限制不能往回走,除非是起点,因为任何时候对于普通的位置,你dfs转了一圈回到了自己相反的方向走回去是没有意义的,这样除了增加两个转弯什么都没有干,所以需要把这种情况判掉,但是起点不一样,从上面走下来,还可以重新出发从下面走,因为起点无所谓方向,而判定一个点是不是起点,应该是x值或者y值有任何一个和起点不一样就不是起点,改正之后如下。

	if (i == now + 2 && (x != qdx || y != qdy) || xx < 0 || yy < 0 || xx == N || yy == N || // 第一个的逻辑是不能往回走,除非是起点
			mat[yy][xx] && !(xx == zdx && yy == zdy))//不合法,最后一个代表已经被占据并且不是终点
			continue;

 优化

下面是一个小优化,之前跑一直没注意,原来dfs里面的flag数组压根没用,所以干脆就直接删掉了,代码整体看着也会清爽一点。

同时也增加了一个条件,在搜索时如果方向时,终点在左,那就不会往右边搜索,左右同理,这样可以有效避免一些无效搜索。

修改后的dfs如下

/*搜索答案*/
void dfs(int x, int y, int qdx, int qdy, int zdx, int zdy, int step, int now, int &stats) {
	if (stats)return;//找到了就不找了
	//判断,参数分别是x,y,(x向右,y向下),起点xy和终点xy,当前转弯次数,当前方向参数,0123分别是上右下左,当前寻找的状态
	if (step == 3) {//超出步数
		if (!st.empty()) {//拿出去
			st.pop();
		}
		return;
	}
	if (zdx == x && zdy == y && step <= 2) {//终点
		stats = 1;
		return;
	}
	for (int i = now; i < now + 4; i++) {//从当前方向开始找
		int index = i % 4;
		int xx = x + dirx[index];
		int yy = y + diry[index];
		if (i == now + 2 && (x != qdx || y != qdy) || xx < 0 || yy < 0 || xx == N || yy == N || // 第一个的逻辑是不能往回走,除非是起点
			mat[yy][xx] && !(xx == zdx && yy == zdy))//不合法,最后一个代表已经被占据并且不是终点
			continue;
		if ((x != qdx || y != qdy) && (i % 2 == 0 && (zdx - qdx)*(xx - x) < 0 || i % 2 == 1 && (zdy - qdy)*(yy - y) > 0))//竖着走和横着走不可能偏离终点
			continue;
		if (now == index || x == qdx && y == qdy) {//方向相同或者是起点
			st.push({ xx,yy,i % 2 });
			dfs(xx, yy, qdx, qdy, zdx, zdy, step, index, stats);
		}
		else {
			st.push({ xx,yy,i % 2 });
			dfs(xx, yy, qdx, qdy, zdx, zdy, step + 1, index, stats);
		}
		if (stats)return;
	}
	if (stats)return;
	if (!st.empty()) {//四个方向都没有
		st.pop();
	}
	return;
}//搜索答案

新增

这里就是单独说一下增加的几个新功能,分别是刷新和暂停,还有自动刷新。

暂停

暂停就是点击之后游戏会停止,但是由于连连看的特殊性,如果仅仅是将程序停止,那么玩家可能就会在这时候看需要消除哪个,那么游戏的乐趣也会失去,因为这样就永远不存在找不到的时候。所以我考虑在暂停的时候将屏幕遮挡。那么执行逻辑就是读取ESC按键---遮挡屏幕---程序暂停---再次点击任意键---恢复游戏,点击任意键是因为C自带的程序暂停函数点击任意键恢复,遮挡屏幕的逻辑就是清屏,然后放上暂停图片,程序再次执行就把原来的东西重新显示即可。

有一个比较坑的地方就是关于计时,因为在暂停的时候time(0)是不会停止的,这个始终是真实的时间,而我们是把time(0)-time0当做游戏时间,所以为了防止暂停之后一会回到游戏直接时间耗尽,等效于time0再加上暂停的时间。

代码如下

	   else if (ch == ESC) {
			time0+=stop_game();//要把计时起点向后挪动
			cleardevice();
			show_mat(mat);
		}
/*游戏暂停*/
int stop_game() {
	int stop_time = time(0);
	cleardevice();
	putimage(MAXX / 2 + 100, MAXY / 2 - 100, &img[27]);
	system("pause");//停止
	return time(0)-stop_time;//返回暂停时间
}//游戏暂停

刷新

刷新也就是在无法消除的时候点击,然后水果会重新排列,我想的比较简单,就是如果按下了,那么就开始从头开始枚举,找到了水果并且这个水果不是其他水果放过来的(也就是一个水果只会被重置一次),那么就找个空位插进去,从头到尾走一遍,就实现了重新排列。

代码如下

flag在这里就是标记这个水果是不是已经被重置过。

/*刷新模块*/
void refresh() {
	int x, y;
	ini_flag(0, 0, 0);//初始化flag为0,借用flag数组标记
	for (int i = 1; i <= N - 1; i++) {
		for (int j = 1; j <= N - 1; j++) {
			if (mat[i][j]&&!flag[i][j]) {//如果里面是有数字的并且不是前面的移过来的
				while (1) {
					x = rand() % (N - 2) + 1;
					y = rand() % (N - 2) + 1;//找到两个没出现过的位置
					if (!mat[y][x]) {//空的
						mat[y][x] = mat[i][j];
						mat[i][j] = 0;
						flag[y][x] = 1;//标记点
						break;
					}
				}
			}
		}
	}
	cleardevice();
	show_mat(mat);
}//刷新模块

无法消除刷新 

这个主要就是关注到可能就是在一些情况下,会有无法消除的情况,这时候自己也很难看出,就需要系统自动刷新,就是结合一下提示和刷新两个代码就好,在每一次消除之后检测。

int check = remind(x1, y1, x2, y2);
if (!check) {
	refresh();//没有可以消除的了,刷新
}
else {
	while (!st.empty())
	st.pop();
}

关卡模式 

下面就是关卡模式的开发部分,关卡模式的每一关是不一样的,同时难度也需要慢慢递进,不可能针对每一关去设计独特的关卡,所以需要将控制每一关难度的参数单独抽离出来,同时也要考虑游戏间的衔接。

难度参数

从每一关来考量,每一关的难度大致由以下五个参数决定:水果数量,水果种类,消除可以增加的时间,游戏的初始时长,最后是提示消耗时长,为了方便游戏的调整,我将其放入了数组中,这样走循环就可以方便取出其中的难度组合。

//关卡参数
int all_time;//可用初始时间
int add_time;//消除一个加1秒
int remind_price;//提示一次需要一秒代价
int all_num;//水果对数目
int cag;//水果种类,1-7
//下面是关卡参数配置
int eve_num[] = {1, 30, 35, 40, 45, 50,60 };//每一关的水果组数 
int eve_cag[] = { 1, 8, 10, 12, 13, 14 ,14};//每一关的种类
int eve_start_time[] = { 100,15,15,20,20,30,30 };//每一关的初始时间
int eve_add_time[] = { 100,3,3,2,2,1 ,1};//每一关的消除加成时间
int eve_remind_price[] = { 1, 1, 1, 1, 2, 2 ,2 };//提示代价

在每一关的初始化中,我们只需要取出对应的参数,gk就是关卡,是传入的参数,对应的是关卡参数的下标,可以看到图片变多了,这是因为前面的那个涂片种类太少,难度也不好设置,所以就新加了一些。

/*初始化*/
void all_ini(int gk) {//传入关卡值
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			mat[i][j] = 0;
		}
	}
	//默认配置
	all_num = (N - 2)*(N - 2)/2;//最多生成的组数
	if_time = 0;//一开始的不需要计时
	time0 = time(0);//开始时间
	rem_num = 0;//消除数量
	max_num = 1;//最大连击
	now_num = 1;//当前连击
	loadimage(&img[1], "./imgs/西瓜.png");
	loadimage(&img[2], "./imgs/苹果.png");
	loadimage(&img[3], "./imgs/草莓.png");
	loadimage(&img[4], "./imgs/梨.png");
	loadimage(&img[5], "./imgs/火龙果.png");
	loadimage(&img[6], "./imgs/桃子.png");
	loadimage(&img[7], "./imgs/香蕉.png");
	loadimage(&img[8], "./imgs/橙子.png");
	loadimage(&img[9], "./imgs/橘子.png");
	loadimage(&img[10], "./imgs/荔枝.png");
	loadimage(&img[11], "./imgs/葡萄.png");
	loadimage(&img[12], "./imgs/芒果.png");
	loadimage(&img[13], "./imgs/菠萝.png");
	loadimage(&img[14], "./imgs/猕猴桃.png");
	loadimage(&img[21], "./imgs/竖线.png");
	loadimage(&img[22], "./imgs/横线.png");
	loadimage(&img[23], "./imgs/下右.png");
	loadimage(&img[24], "./imgs/下左.png");
	loadimage(&img[25], "./imgs/上右.png");
	loadimage(&img[26], "./imgs/上左.png");
	loadimage(&img[27], "./noChangeSizeImgs/暂停.png");
	//关卡配置
	all_time = eve_start_time[gk];
	remind_price = eve_remind_price[gk];
	add_time=eve_add_time[gk];
	cag = eve_cag[gk];
	all_num = eve_num[gk];
}//初始化

为了匹配关卡参数,对生成函数也做了修改,原来考虑为了避免没有可以消除的水果,所以让每一组生成的水果都可以消除,然后最终生成数字矩阵,但是在测试之后这样反而导致了水果扎堆,而且最终的数量也不好管理,所以干脆移除必须要可以消除这个条件,直接找空位,见缝插针,生成指定组数,最终试验的效果也还不错。

/*生成矩阵*/
void create_mat(int max_num) {//采用随机生成方式,生成max_num组
	srand((unsigned)time(NULL));
	bool t[5000];//标记数组,标记有没有数字
	for (int i = 0; i <= (N-2) * (N-2) + 1; i++)//矩阵一维化
		t[i] = 0;
	for (int i = 0; ; i++) {
		for (int now = 1; now <= cag; now++) {
			int z1, z2;//找到两个没出现过的位置
			while (1) {
				z1 = rand() % ((N-2)*(N-2)) + 1;//取模和乘的优先级一样...
				if (!t[z1]) {
					t[z1] = 1;
					break;
				}
			}
			while (1) {
				z2 = rand() % ((N-2)*(N-2)) + 1;
				if (!t[z2]) {
					t[z2] = 1;
					break;
				}
			}
			int x1 = (z1 - 1) % (N - 2) + 1;
			int y1 = (z1 - 1) / (N - 2) + 1;
			int x2 = (z2 - 1) % (N - 2) + 1;
			int y2 = (z2 - 1) / (N - 2) + 1;//计算行列
			mat[y1][x1] = now;
			mat[y2][x2] = now;//全部随机生成
			if (i * cag + now == max_num) {
				show_mat(mat);
				return;
			}
		}
	}
}//生成矩阵

关卡切换

有了每一关的难度参数之后,如何连接呢,一个for循环就可以搞定,所以将游戏部分的主控程序做一个小修改,while(1)是为了可在一局结束之后,可以再开而不是直接中断程序。

/*总控程序*/
void game() {
	initgraph(MAXX + 401, MAXY + 1);
	while (1) {
		for (now_level = 0; now_level <= 6; now_level++) {//七关
			printNextLevel();//打印转场动画
			all_ini(now_level);//初始化所有矩阵和图片库
			create_mat(all_num);//生成矩阵
			int check = play();//开始游戏并返回游戏状态
			if (!check) {
				break;//游戏结束
			}
			else if (now_level == 6) {
				prinfAllOk();//打印全部通关
			}
		}
	}
	//closegraph();//关闭窗口
}//总控程序

关卡动画

新增的需要打印的主要有全通关和关卡切换的界面,比较简单,就不展开了,具体直接看代码。

到这里就全部结束了,以下是全部源码,项目结构在上一篇说明了

head.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<algorithm>
#include<stack>
#include <stdlib.h>
#include <time.h>
#include<graphics.h>//图形绘制库
#include<string>
#include<iostream>
using namespace std;

const int N = 12 + 2; // 游戏界面加上隐藏一圈的大小,第一个数字是真正的游戏界面大小
const int inf = 0x3f3f;
#define SIZE 60 //图片尺寸
#define MAXX N*SIZE
#define MAXY N*SIZE
#define RIGHT 77
#define LEFT 75
#define UP 72
#define DOWN 80
#define ENTER 13
#define W 119
#define A 97
#define S 115
#define D 100
#define KONG 32
#define ESC 27//获取键值
#define EASY 130
#define NORMAL 90
#define DIFFICULT 70
#define DET 3 //连击间隔

void game();//游戏函数入口

main.cpp

#include"head.h"

int main() {
	game();
	return 0;
}

tool.cpp

#include"head.h"
#include"conio.h"

/*全局变量*/
struct node {
	int x, y, dir;//横坐标,纵坐标,方向(上下或者左右)
};

int dirx[] = { 0,1,0,-1 };
int diry[] = { -1,0,1,0 };
stack<node>st;//记录路径
IMAGE img[40];//存储图片
int time0;//游戏开始时间
int rem_num;//消除个数
int flag[N][N];//次数,计算能否相连要用
int mat[N][N];//游戏界面矩阵,记录了每个位置的图片编号
int tmp[N][N];//暂存矩阵,标记线
//显示模块控制模块
clock_t start, stop;//clock_t为clock()函数返回的变量类型
double duration;//记录被测函数运行时间,以秒为单位
bool if_time;//标记是否计时
int max_num;//最高连击次数
int now_num;//当前连击次数
int now_level;//当前关卡
int k = (N - 2) / 2;//大小参数,为了调节不同大小适应的方阵距离
int kr=SIZE/5;//表示当前大小的方块对应的圆半径系数
//关卡参数
int all_time;//可用初始时间
int add_time;//消除一个加1秒
int remind_price;//提示一次需要一秒代价
int all_num;//水果对数目
int cag;//水果种类,1-7
//下面是关卡参数配置
int eve_num[] = {1, 30, 35, 40, 45, 50,60 };//每一关的水果组数 
int eve_cag[] = { 1, 8, 10, 12, 13, 14 ,14};//每一关的种类
int eve_start_time[] = { 100,15,15,20,20,30,30 };//每一关的初始时间
int eve_add_time[] = { 100,2,2,1,1,1 ,1};//每一关的消除加成时间
int eve_remind_price[] = { 1, 1, 1, 1, 2, 2 ,2 };//提示代价

/*初始化*/
void all_ini(int gk) {//传入关卡值
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			mat[i][j] = 0;
		}
	}
	//默认配置
	all_num = (N - 2)*(N - 2)/2;//最多生成的组数
	if_time = 0;//一开始的不需要计时
	time0 = time(0);//开始时间
	rem_num = 0;//消除数量
	max_num = 1;//最大连击
	now_num = 1;//当前连击
	loadimage(&img[1], "./imgs/西瓜.png");
	loadimage(&img[2], "./imgs/苹果.png");
	loadimage(&img[3], "./imgs/草莓.png");
	loadimage(&img[4], "./imgs/梨.png");
	loadimage(&img[5], "./imgs/火龙果.png");
	loadimage(&img[6], "./imgs/桃子.png");
	loadimage(&img[7], "./imgs/香蕉.png");
	loadimage(&img[8], "./imgs/橙子.png");
	loadimage(&img[9], "./imgs/橘子.png");
	loadimage(&img[10], "./imgs/荔枝.png");
	loadimage(&img[11], "./imgs/葡萄.png");
	loadimage(&img[12], "./imgs/芒果.png");
	loadimage(&img[13], "./imgs/菠萝.png");
	loadimage(&img[14], "./imgs/猕猴桃.png");
	loadimage(&img[21], "./imgs/竖线.png");
	loadimage(&img[22], "./imgs/横线.png");
	loadimage(&img[23], "./imgs/下右.png");
	loadimage(&img[24], "./imgs/下左.png");
	loadimage(&img[25], "./imgs/上右.png");
	loadimage(&img[26], "./imgs/上左.png");
	loadimage(&img[27], "./noChangeSizeImgs/暂停.png");
	//关卡配置
	all_time = eve_start_time[gk];
	remind_price = eve_remind_price[gk];
	add_time=eve_add_time[gk];
	cag = eve_cag[gk];
	all_num = eve_num[gk];
}//初始化

/*初始化flag*/
void ini_flag(int x, int y,int ini_num) {//相当于x-行,y-列,最后一个是初始化的值
	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++) {
			flag[i][j] = ini_num;
		}
	flag[x][y] = 0;
}//初始化flag

/*清除圆并补齐画面*/
void clearRound(int x,int y,int r,int detx,int dety) {//分别是xy点坐标,半径,偏移量
	int real_x, real_y;
	real_x = x * SIZE + SIZE / 2+detx;
	real_y = y * SIZE + SIZE / 2+dety;
	clearcircle(real_x, real_y, r);
	if(mat[y][x])
		putimage(x*SIZE, y*SIZE, &img[mat[y][x]]);//补上图片
}//清除圆并补齐画面

/*画一个圆*/
void drawRound(int x,int y,int r,int detx,int dety,int color){
	int real_x = x * SIZE + SIZE / 2;//圆心坐标
	int real_y = y * SIZE + SIZE / 2;
	circle(real_x + detx, real_y + dety, r);
	setfillcolor(color);  //填充色为蓝色
	fillcircle(real_x+detx, real_y +dety, r);
}//画一个圆

/*转换数字为字符串*/
void deal(int x, char *a) {
	stack <int>sst;
	while (x) {
		sst.push(x % 10);
		x /= 10;
	}
	int l = 0;
	while (!sst.empty()) {
		a[l++] = sst.top() + '0';
		sst.pop();
	}
	a[l] = '\0';
	if (a[0] == '\0') {
		a[0] = '0';
		a[1] = '\0';
	}
}//转换数字为字符串

/*计算和统计模块*/
void printCal() {
	char a[100];

	//计算连击
	settextcolor(WHITE);
	settextstyle(40, 40, 0);
	outtextxy(N*SIZE+40, 10, "当前关卡");
	settextstyle(100, 100, 0);
	deal(now_level, a);
	outtextxy(N*SIZE+170, 100, a);

	//统计数目
	settextcolor(WHITE);
	settextstyle(30, 30, 0);
	outtextxy(N*SIZE+40, N*SIZE-250, "剩余水果对");
	settextstyle(100, 100, 0);
	deal(all_num, a);
	outtextxy(N*SIZE + 150, N*SIZE - 170, a);
}//计算连击和统计模块

/*打印时间耗尽*/
void print_timeOver() {
	//打印时间耗尽
	settextcolor(BROWN);
	int size = k * 150 / (N - 2);//字体大小
	settextstyle(size, size, 0);
	outtextxy(N*SIZE / 2 - 4 * size, N*SIZE / 2 - 2 * size, "时间耗尽");
	system("pause");
}//打印时间耗尽

/*打印时间*/
void printTime() {
	char a[100];
	//剩余时间
	settextcolor(WHITE);
	settextstyle(40, 40, 0);
	outtextxy(N*SIZE + 40, 250, "剩余时间");
	settextstyle(100, 100, 0);
	int t = all_time + (rem_num*add_time) - (time(0) - time0);
	deal(t, a);
	if (t % 10 == 9) {
		clearrectangle(N*SIZE + 170, 350, N*SIZE + 400, 450);
	}
	outtextxy(N*SIZE + 170, 350, a);
}//打印时间

/*结束*/
void printOver() {
	//打印消除完毕
	settextcolor(BROWN);
	int size = k*150 / (N - 2);//字体大小
	settextstyle(size, size, 0);
	outtextxy(N*SIZE/2-4*size, N*SIZE / 2 - 2*size, "全部消除");
	line(size, N*SIZE / 2 - size/2, N*SIZE - size, N*SIZE / 2 -  size/2);
	//打印最高连击
	char a[100];
	settextcolor(WHITE);
	size = k*90 / (N - 2);
	settextstyle(size, size, 0);
	outtextxy(N*SIZE / 2 -6 * size, N*SIZE / 2 , "最高连击次数");
	size = k*150 / (N - 2);
	settextstyle(size, size, 0);
	deal(max_num, a);
	outtextxy(N*SIZE / 2 -size/2, N*SIZE / 2+size, a);
	//打印时间
	printTime();
	//打印消除完毕
	settextcolor(WHITE);
	size = k * 60 / (N - 2);//字体大小
	settextstyle(size, size, 0);
	outtextxy(N*SIZE / 2 - 7 * size, N*SIZE / 2 + 6 * size, "点击任意键继续...");
	system("pause");
}//结束

/*打印关卡过渡动画*/
void printNextLevel() {
	cleardevice();
	char a[100];
	char info[100] = "第";//拼接数组
	//打印时间耗尽
	settextcolor(BROWN);
	int size = k * 150 / (N - 2);//字体大小
	settextstyle(size, size, 0);
	deal(now_level, a);
	strcat(info, a);
	strcat(info, "关");
	outtextxy(MAXX / 2+200-size*2, MAXY / 2-size, info);
	Sleep(1000);
	cleardevice();
}//打印关卡过渡动画

/*打印全部通关*/
void prinfAllOk() {
	settextcolor(WHITE);
	int size = k * 150 / (N - 2);//字体大小
	settextstyle(size, size, 0);
	cleardevice();
	outtextxy(N*SIZE / 2 - 4 * size, N*SIZE / 2 - 2 * size, "恭喜你全部通关");
	system("pause");
}//打印全部通关

/*打印当前状态*/
void show_mat(int Mat[N][N]) {
	printCal();
	line(N*SIZE, 0, N*SIZE, MAXY);
	for (int i = 0; i <= N - 1; i++) {
		for (int j = 0; j <= N - 1; j++) {
			if (Mat[i][j] < 0) {
				putimage(j*SIZE, i*SIZE, &img[-Mat[i][j]+20]);
			}
			else if (Mat[i][j] == 0)
				continue;
			else 
				putimage(j*SIZE, i*SIZE, &img[Mat[i][j]]);
		}
	}
}//打印当前状态

/*搜索答案*/
void dfs(int x, int y, int qdx, int qdy, int zdx, int zdy, int step, int now, int &stats) {
	if (stats)return;//找到了就不找了
	//判断,参数分别是x,y,(x向右,y向下),起点xy和终点xy,当前转弯次数,当前方向参数,0123分别是上右下左,当前寻找的状态
	if (step == 3) {//超出步数
		if (!st.empty()) {//拿出去
			st.pop();
		}
		return;
	}
	if (zdx == x && zdy == y && step <= 2) {//终点
		stats = 1;
		return;
	}
	for (int i = now; i < now + 4; i++) {//从当前方向开始找
		int index = i % 4;
		int xx = x + dirx[index];
		int yy = y + diry[index];
		if (i == now + 2 && (x != qdx || y != qdy) || xx < 0 || yy < 0 || xx == N || yy == N || // 第一个的逻辑是不能往回走,除非是起点
			mat[yy][xx] && !(xx == zdx && yy == zdy))//不合法,最后一个代表已经被占据并且不是终点
			continue;
		if ((x != qdx || y != qdy) && (i % 2 == 0 && (zdx - qdx)*(xx - x) < 0 || i % 2 == 1 && (zdy - qdy)*(yy - y) > 0))//竖着走和横着走不可能偏离终点
			continue;
		if (now == index || x == qdx && y == qdy) {//方向相同或者是起点
			st.push({ xx,yy,i % 2 });
			dfs(xx, yy, qdx, qdy, zdx, zdy, step, index, stats);
		}
		else {
			st.push({ xx,yy,i % 2 });
			dfs(xx, yy, qdx, qdy, zdx, zdy, step + 1, index, stats);
		}
		if (stats)return;
	}
	if (stats)return;
	if (!st.empty()) {//四个方向都没有
		st.pop();
	}
	return;
}//搜索答案

/*生成矩阵*/
void create_mat(int max_num) {//采用随机生成方式,生成max_num组
	srand((unsigned)time(NULL));
	bool t[5000];//标记数组,标记有没有数字
	for (int i = 0; i <= (N-2) * (N-2) + 1; i++)//矩阵一维化
		t[i] = 0;
	for (int i = 0; ; i++) {
		for (int now = 1; now <= cag; now++) {
			int z1, z2;//找到两个没出现过的位置
			while (1) {
				z1 = rand() % ((N-2)*(N-2)) + 1;//取模和乘的优先级一样...
				if (!t[z1]) {
					t[z1] = 1;
					break;
				}
			}
			while (1) {
				z2 = rand() % ((N-2)*(N-2)) + 1;
				if (!t[z2]) {
					t[z2] = 1;
					break;
				}
			}
			int x1 = (z1 - 1) % (N - 2) + 1;
			int y1 = (z1 - 1) / (N - 2) + 1;
			int x2 = (z2 - 1) % (N - 2) + 1;
			int y2 = (z2 - 1) / (N - 2) + 1;//计算行列
			mat[y1][x1] = now;
			mat[y2][x2] = now;//全部随机生成
			if (i * cag + now == max_num) {
				show_mat(mat);
				return;
			}
		}
	}
}//生成矩阵

/*标记线*/
void deal(int x1,int x2,int y1,int y2) {
	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			tmp[i][j] = mat[i][j];//复制矩阵
	int bef_dir = st.top().dir;//初始方向
	int bef_x = st.top().x;
	int bef_y = st.top().y;
	st.pop();//终点不需要变成线
	while (!st.empty()) {
		int xx = st.top().x;
		int yy = st.top().y;
		int dir = st.top().dir;
		st.pop();
		if (dir == 0) {//上下
			//tmp[st.top().y][st.top().x] = -1;//表示上下
			if (bef_dir==dir) {
				//跟上一个一样
				tmp[yy][xx] = -1;//上下
			}
			else {         //左右
				if (!st.empty() && st.top().y < yy || st.empty() && y1 < yy) {
					//跟下一个比定上下,此时是下一个在上面
					if (xx < bef_x)//向左走
						tmp[yy][xx] = -5;//上右
					else
						tmp[yy][xx] = -6;//上左
				}
				else {
					if (xx < bef_x)//向左走
						tmp[yy][xx] = -3;//下右
					else
						tmp[yy][xx] = -4;//下左
				}
			}
		}
		else {
			//tmp[st.top().y][st.top().x] = -2;//表示左右
			if (bef_dir == dir) {//也是左右
				tmp[yy][xx] = -2;
			}
			// x1 y1 是起点坐标
			else {
				if (!st.empty() && st.top().x < xx || st.empty() && x1 < xx) {//下一个在左边
					if (yy < bef_y) //向上走
						tmp[yy][xx] = -4;//下左
					else 
						tmp[yy][xx] = -6;//上左
				}
				else {
					if (yy < bef_y)//向上走
						tmp[yy][xx] = -3;//下右
					else
						tmp[yy][xx] = -5;//上右
				}
			}
		}
		bef_x = xx;
		bef_y = yy;
		bef_dir = dir;//更新表示上一个状态的量
	}
}//标记线

/*处理消除之后的样子以及计时模块*/
bool deal_after(int x1,int x2,int y1,int y2) {
	//计时模块
	rem_num++;
	all_num--;
	if (if_time) {
		stop = clock();//停止计时
		duration = (double)(stop - start) / CLOCKS_PER_SEC;//计算运行时间
		if (duration <= DET) {
			now_num += 1;
			max_num = max(max_num, now_num);
		}
		else {
			now_num = 1;//重置连击次数
		}
	}
	//下面显示出消除的样子
	show_mat(tmp);
	Sleep(300);//程序暂停
	mat[y1][x1] = 0;
	mat[y2][x2] = 0;
	cleardevice();
	show_mat(mat);
	if (!all_num) {
		return 1;//代表可以结束游戏了
	}
	start = clock();//开始计时
	if_time = 1;//计时标记
	return 0;
}

/*提示模块*/
bool remind(int &x1, int &y1, int &x2, int &y2) {//返回是否可以消除,返回值内包括起点终点坐标
	for (int i1 = 1; i1 < N - 1; i1++) {
		for (int j1 = 1; j1 < N - 1; j1++) {
			if (!mat[i1][j1])
				continue;//空格
			for (int i2 = 1; i2 < N - 1; i2++) {
				for (int j2 = 1; j2 < N - 1; j2++) {
					if (!mat[i2][j2] || i1 == i2 && j1 == j2||mat[i2][j2]!=mat[i1][j1])
						continue;
					int check = 0;
					dfs(j1, i1, j1, i1, j2, i2, 0, 0, check);
					if(check) {
						x1 = j1;
						y1 = i1;
						x2 = j2;
						y2 = i2;
						return 1;
					}
					else {
						while (!st.empty())//否则要清空栈
							st.pop();
					}
				}
			}
		}
	}
	return 0;
}//提示模块

/*刷新模块*/
void refresh() {
	int x, y;
	srand((unsigned)time(NULL));
	ini_flag(0, 0, 0);//初始化flag,借用flag数组标记
	for (int i = 1; i <= N - 1; i++) {
		for (int j = 1; j <= N - 1; j++) {
			if (mat[i][j]&&!flag[i][j]) {//如果里面是有数字的并且不是前面的移过来的
				while (1) {
					x = rand() % (N - 2) + 1;
					y = rand() % (N - 2) + 1;//找到两个没出现过的位置
					if (!mat[y][x]) {//空的
						mat[y][x] = mat[i][j];
						mat[i][j] = 0;
						flag[y][x] = 1;//标记点
						break;
					}
				}
			}
		}
	}
	cleardevice();
	show_mat(mat);
}//刷新模块

/*游戏暂停*/
int stop_game() {
	int stop_time = time(0);
	cleardevice();
	putimage(MAXX / 2 + 100, MAXY / 2 - 100, &img[27]);
	system("pause");//停止
	return time(0)-stop_time;//返回暂停时间
}//游戏暂停

/*游戏操作部分*/
bool play() {
	int x1=1, y1=1, x2=1, y2=1,now=1;//记录当前位置(x2,y2)和上一个位置(x1,y1),now记录当前状态,1是第一个,2是第二个
	int bef_t = time(0)-time0;
	while (all_num) {//只要还剩下
		MOUSEMSG m;
		if (bef_t != time(0) - time0) {
			printTime();
		}
		printTime();//打印时间
		if (all_time + (rem_num*add_time) - (time(0) - time0)==0) {//时间耗尽
			print_timeOver();
			return 0;//游戏结束
		}
		if (MouseHit())//是否有鼠标消息
		{
			m = GetMouseMsg();
			if (m.uMsg == WM_LBUTTONDOWN)//左键
			{
				//rectangle(m.x - 5, m.y - 5, m.x + 5, m.y + 5);
				if (m.x > N*SIZE || m.y > N*SIZE)
					continue;
				m.x = m.x / SIZE;
				m.y = m.y / SIZE;
				if (now == 1) {//第一个确定状态只要不越界都可以,然后记录上一个位置
					x1 = m.x;
					y1 = m.y;//x1,y1存储上一次状态
					if (!mat[y1][x1])
						continue;//空格无效
					drawRound(x1, y1, kr, 0, 0, GREEN);
					now = 2;
				}
				else {//第二个要判定状态
					x2 = m.x;
					y2 = m.y;
					if (mat[y1][x1] != mat[y2][x2] || x1 == x2 && y1 == y2) {
						clearRound(x1, y1, kr, 0, 0);//重新选择
						now = 1;
						continue;
					}
					if (x1 > 0 && x1 <= N - 2 && y1 > 0 && y1 <= N - 2 && x2 > 0
						&& x2 <= N - 2 && y2 > 0 && y2 <= N - 2) {
						int check = 0;
						dfs(x1, y1, x1, y1, x2, y2, 0, 0, check);
						if (check) {
							deal(x1, x2, y1, y2);//标记线
							bool if_over = deal_after(x1, x2, y1, y2);//判断是否结束,同时善后工作
							if (if_over) {
								printOver();//打印结束语句
								return 1;//通过当前关卡
							}
							int check = remind(x1, y1, x2, y2);
							if (!check) {
								refresh();//没有可以消除的了,刷新
							}
							else {
								while (!st.empty())
									st.pop();
							}
						}
						else {
							clearRound(x1, y1, kr, 0, 0);//不能消除
							while (!st.empty())//清空栈
								st.pop();
						}
					}
					now = 1;
				}
			}
		}
		if (_kbhit()) {//如果有按键
			int ch = _getch();
			if (ch == KONG) {//提示按钮
				int check = remind(x1,y1,x2,y2);
				if (check) {
					deal(x1, x2, y1, y2);//标记线
					show_mat(tmp);
					Sleep(remind_price*1000);//代价
					cleardevice();
					show_mat(mat);
				}
			}
			else if (ch == ESC) {
				time0+=stop_game();//要把计时起点向后挪动
				cleardevice();
				show_mat(mat);
			}
			else if (ch == RIGHT) {//刷新
				refresh();
			}
		}
	}
}//游戏操作部分

/*总控程序*/
void game() {
	initgraph(MAXX + 401, MAXY + 1);
	while (1) {
		for (now_level = 5; now_level <= 6; now_level++) {//七关
			printNextLevel();//打印转场动画
			all_ini(now_level);//初始化所有矩阵和图片库
			create_mat(all_num);//生成矩阵
			show_mat(mat);
			int check = play();//开始游戏并返回游戏状态
			if (!check) {
				break;//游戏结束
			}
			else if (now_level == 6) {
				prinfAllOk();//打印全部通关
			}
		}
	}
	//closegraph();//关闭窗口
}//总控程序

效果图 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值