数独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函数:

 BOOL CMapManager::Create(CWnd *wnd, int width, int height)
{
Window = wnd;
MapRect.SetRect(, , width, height); /* 载入必要的图片 */
/* 先不载入底图,因为不同状态的底图不一样 */
if (!View.Create(width, height, )
|| !Number.LoadBmp("image\\Number2.bmp")
|| !Cursor.LoadBmp("image\\Cursor2.bmp"))
//确保图片,光标等的读取没有问题。
{
return FALSE;
}
//任意一个函数返回FALSE,便输出FALSE。
//确保将数字图片载入。 /* 初始化基本数据 */
CursorPos = ;
//光标 topPos = * 192;
belowPos = * 192;
status = -;
difficulty = EASY; /* 开始主菜单 */
StartMainMenu();
return TRUE;
}

2.2  解释:

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

3.1  DrawMap函数:

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

3.2  解释:

用于绘制游戏时的底图。

4.1  DrawCursor函数:

 void CMapManager::DrawCursor()
{
CPoint point(CursorPos.x * GRID_WIDTH, CursorPos.y * GRID_HEIGHT);
if(status == GAME)
{
MCursor.SetDrawPos(point + CPoint(, ));
}
else if(status == MAINMENU)
{
MCursor.SetDrawPos(CPoint( * GRID_WIDTH, point.y));
}
else if(status == OPTION)
{
/* 绘制选定的前景和背景对应的方块上的光标 */
int index = topPos / 192;
CPoint LT( * GRID_WIDTH, ( + index / ) * GRID_HEIGHT);
MCursor.SetDrawPos(LT + CPoint((index % ) * GRID_WIDTH, ));
MCursor.Draw(View); index = belowPos / 192;
LT.y = ( + index / ) * GRID_HEIGHT;
MCursor.SetDrawPos(LT + CPoint((index % ) * GRID_WIDTH, ));
MCursor.Draw(View); MCursor.SetDrawPos(point);
} MCursor.Draw(View);
}

4.2  解释:

用于绘制光标。

5.1  DrawMap函数:

 void CMapManager::DrawMap(int x, int y, BOOL drawNumber /*= TRUE*/)
{
CPoint point(x * GRID_WIDTH, y * GRID_HEIGHT);
//经过这样简单的变换,获得了所需要的指定格的原点
/*根据状态改变*/
if(status == GAME)
{
if (Map[y][x].isOrigin)
{
/* 题目中给定的数字填充背景方块 */
MBgBelow.SetDrawPos(point);
MBgBelow.SetSrcPos(CPoint(belowPos + x % * GRID_WIDTH, y % * GRID_HEIGHT));
MBgBelow.Draw(View);
}
else
{
/* 玩家写上的数字填充前景方块 */
MBgTop.SetDrawPos(point);
MBgTop.SetSrcPos(CPoint(topPos + x % * GRID_WIDTH, y % * GRID_HEIGHT));
MBgTop.Draw(View);
}
if (drawNumber)
{
DrawNumber(x, y);
}
}
else if(status == MAINMENU)
{
View.Copy(BG, point, CSize(128, ), point);
}
else if(status == OPTION)
{
View.Copy(BG, point, CSize(, ), point);
}
}

5.2  解释:

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

6.1  DrawNumber函数:

 void CMapManager::DrawNumber()
{
for (int i=; i<; i++)
{
for (int j=; j<; j++)
{
if(Map[i][j].num != )
{
DrawNumber(j, i);
}
}
}
}

6.2  解释:

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

7.1  DrawNumber函数:

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

7.2  解释:

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

