二、设计与实现
1、设计思想
建立一个有序二叉树作为决策树,用来作为专家系统的知识库。决策树中的叶子结点存储各种动物的名称,其他节点存储有关动物特征的问题。从根节点开始,访问节点的内容。如果是节点内容是问题,由用户回答“是”、“否”。若回答“是”,访问左孩子结点,若回答否,访问右孩子节点,直到节点的内容为动物名称为止。此时,程序给出猜测结果。如果猜对,专家胜利。如果猜错,用户给出动物名称和特征,程序根据两者更改决策树、添加新的节点。
2、类结构
3、主要数据结构
主要的数据结构是链栈和二叉树。
二叉树,用来作为决策树,叶节点存储动物名称,其他节点存储有关动物特征的问题。同时对于每个问题节点,回答“是”对应的是左孩子节点,回答“否”对应的右孩子节点。
链栈在从文件中读取决策树中起作用。由于在文件中存储的决策树是按照后序遍历的方式存入的。在读取决策树的时候,也按照后序的次序建立相应的节点。栈中存储的是即将建立的双亲结点的孩子节点。也就是在每一轮中,首先被读入的是两个孩子节点,被存入到栈中,当读到它们的双亲结点时,两个孩子节点出栈,作为双亲结点的左右孩子后,双亲结点入栈。
4、算法设计
5、核心代码展示
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// To paint with a DDB it first needs to be associated
// with a memory device context. We make a DC that
// is compatible with the screen by passing NULL to
// CreateCompatibleDC.
// Then we need to associate our saved bitmap with the
// device context.
HDC hdcMem = CreateCompatibleDC(NULL);
HBITMAP hbmT = SelectBitmap(hdcMem, hbm);
// Now, the BitBlt function is used to transfer the contents of the
// drawing surface from one DC to another. Before we can paint the
// bitmap however we need to know how big it is. We call the GDI
// function GetObject to get the relevent details.
BITMAP bm;
GetObject(hbm, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
// Now, clean up. A memory DC always has a drawing
// surface in it. It is created with a 1X1 monochrome
// bitmap that we saved earlier, and need to put back
// before we destroy it.
SelectBitmap(hdcMem, hbmT);
DeleteDC(hdcMem);
// EndPaint balances off the BeginPaint call.
EndPaint(hWnd, &ps);
break;
}
case WM_CREATE:
{ static2 = CreateWindow(TEXT("STATIC"), TEXT("欢迎来到专家系统应用"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 10, 500, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
button5 = CreateWindow(TEXT("BUTTON"), TEXT("进入专家系统应用"), WS_CHILD | WS_VISIBLE, 360, 100, 150, 40, hWnd, (HMENU)IDC_BUTTON5, NULL, NULL);
SendMessage(button5, WM_SETFONT, (WPARAM)hFont, 1);
HINSTANCE hInstance = GetWindowInstance(hWnd);
//hbm = LoadBitmapW(hInstance,MAKEINTRESOURCE(IDB_BITMAP1));//C:\\Users\\Administrator.33XBYKMCA2C5EX4\Desktop\SpecPic
hbm = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
return 0;
}
case WM_COMMAND:
{
HWND hwndTmp;
int wmId = LOWORD(wParam);
switch (wmId)
{
case ID_40003:
{
MessageBox(hWnd, TEXT("开发人员名单:武祎、王艺然、何鸿丞、辛喆\n开发时间:2020年6月\n版本号:8.0\n学校:北京理工大学"), TEXT("开发者信息"), MB_OK);
break;
}
case ID_40001:
{
MessageBox(hWnd, in_address, TEXT("当前决策树的文件路径"), MB_OK);
break;
}
case ID_40004:
{
AnimalNumber();
MessageBox(hWnd, t_num,TEXT ("决策树中的动物数量"), MB_OK);
break;
}
case ID_40005:
{
if (IsTreeEmpty())
{
MessageBox(hWnd, TEXT("当前决策树为空!"), TEXT("提示"), MB_ICONWARNING);
}
else
{
TCHAR2Char(in_address, a);
Save(hWnd);
}
break;
}
case ID_40006:
{
TCHAR2Char(in_address, a);
Build(hWnd);
break;
}
case IDC_BUTTON3:
GetDlgItemText(hWnd, IDC_EDIT1, address, 100);
TCHAR2Char(address, a);
if (Build(hWnd))
{
if (flag == 0)
{
static3 = CreateWindow(TEXT("STATIC"), TEXT("后序遍历输出的语句"), WS_CHILD | WS_VISIBLE|WS_EX_TRANSPARENT, 10, 200, 200, 20, hWnd, (HMENU)IDC_STATIC3, NULL, NULL);
SendMessage(static3, WM_SETFONT, (WPARAM)hFont, 1);
edit2=CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE|WS_HSCROLL|WS_VSCROLL , 10, 220, 300, 200, hWnd, (HMENU)IDC_EDIT2, NULL, NULL);
SendMessage(edit2, WM_SETFONT, (WPARAM)hFont, 1);
flag++;
}
GetDlgItemText(hWnd, IDC_EDIT1, in_address, 100);
Display(hWnd);
}
break;
case IDC_BUTTON4:
{
if (Expert(-1, hWnd))
{
}
else
{
ClearAllWindows();
static1 = CreateWindow(TEXT("STATIC"), TEXT("猜动物"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 10, 50, 20, hWnd, (HMENU)IDC_STATIC1, NULL, NULL);
SendMessage(static1, WM_SETFONT, (WPARAM)hFont, 1);
static2 = CreateWindow(TEXT("STATIC"), question, WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 50, 500, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
button5 = CreateWindow(TEXT("BUTTON"), TEXT("结束本轮游戏"), WS_CHILD | WS_VISIBLE, 360, 300, 120, 30, hWnd, (HMENU)IDC_BUTTON5, NULL, NULL);
SendMessage(button5, WM_SETFONT, (WPARAM)hFont, 1);
button8 = CreateWindow(TEXT("BUTTON"), TEXT("是"), WS_CHILD | WS_VISIBLE, 10, 100, 60, 30, hWnd, (HMENU)IDC_BUTTON8, NULL, NULL);
SendMessage(button8, WM_SETFONT, (WPARAM)hFont, 1);
button11 = CreateWindow(TEXT("BUTTON"), TEXT("否"), WS_CHILD | WS_VISIBLE, 100, 100, 60, 30, hWnd, (HMENU)IDC_BUTTON11, NULL, NULL);
SendMessage(button11, WM_SETFONT, (WPARAM)hFont, 1);
}
break;
}
case IDC_BUTTON8:
{
if (!Expert(1, hWnd))
{
DestroyWindow(static2);
static2 = NULL;
static2 = CreateWindow(TEXT("STATIC"), question, WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 50, 500, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
}
else
{
DestroyWindow(static2);
static2 = NULL;
static2 = CreateWindow(TEXT("STATIC"), question, WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 50, 500, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
DestroyWindow(button8);
button8 = NULL;
DestroyWindow(button11);
button11 = NULL;
button12 = CreateWindow(TEXT("BUTTON"), TEXT("猜对了"), WS_CHILD | WS_VISIBLE, 10, 250, 60, 30, hWnd, (HMENU)IDC_BUTTON12, NULL, NULL);
SendMessage(button12, WM_SETFONT, (WPARAM)hFont, 1);
button13 = CreateWindow(TEXT("BUTTON"), TEXT("猜错了"), WS_CHILD | WS_VISIBLE, 100, 250, 60, 30, hWnd, (HMENU)IDC_BUTTON13, NULL, NULL);
SendMessage(button13, WM_SETFONT, (WPARAM)hFont, 1);
}
break;
}
case IDC_BUTTON11:
{
if (!Expert(0, hWnd))
{
DestroyWindow(static2);
static2 = NULL;
static2 = CreateWindow(TEXT("STATIC"), question, WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 50, 500, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
}
else
{
DestroyWindow(static2);
static2 = NULL;
static2 = CreateWindow(TEXT("STATIC"), question, WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 50, 500, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
DestroyWindow(button8);
button8 = NULL;
DestroyWindow(button11);
button11 = NULL;
button12 = CreateWindow(TEXT("BUTTON"), TEXT("猜对了"), WS_CHILD | WS_VISIBLE, 10, 250, 60, 30, hWnd, (HMENU)IDC_BUTTON12, NULL, NULL);
SendMessage(button12, WM_SETFONT, (WPARAM)hFont, 1);
button13 = CreateWindow(TEXT("BUTTON"), TEXT("猜错了"), WS_CHILD | WS_VISIBLE, 100, 250, 60, 30, hWnd, (HMENU)IDC_BUTTON13, NULL, NULL);
SendMessage(button13, WM_SETFONT, (WPARAM)hFont, 1);
}
break;
}
case IDC_BUTTON5:
{
ClearAllWindows();
static2 = CreateWindow(TEXT("STATIC"), TEXT("菜单"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 10, 50, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
button4 = CreateWindow(TEXT("BUTTON"), TEXT("进入猜动物环节"), WS_CHILD | WS_VISIBLE, 360, 200, 150, 40, hWnd, (HMENU)IDC_BUTTON4, NULL, NULL);
SendMessage(button4, WM_SETFONT, (WPARAM)hFont, 1);
button6 = CreateWindow(TEXT("BUTTON"), TEXT("将决策树写入文件"), WS_CHILD | WS_VISIBLE, 360, 300, 150, 40, hWnd, (HMENU)IDC_BUTTON6, NULL, NULL);
SendMessage(button6, WM_SETFONT, (WPARAM)hFont, 1);
button7 = CreateWindow(TEXT("BUTTON"), TEXT("从文件中读入决策树"), WS_CHILD | WS_VISIBLE, 360, 100, 150, 40, hWnd, (HMENU)IDC_BUTTON7, NULL, NULL);
SendMessage(button7, WM_SETFONT, (WPARAM)hFont, 1);
button10 = CreateWindow(TEXT("BUTTON"), TEXT("退出系统"), WS_CHILD | WS_VISIBLE, 360, 400, 150, 40, hWnd, (HMENU)IDC_BUTTON10, NULL, NULL);
SendMessage(button10, WM_SETFONT, (WPARAM)hFont, 1);
break;
}
case IDC_BUTTON6:
{
if (IsTreeEmpty())
{
MessageBox(hWnd, TEXT("您的决策树为空树!"), TEXT("提示"), MB_ICONWARNING);
break;
}
ClearAllWindows();
static1 = CreateWindow(TEXT("STATIC"), TEXT("将决策树写入文件"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 10, 140, 20, hWnd, (HMENU)IDC_STATIC1, NULL, NULL);
SendMessage(static1, WM_SETFONT, (WPARAM)hFont, 1);
static2 = CreateWindow(TEXT("STATIC"), TEXT("请输入txt文件的地址:"), WS_CHILD | WS_VISIBLE, 10, 100, 160, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
button9 = CreateWindow(TEXT("BUTTON"), TEXT("确定"), WS_CHILD | WS_VISIBLE, 320, 140, 80, 30, hWnd, (HMENU)IDC_BUTTON9, NULL, NULL);
SendMessage(button9, WM_SETFONT, (WPARAM)hFont, 1);
button5 = CreateWindow(TEXT("BUTTON"), TEXT("返回"), WS_CHILD | WS_VISIBLE, 320, 200, 80, 30, hWnd, (HMENU)IDC_BUTTON5, NULL, NULL);
SendMessage(button5, WM_SETFONT, (WPARAM)hFont, 1);
flag = 0;
edit1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER|ES_MULTILINE|WS_HSCROLL, 10, 140, 300, 50, hWnd, (HMENU)IDC_EDIT1, NULL, NULL);
SendMessage(edit1, WM_SETFONT, (WPARAM)hFont, 1);
break;
}
case IDC_BUTTON9:
{
GetDlgItemText(hWnd, IDC_EDIT1, address, 100);
TCHAR2Char(address, a);
Save(hWnd);
break;
}
case IDC_BUTTON7:
{
ClearAllWindows();
static1 = CreateWindow(TEXT("STATIC"), TEXT("读取文件建立决策树"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 10, 140, 20, hWnd, (HMENU)IDC_STATIC1, NULL, NULL);
SendMessage(static1, WM_SETFONT, (WPARAM)hFont, 1);
static2 = CreateWindow(TEXT("STATIC"), TEXT("请输入txt文件的地址:"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 100, 160, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
button3 = CreateWindow(TEXT("BUTTON"), TEXT("确定"), WS_CHILD | WS_VISIBLE, 320, 140, 80, 30, hWnd, (HMENU)IDC_BUTTON3, NULL, NULL);
SendMessage(button3, WM_SETFONT, (WPARAM)hFont, 1);
flag = 0;
edit1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER| WS_HSCROLL | ES_MULTILINE, 10, 140, 300, 50, hWnd, (HMENU)IDC_EDIT1, NULL, NULL);
SendMessage(edit1, WM_SETFONT, (WPARAM)hFont, 1);
button5 = CreateWindow(TEXT("BUTTON"), TEXT("返回"), WS_CHILD | WS_VISIBLE, 320, 200, 80, 30, hWnd, (HMENU)IDC_BUTTON5, NULL, NULL);
SendMessage(button5, WM_SETFONT, (WPARAM)hFont, 1);
break;
}
case IDC_BUTTON12:
{
MessageBox(hWnd, TEXT("你竟然被一台电脑打败了,,,"), TEXT("提示"), MB_OK);
ClearAllWindows();
button4 = CreateWindow(TEXT("BUTTON"), TEXT("再玩一次"), WS_CHILD | WS_VISIBLE, 420, 200, 150, 40, hWnd, (HMENU)IDC_BUTTON4, NULL, NULL);
SendMessage(button4, WM_SETFONT, (WPARAM)hFont, 1);
button5 = CreateWindow(TEXT("BUTTON"), TEXT("返回"), WS_CHILD | WS_VISIBLE, 160, 200, 150, 40, hWnd, (HMENU)IDC_BUTTON5, NULL, NULL);
SendMessage(button5, WM_SETFONT, (WPARAM)hFont, 1);
break;
}
case IDC_BUTTON13:
{
MessageBox(hWnd, TEXT("专家猜错,请告诉专家动物信息"), TEXT("提示"), MB_OK);
ClearAllWindows();
static1 = CreateWindow(TEXT("STATIC"), TEXT("动物名称"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 110, 150, 20, hWnd, (HMENU)IDC_STATIC1, NULL, NULL);
SendMessage(static1, WM_SETFONT, (WPARAM)hFont, 1);
static2 = CreateWindow(TEXT("STATIC"), TEXT("区别于专家猜测的动物的特征(例如“它(不)是/(不)会/(没)有”句式)"), WS_CHILD | WS_VISIBLE | WS_EX_TRANSPARENT, 10, 200, 600, 20, hWnd, (HMENU)IDC_STATIC2, NULL, NULL);
SendMessage(static2, WM_SETFONT, (WPARAM)hFont, 1);
edit1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL | ES_MULTILINE, 10, 140, 300, 50, hWnd, (HMENU)IDC_EDIT1, NULL, NULL);
SendMessage(edit1, WM_SETFONT, (WPARAM)hFont, 1);
edit2 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL | ES_MULTILINE, 10, 230, 300, 50, hWnd, (HMENU)IDC_EDIT2, NULL, NULL);
SendMessage(edit2, WM_SETFONT, (WPARAM)hFont, 1);
button14 = CreateWindow(TEXT("BUTTON"), TEXT("确定"), WS_CHILD | WS_VISIBLE, 320, 300, 80, 30, hWnd, (HMENU)IDC_BUTTON14, NULL, NULL);
SendMessage(button14, WM_SETFONT, (WPARAM)hFont, 1);
button5 = CreateWindow(TEXT("BUTTON"), TEXT("返回"), WS_CHILD | WS_VISIBLE, 320, 350, 80, 30, hWnd, (HMENU)IDC_BUTTON5, NULL, NULL);
SendMessage(button5, WM_SETFONT, (WPARAM)hFont, 1);
break;
}
case IDC_BUTTON14:
{
GetDlgItemText(hWnd, IDC_EDIT1, a_name, 100);
GetDlgItemText(hWnd, IDC_EDIT2, a_feature, 100);
if (a_name[0] == '\0')
{
MessageBox(hWnd, TEXT("动物名称不能为空"), TEXT("提示"), MB_ICONWARNING);
break;
}
if (a_feature[0] == '\0')
{
MessageBox(hWnd, TEXT("相关特征不能为空"), TEXT("提示"), MB_ICONWARNING);
break;
}
Expert(14, hWnd);
ClearAllWindows();
button4 = CreateWindow(TEXT("BUTTON"), TEXT("再玩一次"), WS_CHILD | WS_VISIBLE, 420, 200, 150, 40, hWnd, (HMENU)IDC_BUTTON4, NULL, NULL);
SendMessage(button4, WM_SETFONT, (WPARAM)hFont, 1);
button5 = CreateWindow(TEXT("BUTTON"), TEXT("返回"), WS_CHILD | WS_VISIBLE, 160, 200, 150, 40, hWnd, (HMENU)IDC_BUTTON5, NULL, NULL);
SendMessage(button5, WM_SETFONT, (WPARAM)hFont, 1);
break;
}
case IDC_BUTTON10:
{
PostQuitMessage(0);
return 0;
}
}
}
return 0;
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC)wParam;
}
return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
三、测试与结论
1、测试环境:Visual Studio 2019
1)从文件读取决策树如下图:
2)用户依次选择问题答案如下图:
3)程序输出猜测结果如下图:
4)若猜测正确则显示如下图:
5)猜测错误则用户输入动物名称并按照提示描述特征如下图(如果用户输入为空系统会发出提示):
6)用户可选择再玩一次或将新的决策树保存到文件中如下图:
7)在上侧菜单中,文件栏下,有以下辅助功能;在关于栏下,有开发者信息;在数据栏下,有动物的个数。
- 若用户没有读入决策树就开始进入猜动物环节,程序有相应提醒。
9)当用户非法输入时会有相应提醒。
- 当用户未建立决策树就开始保存到新文件时,会有相应提醒。
程序源码:
基于有序二叉树的专家系统应用——动物游戏(C++)源代码与应用程序-C/C++文档类资源-CSDN文库