PS:此游戏为c语言开发,因EasyX库需要.cpp文件才能编译,所以工程文件后缀为.cpp,并未使用cpp的语法。
链接:https://pan.baidu.com/s/13-rlQIJX7Auq6DJcbAxkSw
提取码:rj54
将llkcpp、llk.h、main.cpp文件添加到工程里
#include "llk.h"
#include "math.h"
#define Len_Image 50//正方形图片边长
IMAGE img[26];//存储图库
int xt[2], yt[2];//存储点击位置
int presscount = 0;//存储点击次数
int remaining = MapSize * MapSize;//剩余字母数
int kb_key;//键盘输入
int resist_time = 30 * MapSize;//游戏剩余时间
int helpcount = MapSize/2;//提示的次数
int guan_count = 1;//当前关卡数
int pause_flag = 0;//置为1说明游戏暂停 不计时
char element[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";//地图中的元素(字母)
char Map[MapSize + 2][MapSize + 2] = { 0 };//字母前后要各有1个位置空白处用来走线
LRESULT CALLBACK CBTHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HWND hwnd = (HWND)wParam;
if (nCode == HCBT_ACTIVATE)
{
if (nCode == HCBT_ACTIVATE)
{
SetDlgItemText((HWND)wParam, IDYES, "&直接开始");
SetDlgItemText((HWND)wParam, IDNO, "&读档开始");
SetDlgItemText((HWND)wParam, IDOK, "&OK");
SetDlgItemText((HWND)wParam, IDCANCEL, "&Cancel");
SetDlgItemText((HWND)wParam, IDABORT, "&ABORT");
SetDlgItemText((HWND)wParam, IDRETRY, "&RETRY");
SetDlgItemText((HWND)wParam, IDIGNORE, "&IGNORE");
}
}
return 0;
}
int Msg(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType)
{
int ret;
HHOOK hHook = SetWindowsHookEx(
WH_CBT,
CBTHookProc,
NULL,
GetCurrentThreadId()
);
ret = MessageBox(hwnd, szText, szCaption, uType);
UnhookWindowsHookEx(hHook);
return ret;
}
//判断三条线能否消除
int Line(int start[2], int end[2])
{
int i;
if (start[0] == end[0])
{
if (start[1] > end[1])
{
for (i = start[1] - 1; i > end[1]; i--)
if (Map[start[0]][i] != 0)
return 0;
return 1;
}
else
{
for (i = start[1] + 1; i < end[1]; i++)
if (Map[start[0]][i] != 0)
return 0;
return 1;
}
}
else if (start[1] == end[1])
{
if (start[0] > end[0])
{
for (i = start[0] - 1; i > end[0]; i--)
if (Map[i][start[1]] != 0)
return 0;
return 1;
}
else
{
for (i = start[0] + 1; i < end[0]; i++)
if (Map[i][start[1]] != 0)
return 0;
return 1;
}
}
return 0;
}
int TwoLines(int start[2], int end[2])
{
if (Line(start, end) == 1)
return 1;
int p1[2], p2[2];
p1[0] = start[0];
p1[1] = end[1];
p2[0] = end[0];
p2[1] = start[1];
if (Map[p1[0]][p1[1]] == 0 && Line(start, p1) && Line(end, p1))
return 1;
if (Map[p2[0]][p2[1]] == 0 && Line(start, p2) && Line(end, p2))
return 1;
return 0;
}
int TreeLines(int start[2], int end[2])
{
if (
Map[start[0]][start[1]] == Map[end[0]][end[1]]
&& (!(start[0] == end[0] && start[1] == end[1]))
)
{//先后点击的start end字母相同且不是同一个位置
if (TwoLines(start, end))
return 1;
int i, mid[2];
for (i = start[0] - 1; i >= 0; i--)
{
if (Map[i][start[1]] != 0) break;
mid[0] = i;
mid[1] = start[1];
if (TwoLines(mid, end)) return 1;
}
for (i = start[0] + 1; i <= MapSize + 1; i++)
{
if (Map[i][start[1]] != 0) break;
mid[0] = i;
mid[1] = start[1];
if (TwoLines(mid, end)) return 1;
}
for (i = start[1] - 1; i >= 0; i--)
{
if (Map[start[0]][i] != 0) break;
mid[0] = start[0];
mid[1] = i;
if (TwoLines(mid, end)) return 1;
}
for (i = start[1] + 1; i <= MapSize + 1; i++)
{
if (Map[start[0]][i] != 0) break;
mid[0] = start[0];
mid[1] = i;
if (TwoLines(mid, end)) return 1;
}
}
return 0;
}
//可以消除时画消除路径
int DrawLine(int start[2], int end[2])
{
int i;
if (start[0] == end[0])
{
if (start[1] > end[1])
{
for (i = start[1] - 1; i > end[1]; i--)
if (Map[start[0]][i] != 0)
return 0;
line(start[0] * 50 + 25, start[1] * 50 + 25, end[0] * 50 + 25, end[1] * 50 + 25);
return 1;
}
else
{
for (i = start[1] + 1; i < end[1]; i++)
if (Map[start[0]][i] != 0)
return 0;
line(start[0] * 50 + 25, start[1] * 50 + 25, end[0] * 50 + 25, end[1] * 50 + 25);
return 1;
}
}
else if (start[1] == end[1])
{
if (start[0] > end[0])
{
for (i = start[0] - 1; i > end[0]; i--)
if (Map[i][start[1]] != 0)
return 0;
line(start[0] * 50 + 25, start[1] * 50 + 25, end[0] * 50 + 25, end[1] * 50 + 25);
return 1;
}
else
{
for (i = start[0] + 1; i < end[0]; i++)
if (Map[i][start[1]] != 0)
return 0;
line(start[0] * 50 + 25, start[1] * 50 + 25, end[0] * 50 + 25, end[1] * 50 + 25);
return 1;
}
}
return 0;
}
int DrawTwoLines(int start[2], int end[2])
{
if (DrawLine(start, end) == 1)
return 1;
int p1[2], p2[2];
p1[0] = start[0];
p1[1] = end[1];
p2[0] = end[0];
p2[1] = start[1];
if (Map[p1[0]][p1[1]] == 0 && Line(start, p1) && Line(end, p1))
{
DrawLine(start, p1);
DrawLine(end, p1);
return 1;
}
if (Map[p2[0]][p2[1]] == 0 && Line(start, p2) && Line(end, p2))
{
DrawLine(start, p2);
DrawLine(end, p2);
return 1;
}
return 0;
}
int DrawTreeLines(int start[2], int end[2])
{
if (
Map[start[0]][start[1]] == Map[end[0]][end[1]]
&& (!(start[0] == end[0] && start[1] == end[1]))
)
{//先后点击的start end字母相同且不是同一个位置
if (DrawTwoLines(start, end))
return 1;
int i, mid[2];
for (i = start[0] - 1; i >= 0; i--)
{
if (Map[i][start[1]] != 0) break;
mid[0] = i;
mid[1] = start[1];
if (DrawTwoLines(mid, end))
{
DrawLine(mid, start);
return 1;
}
}
for (i = start[0] + 1; i <= MapSize + 1; i++)
{
if (Map[i][start[1]] != 0) break;
mid[0] = i;
mid[1] = start[1];
if (DrawTwoLines(mid, end))
{
DrawLine(mid, start);
return 1;
}
}
for (i = start[1] - 1; i >= 0; i--)
{
if (Map[start[0]][i] != 0) break;
mid[0] = start[0];
mid[1] = i;
if (DrawTwoLines(mid, end))
{
DrawLine(mid, start);
return 1;
}
}
for (i = start[1] + 1; i <= MapSize + 1; i++)
{
if (Map[start[0]][i] != 0) break;
mid[0] = start[0];
mid[1] = i;
if (DrawTwoLines(mid, end))
{
DrawLine(mid, start);
return 1;
}
}
}
return 0;
}
//初始化 生成并显示地图
void Oninit_all()
{
xt[0] = 0;//清空点击位置
xt[1] = 0;//清空点击位置
yt[0] = 0;//清空点击位置
yt[1] = 0;//清空点击位置
presscount = 0;//清空点击次数
remaining = MapSize * MapSize;//加满剩余字母数
for (int i = 0; i < MapSize + 1; i++)//清空地图
for (int j = 0; j < MapSize + 1; j++)
Map[i][j] = 0;
resist_time = 30 * MapSize - (guan_count - 1) * 5;//恢复剩余时间
if (resist_time <= 30)
resist_time = 30;
}
void CreateMap()//随机生成地图
{
srand((unsigned int)time(NULL));
for (int i = 0, j = 0; i < MapSize * MapSize/* - 60*/;)
{
int tx = rand() % MapSize + 1;
int c = j % 18/*24*/ /*25//Mapsize=10*/ /*MapSize//简单模式直接用MapSize*/;
int ty = rand() % MapSize + 1;
if (Map[tx][ty] == 0)
{
Map[tx][ty] = element[c];
i++;
j++;
}
}
}
void ShowMap()//显示地图
{
cleardevice();//背景色清空屏幕
pause_flag = 0;//刷新地图时时间不暂停
for (int i = 1; i < MapSize + 1; i++)
for (int j = 1; j < MapSize + 1; j++)
if (Map[i][j] != 0 && Map[i][j] != '*')
{
int num = strlen(element) - strlen(strchr(element, Map[i][j]));
putimage(i * Len_Image, j * Len_Image, &img[num]);
}
}
void xipai()
{
char str2[50];
sprintf(str2, "已无解,正在洗牌中···");
outtextxy((MapSize + 2)*Len_Image / 2, 0, str2);
int x1[2], x2[2];
int flag = 0;
srand((unsigned int)time(NULL));
while (1)
{
int tx = rand() % MapSize + 1;
int ty = rand() % MapSize + 1;
if (!flag)
{
if (Map[tx][ty] != 0)//不为空
{
x1[0] = tx;
x1[1] = ty;
flag = 1;
}
}
else
{
if (Map[tx][ty] != 0 && Map[tx][ty] != Map[x1[0]][x1[1]])//不为空
{
x2[0] = tx;
x2[1] = ty;
break;
}
}
}
char chtem = Map[x1[0]][x1[1]];
Map[x1[0]][x1[1]] = Map[x2[0]][x2[1]];
Map[x2[0]][x2[1]] = chtem;
}
void MoveDir(int Dir)
{
switch (Dir)
{
case 2://左
for (int i = 2; i < MapSize + 1; i++)
for (int j = 1; j < MapSize + 1; j++)
if (Map[i - 1][j] == 0)//左面的为空
{
Map[i - 1][j] = Map[i][j];
Map[i][j] = 0;
}
break;
case 3://右
for (int i = MapSize - 1; i > 0; i--)
for (int j = 1; j < MapSize + 1; j++)
if (Map[i + 1][j] == 0) //右面的为空
{
Map[i + 1][j] = Map[i][j];
Map[i][j] = 0;
}
break;
case 4://上
for (int i = 1; i < MapSize + 1; i++)
for (int j = 2; j < MapSize + 1; j++)
if (Map[i][j - 1] == 0)//上面的为空
{
Map[i][j - 1] = Map[i][j];
Map[i][j] = 0;
}
break;
case 5://下
for (int i = 1; i < MapSize + 1; i++)
for (int j = MapSize - 1; j > 0; j--)
if (Map[i][j + 1] == 0)//下面的为空
{
Map[i][j + 1] = Map[i][j];
Map[i][j] = 0;
}
break;
}
}
void MoveMap()//移动地图
{
if (guan_count > 1)
{
for (int i = 1; i < MapSize - 1; i++)
switch (guan_count)
{
case 2://左
MoveDir(2);
break;
case 3://右
MoveDir(3);
break;
case 4://上
MoveDir(4);
break;
case 5://下
MoveDir(5);
break;
case 6://左上
MoveDir(2);
MoveDir(4);
break;
case 7://右上
MoveDir(3);
MoveDir(4);
break;
case 8://左下
MoveDir(2);
MoveDir(5);
break;
case 9://右下
MoveDir(3);
MoveDir(5);
break;
default://其余情况左下
MoveDir(2);
MoveDir(5);
break;
}
}
}
void Show_time(void *)
{
while (1)
{
outtextxy(0, 0, _T(" "));
char str2[50];
sprintf(str2, "空格键提示(%d次)剩余时间:%ds 关卡:%d", helpcount, resist_time, guan_count);
outtextxy(0, 0, str2);
Sleep(1000);
if (pause_flag == 0)
{
if (resist_time > 0)
resist_time--;
}
}
}
//游戏操作和判断结果
void Save_game()//存档
{
char str[20];
sprintf(str, "del memory.txt");
system(str);//删除已有存档
FILE *mem;
if ((mem = fopen("memory.txt", "w+")) == NULL)//创建失败则继续游戏
{
MessageBoxA(0, "存档失败", "连连看", MB_OK || MB_SYSTEMMODAL);
pause_flag = 1 - pause_flag;//游戏继续
}
else
{
//保存点击位置
fwrite(&xt[0], sizeof(xt[0]), 1, mem);
fwrite(&xt[1], sizeof(xt[1]), 1, mem);
fwrite(&yt[0], sizeof(yt[0]), 1, mem);
fwrite(&yt[1], sizeof(yt[1]), 1, mem);
//保存点击次数
fwrite(&presscount, sizeof(presscount), 1, mem);
//保存剩余图片数
fwrite(&remaining, sizeof(remaining), 1, mem);
//保存剩余时间
fwrite(&resist_time, sizeof(resist_time), 1, mem);
//保存剩余求助次数
fwrite(&helpcount, sizeof(helpcount), 1, mem);
//保存关卡数
fwrite(&guan_count, sizeof(guan_count), 1, mem);
//保存地图
for (int i = 0; i < MapSize + 2; i++)
for (int j = 0; j < MapSize + 2; j++)
fwrite(&Map[i][j], sizeof(Map[i][j]), 1, mem);
fclose(mem);
int memres = MessageBoxA(0, "存档完成,是否退出", "连连看", MB_OKCANCEL || MB_SYSTEMMODAL);
if (memres == IDOK)//选择确定
{
closegraph();
exit(0);
}
else if (memres == IDCANCEL)
pause_flag = 1 - pause_flag;//游戏继续
}
}
void Read_game()//读取游戏存档
{
FILE *mem;
if ((mem = fopen("memory.txt", "r")) == NULL)
{
MessageBoxA(0, "没有存档,请重新选择", "连连看", MB_OK || MB_SYSTEMMODAL);
startgame();
}
else
{
//读取点击位置
fread(&xt[0], sizeof(xt[0]), 1, mem);
fread(&xt[1], sizeof(xt[1]), 1, mem);
fread(&yt[0], sizeof(yt[0]), 1, mem);
fread(&yt[1], sizeof(yt[1]), 1, mem);
//读取点击次数
fread(&presscount, sizeof(presscount), 1, mem);
//读取剩余图片数
fread(&remaining, sizeof(remaining), 1, mem);
//读取剩余时间
fread(&resist_time, sizeof(resist_time), 1, mem);
//读取剩余求助次数
fread(&helpcount, sizeof(helpcount), 1, mem);
//读取关卡数
fread(&guan_count, sizeof(guan_count), 1, mem);
//读取地图
for (int i = 0; i < MapSize + 2; i++)
for (int j = 0; j < MapSize + 2; j++)
fread(&Map[i][j], sizeof(Map[i][j]), 1, mem);
fclose(mem);
}
}
int Modify_XY(double pos)//修正鼠标位置到图片中心
{
return (int)(pos / Len_Image ) * Len_Image + Len_Image / 2;
}
void Get_Mouse()//获取鼠标事件
{
if (MouseHit())
{
MOUSEMSG msg = GetMouseMsg();
switch (msg.uMsg)
{
case WM_LBUTTONDOWN://左键按下
int loc_BUTTONDOWN[2] = {(Modify_XY(msg.x) - Len_Image / 2) / Len_Image, (Modify_XY(msg.y) - Len_Image / 2) / Len_Image };//重新保存下标
if (Map[loc_BUTTONDOWN[0]][loc_BUTTONDOWN[1]] != 0)//所点击的位置不为空
{
fillrectangle(
Modify_XY(msg.x) - Len_Image / 2,
Modify_XY(msg.y) - Len_Image / 2,
Modify_XY(msg.x) + Len_Image / 2,
Modify_XY(msg.y) + Len_Image / 2);
Sleep(30);//等待 屏幕显示
presscount++;//计点击次数
xt[presscount - 1] = Modify_XY(msg.x);//存储坐标
yt[presscount - 1] = Modify_XY(msg.y);
if (presscount == 2)//每点击两次 消除
{
for (int i = 0; i < 2;i++)
fillrectangle(
xt[i] - Len_Image / 2,
yt[i] - Len_Image / 2,
xt[i] + Len_Image / 2,
yt[i] + Len_Image / 2);
presscount = 0;//重新计点击次数
delete_or_not(xt, yt);
}
}
break;
}
}
}
void get_kbeve()
{
if (_kbhit() != 0) //检查是否有键盘输入,若有则返回一个非0值,否则返回0
{
while (_kbhit() != 0) //可能存在多个按键,要全部取完,以最后一个为主
kb_key = _getch(); //将按键从控制台中取出并保存到key中
if (GetAsyncKeyState(VK_ESCAPE) & 0x8000)//按ESC存档
{
pause_flag = 1 - pause_flag;
int saveres = MessageBoxA(0, "请选择是否存档", "连连看", MB_OKCANCEL || MB_SYSTEMMODAL);
if (saveres == IDOK)//选择确定
Save_game();
else if (saveres == IDCANCEL)
pause_flag = 1 - pause_flag;
}
switch (kb_key)
{
case 32: //按下空格 给予提示
if (helpcount)
{
Show_helpstep(kb_key);
helpcount--;
}
break;
case 9: //按下Tab 全部消除
while (Find_step_onexit())
{
Show_helpstep(kb_key);
}
break;
default:
break;
}
}
}
void delete_or_not(int xt[2], int yt[2])//执行判断消除操作
{
int start[2] = { (xt[0] - Len_Image / 2) / Len_Image,
(yt[0] - Len_Image / 2) / Len_Image };//重新保存下标
int end[2] = { (xt[1] - Len_Image / 2) / Len_Image,
(yt[1] - Len_Image / 2) / Len_Image };
if (TreeLines(start, end))//可以三条线以内消除
{
DrawTreeLines(start, end);//画消除路径
Sleep(100);//等待路径显示
Map[start[0]][start[1]] = 0;//清除这两个位置
Map[end[0]][end[1]] = 0;
remaining -= 2;//剩余字母数减2
MoveMap();
ShowMap();//刷新地图
}
}
int find_step(int x, int y)//0代表已无解
{
int start[2] = { x, y };//重新保存下标
for (int i = 1; i < MapSize + 1; i++)
{
for (int j = 1; j < MapSize + 1; j++)
{
int end[2] = { i, j };
if (!(x == i&&y == j)
&& Map[i][j] == Map[x][y]
&& TreeLines(start, end)
&& Map[x][y] != 0)
return 1;
}
}
return 0;
}
void Show_helpstep(int kb_key)//显示提示
{
for (int x = 1; x < MapSize + 1; x++)
{
for (int y = 1; y < MapSize + 1; y++)
{
int start[2] = { x, y };//重新保存下标
for (int i = 1; i < MapSize + 1; i++)
{
for (int j = 1; j < MapSize + 1; j++)
{
int end[2] = { i, j };
if (!(x == i&&y == j)
&& Map[i][j] == Map[x][y]
&& TreeLines(start, end)
&& Map[x][y] != 0)
{
rectangle(
i*Len_Image,
j*Len_Image,
(i + 1)*Len_Image,
(j + 1)*Len_Image);
rectangle(
x*Len_Image,
y*Len_Image,
(x + 1)*Len_Image,
(y + 1)*Len_Image);
int start[2] = { i,j };//保存下标
int end[2] = { x,y };
DrawTreeLines(start, end);//画消除路径
if (kb_key != 9)//如果不是Tab键消除 就长时间显示路径
Sleep(100);//等待路径显示
else
Sleep(5);//等待路径显示
Map[start[0]][start[1]] = 0;//清除这两个位置
Map[end[0]][end[1]] = 0;
remaining -= 2;//剩余字母数减2
MoveMap();
ShowMap();//刷新地图
return;
}
}
}
}
}
}
int Find_step_onexit()
{
int flag = 0;
for (int i = 1; i < MapSize + 1; i++)
for (int j = 1; j < MapSize + 1; j++)
if (find_step(i, j))//有解
return 1;
return 0;
}
void judge_game()
{
if (!remaining)
{
pause_flag = 1;
int res = MessageBoxA(0, "消除成功,是否开始下一关?", "连连看", MB_YESNO | MB_SYSTEMMODAL);
if (res == IDYES)
{
guan_count++;
Oninit_all();
CreateMap();//生成地图
ShowMap();//显示地图
pause_flag = 0;
while (1)
{
Get_Mouse();
get_kbeve();
judge_game();//判断游戏是否结束
Sleep(5);
}
}
else
{
closegraph();
exit(0);
}
}
if (Find_step_onexit() == 0)
{
pause_flag = 1;//时间暂停
while (!Find_step_onexit()) //洗牌出结果
{
for (int i = 0; i <= 2;i++)
xipai();
}
ShowMap();
}
if (resist_time <= 0)
{
int res = MessageBoxA(0, "时间到!是否重新开始?", "连连看", MB_YESNO | MB_SYSTEMMODAL);
if (res == IDYES)
{
guan_count = 1;
pause_flag = 0;
restart_game();
}
else
{
closegraph();
exit(0);
}
}
}
void startgame()
{
pause_flag = 1 - pause_flag;//游戏暂停
int res = Msg(0, "请选择模式", "连连看", MB_YESNO | MB_SYSTEMMODAL);
if (res == IDNO)//选择读档
{
Read_game();//读档生成地图
}
else
{
CreateMap();//生成地图
}
pause_flag = 1 - pause_flag;//游戏继续
ShowMap();//显示地图
while (1)
{
Get_Mouse();
get_kbeve();
judge_game();//判断游戏是否结束
Sleep(5);
}
}
void restart_game()
{
Oninit_all();
startgame();
}
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
#include
#define MapSize 12 //矩形地图的长 必须为偶数
#define Len_Image 50//正方形图片边长
extern IMAGE img[26];//存储图库
extern char element[];//地图中的元素(字母)
extern int pause_flag;//置为1说明游戏暂停 不计时
LRESULT CALLBACK CBTHookProc(int nCode, WPARAM wParam, LPARAM lParam);
int Msg(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType);
//判断是否可以消除
int Line(int start[2], int end[2]);//1条线
int TwoLines(int start[2], int end[2]);//2条线
int TreeLines(int start[2], int end[2]);//3条线
//可以消除画路径
int DrawLine(int start[2], int end[2]);//1条线
int DrawTwoLines(int start[2], int end[2]);//2条
int DrawTreeLines(int start[2], int end[2]);//3条
void Oninit_all();//初始化所有数据
void CreateMap();//随机生成地图
void ShowMap();//显示地图
void MoveDir(int Dir);//执行Map数组元素移动
void MoveMap();//移动地图
void Show_time(void *);//显示时间
void Save_game();
void Read_game();
int Modify_XY(double pos);//修正鼠标位置
void Get_Mouse();//获取鼠标事件
void get_kbeve();//获取键盘事件
void delete_or_not(int xt[2], int yt[2]);//执行判断消除操作
int find_step(int x, int y);//找寻下一组解 返回0代表已无解
void Show_helpstep(int kb_key);//提示下一组解
int Find_step_onexit();//判断游戏是否无解
void judge_game();//判断游戏是否结束
void startgame();//开始游戏
void restart_game();//重开游戏
#include "llk.h"
int main()
{
int len = Len_Image * (MapSize + 2) + 20;
initgraph(len, len);
HWND hwnd = GetHWnd();
MoveWindow(hwnd, 0, 0, len, len, 1);
setwritemode(R2_XORPEN); // 设置 XOR 绘图模式
setfillcolor(BLACK);
for (int i = 0; i < 25; i++)//加载图像
{
char s[10];
sprintf(s, "图库\\%c.png", element[i]);
loadimage(&img[i], s);
}
_beginthread(&Show_time, 0, 0);
startgame();
system("pause");
return 0;
}
由 LiangJian 写于 2019 年 10 月 15 日