8.1  LoadMap函数:

 void CMapManager::LoadMap()
{
/* 打开题目文件 */
CFile file;
file.Open("MapData.txt", CFile::modeRead); /* 初始化随机种子 */
srand((unsigned)time()); /* 根据难度来偏移 */
/* 81代表一题的81个数,8代表每一题前的号码"#000# "加上一回车换行符共8 个字符 */
if (difficulty == MIDDLE)
{
/* 中等难度偏移EASY个 */
file.Seek(sizeof(char) * EASY * ( + ), CFile::current);
}
else if (difficulty == HARD)
{
/* 困难难度偏移(EASY + MIDDLE)个 */
file.Seek(sizeof(char) * (EASY + MIDDLE) * ( + ), CFile::current);
} /* 随机得到题号(这是在偏移基础上的题号) */
int No = rand() % difficulty;
/* 根据题号偏移 */
/* 6代表题目前的号码"#000# "6个字符 */
file.Seek(sizeof(char) * No * ( + ) + , CFile::current); /* 读入题目 */
char txt[];
file.Read(&txt, sizeof(char) * ); /* 根据题目初始化Map */
for (int i=; i<; i++)
{
for (int j=; j<; j++)
{
Map[i][j].num = txt[i * + j] - '';
Map[i][j].isOrigin = (Map[i][j].num == ? FALSE : TRUE);
}
} /* 关闭文件 */
file.Close();
}

8.2  解释:

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

9.1  InitMap函数:

 void CMapManager::InitMap()
{
for (int i=; i<; i++)
{
for (int j=; j<; j++)
{
if(Map[i][j].isOrigin == TRUE)
{
MBgBelow.SetDrawPos(IndexToPoint(j, i));
MBgBelow.SetSrcPos(CPoint(belowPos + j % * GRID_WIDTH,
i % * GRID_HEIGHT));
MBgBelow.Draw(View);
}
else
{
MBgTop.SetDrawPos(IndexToPoint(j, i));
MBgTop.SetSrcPos(CPoint(topPos + j % * GRID_WIDTH,
i % * GRID_HEIGHT));
MBgTop.Draw(View);
}
}
}
}

9.2  解释:

根据题目组装整个底图。

10.1  CheckFinish函数:

 BOOL CMapManager::CheckFinish()
{
for (int i=; i<; i++)
{
for (int j=; j<; j++)
{
if (Map[i][j].num == )
{
return FALSE;
}
}
}
return TRUE;
}

10.2  解释:

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

11.1  CheckRow函数:

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

11.2  解释:

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

12.1  CheckCol函数:

 BOOL CMapManager::CheckCol(int col)
{
DWORD check = 0x01FF;
DWORD tmp = ;
for (int i=; i<; i++)
{
tmp = << (Map[i][col].num - );
check &= ~tmp;
}
return check;
}

12.2  解释:

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

13.1  CheckGrid函数:

 BOOL CMapManager::CheckGrid(int grid)
{
DWORD check = 0x01FF;
DWORD tmp = ;
int top = grid / * ;
int left = (grid % ) * ;
for (int i=top; i<top+; i++)
{
for (int j=left; j<left+; j++)
{
tmp = << (Map[i][j].num - );
check &= ~tmp;
}
}
return check;
}

13.2  解释:

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

14.1  CheckSuccess函数:

 BOOL CMapManager::CheckSuccess()
{
int i;
for (i=; i<; i++)
{
if(CheckRow(i) != || CheckCol(i) != || CheckGrid(i) != )
{
return FALSE;
}
}
return TRUE;
}

14.2  解释:

检查答案是否正确。

15.1  Show函数:

 void CMapManager::Show(CDC* dc)
{
/* 以下的绘制函数不会重绘整个底图 */
/* 只绘制需重绘的地方 */
switch(status)
{
case MAINMENU:
ShowMainMenu();
break;
case GAME:
ShowGame();
break;
case OPTION:
ShowOption();
break;
default:
break;
}
if (View.IsOK())
{
View.Draw(*dc, , , View.Width(), View.Height());
}
}

15.2  解释:

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

16.1  ShowMainMenu函数:

 void CMapManager::ShowMainMenu()
{
CRect ButtonRect;
ButtonRect.SetRect(IndexToPoint(, ), IndexToPoint(, ));
if (ButtonRect.PtInRect(IndexToPoint(CursorPos)))
{
DrawCursor();
}
}

16.2  解释:

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

