作业题目
P&D 过河游戏智能帮助实现,程序具体要求:
- 实现状态图的自动生成
- 讲解图数据在程序中的表示方法
- 利用算法实现下一步的计算
- 参考:P&D 过河游戏智能帮助实现
我们先来分析一下这一次作业的要求。这一次的作业大致就是要对之前的牧师与魔鬼的游戏进行改进,使得这一次可以实现一个游戏智能自动实现每一步的寻路操作。
状态图的自动生成
首先我们先来对游戏进行简单的分析。我们可以用当前左右两岸的牧师和魔鬼数以及当前船在哪一边作为每一个状态的标识。并且可以根据游戏的规则来制定状态之间的转移条件:只有当河两岸的牧师数量都大于或等于魔鬼数量且船上人数大于0小于2时才能进行状态间的转移。这样我们就可以生成一个无向有环图,并将问题转换为生成一条从起始状态为3牧师3魔鬼在左岸到结束状态为3牧师3魔鬼在右岸的路径,具体状态图如下(转载自题目中给出的参考博客):
上图通过只记录左岸的情况来表示游戏此刻的状态。P代表牧师,D代表魔鬼,B代表船。当船在右岸时不记录。双箭头代表两个状态可以相互转化。
图数据在程序中的表示
接下来我们看如何在代码中表示图中的每一个状态。由之前的分析可得,我们可以通过左岸的牧师和魔鬼的数量和船停靠在哪一岸对每一个状态进行标识。并且由之前的转化规则可得,当船在左岸时,我们还需要对牧师和魔鬼数量进行统计,代码具体如下:
public class State {
public int priest;//左岸牧师数
public int devil;//左岸魔鬼数
public int boat;//船在哪一岸
public State() {
}
public State(int priest, int devil, int bpriest, int bdevil, int boat){
if(boat == 1){//船在右岸
this.priest = priest;
this.devil = devil;
}
else{//船在左岸
this.priest = priest + bpriest;//船上牧师数
this.devil =devil + bdevil;//船上魔鬼数
}
this.boat = boat;
}
利用算法实现下一步的计算
最后我们需要实现每个状态下一步的计算。通过之前的分析,我们可以通过对船的状态也进行表示来实现每一个要执行的动作,从而实现状态转化图中每一个状态的转换,具体可以表示如下:
public enum Boataction { P, D, PP, DD, PD,empty }
//P:船运载一个牧师
//D:船运载一个恶魔
//PP:船运载两个牧师
//DD:船运载两个恶魔
//PD:船运载一个牧师,一个恶魔
//empty: 空船
然后我们可以实现一个函数,就是根据当前的状态来判断下一步船将要执行的动作,即就是船的状态,该函数的具体实现如下:
private BoatAction getNextAction()
{
BoatAction next = BoatAction.empty;
if(boat == 0){//船在左岸
if(priest == 3 && devil == 3){
if(Random.Range(0f,1f) <= 0.5f){
next = BoatAction.PD;
}
else{
next = BoatAction.DD;
}
}
if(priest == 3 && devil == 2){
next = BoatAction.DD;
}
if(priest == 3 && devil == 1){
next = BoatAction.PP;
}
if(priest == 2 && devil == 2){
next = BoatAction.PP;
}
if(priest == 0 && devil == 3){
next = BoatAction.DD;
}
if(priest == 2 && devil == 1){
next = BoatAction.P;
}
if(priest == 0 && devil == 2){
next = BoatAction.DD;
}
if(priest == 1 && devil == 1){
next = BoatAction.PD;
}
}
else{//船在右岸
if(priest == 2 && devil == 2){
next = BoatAction.P;
}
if(priest == 3 && devil == 2){
next = BoatAction.D;
}
if(priest == 3 && devil == 1){
next = BoatAction.D;
}
if(priest == 3 && devil == 0){
next = BoatAction.D;
}
if(priest == 1 && devil == 1){
next = BoatAction.PD;
}
if(priest == 0 && devil == 2){
next = BoatAction.D;
}
if(priest == 0 && devil == 1){
if(Random.Range(0f,1f) <= 0.5f){
next = BoatAction.D;
}
else{
next = BoatAction.P;
}
}
}
return next;
}
最后,在GUI中增加Next按钮,使得当用户按下Next按钮时,会根据当前两岸的状态调用getNextAction()函数获取下一个需要执行的船状态,具体实现就是根据得到的船状态调用之前实现好的上船下船函数即可,这里就不贴出具体的代码了。
游戏效果图如下: