连连看C语言课程设计报告.doc
连连看1、 问题描述连连看是一款简单有趣的小游戏,曾经风靡一时,玩家要将相同的两张牌用三根以内的直线连在一起就可以消除,规则简单容易上手,游戏速度节奏快,画面清晰可爱,适合细心的玩家。游戏胜利条件的判定:将棋盘上面的格子全部消除掉;失败的判定:规定的时间内格子没有消除。2、 问题分析 连连看需要解决的问题包括: (1)整个游戏界面和各种图案的图形显示; (2)如何判断鼠标所点的两个图像能否相消; (3)如何判断是否消除完全; (4)如何判断游戏的结束及如何终止游戏; (5)游戏难度的设计和得分规则游戏总的流程可以描述如下:首先出现游戏界面,一幅由10*6的小图片的构成的画面,玩家需要点击 2 个相同图案的对子,其连接线不多于 3 根直线,也就是说连接相同图案时,直线只能有两个折点,就可以成功将对子消除。此外,需要重点考虑的是游戏面板和各种形状的方块的数据结构表示。格子面板可以用一个二维数组来表示。3、 开发工具的介绍【 VS2008 】VS2008引入了250多个新特性,整合了对象、关系型数据、XML的访问方式,语言更加简洁。使用Visual Studio 2008可以高效开发Windows应用程序。设计器中可以实时反映变更,XAML中智能感知功能可以提高开发效率。同时Visual Studio 2008支持项目模板、调试器和部署程序。Visual Studio 2008可以高效开发Web应用,集成了AJAX 1.0,包含AJAX项目模板,它还可以高效开发Office应用和Mobile应用。VS的开发界面分为两个版本:整合模式和孤立模式,分别对基于语言的开发和基于特别工具的开发作了优化。【 EasyX 】 EasyX 是针对 C+ 的图形库,可以帮助 C 语言初学者快速上手图形和游戏编程。许多学编程的都是从 C 语言开始入门的,而目前的现状是:1. 有些学校以 Turbo C 为环境讲 C 语言,只是 Turbo C 的环境实在太老了,复制粘贴都很不方便。2. 有些学校直接拿 VC 来讲 C 语言,因为 VC 的编辑和调试环境都很优秀,并且 VC 有适合教学的免费版本。可惜在 VC 下只能做一些文字性的练习题,想画条直线画个圆都很难,还要注册窗口类、建消息循环等等,初学者会受严重打击的。初学编程想要绘图就得用 TC,很是无奈。3. 还有计算机图形学,这门课程的重点是绘图算法,而不是 Windows 编程。所以,许多老师不得不用 TC 教学,因为 Windows 绘图太复杂了,会偏离教学的重点。新的图形学的书有不少是用的 OpenGL,可是门槛依然很高。所以,于是就有了EasyX 库方便的开发平台和 TC 简单的绘图功能。4、 设计【程序总体结构】:进入主函数Init()函数初始图形初始化进度条1读取鼠标状态WM_MOUSEMOVEWM_LBUTTONDOWNBreak;leftbottondown()方块全消除进度条到头插入成功图片插入失败图片ifif【界面设计】:界面的设计通过插入背景图片 beijing.bmp , 在对每一个格子的赋图片,通过getimage( )从board.bmp中截下所需要的图,再利用putimage( )放置每一个小图。具体算法:loadimage(&beijing,"D:llkbeijing.bmp");/载入背景图putimage(0,0,&beijing);/放置背景图 for(int i = 0 ;i < ROW; i +)for(int j = 0 ;j < COL ; j+)idy = i * 54 + board_Y ,idx = j * 48+ board_X;putimage(idx,idy,&imageboardi + 1j + 1);/根据赋的值放置每一个格子图beijing.bmpboard.bmp【重要数据的数据结构设计】:struct BOARD /点击的格子信息int tx,ty; /格子坐标int xx,yy; /屏幕坐标int data; /图片类型;定义BOARD型的结构体,里面包含的是鼠标点击以后,所读取的点击点在屏幕里的坐标(xx,yy)在二维数组里的坐标boardtxty,以及在给每一个二维数组元素赋的值data(用来判断两个格子是否相等,相等以后赋值为0,以便判断是否存在格子,为之后的寻找路径铺垫)int boardROW + 2COL + 2;二维数组,用来记录格子的信息,其值是判定是否有方块的标志。MOUSEMSG mouse;鼠标的记录类型struct MOUSEMSG UINT uMsg; / 当前鼠标消息 bool mkCtrl; / Ctrl 键是否按下 bool mkShift; / Shift 键是否按下 bool mkLButton; / 鼠标左键是否按下 bool mkMButton; / 鼠标中键是否按下 bool mkRButton; / 鼠标右键是否按下 int x; / 当前鼠标 x 坐标(物理坐标) int y; / 当前鼠标 y 坐标(物理坐标) int wheel; / 鼠标滚轮滚动值;#define board_X 140 /格子区距左边框距离#define board_Y 140 /格子呢区距上边框距离#define COL 10 /格子区列数#define ROW 6 /格子区行数#define board_number 15 /游戏图片数目【函数设计】:函数的功能列表:void Init();/初始化界面void load_picture (); /加载图片void del(BOARD &rec);/每个格子赋值为void leftbottondown (MOUSEMSG mouse);/实现鼠标左击效果void frame (int leftx,int lefty);/画边框bool judge_mouse(int leftx,int lefty);/判断鼠标是否在游戏区void select(int leftx,int lefty);/显示选中效果void accurate(int& leftx,int& lefty);/使格子的坐标都在边缘void cover(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4); /覆盖直线void line(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4) ; /用直线连接void exchange(BOARD& pre,BOARD& cur);/交换格子信息bool click(int mousex,int mousey); /判断单击是否有效void record(int leftx,int lefty,BOARD &rec);/记录选中的格子void mouse_to_board(int mousex,int mousey,int *idx,int *idy);/鼠标的坐标转为格子的数组下标int findpath(BOARD &pre,BOARD &cur);/寻找路径,判断是否相等void progressbar();/画进度条void updateprogressbar(long t);/更新进度条因为有许多的函数是执行图像处理以及坐标转化的简单操作,这里画出包含关键算法的主要函数的流程图,其他的函数只做文字的描述介绍。findpath(BOARD &pre,BOARD &cur)函数根据导入的pre,cur结构体进入函数两值是否相等NflaseY记录两点的横坐标,取其max和min在左或右格子为0的情况下,向左或右增max,减min如果最左min为0,则左边界连线,并覆盖如果最右max为11,则右边界连线,并覆盖在左右的公共无阻碍区域里,从左往右,在两个点的纵向查找是否没有阻碍,无则根据拐点连线,并覆盖记录两点的纵坐标,取其max和min在上或下格子为0的情况下,向上或下增max,减min如果最上min为0,则上边界连线,并覆盖如果最下max为7,则下边界连线,并覆盖在上下的公共无阻碍区域里,从上往下,在两个点的横向查找是否没有阻碍,无则根据拐点连线,并覆盖cover(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)函数导入4个点的坐标(1234表示先后顺序)如果x1=x2,否则y1=y2x1=x2y1=y2从y1到y2的路径覆盖从x1到x2的路径覆盖如果x2=x3,否则y2=y3X2=x3Y2=y3从y2到y3的路径覆盖从x2到x3的路径覆盖如果x3=x4,否则y3=y4X3=x4Y3=y4从y3到y4的路径覆盖从x3到x4的路径覆盖Init()函数进入函数利用双重循环对二维数组赋值加载背景,放置背景图片利用系统时间为种子,取随机数,打乱二维数组的值根据赋的值,给每个二维数组对应区加图片,所加图片根据每一个点的值来赋imageboard_number里的相应图利用outtextxy函数在相应位置画计分板,设置相应的格式以及初始的分数000load_picture ()函数:这是一个类似于初始图片的函数,利用 loadimage ,SetWorkingImage,对含有格子图片信息的图进行分割读取,并存入image的IMAGE类型的数组里,同时还读入了填充背景的图(填充线条)。del(BOARD &rec)函数:删除函数,将之前记录的鼠标信息全部赋值为0;leftbottondown (MOUSEMSG mouse)函数:记录鼠标的信息,如果鼠标是第一次点击的话,将其信息存入pre里,如果鼠标是第二次点击的话,将其存入cur里,通过findpath()函数来相消与否,如果能相消,则将鼠标点的两块区域覆盖,同时调用del函数,来清除信息,在此过程中,根据统计剩余格子对数的变量counter,来给出分数(用s数组来存储分数信息,并在相应位置输出)。frame (int leftx,int lefty)函数:根据导入的参数画一个格子边框。judge_mouse(int leftx,int lefty)函数:判断鼠标的点击区域是否在规定的范围里。 select(int leftx,int lefty)函数:在鼠标点击选择以后,显示frame的效果,并记录数据。accurate(int& leftx,int& lefty)函数:规范坐标的函数,鼠标点击的区域是在一个范围内的,此函数能把属于一个区域的坐标归整为统一的坐标。line(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4) 函数:连线函数,通过导入的点的坐标来调用moveto,lineto函数画线。exchange(BOARD& pre,BOARD& cur)函数:交换格子信息的函数,在点击2次后,发现两个格子并不能相消后,将pre取第二次点击的格子信息。click(int mousex,int mousey)函数:判断点击的地方是不是有效,即已经被消去的格子,点击时,没有像有格子的地方一样会出现线框的情况。record(int leftx,int lefty,BOARD &rec)函数:将鼠标的屏幕坐标转化并记录在BOARD类型的变量里。mouse_to_board(int mousex,int mousey,int *idx,int *idy)函数:将鼠标的坐标转化为格子信息。progressbar( )函数:画进度条,作矩形,并填充相应颜色。updateprogressbar(long t)函数:进度条的更新函数,读取当前系统时间与游戏开始时系统时间的差,来画图覆盖进度条,从而达到进度条运动的效果。【程序运行整体视图】5、 源程序#include <stdio.h>#include <graphics.h>#include <conio.h>#include <time.h>#include <windows.h>#include <mmsystem.h>/播放音乐用的库#pragma comment(lib, "WINMM.LIB")/播放音乐用的库#define ture 1#define flase 0#define board_X 140 /格子区距左边框距离#define board_Y 140 /格子呢区距上边框距离#define COL 10 /格子区列数#define ROW 6 /格子区行数#define board_number 15 /游戏图片数目#define W 48 /格子的长#define H 54 /格子的宽#define N 555 /背景的宽#define M 797 /背景的长#define max(a,b) a>b?a:b#define min(a,b) a<b?a:b#define DGREE 2 /时间条的运动速度struct BOARD /点击的格子信息int tx,ty; /格子坐标int xx,yy; /屏幕坐标int data; /图片类型pre,cur,dur;IMAGE imageboard_number+ 1; /图片库IMAGE tianchong; /填充图片IMAGE tianchong1; /覆盖分数的图片IMAGE tianchong2; /覆盖分数的图片int boardROW + 2COL + 2; /游戏图纸MOUSEMSG mouse;int counter=board_number*2;void Init();/初始化界面 void load_picture ();/加载图片void del(BOARD &rec);/每个格子赋值为void leftbottondown (MOUSEMSG mouse);/实现鼠标左击效果void frame (int leftx,int lefty);/画边框bool judge_mouse(int leftx,int lefty);/判断鼠标是否在游戏区void select(int leftx,int lefty);/显示选中效果void accurate(int& leftx,int& lefty);/使格子的坐标都在边缘void cover(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4); /覆盖直线void line(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4) ; /用直线连接void exchange(BOARD& pre,BOARD& cur);/交换格子信息bool click(int mousex,int mousey); /判断单击是否有效void record(int leftx,int lefty,BOARD &rec);/记录选中的格子void mouse_to_board(int mousex,int mousey,int *idx,int *idy);/鼠标的坐标转为格子的数组下标int findpath(BOARD &pre,BOARD &cur);/寻找路径,判断是否相等void progressbar();/画进度条void updateprogressbar(long t);/更新进度条int findpath(BOARD &pre,BOARD &cur) int i,j,path,min1,max1,min2,max2,left,right,top,bottom;int m,n;if (boardpre.typre.tx!= boardcur.tycur.tx) /判断是否点中相同 return false;min1=max1=pre.tx;min2=max2=cur.tx;while (min1-1>=0 && boardpre.tymin1-1=0) min1-;/查找水平方向公共区域while (min2-1>=0 && boardcur.tymin2-1=0) min2-; left=max(min1,min2); / 左边界while (max1+1<=11 && boardpre.tymax1+1=0) max1+;while (max2+1<=11 && boardcur.tymax2+1=0) max2+; right=min(max1,max2); / 右边界 if (left=0) line(pre.tx,pre.ty,0,pre.ty,0,cur.ty,cur.tx,cur.ty);/左边缘连通 Sleep(300); cover(pre.xx,pre.yy,board_X-48,pre.yy,board_X-48,cur.yy,cur.xx,cur.yy);/覆盖线条 return true; if (right=11) line(pre.tx,pre.ty,11,pre.ty,11,cur.ty,cur.tx,cur.ty); Sleep(300); cover(pre.xx,pre.yy,board_X+480,pre.yy,board_X+480,cur.yy,cur.xx,cur.yy);/覆盖线条 return true; for (i=left;i<=right;i+) path=0;/记录竖直方向上的长度m=min(pre.ty,cur.ty);n=max(pre.ty,cur.ty);for (j=m+1;j<n;j+) path=path+boardji; if (path>0) break; if (path=0) line(pre.tx,pre.ty,i,pre.ty,i,cur.ty,cur.tx,cur.ty); Sleep(300); cover(pre.xx,pre.yy,board_X+(i-1)*48,pre.yy,board_X+(i-1)*48,cur.yy,cur.xx,cur.yy);/覆盖线条 return true; min1=max1=pre.ty;/查找垂直方向公共区域min2=max2=cur.ty;while (min1-1>=0 && boardmin1-1pre.tx=0) min1-;while (min2-1>=0 && boardmin2-1cur.tx=0) min2-; top=max(min1,min2);while (max1+1<=7 && boardmax1+1pre.tx=0) max1+;while (max2+1<=7 && boardmax2+1cur.tx=0) max2+; bottom=min(max1,max2); if (top=0) line(pre.tx,pre.ty,pre.tx,0,cur.tx,0,cur.tx,cur.ty);/同在顶端消除 Sleep(300); cover(pre.xx,pre.yy,pre.xx,board_Y-54,cur.xx,board_Y-54,cur.xx,cur.yy);/覆盖线条 return true; if (bottom=7) line(pre.tx,pre.ty,pre.tx,7,cur.tx,7,cur.tx,cur.ty); Sleep(300); cover(pre.xx,pre.yy,pre.xx,board_Y+324,cur.xx,board_Y+324,cur.xx,cur.yy);/覆盖线条 return true; for (j=top;j<=bottom;j+) path=0;/记录水平方向的长度m=min(pre.tx,cur.tx);n=max(pre.tx,cur.tx);for (i=m+1; i<n; i+) path+=boardji; if (path>0) break; if (path=0) line(pre.tx,pre.ty,pre.tx,j,cur.tx,j,cur.tx,cur.ty); Sleep(300); cover(pre.xx,pre.yy,pre.xx,board_Y+(j-1)*54,cur.xx,board_Y+(j-1)*54,cur.xx,cur.yy);/覆盖线条 return true; return false;void Init() /初始化int ix,iy,jx,jy,idx,idy,temp;srand(unsigned)time(NULL);load_picture();IMAGE beijing;for(int i= 0, x = 1; x <= ROW; +x )for( int y = 1; y <= COL; +y )boardxy = i+ % board_number + 1;loadimage(&beijing,"D:llkbeijing.bmp");/载入背景图putimage(0,0,&beijing);/放置背景图getimage(&tianchong,3 * 48,2 * 54,49,55);getimage(&tianchong1,3 * 48,2 * 54,70,55);getimage(&tianchong2,758,150,50,55); for( int k = 0; k <60; +k )ix = rand() % ROW + 1;iy = rand() % COL + 1;jx = rand() % ROW + 1; jy = rand() % COL + 1;if( boardixiy != boardjxjy) /使数据打乱temp= boardixiy;boardixiy = boardjxjy;boardjxjy =temp; for(int i = 0 ;i < ROW; i +)for(int j = 0 ;j < COL ; j+)idy = i * 54 + board_Y ,idx = j * 48+ board_X;putimage(idx,idy,&imageboardi + 1j + 1);/根据赋的值放置每一个格子图setbkmode(TRANSPARENT);setfont(35, 0,"华文琥珀");setcolor(YELLOW); outtextxy(700, 100,"分数");outtextxy(700, 150,"000");void load_picture()IMAGE image1,background;loadimage(&image1,"D:llkboard.bmp"); SetWorkingImage(&image1);for(int i = 1 ;i <=board_number;i +)getimage(&imagei,0,i * 54,48, 54);loadimage(&background,"IMAGE","bg");SetWorkingImage(&background);getimage(&tianchong,3 * 56,2 * 60,56,60);SetWorkingImage();putimage(0,0,&background);void leftbottondown (MOUSEMSG mouse) /单击左键static int click1 = 0,x,y;char s3;int number,i;click1+;select(mouse.x,mouse.y);/显示选中效果if(click1 = 1)record(mouse.x,mouse.y,pre);if(click1 = 2) mouse_to_board(mouse.x,mouse.y,&x,&y);if(x != pre.tx|y != pre.ty)record(mouse.x,mouse.y,cur);if(findpath(pre,cur)boardpre.typre.tx = boardcur.tycur.tx =0;putimage(pre.xx,pre.yy,&tianchong);putimage(cur.xx,cur.yy,&tianchong);del(pre); del(cur); click1 = 0;counter-;putimage(700,150,&tianchong1);number=20*(board_number*2-counter);for(i=2;i>=0;i-) si = number % 10 + '0' number/= 10; setbkmode(TRANSPARENT); setfont(35, 0,"华文琥珀"); setcolor(YELLOW);outtextxy(700, 150,s);putimage(758,150,&tianchong2);elseexchange(dur,pre); exchange(pre,cur); del(cur);click1 = 1;putimage(dur.xx,dur.yy,&imageboarddur.tydur.tx);else click1= 1;void select (int leftx,int lefty)/选中时效果if(judge_mouse(leftx,lefty)int x,y;mouse_to_board(leftx,lefty,&x,&y);accurate(leftx,lefty); frame(leftx,lefty);bool judge_mouse(int leftx,int lefty) /判断鼠标是否在游戏区return leftx >board_X && leftx < board_X + W * COL && lefty >board_Y && lefty < board_Y+ H * ROW;void mouse_to_board(int mousex,int mousey ,int *x,int *y) /鼠标坐标转化为图纸坐标if(judge_mouse(mousex,mousey)*x = (mousex -board_X) / 48 + 1;*y = (mousey -board_Y) / 54 + 1 ;void record(int leftx,int lefty,BOARD &rec)/记录选中的信息mouse_to_board(leftx,lefty,&rec.tx,&rec.ty);rec.xx = (rec.tx - 1) * 48 +board_X;rec.yy = (rec.ty - 1) * 54 +board_Y;rec.data = boardrec.tyrec.tx;bool click(int mousex,int mousey)/判断单击是否有效int x,y;mouse_to_board(mousex,mousey,&x,&y);/转化为格子坐标if(judge_mouse(mouse.x,mouse.y) && boardyx != 0)return true;return false;void frame(int leftx,int lefty)/画方框setcolor(RGB(126,91,68);setlinestyle(PS_SOLID,3);rectangle(leftx,lefty,leftx+47,lefty+53);rectangle(leftx + 2,lefty + 2,leftx+47,lefty+53);setcolor(RGB(250,230,169);rectangle(leftx + 1,lefty + 1,leftx+48,lefty+54);void accurate(int& leftx,int& lefty)leftx = (leftx -board_X) / 48) *48 +board_X;lefty = (lefty -board_Y) / 54) *54 +board_Y;void exchange(BOARD &pre,BOARD &cur)/交换格子信息pre.data = cur.data;pre.tx = cur.tx;pre.ty = cur.ty;pre.xx= cur.xx;pre.yy = cur.yy;void del(BOARD &rec