17.1  ShowGame函数:

 void CMapManager::ShowGame()
{
if(MapRect.PtInRect(IndexToPoint(CursorPos)))
{
DrawMap(CursorPos.x, CursorPos.y, FALSE);
DrawNumber(CursorPos.x, CursorPos.y);
DrawCursor();
}
}

17.2  解释:

游戏状态的显示函数。

18.1  ShowOption函数:

 void CMapManager::ShowOption()
{
CRect optionRect1;
CRect optionRect2;
optionRect1.SetRect(IndexToPoint(, ), IndexToPoint(, ));
optionRect2.SetRect(IndexToPoint(, ), IndexToPoint(, ));
if (optionRect1.PtInRect(IndexToPoint(CursorPos)) ||
optionRect2.PtInRect(IndexToPoint(CursorPos)))
{
DrawCursor();
}
}

18.2  解释:

选项状态的显示函数。

19.1  Process函数:

 void CMapManager::Process(CALLBACKTYPE type, void *data)
{
switch(status)
{
case MAINMENU:
ProcessMenu(type, data);
break;
case GAME:
ProcessGame(type, data);
break;
case OPTION:
ProcessOption(type, data);
break;
case WIN:
ProcessWin(type, data);
break;
default:
break;
}
InvalidateRect(Window->m_hWnd, MapRect, FALSE);
}

19.2  解释:

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

20.1  ProcessGame函数:

 void CMapManager::ProcessGame(CALLBACKTYPE type, void *data)
{
switch(type)
{
case ONMOUSEMOVE:
GameMouseMove(*(CPoint*)data);
break;
case ONCHAR:
GameChar(*(UINT*)data);
break;
default:
break;
}
}

20.2  解释:

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

21.1  ProcessMenu函数:

 void CMapManager::ProcessMenu(CALLBACKTYPE type, void *data)
{
switch(type)
{
case ONLBUTTONDOWN:
MenuLButtonDown(*(CPoint*)data);
break;
case ONMOUSEMOVE:
MenuMouseMove(*(CPoint*)data);
break;
case ONLBUTTONUP:
MenuLButtonUp(*(CPoint*)data);
break;
default:
break;
}
}

21.2  解释:

主界面的事件处理器。

22.1  ProcessOption函数:

 void CMapManager::ProcessOption(CALLBACKTYPE type, void *data)
{
switch(type)
{
case ONMOUSEMOVE:
OptionMouseMove(*(CPoint*)data);
break;
case ONLBUTTONDOWN:
OptionLButtonDown(*(CPoint*)data);
break;
default:
break;
}
}

22.2  解释:

选项界面的事件处理器。

23.1  ProcessWin函数:

 void CMapManager::ProcessWin(CALLBACKTYPE type, void *data)
{
switch(type)
{
case ONLBUTTONDOWN:
WinLButtonDown(*(CPoint*)data);
break;
default:
break;
}
}

23.2  解释:

胜利状态的事件处理器。

24.1  GameMouseMove函数:

 void CMapManager::GameMouseMove(CPoint point)
{
if(MapRect.PtInRect(point))
{
CPoint Pos = PointToIndex(point);
if (CursorPos != Pos)
{
DrawMap(CursorPos.x, CursorPos.y);
CursorPos = Pos;
}
}
}

24.2  解释:

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

25.1  GameChar函数:

 void CMapManager::GameChar(UINT nChar)
{
int x = CursorPos.x;
int y = CursorPos.y; /* 不能修改题目给定的数字 */
if (Map[y][x].isOrigin)
{
return;
} switch(nChar)
{
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
case '':
Map[y][x].num = ;
break;
/* ESC键返回主界面 */
case VK_ESCAPE:
Return();
break;
default:
break;
} if (CheckFinish())
{
if(CheckSuccess())
{
StartWin();
//AfxMessageBox("You are good!");
}
else
{
StartDefeat();
//AfxMessageBox("You are wrong!");
}
}
}

25.2  解释:

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

