广州大学 人工智能原理 实验一
猴子摘香蕉问题
背景介绍:利用一阶谓词逻辑求解猴子摘香蕉问题:房内有一个猴子,一个箱子,天花板上挂了一串香蕉,其位置如图1所示,猴子为了拿到香蕉,它必须把箱子搬到香蕉下面,然后再爬到箱子上。请定义必要的谓词,列出问题的初始化状态(即下图所示状态),目标状态(猴子拿到了香蕉,站在箱子上,箱子位于位置b)
分析
从初始状态到目标状态的转化,猴子需要完成一系列操作
首先,分析,这里有三个物体,猴子(Monkey),香蕉(banana),箱子(box),那么猴子要怎么移动才可以到达b处的香蕉呢,也就是分为4步,移动到C处,搬箱子到B处,爬箱子,拿到香蕉。那么就是对每一种可能出现的情况进行分析,然后判断下一步需要做什么做什么,一步一步进行。
在这之中所有所有的状态一共有四个动作。因为最后还需要将移动步骤一点点输出,就用一个数组来进行存储
string routesave[150];//用来记录每一次路线的变化
主要函数
猴子移动
void monkeymove(int b,int i) //第i步,移动到b处
{
int a;
a = b;
if (a == -1)
{
routesave[i] = "Monkey move to A";
States[i + 1] = States[i]; //结合下一句,其他状态不变,但猴子位置发生改变
States[i + 1].monkey = -1;
}
else if (a == 0)
{
routesave[i] = "Monkey move to B";
States[i + 1] = States[i];
States[i + 1].monkey = 0;
}
else if (a == 1)
{
routesave[i] = "Monkey move to C";
States[i + 1] = States[i];
States[i + 1].monkey = 1;
}
else
{
//猴子不在指定位置,出错
cout << "Parameter is wrong" << endl;
}
}
猴子搬箱子,箱子移动函数
void movebox(int a, int i) //第i步,把箱子搬到a处
{
int B;
B = a;
if (B == -1)
{
routesave[i] = "Monkey move box to A";
States[i + 1] = States[i];
States[i + 1].monkey = -1;
States[i + 1].box = -1;
}
else if (B == 0)
{
routesave[i] = "Monkey move box to B";
States[i + 1] = States[i];
States[i + 1].monkey = 0;
States[i + 1].box = 0;
}
else if (B == 1)
{
routesave[i] = "Monkey move box to C";
States[i + 1] = States[i];
States[i + 1].monkey = 1;
States[i + 1].box = 1;
}
else
{
cout << "Parameter is wrong" << endl;
}
}
猴子爬箱子/爬上/爬下
void climbonto(int i) //爬上箱子
{
routesave[i] = "Monkey climb onto the box";
States[i + 1] = States[i];
States[i + 1].monbox = 1;
}
void climbdown(int i) //爬下箱子
{
routesave[i] = "Monkey climb down from the box";
States[i + 1] = States[i];
States[i + 1].monbox = -1;
}
拿到香蕉并进行打印输出
void reach(int i)
{
routesave[i] = "Monkey reach the banana"; //得到香蕉
int c;
cout << "Result to problem" << endl;
for (c = 0; c < i + 1; c++)
{
cout << "Step" << c + 1 << " " << routesave[c] << endl;
}
cout << "解决问题" << endl;
}
OK
到这里,我们完成了主要的函数部分,后面就是一步一步的逻辑判断了
每一步都要向着最终状态前进,如果猴子和箱子不在一起,那就猴子移动到箱子,在满足前提条件下,猴子搬箱子到香蕉下,无论怎么样,一步步向最终状态,也就是猴子,箱子,香蕉都在同一个点位的状态改变。
核心函数
void nextStep(int i)
{
int c;
int j;
if (i >= 150)
{
cout << "超过150,出现问题" << endl;
return;
}
for (c = 0; c < i; c++)
{
if (States[c].monkey == States[i].monkey && States[c].box == States[i].box && States[c].banana == States[i].banana && States[c].monbox == States[i].monbox)
{
return;//出现相同状态,陷入循环,退出
}
}
j = i + 1;
if (States[i].monkey == 0)
{
if (States[i].box == 0)
{
if (States[i].monbox == 0) //猴子箱子都在B位置且猴子没爬上箱子
{
climbonto(i);
reach(i + 1);
return;
}
else //猴子已经爬上了箱子
{
reach(i + 1);
return;
}
}
else if (States[i].box == 1)
{
monkeymove(1, i); //将猴子移动到箱子的位置
nextStep(j);
return;
}
else
{
monkeymove(-1, i);
nextStep(j);
return;
}
}
if (States[i].monkey == -1)
{
if (States[i].box == -1) //猴子箱子都在A位置
{
if (States[i].monbox == -1)
{
movebox(0, i); //
nextStep(j);
return;
}
else
{
climbdown(i);
nextStep(j);
return;
}
}
else if (States[i].box == 0)
{
monkeymove(0, i);
nextStep(j);
return;
}
else
{
monkeymove(1, i);
nextStep(j);
return;
}
}
if (States[i].monkey == 1)
{
if (States[i].box == 1)
{
if (States[i].monbox == 0)
{
movebox(0, i);
nextStep(j);
return;
}
else
{
climbdown(i);
nextStep(j);
return;
}
}
else if (States[i].banana == -1)
{
monkeymove(-1, i);
nextStep(j);
return;
}
else
{
monkeymove(0, i);
nextStep(j);
return;
}
}
return;
}
需要注意一下实验指导书上的代码
大概都是这样子的,但这样子会有问题,会重复输出,也就是当你执行nextstep函数的时候一步步进行下去,然后最后拿到香蕉了,这样子就可以了,但它会重新跳转到这里的if语句中,继续movebox,继续nextstep,就会继续输出残缺的结果。
但在nextstep的核心中,其实就是根据每一个状态来判断下一步应该干嘛,然后嵌套循环进行下去
完整的代码
#include <iostream>
#include <string>
using namespace std;
struct State //每一个状态的记录
{
int monkey; // -1: Monkey at A; 0: Monkey at B; 1: Monkey at C;
int box; // -1: Box at A; 0: Box at B; 1: Box at C;
int banana; // Banana at B, Banana=0
int monbox; // -1: Monkey on the box; 1: Monkey off the box;
};
State States[150]; //提供一个最大数组来存储
string routesave[150];//用来记录每一次路线的变化
//猴子的目标状态是b,想要前往b地点
void monkeymove(int b,int i)
{
int a;
a = b;
if (a == -1)
{
routesave[i] = "Monkey move to A";
States[i + 1] = States[i]; //结合下一句,其他状态不变,但猴子位置发生改变
States[i + 1].monkey = -1;
}
else if (a == 0)
{
routesave[i] = "Monkey move to B";
States[i + 1] = States[i];
States[i + 1].monkey = 0;
}
else if (a == 1)
{
routesave[i] = "Monkey move to C";
States[i + 1] = States[i];
States[i + 1].monkey = 1;
}
else
{
//猴子不在指定位置,出错
cout << "Parameter is wrong" << endl;
}
}
void movebox(int a, int i)
{
int B;
B = a;
if (B == -1)
{
routesave[i] = "Monkey move box to A";
States[i + 1] = States[i];
States[i + 1].monkey = -1;
States[i + 1].box = -1;
}
else if (B == 0)
{
routesave[i] = "Monkey move box to B";
States[i + 1] = States[i];
States[i + 1].monkey = 0;
States[i + 1].box = 0;
}
else if (B == 1)
{
routesave[i] = "Monkey move box to C";
States[i + 1] = States[i];
States[i + 1].monkey = 1;
States[i + 1].box = 1;
}
else
{
cout << "Parameter is wrong" << endl;
}
}
void climbonto(int i)
{
routesave[i] = "Monkey climb onto the box";
States[i + 1] = States[i];
States[i + 1].monbox = 1;
}
void climbdown(int i)
{
routesave[i] = "Monkey climb down from the box";
States[i + 1] = States[i];
States[i + 1].monbox = -1;
}
void reach(int i)
{
routesave[i] = "Monkey reach the banana"; //得到香蕉
int c;
cout << "Result to problem" << endl;
for (c = 0; c < i + 1; c++)
{
cout << "Step" << c + 1 << " " << routesave[c] << endl;
}
cout << "解决问题" << endl;
}
void nextStep(int i)
{
int c;
int j;
if (i >= 150)
{
cout << "超过150,出现问题" << endl;
return;
}
for (c = 0; c < i; c++)
{
if (States[c].monkey == States[i].monkey && States[c].box == States[i].box && States[c].banana == States[i].banana && States[c].monbox == States[i].monbox)
{
return;//出现相同状态,陷入循环,退出
}
}
j = i + 1;
if (States[i].monkey == 0)
{
if (States[i].box == 0)
{
if (States[i].monbox == 0) //猴子箱子都在B位置且猴子没爬上箱子
{
climbonto(i);
reach(i + 1);
return;
}
else //猴子已经爬上了箱子
{
reach(i + 1);
return;
}
}
else if (States[i].box == 1)
{
monkeymove(1, i); //将猴子移动到箱子的位置
nextStep(j);
return;
}
else
{
monkeymove(-1, i);
nextStep(j);
return;
}
}
if (States[i].monkey == -1)
{
if (States[i].box == -1) //猴子箱子都在A位置
{
if (States[i].monbox == -1)
{
movebox(0, i); //
nextStep(j);
return;
}
else
{
climbdown(i);
nextStep(j);
return;
}
}
else if (States[i].box == 0)
{
monkeymove(0, i);
nextStep(j);
return;
}
else
{
monkeymove(1, i);
nextStep(j);
return;
}
}
if (States[i].monkey == 1)
{
if (States[i].box == 1)
{
if (States[i].monbox == 0)
{
movebox(0, i);
nextStep(j);
return;
}
else
{
climbdown(i);
nextStep(j);
return;
}
}
else if (States[i].banana == -1)
{
monkeymove(-1, i);
nextStep(j);
return;
}
else
{
monkeymove(0, i);
nextStep(j);
return;
}
}
return;
}
int main()
{
States[0].monkey = -1;
States[0].box = 1;
States[0].banana = 0;
States[0].monbox = 0;
nextStep(0);
}
需要注意,因为我们的核心函数nextstep是根据猴子箱子香蕉的初始状态来写的,就可能你改变猴子,箱子的位置最后结果可能就会出现错误,所以如果说,代码哪里还可以再优化,应该就是把函数里面的-1,0,1改的更详细一点,States[i].monkey这样子的。
输出结果
进行改进
前面提到如果初始状态进行改变,那么就不能用上原先的代码了,而且经过前面的分析,其实就是猴子箱子先在一起,然后猴子箱子香蕉在同一个位置。那么改变nextStep函数中的数值,都改成States[i]. 的形式,这是进行优化后可以改变初始状态的代码
#include <iostream>
#include <string>
using namespace std;
struct State //每一个状态的记录
{
int monkey; // -1: Monkey at A; 0: Monkey at B; 1: Monkey at C;
int box; // -1: Box at A; 0: Box at B; 1: Box at C;
int banana; // Banana at B, Banana=0
int monbox; // -1: Monkey on the box; 1: Monkey off the box;
};
State States[150]; //提供一个最大数组来存储
string routesave[150];//用来记录每一次路线的变化
void monkeymove(int b, int i)
{
if (b < -1 || b > 1)
{
cout << "Parameter is wrong" << endl;
return;
}
if (b == States[i].monkey)
{
routesave[i] = "Monkey stay at current position";
}
else if (b == -1)
{
routesave[i] = "Monkey move to A";
States[i + 1] = States[i]; //结合下一句,其他状态不变,但猴子位置发生改变
States[i + 1].monkey = -1;
}
else if (b == 0)
{
routesave[i] = "Monkey move to B";
States[i + 1] = States[i];
States[i + 1].monkey = 0;
}
else if (b == 1)
{
routesave[i] = "Monkey move to C";
States[i + 1] = States[i];
States[i + 1].monkey = 1;
}
}
void movebox(int a, int i)
{
if (a < -1 || a > 1)
{
cout << "Parameter is wrong" << endl;
return;
}
if (States[i].monbox == -1)
{
routesave[i] = "Monkey cannot move box when it is on the box";
}
else if (a == States[i].box)
{
routesave[i] = "Box stay at current position";
}
else if (a == -1)
{
routesave[i] = "Monkey move box to A";
States[i + 1] = States[i];
States[i + 1].monkey = -1;
States[i + 1].box = -1;
}
else if (a == 0)
{
routesave[i] = "Monkey move box to B";
States[i + 1] = States[i];
States[i + 1].monkey = 0;
States[i + 1].box = 0;
}
else if (a == 1)
{
routesave[i] = "Monkey move box to C";
States[i + 1] = States[i];
States[i + 1].monkey = 1;
States[i + 1].box = 1;
}
}
void climbonto(int i)
{
routesave[i] = "Monkey climb onto the box";
States[i + 1] = States[i];
States[i + 1].monbox = -1;
}
void climbdown(int i)
{
routesave[i] = "Monkey climb down from the box";
States[i + 1] = States[i];
States[i + 1].monbox = 1;
}
void reach(int i)
{
routesave[i] = "Monkey reach the banana"; //得到香蕉
cout << "Result to problem" << endl;
for (int c = 0; c < i + 1; c++)
{
cout << "Step" << c + 1 << " " << routesave[c] << endl;
}
cout << "解决问题" << endl;
}
void nextStep(int i, int n)
{
if (i >= 150)
{
cout << "超过150,出现问题" << endl;
return;
}
if (States[i].box ==States[i].monkey && States[i].monkey == States[i].banana && States[i].monbox==-1) // 猴子、箱子和香蕉都在B位置且猴子在箱子上
{
reach(i);
return;
}
for (int c = 0; c < i; c++)
{
if (States[c].monkey == States[i].monkey && States[c].box == States[i].box && States[c].banana == States[i].banana && States[c].monbox == States[i].monbox)
{
return;//出现相同状态,陷入循环,退出
}
}
int j = i + 1;
if (States[i].box == States[i].monkey) //箱子和猴子在相同位置
{
if (States[i].banana == States[i].monkey) //猴子箱子香蕉都在相同位置且猴子没爬上箱子
{
if (States[i].monbox == 1)
{
climbonto(i);
nextStep(i + 1, n);
return;
}
else //猴子已经爬上了箱子
{
nextStep(i + 1, n);
return;
}
}
else //猴子箱子在相同位置,但不和香蕉在同一个位置
{
movebox(States[i].banana, i); //把箱子搬到香蕉的相同位置
nextStep(i + 1, n);
return;
}
}
else //将猴子移动到箱子的位置
{
monkeymove(States[i].box, i);
nextStep(j, n);
return;
}
}
int main()
{
States[0].monkey = -1;
States[0].banana = 0;
States[0].box = 1;
States[0].monbox = 1;
nextStep(0, 10); // 查找第10步的状态
}
应该是对的