• 数独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         else
    18         {
    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# "加上一回车换行符共8
    12 
    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             else
    17             {
    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 ONMOUSEMOVE:
     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 ONMOUSEMOVE:
     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 ONMOUSEMOVE:
     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         else
    60         {
    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))).PtInRect
    15 
    16 (point))
    17         {    
    18              StartGame();
    19         }
    20 
    21         /* 选项按钮 */
    22         if ((CRect(IndexToPoint(5, 5), IndexToPoint(7, 6))).PtInRect
    23 
    24 (point))
    25         {
    26             StartOption();
    27         }
    28 
    29         /* 结束游戏按钮 */
    30         if ((CRect(IndexToPoint(5, 6), IndexToPoint(7, 7))).PtInRect
    31 
    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)).PtInRect
    16 
    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)).PtInRect
    28 
    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)).PtInRect
    10 
    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)).PtInRect
    10 
    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的希冀所在。

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

     

  • 相关阅读:
    VS2008 插件开发.
    防刷新.
    JavaScript 的数据类型
    SQL Redist content: Command line option syntax error. Type Command /? for Help. 错误!!!
    使用Yahoo.com.cn的POP和SMTP
    文件上传
    待解决的问题
    [转]不常见但有用的HTML下拉选单
    新工具 BuildSql 生成数据库文档 .
    重写 DropDownList !
  • 原文地址:https://www.cnblogs.com/Tiancheng-Duan/p/7011347.html
Copyright © 2020-2023  润新知