二、设计与实现
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文库