26.1  MenuLButtonDown函数:

 void CMapManager::MenuLButtonDown(CPoint point)
{
CRect ButtonRect;
ButtonRect.SetRect(IndexToPoint(, ), IndexToPoint(, ));
if (ButtonRect.PtInRect(point))
{
/* 这里本来要绘制鼠标按下效果,但程序速度太快,根本显示不了 */
DrawMap(, CursorPos.y);
MCursor.SetSrcPos(, );
MCursor.Draw(View);
InvalidateRect(Window->m_hWnd, MapRect, FALSE); /* 开始游戏按钮 */
if ((CRect(IndexToPoint(, ), IndexToPoint(, ))).PtInRect (point))
{
StartGame();
} /* 选项按钮 */
if ((CRect(IndexToPoint(, ), IndexToPoint(, ))).PtInRect (point))
{
StartOption();
} /* 结束游戏按钮 */
if ((CRect(IndexToPoint(, ), IndexToPoint(, ))).PtInRect (point))
{
EndGame();
MenuLButtonUp(point);
}
}
}

26.2  解释:

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

27.1  MenuMouseMove函数:

 void CMapManager::MenuMouseMove(CPoint point)
{
CRect ButtonRect;
ButtonRect.SetRect(IndexToPoint(, ), IndexToPoint(, ));
if (ButtonRect.PtInRect(point))
{
DrawMap(, CursorPos.y);
CursorPos = PointToIndex(point);
}
}

27.2  解释:

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

28.1  MenuLButtonUp函数:

 void CMapManager::MenuLButtonUp(CPoint point)
{
CRect ButtonRect;
ButtonRect.SetRect(IndexToPoint(, ), IndexToPoint(, ));
if (ButtonRect.PtInRect(point))
{
DrawMap(, CursorPos.y);
MCursor.SetSrcPos(, );
         MCursor.Draw(View);
}
}

28.2  解释:

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

29.1  OptionMouseMove函数:

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

29.2  解释:

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

30.1  OptionLButtonDown函数:

 void CMapManager::OptionLButtonDown(CPoint point)
{
int index;
/* 前景方块 */
if (CRect(IndexToPoint(, ), IndexToPoint(, )).PtInRect(point))
{
index = topPos / ;
DrawMap( + index % , ( + index / )); topPos = ((point.y / GRID_HEIGHT - ) * + (point.x / GRID_WIDTH - )) * ;
}
/* 背景方块 */
else if (CRect(IndexToPoint(, ), IndexToPoint(, )).PtInRect (point))
{
index = belowPos / ;
DrawMap( + index % , ( + index / )); belowPos = ((point.y / GRID_HEIGHT - ) * + (point.x / GRID_WIDTH - )) * ;
}
/* 返回按钮 */
else if (CRect(IndexToPoint(, ), IndexToPoint(, )).PtInRect (point))
{
StartMainMenu();
}
}

30.2  解释:

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

31.1  WinLButtonDown函数:

 void CMapManager::WinLButtonDown(CPoint point)
{
/* 继续按钮 */
if (CRect(IndexToPoint(, ), IndexToPoint(, )).PtInRect(point))
{
StartGame();
}
/* 返回按钮 */
else if (CRect(IndexToPoint(, ), IndexToPoint(, )).PtInRect (point))
{
StartMainMenu();
}
}

31.2  解释:

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

32.1  defeatLButtonDown函数:

 void CMapManager::DefeatLButtonDown(CPoint point)
{
/* 继续按钮 */
if (CRect(IndexToPoint(, ), IndexToPoint(, )).PtInRect(point))
{
StartGame();
}
/* 返回按钮 */
else if (CRect(IndexToPoint(, ), IndexToPoint(, )).PtInRect (point))
{
StartMainMenu();
}
}

32.2  解释:

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

33.1  StartGame函数:

 void CMapManager::StartGame()
{
/* 开始游戏 */
if(status != GAME)
{
if(!BG.LoadBmp("image\\BG.bmp"))
{
EndGame();
}
status = GAME;
MBgTop.Set(&BG, CPoint(, ), CSize(, ), CPoint(, ));
MBgBelow.Set(&BG, CPoint(, ), CSize(, ), CPoint(, )); MCursor.SetSrcPos(, );
MCursor.SetSize(CSize(, ));
LoadMap();
DrawMap();
}
/* 重开游戏 */
else if(status == GAME)
{
LoadMap();
DrawMap();
}
}

