数独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. EJB:快速入门

    1.EJB概念 2.EJB体系结构 3.SessionBean 3.1 SessionBean 服务端组件 3.2 Remote 与 Local 模式 3.3 Client访问处理流程 3.3.1 R ...

  2. [原创]mysql的zip包如何在windows下安装

    今天在尝试zipkin的链路数据写入mysql,本机恰好没有按照mysql.找到一个很久前谁发的mysql-5.6.19-winx64.zip,版本不新?别挑剔啦,只是本机测试,能用就好哈哈.. 解压 ...

  3. gulp实现公共html代码复用

    在开发网站的时候,尤其是类似于官网这样的项目,顶部都会有一个导航栏,底部会有一些其他信息,而这两个部分在每一个页面都是有的.我们不可能在每个html页面都写一遍,这样也不便后期维护等操作,所以可以把顶 ...

  4. iOS手势之pinch

    今天用地图的时候有用到pinch 捏合手势 通过捏合手势动作可以很轻松的来改变视图元素的一个比例 手势的动作状态有如下三种,一般是按照顺序来进行转换的. 1. UIGestureRecognizerS ...

  5. python爬虫入门学习

    近期写的一个爬虫的Demo,只是简单的用了几个函数.实现了简单的爬取网页的功能(以途牛为例). import urllib2 import re import urlparse import robo ...

  6. iOS OC Swift3.0 TableView 中tableviewcell的线左边不到边界

    Swift 3.0 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt index ...

  7. 查看windows、linux的SN

     gwmi win32_bios    [root@live-al-ops-pxe-2 ~]# dmidecode | grep Number | sed -n '1p' Serial Number: ...

  8. Error Running Git Empty git --version output:IDEA关联GitHub时出现这个错误

    刚刚学习使用idea中,想要把自己的项目上传到github,遇到这样一个问题,先记录下来,到时候解决了在把方法贴出来. ---------------------------------------- ...

  9. javascript + sql编写SQL客户端工具tabris

    祝大家2018新年快乐, 前不久发现了一个创意的脚本JtSQL(java编写) 开源地址为:https://github.com/noear/JtSQL JtSQL 特点:*.结合了JS.SQL.模板 ...

  10. bzoj 3999: [TJOI2015]旅游

    Description 为了提高智商,ZJY准备去往一个新世界去旅游.这个世界的城市布局像一棵树.每两座城市之间只有一条路径可 以互达.每座城市都有一种宝石,有一定的价格.ZJY为了赚取最高利益,她会 ...