所谓的机器人“追逐”,就是在一个二维网格中,有两个机器人,位于网格中的两个相异的位置。一个“追捕”,一个“逃跑”。每回合机器人必须走出一步,且只能上下左右移动,不可斜向移动。当两个机器人位于同一位置时,追捕方获胜。
通俗的说,“追捕”希望离“逃跑”的距离越近越好,“逃跑”则希望和“追捕”保持足够的距离。每一方在决策此回合怎么走时,需要依据当前敌我双方在网格中的位置,并且预测对方可能的决策来做出最优的选择。例如,“追捕”在选择下一步的位置时,有四个选择,每一个选择都有一个评价值标明该选择的好坏。“追捕”需要在四个位置中选取评价分值最高的。当然,“逃跑”在其决策过程中也会采取其最优的选择,因此,“追捕”在决策时不但要考虑本回合我怎么走,还要考虑下回合对方怎么走,以及下下回合我该怎么应对。这就好比一个不断揣测对方心里的过程。
上述的决策过程在程序内部被表示成为在博弈树中计算倒退值的过程。由于每个节点都有n种决策,即n个子节点,k层的博弈树就有 n的k次方-1 个节点。决策中,完全遍历这些子节点在时空上都是不允许的。因此,需要引进裁剪技术。alpha-beta剪枝就是这样一种裁剪技术,其好处在于在遍历的同时,实时地删去一些无用节点。具体的做法就不再赘述了,只要记住永远是“与节点”的下界alpha值>大于“或节点”的上界beta值即可。
我的这个示例程序,设定了一个15*15的网格,红方为“追捕”机器人,由程序控制;绿方为用户控制的“逃跑”机器人。“逃跑”先行。很简陋。。。。
通过对话框设定双方初始位置
经过n步。。。被捕了。。。
附上alpha-beta剪枝的核心代码。
第一个是机器人做决策的线程函数,机器人通过博弈树搜索得到了下一步将要走到的位置,一个递归搜索的过程。
1: UINT RobotMakeChoice(LPVOID pParam)
2: {
3: CRobotChaseDlg *pRobotDlg = (CRobotChaseDlg *)(AfxGetApp()->m_pMainWnd);
4: if (IsRobotCatchHuman())//Judge whether the game is over
5: {
6: AfxMessageBox("Successfully Catch!");
7: gl_isGameStart = false;
8: gl_isRobotExist = false;
9: gl_isHumanExist = false;
10: gl_isCanHumanMove = false;
11: (CButton *)(pRobotDlg->GetDlgItem(IDC_BTN_SETPOS))->EnableWindow(true);
12: (CButton *)(pRobotDlg->GetDlgItem(IDC_BTN_START))->EnableWindow(false);
13: return 0;
14: }
15: int i = 0, iReturn;
16: int iMaxScore = INT_MIN;
17: Position posRobotNext;
18: Position posRobotNext_Best;
19: while (i < DIRECTION_NUM)
20: {
21: posRobotNext = gl_RobotPos;
22: if (i == 0)//left
23: {
24: --posRobotNext.X;
25: }
26: else if (i == 1)//right
27: {
28: ++posRobotNext.X;
29: }
30: else if (i == 2)//up
31: {
32: --posRobotNext.Y;
33: }
34: else//i == 3 down
35: {
36: ++posRobotNext.Y;
37: }
38: if (IsMoveLegal(posRobotNext))
39: {
40: iReturn = BetaPruning(1, iMaxScore, posRobotNext, gl_HumanPos);
41: if (iReturn > iMaxScore)
42: {
43: iMaxScore = iReturn;
44: posRobotNext_Best = posRobotNext;
45: }
46: }
47: i++;
48: }
49:
50: CDC *pDC = pRobotDlg->m_GameGrid.GetDC();
51: pRobotDlg->m_GameGrid.EraseRobot(pDC);
52: gl_RobotPos = posRobotNext_Best; // the robot move!!!
53: pRobotDlg->m_GameGrid.DrawRobot(pDC);
54: pRobotDlg->ReleaseDC(pDC);
55:
56: if (IsRobotCatchHuman())//Judge whether the game is over after the robot move
57: {
58: AfxMessageBox("Successfully Catch!");
59: gl_isGameStart = false;
60: gl_isRobotExist = false;
61: gl_isHumanExist = false;
62: gl_isCanHumanMove = false;
63: (CButton *)(pRobotDlg->GetDlgItem(IDC_BTN_SETPOS))->EnableWindow(true);
64: (CButton *)(pRobotDlg->GetDlgItem(IDC_BTN_START))->EnableWindow(false);
65: }
66: else
67: {
68: gl_isCanHumanMove = true;
69: }
70: return 0;
71: }
然后是节点处理函数,在处理节点的时候应用剪枝规则。(注意我的函数名起的不好,AlphaPruning实际上是在或节点中应用beta剪枝,BetaPruning是在与节点中应用alpha剪枝)
1: //alpha_pruning
2: int AlphaPruning(int iDepth, int iParentBeta, Position posRobot, Position posHuman)
3: {
4: if (iDepth == SEARCH_DEPTH)
5: {
6: return -(abs((posRobot.X-posHuman.X)*(posRobot.X-gl_HumanPos.X)) +
7: abs((posRobot.Y-posHuman.Y)*(posRobot.Y-posHuman.Y)));
8: }
9: else
10: {
11: int i = 0, iReturn;
12: int iNowAlpha = INT_MIN;
13: Position posRobotNext;
14: while (i < DIRECTION_NUM)
15: {
16: posRobotNext = posRobot;
17: if (iNowAlpha > iParentBeta)
18: {
19: //AfxMessageBox("beta!");//debug output
20: break;
21: }
22: if (i == 0)//left
23: {
24: --posRobotNext.X;
25: }
26: else if (i == 1)//right
27: {
28: ++posRobotNext.X;
29: }
30: else if (i == 2)//up
31: {
32: --posRobotNext.Y;
33: }
34: else//i == 3 down
35: {
36: ++posRobotNext.Y;
37: }
38: if (IsMoveLegal(posRobotNext))
39: {
40: iReturn = BetaPruning(iDepth+1, iNowAlpha, posRobotNext, posHuman);
41: if (iReturn > iNowAlpha)
42: {
43: iNowAlpha = iReturn;
44: }
45: }
46: i++;
47: }
48: return iNowAlpha;
49: }
50: }
51:
52: int BetaPruning(int iDepth, int iParentAlpha, Position posRobot, Position posHuman)
53: {
54: if (iDepth == SEARCH_DEPTH)
55: {
56: return -(abs((posRobot.X-posHuman.X)*(posRobot.X-gl_HumanPos.X)) +
57: abs((posRobot.Y-posHuman.Y)*(posRobot.Y-posHuman.Y)));
58: }
59: else
60: {
61: int i = 0, iReturn;
62: int iNowBeta = INT_MAX;
63: Position posHumanNext;
64: while (i < DIRECTION_NUM)
65: {
66: posHumanNext = posHuman;
67: if (iNowBeta < iParentAlpha)
68: {
69: //AfxMessageBox("alpha!");//debug output
70: break;
71: }
72: if (i == 0)//left
73: {
74: --posHumanNext.X;
75: }
76: else if (i == 1)//right
77: {
78: ++posHumanNext.X;
79: }
80: else if (i == 2)//up
81: {
82: --posHumanNext.Y;
83: }
84: else//i == 3 down
85: {
86: ++posHumanNext.Y;
87: }
88: if (IsMoveLegal(posHumanNext))
89: {
90: iReturn = AlphaPruning(iDepth+1, iNowBeta, posRobot, posHumanNext);
91: if (iReturn < iNowBeta)
92: {
93: iNowBeta = iReturn;
94: }
95: }
96: i++;
97: }
98: return iNowBeta;
99: }
100: }