33.2  解释:

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

34.1  StartMainMenu函数:

 void CMapManager::StartMainMenu()
{
if(status != MAINMENU)
{
if(!BG.LoadBmp("image\\Menu3.bmp"))
{
EndGame();
}
View.Copy(BG, CPoint(, ), CSize(, ), CPoint(, ));
MCursor.Set(&Cursor, CPoint(, ), CSize(, ), CPoint(, ));
status = MAINMENU;
}
}

34.2  解释:

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

35.1  StartOption函数:

 void CMapManager::StartOption()
{
if(status != OPTION)
{
if(!BG.LoadBmp("image\\Option.bmp"))
{
EndGame();
}
status = OPTION;
View.Copy(BG, CPoint(, ), CSize(, ), CPoint(, )); MCursor.SetSrcPos(, );
MCursor.SetSize(CSize(, )); int index = topPos / ;
CPoint LT( * GRID_WIDTH, ( + index / ) * GRID_HEIGHT);
MCursor.SetDrawPos(LT + CPoint((index % ) * GRID_WIDTH, ));
MCursor.Draw(View); index = belowPos / ;
LT.y = ( + index / ) * GRID_HEIGHT;
MCursor.SetDrawPos(LT + CPoint((index % ) * GRID_WIDTH, ));
MCursor.Draw(View);
}
}

35.2  解释:

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

36.1  StartWin函数:

 void CMapManager::StartWin()
{
if (status != WIN)
{
if(!BG.LoadBmp("image\\Win.bmp"))
{
EndGame();
}
status = WIN;
View.Copy(BG, CPoint(, ), CSize(, ), CPoint(, ));
}
}

36.2  解释:

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

37.1  StartDefeat函数:

 void CMapManager::StartDefeat()
{
if (status != DEFEAT)
{
if(!BG.LoadBmp("image\\Defeat.bmp"))
{
EndGame();
}
status = DEFEAT;
View.Copy(BG, CPoint(, ), CSize(, ), CPoint(, ));
}
}

37.2  解释:

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

38.1  EndGame函数:

 void CMapManager::EndGame()
{
if (AfxMessageBox("离开游戏吗?", MB_YESNO) == IDYES)
{
PostQuitMessage();
}
}

38.2  解释:

结束游戏。

39.1  Return函数:

 void CMapManager::Return()
{
StartMainMenu();
}

39.2  解释:

返回到主界面。

40.1  SetDifficulty函数:

 void CMapManager::SetDifficulty(int dif)
{
     Difficulty = dif;
}

40.2  解释:

用以设定游戏难度。

41.1  GetDifficulty函数:

 int CMapManager::GetDifficulty()
{
return Difficulty;
}

41.2  解释:

用以取得游戏当前难度。

42  备注:

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

三、总结

1  参考:

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

2  图片:

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

3  感悟:

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

四、后续版本:

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

1  Studying模式的实现:

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

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

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

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

2  界面的修改:

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

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

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

