首页 > 代码库 > 数独GUI程序项目实现

数独GUI程序项目实现

数独GUI程序项目实现

导语:最近玩上了数独这个游戏,但是找到的几个PC端数独游戏都有点老了。。。我就想自己做一个数独小游戏,也是一个不错的选择。

前期我在网上简单地查看了一些数独游戏的界面,代码。好好地了解了一下现在数独游戏的大概的框架。当然,我自己写的小游戏,也许没那么好。但是我一定会一点点升级这个小游戏的。

目前,我做的游戏是V1.0版本的,只能说实现了这个游戏的基本功能:可以进行数独游戏、可以更换背景色以及一些其他的基本功能。接下来,在空余时间,我会进行对其中一Studying功能的实现,就是数独独有数学逻辑学习。因为我发现现在的数独游戏都只是简单地游戏而已,并没有教游戏者如何去破解数独。比如摒除法、余数法、区块法、数对法这些方法都没有显式教给游戏者。这样对于游戏者的进步是不利的。一方面游戏者是为了娱乐,那么正常的Playing模式可以满足,另一方面游戏者是为了锻炼自己的大脑,那么额外的studying模式就很有必要了。想一想还有一点小激动呢。

 

 

一、项目素材:

一个游戏首先要有一个不错的界面,毕竟界面的友好度是重要决定因素。我挑选了战锤40K的几张图片作为背景图片。

游戏界面大小设定为576*576,即(64*9)*(64*9),数独9*9格子中,每个格子大小为64*64。所以编辑后的每个图片大小为576*576。

 

1.1  开始界面的图片选择:

技术分享

 

 

1.2  编辑后的开始界面:

技术分享

 

 

2.1  胜利界面的图片选择:

 技术分享

 

 

2.2  编辑后的胜利界面:

技术分享

 

 

3.1  失败界面的图片选择:

技术分享

 

 

3.2  编辑后的失败界面:

技术分享

 

 

4  备注:

还有一些诸如说明界面的选择,就不展现了。

 

 

二、代码解释:

接下来的是有关于其中代码的解释。

 1.1  头文件代码:

#include "stdafx.h"//标准应用程序框架的拓展头文件。将一些MFC标准头文件包含其中。加快编译速度。#include "Sudo.h"//这个是一个独立的头文件,用于处理一些诸如确立stdafx.h位置等语句。#include "MapManager.h"//主函数的接口函数,确立了CmapManager类的构造/析构函数,展现了程序大致框架。#include "coordinate.h"//坐标计算的头文件#include <time.h>//获取系统时间的头文件#include <stdlib.h>//标准库头文件,如malloc()、calloc()、system()、free()、rand()、exit()等#ifdef _DEBUG//如果宏定义了_DEBUG,则执行#ifdef与#endif之间的语句#undef THIS_FILE//取消之前对THIS_FILE的宏定义static char THIS_FILE[]=__FILE__;//静态设置全局#define new DEBUG_NEW//宏定义new为DEBUG_NEW#endif//与#ifdef连用。//该连用将分配内存时的new转换为DEBUG_NEW,这样会在内存中保留源文件名和行号。//这样在发生内存泄漏时,便于我们调试,找出问题代码。//表示这也算是我学到的。。。还有这种套路了。

1.2  解释:

头文件的整合编译,加快了程序的速度。

 

