初始局面不能随机随便生成,因为有时候会无法完成游戏,所以要有一个生成算法。
:
- // 生成初始局面
- void CPuzzleLogic::MakeInit(int Num)
- {
- int EmptyPos = m_BlockNum*m_BlockNum-1;//记录空格子的位置
- srand((unsigned)time(NULL));//随机数种子,为了调用time函数,还要在代码前加上#include <time.h>
- for(int i=0; i<Num; )
- {
- //randnum中0->左; 1->上; 2->右; 3->下
- int randnum = rand()%4; //只有四个方向
- int Pos;
- switch(randnum)
- {
- case 0://表示左
- Pos = EmptyPos-1;
- break;
- case 1://表示上
- Pos = EmptyPos-m_BlockNum;
- break;
- case 2://表示右
- Pos = EmptyPos+1;
- break;
- case 3://表示下
- Pos = EmptyPos+m_BlockNum;
- break;
- default://其他情况,一般是不会出现的
- Pos = -1;
- break;
- }
- if(!MoveBlock(Pos, true))
- {//调用移动函数
- continue;
- }
- EmptyPos = Pos;
- ++i;
- }
- }
在初始化的函数里
- // BlockNum为要切割的方块数,只在横向或纵向的
- bool CPuzzleLogic::InitLogic(int BlockNum)
- {
- if(m_Block != NULL)
- {
- delete m_Block;
- }
- m_BlockNum = BlockNum;
- m_Block = new int[BlockNum*BlockNum];
- for(int i=0; i<BlockNum; ++i)
- {
- for(int j=0; j<BlockNum; ++j)
- {
- int num = i;
- m_Block[i*BlockNum+j] = num*10+j;
- }
- }
- MakeInit(100);//这里调用生成初始局面的函数
- return true;
- }
首先按原来的,生成一个完成的局面,就是最终局面,也就是说显示出来的图像就是完成时的那样的。
EmptyPosEmptyPos然后,思想是这样的,生成一个随机数,这个数是从到的,正好四个数字,代表四个方向,我们就是让这个空格子和它相邻的非空的格子去交换,简单说,就是我们自己新买了一个拼图的玩具,原先是拼好的,我们自己用手把它打散了,自己弄乱的,因为弄乱的时候也是按照它的规则(只能向空格移动),所以必然至少有一种可能可以完成(完不成是个人拼图水平问题,我不管啊,呵呵)。
有了思想就好办了,我们先随机生成了一个它可能移动的位置,然后就假设我们单击了这个位置,去调用单击时的函数,就移动了。
MoveBlockfalse我的解决方案是再加一个的参数,加上默认值为,用来表示是否是在初始化,加上默认值后,我们前面的调用部分就都不用改了,当然你也可以重载……不过很麻烦,把函数修改的地方也贴出来。
- bool CPuzzleLogic::MoveBlock(int Pos, bool bInit)
- {
- //判断游戏是否在进行中
- if(m_IsPlaying == false && bInit ==false)//这里用与运算,全为真是才为真
- {
- return false;
- }
int NumNum100当然有一种最坏的情况就是永远生成不出来游戏,因为随机时如果每次随机数都越界,那就无法生成了,一般是不会出现的,计算机秒可以运算几次,次对它来说根本不算什么。
不过,我们还没有计算步数的。
int m_StepNum;
0再添加两个函数,,这两个函数貌似没什么用,有人会说直接加就行了,但是不符合类的封装。尽量不要从类外可以修改类里的变量,容易出错。
把函数在下面的地方添加上。
这样,单击的时候就会每移动一次都可以增加了,有没有人发现问题?细心的也许可以想到,我们在初始化时就是调用的这个函数,如果这样写的话,初始化后,不就已经不是了吗?当然是的,所以我们在InitLogic
{
……
先生成开始局面
再将步数设为 return true;
0然后就要显示出来了。
CPuzzleViewTCHAR m_StepNum[3],
先要有设置步数的,设置数组的项,所以我们最大只能到步。
48ASCII04848下面,就是画图了,画图当然要在里了,看看代码,我当然还是只贴出来修改的地方。
- void CPuzzleView::OnPaint(void)
- {
- ……
- HFONT hFont;//创建字体
- hFont = CreateFont(50, // nHeight
- 0, // nWidth
- 0, // nEscapement
- 0, // nOrientation
- FW_NORMAL, // nWeight
- FALSE, // bItalic
- FALSE, // bUnderline
- 0, // cStrikeOut
- ANSI_CHARSET, // nCharSet
- OUT_DEFAULT_PRECIS, // nOutPrecision
- CLIP_DEFAULT_PRECIS, // nClipPrecision
- DEFAULT_QUALITY, // nQuality
- DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
- _T("Arial"));
- HFONT hOldFont = (HFONT)SelectObject(hdcMem, hFont);//绑定字体
- SetBkMode(hdcMem, TRANSPARENT);//设置背景为透明
- SetTextColor(hdcMem, RGB(87, 17, 147));//设置字体颜色
- TextOut(hdcMem, 662, 315, m_StepNum, 3);
- SelectObject(hdcMem, hOldFont);
- DeleteObject(hFont);
- BitBlt(hdcScr, 0, 0, SCRWIDTH, SCRHEIGHT, hdcMem, 0, 0, SRCCOPY);
- ……
- }
这里我们用了几个函数,这个函数就是创建一个字体,具体参数都是什么意思,网上一查到处都是,我就不解释了,然后又一次出现了,这次是绑定了字体,再下面SetBkMode()TextOut()现在说为什么设置为透明模式。因为输出的文字是带有背景色的,如果我们就是默认背景,不设置也没什么影响,但是,我们的背景是一张图,如果没设置为透明,结果就是文字下面是白色的(或者是你以前设置的背景色)。这当然不是我们想要的,所以要设置为透明的。
hFont下面就是把逻辑和显示结合到一起了,当然是要在里写了。
- void CPuzzleMain::OnClick(int x, int y)
- {
- if(m_View.IsInside(x, y)==true)
- {
- int p = m_View.GetPoint(x, y);
- if(m_Logic.MoveBlock(p))
- {
- m_View.SetStepNum(m_Logic.GetStep());//多加了这句
- m_View.LoadBMPList(m_Logic.GetBlock());
- m_View.OnPaint();
- if(m_Logic.IsVictor())
- {
- m_View.SetGameStarted(false);
- m_Logic.SetGamePlaying(false);
- MessageBox(NULL, _T("恭喜您成功完成了拼图"), _T("恭喜"), MB_OK);
- }
- }
- }
- }
本来还想完成声音的,结果今天有点事,没弄,下次吧。