数独GUI程序项目实现的更多相关文章

  1. 客户端GUI程序开发漫谈

    这篇文章包含了这个领域的很多开源项目的介绍,还有我多年来的心血和汗水  去年夏天的时候,我用QT做了一个小工具 后来还用QT做了流程设计器 我把程序分享给飞扬青云之后,他甚至搞出来一套QT的皮肤来 说 ...

  2. 使用PyQt来编写第一个Python GUI程序

    原文:使用PyQt来编写第一个Python GUI程序 本文由 伯乐在线 - Lane 翻译,Daetalus 校稿.未经许可,禁止转载!英文出处:pythonforengineers.com.欢迎加 ...

  3. C++ gui程序附加dos输出窗口

    C++ gui程序附加console qtcreator 1:在.pro文件中加入一句: CONFIG+= console 2:在运行设置里勾选在终端运行的选项 vs 1.新建gui项目 2.连接器( ...

  4. QT +go 开发 GUI程序

      ,转载 https://blog.csdn.net/lanbery/article/details/81745611 如果你是一个墨守成规的coding,请移步其他内容,这部分内容可能不适合你.如 ...

  5. 用 PHP-GTK2 做 Win32 GUI 程序

    PHP通常是做为服务器端脚本执行,如果告诉你PHP可以编写普通的GUI程序,你应该很感兴趣.下面介绍的PHP-GTK就是PHP的GUI扩展.GTK是一个业界标准的图形库,具有良好的移植性.如果你用过l ...

  6. 在Docker for Windows中运行GUI程序

    Docker运行GUI原理 Docker目前大多应用在服务器领域,那么在Docker中可以运行GUI程序吗?怀着好奇心google了一番,还真有人写了一篇文章 running-gui-applicat ...

  7. 在OSX和Windows版本Docker上运行GUI程序

    看到很多人在Docker问题区讨论:如何在OS X和Windows的Docker上运行GUI程序, 随手记录几个参考资料: https://github.com/docker/docker/issue ...

  8. Python In Action:二、 最小的GUI程序:麻雀虽小,五脏俱全

    Python in Action第二个例子,倒是很简单,却是最基本的GUI程序框架,里面有最基本的实现GUI流程 import wx class MyApp(wx.App): def OnInit(s ...

  9. Web应用程序项目XX已配置为使用IIS

    今天在看开源项目Umbraco是,出现一个项目加载不了,并报如下错误: Web应用程序项目Umbraco.Cms.Web.UI已配置为使用IIS.若要访问本地IIS网站,必须安装下列IIS组件..,如 ...

随机推荐

  1. System.out.println()的解释

    上周面试的时候,面试官让我解释一下输出语句每一个单词是什么意思.当是有点蒙,后来想想这不就是考察对java中常用的的包,类和其中的方法的熟悉程度吗? 我们首先来看看System:这是一个类名,存在于j ...

  2. [UWP]了解模板化控件(10):原则与技巧

    1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...

  3. eclipse ctrl shift t 失效的恢复方法

    Window-->Perspective-->Customize Perspective 在弹出框选择: Action Set Avaliability ---将最右边的java Navi ...

  4. 个推demo

    官网文档更详细,这里是只做个测试 http://docs.getui.com/server/java/start/ 全部推送(针对app应用) public static final String a ...

  5. Influxdb1.2.2安装

    一.文件准备 1.1 文件名称 influxdb-1.2.2.x86_64.rpm 1.2 下载地址 https://portal.influxdata.com/downloads [注意.注意.注意 ...

  6. Lua中使用状态机FSM简单例子

    FSM 有限状态机: 一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生.一个有 ...

  7. IEEE1588协议简介

    IEEE1588协议,又称PTP(precise time protocol,精确时间协议),可以达到亚微秒级别时间同步精度,于2002年发布version1,2008年发布version2. IEE ...

  8. Android 环境搭建、基础窗口window/Mac

    1.五步搞定Android开发环境部署--非常详细的Android开发环境搭建教程 2.Android开发学习之路--MAC下Android Studio开发环境搭建 4.Android常用开发工具以 ...

  9. xml文件生成方式一(字符串拼接,将多实体类对象写入xml文件)

    1.xml文件生成,拼接字符串使用StringBuffer或StringBuilder 2.拼接好后写入文件即可,将多个实体类写入xml文件 3.这种方式比较简单,但是操作也比较麻烦 4.下面是我的代 ...

  10. ArcGIS API for JavaScript 4.2学习笔记[23] 没有地图如何进行查询?【FindTask类的使用】

    从第一篇到现在都是基于地图的,不管怎么样,不管是2D还是3D,至少有个图. 这次来个没有图的例子,看看纯文字的空间查询是什么样的. 本例适用于后台查询或低性能电脑的查询. 预览图 由于4.3和4.2的 ...