2.1  Create函数:

 1 BOOL CMapManager::Create(CWnd *wnd, int width, int height) 2 { 3     Window = wnd; 4     MapRect.SetRect(0, 0, width, height); 5  6     /* 载入必要的图片 */ 7     /* 先不载入底图,因为不同状态的底图不一样 */ 8     if (!View.Create(width, height, 24) 9         || !Number.LoadBmp("image\\Number2.bmp")10         || !Cursor.LoadBmp("image\\Cursor2.bmp"))11         //确保图片,光标等的读取没有问题。12     {13         return FALSE;14     }15     //任意一个函数返回FALSE,便输出FALSE。16     //确保将数字图片载入。17     18     /* 初始化基本数据 */19     CursorPos = 0;20     //光标21 22     topPos    = 0 * 192;23     belowPos  = 1 * 192;24     status    = -1;25     difficulty = EASY;26     27     /* 开始主菜单 */28     StartMainMenu();29     return TRUE;30 }

2.2  解释:

这是程序的主入口函数,载入主要数据并初始化。其中*wnd取得主窗口的指针,主要用于得到主窗口句柄。至于width与height表示view类的宽度和高度,都是在头文件中确立的固定值。

 

3.1  DrawMap函数:

void CMapManager::DrawMap(){    InitMap();        //初始化地图    DrawNumber();}

3.2  解释:

用于绘制游戏时的底图。

 

4.1  DrawCursor函数:

 1 void CMapManager::DrawCursor() 2 { 3     CPoint point(CursorPos.x * GRID_WIDTH, CursorPos.y * GRID_HEIGHT); 4     if(status == GAME) 5     { 6         MCursor.SetDrawPos(point + CPoint(2, 2)); 7     } 8     else if(status == MAINMENU) 9     {10         MCursor.SetDrawPos(CPoint(5 * GRID_WIDTH, point.y));11     }12     else if(status == OPTION)13     {14         /* 绘制选定的前景和背景对应的方块上的光标 */15         int index = topPos / 192;16         CPoint LT(3  * GRID_WIDTH, (1 + index / 5) * GRID_HEIGHT);17         MCursor.SetDrawPos(LT + CPoint((index % 5) * GRID_WIDTH, 0));18         MCursor.Draw(View);19 20         index = belowPos / 192;21         LT.y = (4 + index / 5) * GRID_HEIGHT;22         MCursor.SetDrawPos(LT + CPoint((index % 5) * GRID_WIDTH, 0));23         MCursor.Draw(View);24 25         MCursor.SetDrawPos(point);26     }27 28     MCursor.Draw(View);29 }

4.2  解释:

用于绘制光标。

 

5.1  DrawMap函数:

 1 void CMapManager::DrawMap(int x, int y, BOOL drawNumber /*= TRUE*/) 2 { 3     CPoint point(x * GRID_WIDTH, y * GRID_HEIGHT); 4         //经过这样简单的变换,获得了所需要的指定格的原点 5         /*根据状态改变*/ 6     if(status == GAME) 7     {     8         if (Map[y][x].isOrigin) 9         {10             /* 题目中给定的数字填充背景方块 */11             MBgBelow.SetDrawPos(point);12             MBgBelow.SetSrcPos(CPoint(belowPos + x % 3 * 13 14 GRID_WIDTH, y % 3 * GRID_HEIGHT));15             MBgBelow.Draw(View);16         }17         else18         {19             /* 玩家写上的数字填充前景方块 */20             MBgTop.SetDrawPos(point);21             MBgTop.SetSrcPos(CPoint(topPos + x % 3 * GRID_WIDTH, y 22 23 % 3 * GRID_HEIGHT));24             MBgTop.Draw(View);25         }26         if (drawNumber)27         {28             DrawNumber(x, y);29         }30     }    31     else if(status == MAINMENU)32     {33         View.Copy(BG, point, CSize(128, 64), point);34     }35     else if(status == OPTION)36     {37         View.Copy(BG, point, CSize(64, 64), point);38     }39 }

5.2  解释:

这也是一个DrawMap函数,但是它的参数列表与之前的那个DrawMap函数不同。这个函数绘制指定格的底图,并设定是否重绘其上的数字。

 

6.1  DrawNumber函数:

 1 void CMapManager::DrawNumber() 2 { 3     for (int i=0; i<9; i++) 4     { 5         for (int j=0; j<9; j++) 6         { 7             if(Map[i][j].num != 0) 8             { 9                 DrawNumber(j, i);10             }11         }12     }13 }

6.2  解释:

该函数用以绘制开始状态时的所有数字(其实也就1-9),也就是问题一开始显示的数字。

 

7.1  DrawNumber函数:

 1 void CMapManager::DrawNumber(int x, int y) 2 //注意这个是有参数的。 3 { 4     CPoint point(x * GRID_WIDTH, y * GRID_HEIGHT); 5     if(Map[y][x].num > 0 && Map[y][x].num < 10) 6         //确保输入的数字并没有超出范围。如果没有这个限制,将会导致程序出现错误。 7     { 8         View.Mix(Number, point, CSize(GRID_WIDTH, GRID_HEIGHT),  9             CPoint((Map[y][x].num) * GRID_WIDTH));10     }11 }

7.2  解释:

这也是一个DrawNumber函数,但是参数列表与之前不同。该函数是为了绘制指定位置的数字。即我们输入的数字,将会通过这个函数来绘制出来。

 

8.1  LoadMap函数:

 1 void CMapManager::LoadMap() 2 { 3     /* 打开题目文件 */ 4     CFile file; 5     file.Open("MapData.txt", CFile::modeRead); 6      7     /* 初始化随机种子 */ 8     srand((unsigned)time(0)); 9 10     /* 根据难度来偏移 */11     /* 81代表一题的81个数,8代表每一题前的号码"#000# "加上一回车换行符共812 13 个字符 */14     if (difficulty == MIDDLE)15     {16         /* 中等难度偏移EASY个 */17         file.Seek(sizeof(char) * EASY * (81 + 8), CFile::current);18     }19     else if (difficulty == HARD)20     {21         /* 困难难度偏移(EASY + MIDDLE)个 */22         file.Seek(sizeof(char) * (EASY + MIDDLE) * (81 + 8), 23 24 CFile::current);25     }26 27     /* 随机得到题号(这是在偏移基础上的题号) */28     int No = rand() % difficulty;29     /* 根据题号偏移 */30     /* 6代表题目前的号码"#000# "6个字符 */31     file.Seek(sizeof(char) * No * (81 + 8) + 6, CFile::current);32      33     /* 读入题目 */34     char txt[81];35     file.Read(&txt, sizeof(char) * 81);36     37     /* 根据题目初始化Map */38     for (int i=0; i<9; i++)39     {40         for (int j=0; j<9; j++)41         {42              Map[i][j].num = txt[i * 9 + j] - 0;43             Map[i][j].isOrigin = (Map[i][j].num == 0? FALSE : 44 45 TRUE);46         }47     }48 49     /* 关闭文件 */50     file.Close();51 }

8.2  解释:

打开题目文件,并通过了解程序难度设置,来读取相应难度的题目。

 

9.1  InitMap函数:

 1 void CMapManager::InitMap() 2 { 3     for (int i=0; i<9; i++) 4     { 5         for (int j=0; j<9; j++) 6         { 7             if(Map[i][j].isOrigin == TRUE) 8             { 9                 MBgBelow.SetDrawPos(IndexToPoint(j, i));10                 MBgBelow.SetSrcPos(CPoint(belowPos + j % 3 * 11 12 GRID_WIDTH, 13                     i % 3 * GRID_HEIGHT));14                 MBgBelow.Draw(View);15             }16             else17             {18                 MBgTop.SetDrawPos(IndexToPoint(j, i));19                 MBgTop.SetSrcPos(CPoint(topPos + j % 3 * 20 21 GRID_WIDTH, 22                     i % 3 * GRID_HEIGHT));23                 MBgTop.Draw(View);24             }25         }26     }27 }

9.2  解释:

根据题目组装整个底图。

 

10.1  CheckFinish函数:

 1 BOOL CMapManager::CheckFinish() 2 { 3     for (int i=0; i<9; i++) 4     { 5         for (int j=0; j<9; j++) 6         { 7             if (Map[i][j].num == 0) 8             { 9                 return FALSE;10             }11         }12     }13     return TRUE;14 }

10.2  解释:

检查是否完成题目,也就是是否所有的空白格子都被写入数字。不过正确与否无关。

 

11.1  CheckRow函数:

 1 BOOL CMapManager::CheckRow(int row) 2 { 3     /* 这里要说明一下 */ 4     /* 如果这一行是正确的,那它必然包括123456789,9个数字 */ 5     /* 这里采用位与运算检查 */ 6     /* 其中check = 0000 0001 1111 1111,后9位为1 */ 7     /* 假设当前位置的数字为3,则tmp = 1 << 2 = 0000 0000 0000 0100 */ 8     9     DWORD check = 0x01FF;10     DWORD tmp = 0;11     for (int i=0; i<9; i++)12     {13         tmp = 1 << (Map[row][i].num - 1);14         check &= ~tmp;15     }16     return check;17 }

11.2  解释:

检查某一行的数据是否正确(1-9都有且唯一)。另外,重点是位运算的应用,确实很好。

 

12.1  CheckCol函数:

 1 BOOL CMapManager::CheckCol(int col) 2 { 3     DWORD check = 0x01FF; 4     DWORD tmp = 0; 5     for (int i=0; i<9; i++) 6     { 7         tmp = 1 << (Map[i][col].num - 1); 8         check &= ~tmp; 9     }10     return check;11 }

12.2  解释:

检查某一列的数据是否正确(1-9都有且唯一)。

 

13.1  CheckGrid函数:

 1 BOOL CMapManager::CheckGrid(int grid) 2 { 3     DWORD check = 0x01FF; 4     DWORD tmp = 0; 5     int top = grid / 3 * 3; 6     int left = (grid % 3) * 3; 7     for (int i=top; i<top+3; i++) 8     { 9         for (int j=left; j<left+3; j++)10         {11             tmp = 1 << (Map[i][j].num - 1);12             check &= ~tmp;13         }14     }15     return check;16 }

13.2  解释:

检查某一个九宫格内的数据是否正确(1-9都有且唯一)。

 

14.1  CheckSuccess函数:

 1 BOOL CMapManager::CheckSuccess() 2 { 3     int i; 4     for (i=0; i<9; i++) 5     { 6         if(CheckRow(i) != 0 || CheckCol(i) != 0 || CheckGrid(i) != 0) 7         { 8             return FALSE; 9         }10     }11     return TRUE;12 }

14.2  解释:

检查答案是否正确。

 

15.1  Show函数:

 1 void CMapManager::Show(CDC* dc) 2 { 3     /* 以下的绘制函数不会重绘整个底图 */ 4     /* 只绘制需重绘的地方 */ 5     switch(status) 6     { 7     case MAINMENU: 8         ShowMainMenu(); 9         break;10     case GAME:11         ShowGame();12         break;13     case OPTION:14         ShowOption();15         break;16     default:17         break;18     }19     if (View.IsOK())20     {21         View.Draw(*dc, 0, 0, View.Width(), View.Height());22     }23 }

15.2  解释:

主要的显示函数。根据状态的不同显示不同界面,如开始,胜利,失败,游戏,选项等等。其中dc表示设备上下文句柄。

 

16.1  ShowMainMenu函数:

1 void CMapManager::ShowMainMenu()2 {3     CRect ButtonRect;4     ButtonRect.SetRect(IndexToPoint(5, 4), IndexToPoint(7, 7));5     if (ButtonRect.PtInRect(IndexToPoint(CursorPos)))6     {7         DrawCursor();8     }9 }

16.2  解释:

主界面显示函数(即开始界面显示函数)。

 

17.1  ShowGame函数:

1 void CMapManager::ShowGame()2 {    3     if(MapRect.PtInRect(IndexToPoint(CursorPos)))4     {5         DrawMap(CursorPos.x, CursorPos.y, FALSE);6         DrawNumber(CursorPos.x, CursorPos.y);7         DrawCursor();8     }9 }

17.2  解释:

游戏状态的显示函数。

 

18.1  ShowOption函数:

 1 void CMapManager::ShowOption() 2 { 3     CRect optionRect1; 4     CRect optionRect2; 5     optionRect1.SetRect(IndexToPoint(3, 1), IndexToPoint(8, 3)); 6     optionRect2.SetRect(IndexToPoint(3, 4), IndexToPoint(8, 6)); 7     if (optionRect1.PtInRect(IndexToPoint(CursorPos)) ||  8         optionRect2.PtInRect(IndexToPoint(CursorPos))) 9     {10         DrawCursor();11     }12 }

18.2  解释:

选项状态的显示函数。

 

19.1  Process函数:

 1 void CMapManager::Process(CALLBACKTYPE type, void *data) 2 { 3     switch(status) 4     { 5     case MAINMENU: 6         ProcessMenu(type, data); 7         break; 8     case GAME: 9         ProcessGame(type, data);10         break;11     case OPTION:12         ProcessOption(type, data);13         break;14     case WIN:15         ProcessWin(type, data);16         break;17     default:18         break;19     }20     InvalidateRect(Window->m_hWnd, MapRect, FALSE);21 }

19.2  解释:

主处理器。根据状态不同,将反馈的参数进行不同的操作 。程序里的所有事件(鼠标时间、键盘事件)都是由process函数来处理。

 

20.1  ProcessGame函数:

 1 void CMapManager::ProcessGame(CALLBACKTYPE type, void *data) 2 { 3     switch(type) 4     { 5     case onm ouseMOVE: 6         GameMouseMove(*(CPoint*)data); 7         break; 8     case ONCHAR: 9         GameChar(*(UINT*)data);10         break;11     default:12         break;13     }14 }

20.2  解释:

游戏状态下的事件处理器。

 

21.1  ProcessMenu函数:

 1 void CMapManager::ProcessMenu(CALLBACKTYPE type, void *data) 2 { 3     switch(type) 4     { 5     case ONLBUTTONDOWN: 6         MenuLButtonDown(*(CPoint*)data); 7         break; 8     case onm ouseMOVE: 9         MenuMouseMove(*(CPoint*)data);10         break;11     case ONLBUTTONUP:12         MenuLButtonUp(*(CPoint*)data);13         break;14     default:15         break;16     }17 }

21.2  解释:

主界面的事件处理器。

 

22.1  ProcessOption函数:

 1 void CMapManager::ProcessOption(CALLBACKTYPE type, void *data) 2 { 3     switch(type) 4     { 5     case onm ouseMOVE: 6         OptionMouseMove(*(CPoint*)data); 7         break; 8     case ONLBUTTONDOWN: 9         OptionLButtonDown(*(CPoint*)data);10         break;11     default:12         break;13     }14 }

22.2  解释:

选项界面的事件处理器。

 

23.1  ProcessWin函数:

 1 void CMapManager::ProcessWin(CALLBACKTYPE type, void *data) 2 { 3     switch(type) 4     { 5     case ONLBUTTONDOWN: 6         WinLButtonDown(*(CPoint*)data); 7         break; 8     default: 9         break;10     }11 }

23.2  解释:

胜利状态的事件处理器

 

24.1  GameMouseMove函数:

 1 void CMapManager::GameMouseMove(CPoint point) 2 { 3     if(MapRect.PtInRect(point)) 4     { 5         CPoint Pos = PointToIndex(point); 6         if (CursorPos != Pos)  7         { 8             DrawMap(CursorPos.x, CursorPos.y); 9             CursorPos = Pos;    10         }11     }12 }

24.2  解释:

之前游戏状态下的鼠标移动事件处理器。

 

25.1  GameChar函数:

 1 void CMapManager::GameChar(UINT nChar) 2 { 3     int x = CursorPos.x; 4     int y = CursorPos.y; 5      6     /* 不能修改题目给定的数字 */ 7     if (Map[y][x].isOrigin) 8     { 9         return;10     }11     12     switch(nChar)13     {14     case 0:15         Map[y][x].num = 0;16         break;17     case 1:18         Map[y][x].num = 1;19         break;20     case 2:21         Map[y][x].num = 2;22         break;23     case 3:24         Map[y][x].num = 3;25         break;26     case 4:27         Map[y][x].num = 4;28         break;29     case 5:30         Map[y][x].num = 5;31         break;32     case 6:33         Map[y][x].num = 6;34         break;35     case 7:36         Map[y][x].num = 7;37         break;38     case 8:39         Map[y][x].num = 8;40         break;41     case 9:42         Map[y][x].num = 9;43         break;44     /* ESC键返回主界面 */45     case VK_ESCAPE:46         Return();47         break;48     default:49         break;50     }51 52     if (CheckFinish())53     {54         if(CheckSuccess())55         {56             StartWin();57              //AfxMessageBox("You are good!");58         }59         else60         {61                         StartDefeat();62             //AfxMessageBox("You are wrong!");63         }64     }65 }

25.2  解释:

游戏状态下的键盘输入事件处理器。输入的ASCII码。如果试图修改游戏题目数据,就会返回NULL。

 

26.1  MenuLButtonDown函数:

 1 void CMapManager::MenuLButtonDown(CPoint point) 2 {     3     CRect ButtonRect; 4     ButtonRect.SetRect(IndexToPoint(5, 4), IndexToPoint(7, 7)); 5     if (ButtonRect.PtInRect(point)) 6     { 7         /* 这里本来要绘制鼠标按下效果,但程序速度太快,根本显示不了 */ 8         DrawMap(5, CursorPos.y); 9          MCursor.SetSrcPos(192, 0);10          MCursor.Draw(View);11         InvalidateRect(Window->m_hWnd, MapRect, FALSE);12         13         /* 开始游戏按钮 */14         if ((CRect(IndexToPoint(5, 4), IndexToPoint(7, 5))).PtInRect15 16 (point))17         {    18              StartGame();19         }20 21         /* 选项按钮 */22         if ((CRect(IndexToPoint(5, 5), IndexToPoint(7, 6))).PtInRect23 24 (point))25         {26             StartOption();27         }28 29         /* 结束游戏按钮 */30         if ((CRect(IndexToPoint(5, 6), IndexToPoint(7, 7))).PtInRect31 32 (point))33         {34              EndGame();35             MenuLButtonUp(point);36         }37     }    38 }

26.2  解释:

主界面状态下的鼠标单击事件处理器。

 

27.1  MenuMouseMove函数:

 1 void CMapManager::MenuMouseMove(CPoint point) 2 { 3     CRect ButtonRect; 4     ButtonRect.SetRect(IndexToPoint(5, 4), IndexToPoint(7, 7)); 5     if (ButtonRect.PtInRect(point)) 6     { 7         DrawMap(5, CursorPos.y); 8         CursorPos = PointToIndex(point); 9     }10 }

27.2  解释:

主界面状态下的鼠标移动事件处理器。

 

28.1  MenuLButtonUp函数:

 1 void CMapManager::MenuLButtonUp(CPoint point) 2 { 3     CRect ButtonRect; 4     ButtonRect.SetRect(IndexToPoint(5, 4), IndexToPoint(7, 7)); 5     if (ButtonRect.PtInRect(point)) 6     { 7         DrawMap(5, CursorPos.y); 8         MCursor.SetSrcPos(64, 0);
 9         MCursor.Draw(View);10     }11 }

28.2  解释:

主界面状态下的鼠标左键弹起事件处理器。

 

29.1  OptionMouseMove函数:

void CMapManager::OptionMouseMove(CPoint point){    if (CRect(IndexToPoint(3, 1), IndexToPoint(8, 3)).PtInRect(point) ||         CRect(IndexToPoint(3, 4), IndexToPoint(6, 6)).PtInRect(point))    {        DrawMap(CursorPos.x, CursorPos.y);        CursorPos = PointToIndex(point);    }}

29.2  解释:

选项状态下的鼠标移动事件处理器。

 

30.1  OptionLButtonDown函数:

 1 void CMapManager::OptionLButtonDown(CPoint point) 2 { 3     int index; 4     /* 前景方块 */ 5     if (CRect(IndexToPoint(3, 1), IndexToPoint(8, 3)).PtInRect(point)) 6     { 7         index = topPos / 192; 8         DrawMap(3 + index % 5, (1 + index / 5)); 9     10         topPos = ((point.y / GRID_HEIGHT - 1) * 5 + (point.x / 11 12 GRID_WIDTH - 3)) * 192;13     }14     /* 背景方块 */15     else if (CRect(IndexToPoint(3, 4), IndexToPoint(8, 6)).PtInRect16 17 (point))18     {19         index = belowPos / 192;20         DrawMap(3 + index % 5, (4 + index / 5));21 22         belowPos = ((point.y / GRID_HEIGHT - 4) * 5 + (point.x / 23 24 GRID_WIDTH - 3)) * 192;25     }26     /* 返回按钮 */27     else if (CRect(IndexToPoint(6, 7), IndexToPoint(8, 8)).PtInRect28 29 (point))30     {31         StartMainMenu();32     }33 }

30.2  解释:

选项状态 下的鼠标单击事件处理器。

 

31.1  WinLButtonDown函数:

 1 void CMapManager::WinLButtonDown(CPoint point) 2 { 3     /* 继续按钮 */ 4     if (CRect(IndexToPoint(0, 8), IndexToPoint(3, 9)).PtInRect(point)) 5     { 6         StartGame(); 7     } 8     /* 返回按钮 */ 9     else if (CRect(IndexToPoint(5, 8), IndexToPoint(8, 9)).PtInRect10 11 (point))12     {13         StartMainMenu();14     }15 }

31.2  解释:

胜利状态下的单击事件处理器。

 

32.1  defeatLButtonDown函数:

 1 void CMapManager::DefeatLButtonDown(CPoint point) 2 { 3     /* 继续按钮 */ 4     if (CRect(IndexToPoint(0, 8), IndexToPoint(3, 9)).PtInRect(point)) 5     { 6         StartGame(); 7     } 8     /* 返回按钮 */ 9     else if (CRect(IndexToPoint(5, 8), IndexToPoint(8, 9)).PtInRect10 11 (point))12     {13         StartMainMenu();14     }15 }

32.2  解释:

失败状态下的单击事件处理器。

 

33.1  StartGame函数:

 1 void CMapManager::StartGame() 2 { 3     /* 开始游戏 */ 4     if(status != GAME) 5     { 6         if(!BG.LoadBmp("image\\BG.bmp")) 7         { 8             EndGame(); 9         }10         status = GAME;11         MBgTop.Set(&BG, CPoint(0, 0), CSize(64, 64), CPoint(0, 0));12         MBgBelow.Set(&BG, CPoint(0, 0), CSize(64, 64), CPoint(192, 0));13 14         MCursor.SetSrcPos(2, 2);15         MCursor.SetSize(CSize(28, 28));16         LoadMap();17         DrawMap();18     }19     /* 重开游戏 */20     else if(status == GAME)21     {22         LoadMap();23         DrawMap();24     }25 }    

33.2  解释:

跳转至开始游戏(转为游戏状态)。

 

34.1  StartMainMenu函数:

 1 void CMapManager::StartMainMenu() 2 { 3     if(status != MAINMENU) 4     { 5         if(!BG.LoadBmp("image\\Menu3.bmp")) 6         { 7             EndGame(); 8         } 9         View.Copy(BG, CPoint(0, 0), CSize(288, 288), CPoint(0, 0));10         MCursor.Set(&Cursor, CPoint(0, 0), CSize(64, 64), CPoint(64, 11 12 0));13         status = MAINMENU;14     }15 }

34.2  解释:

跳转至开始主界面(转为主界面状态)。

 

35.1  StartOption函数:

 1 void CMapManager::StartOption() 2 { 3     if(status != OPTION) 4     { 5         if(!BG.LoadBmp("image\\Option.bmp")) 6         { 7             EndGame(); 8         } 9         status = OPTION;10         View.Copy(BG, CPoint(0, 0), CSize(288, 288), CPoint(0, 0));    11 12     13         MCursor.SetSrcPos(0, 0);14         MCursor.SetSize(CSize(64, 64));15 16         int index = topPos / 192;17         CPoint LT(3  * GRID_WIDTH, (1 + index / 5) * GRID_HEIGHT);18         MCursor.SetDrawPos(LT + CPoint((index % 5) * GRID_WIDTH, 0));19         MCursor.Draw(View);20 21         index = belowPos / 192;22         LT.y = (4 + index / 5) * GRID_HEIGHT;23         MCursor.SetDrawPos(LT + CPoint((index % 5) * GRID_WIDTH, 0));24         MCursor.Draw(View);25     }26 }

35.2  解释:

跳转至开始选项(转为选项状态)。

 

36.1  StartWin函数:

 1 void CMapManager::StartWin() 2 { 3     if (status != WIN) 4     {     5         if(!BG.LoadBmp("image\\Win.bmp")) 6         { 7             EndGame(); 8         } 9         status = WIN;10         View.Copy(BG, CPoint(0, 0), CSize(288, 288), CPoint(0, 0));11     }12 }

36.2  解释:

跳转至开始胜利(转为胜利状态)。

 

37.1  StartDefeat函数:

 1 void CMapManager::StartDefeat() 2 { 3     if (status != DEFEAT) 4     {     5         if(!BG.LoadBmp("image\\Defeat.bmp")) 6         { 7             EndGame(); 8         } 9         status = DEFEAT;10         View.Copy(BG, CPoint(0, 0), CSize(288, 288), CPoint(0, 0));11     }12 }

37.2  解释:

跳转至开始失败(转为失败状态)。

 

38.1  EndGame函数:

1 void CMapManager::EndGame()2 {3     if (AfxMessageBox("离开游戏吗?", MB_YESNO) == IDYES)4     {5         PostQuitMessage(0);6     }7 }

38.2  解释:

结束游戏。

 

39.1  Return函数:

1 void CMapManager::Return()2 {3     StartMainMenu();4 }

39.2  解释:

返回到主界面。

 

40.1  SetDifficulty函数:

1 void CMapManager::SetDifficulty(int dif)2 {3
     Difficulty = dif;4 }

40.2  解释:

用以设定游戏难度。

 

41.1  GetDifficulty函数:

1 int CMapManager::GetDifficulty()2 {3     return Difficulty;4 }

41.2  解释:

用以取得游戏当前难度。

 

42  备注:

主体代码就这些。其他代码就不注释了。至于完整代码及程序,我之后会找个地方上传试试。

 

 

三、总结

1  参考:

程序的设计参考了多个已完成程序,以及部分程序的代码。其中有啊古的题库,结构、《算法宝典》的位运算、fzhman有关虚函数的博客等等。

 

2  图片:

图片采用的的是战锤40k的传说风格的壁纸以及暗黑三勇天使的壁纸。

 

3  感悟:

题目中虚函数的应用(令我回想起Java。。。)让我有了与虚函数的真正接触。(表示以前只是在一些网站要求上看到过,表示完全没遇到过。)

 

 

四、后续版本:

对之后版本V2.0已经有了一定的想法,不过许多问题还在思考、完善中。

 

1  Studying模式的实现:

其实这个模式主要就是能够实现数独解法的同步展示。同时,我不想将这个做成一个鸡肋的教学展示。但是这个模式一开始就有了一个难点,不是特定解法的算法实现,而是如何确定当前先解哪个点,用哪个解法解。

对于点的确定,我认为应该从信息量的角度来解决。首先任何一个空白点的解决,基本都离不开其所在九宫格及九宫格所在的行、列。那么当这么三者区域内的数字越多,往往信息量越大,与此同时,解出来的可能性就越高。当然,通过对数独游戏的体验以及事后的具体分析后,我发现其实并不是数字越多就一定信息量越大。因为当数字多时,常常会有重复的信息量,导致最终总的信息量下降。所以在这里还需要做一个总信息量计算的算法。但是考虑到计算量的问题,之后需要细致分析是否需要简化操作,或者说就用数字多少来表示信息量。

至于对解法的选择,我认为应当对解法设置优先级。优先级的确立应当以该解法所需要的计算资源、内存资源等等。同时,更需要注意的是游戏者对这个算法的接受程度。因为,有时候,计算机资源消耗低的解法,并不一定就适应人脑的选择。毕竟这款游戏最终的目的是服务游戏者。

点与解法的选择问题,还需要设置一个算法,来决定解法与点的综合优先级。这个算法可以复杂、细致,也可以简单到呈线性,但是必须有。

 

2  界面的修改:

因为上述模式的实现,以及处于美化界面的需要,游戏需要更为宽广的界面。上述模式,经过我和朋友的简单商议后,觉得应当添加一个解释说明的文字区域。与此同时,我们需要通过1-9与A-I两个坐标轴来确定数独内的点。当然,对于解法涉及到的行、列、九宫格等区域,我会考虑用鲜明的颜色将需要的区域标识出来。

 

 

上述就是我对这款游戏的V2.0的希冀所在。

希望,我可以很好的解决掉它。虽然估计需要花费不少的时间。。。。

 

数独GUI程序项目实现