前面已经实现了牧师魔鬼过河小游戏,这次,学习了AI之后,尝试了实现其智能帮助功能。以下是效果图:
这个AI比较简单,只要分析清楚了牧师魔鬼过河过程中的各个状态,再用算法实现即可。以下是状态图:
该状态图只记录了游戏过程中左岸的情况。P代表牧师,D代表魔鬼,B代表船。当船在右岸时不记录。双箭头代表两个状态可以相互转化。
实现AI的基本原理是:点击next之后,分析当前场景的状态,读取成功路径上的下一个状态,执行相应动作以达到下一个状态。
基于以上原理,我们可以按如下方式实现。首先,根据当前的状态,分析接下来需要执行的一个动作。以第一个状态为例:3P3DB。代表左岸有3个牧师,3个魔鬼,船也在左岸。它的下一个状态有4个,但是其中一个执行后会输掉游戏,不考虑。剩下三个状态中,只有2P2D和3P1D是成功路径上的状态,可以任选一个,所以我增加了随机选择函数。找到对应状态后,把达到下一个状态需要运送的船员数目作为返回值,这样就可以直接执行上下船了。具体实现如下:
//要执行的动作
public enum Boataction { P, D, PP, DD, PD }
//P:船运载一个牧师
//D:船运载一个恶魔
//PP:船运载两个牧师
//DD:船运载两个恶魔
//PD:船运载一个牧师,一个恶魔
//船将要承载的船员的情况及船的状态
public struct nextPassenger
{
public Status boat;
public Boataction boataction;
}
//获取一个随机数 有的路径有两条可选,随机选择一条
private int randomValue()
{
float num = Random.Range(0f, 1f);
if (num <= 0.5f) return 1;
else return 2;
}
//获取目前的牧师、魔鬼分布状态 返回值 = 船将要承载的船员的情况
private nextPassenger getNextPassenger()
{
next.boat = nowStatue;
if (next.boat == Status.BLRING ||
next.boat == Status.BRLING ||
next.boat == Status.LOSE ||
next.boat == Status.WIN || boatSize() != 0)
{
next.boat = Status.None;
return next;
}
else
{
if (next.boat == Status.BLEFT &&
leftPriests.Count == 3 &&
leftDevils.Count == 3)
{
int turn = randomValue();
if (turn == 1)
next.boataction = Boataction.PD;
else
next.boataction = Boataction.DD;
}
else if (next.boat == Status.BRIGHT &&
leftPriests.Count == 2 &&
leftDevils.Count == 2)
{
next.boataction = Boataction.P;
}
else if (next.boat == Status.BRIGHT &&
leftPriests.Count == 3 &&
leftDevils.Count == 2)
{
next.boataction = Boataction.D;
}
else if (next.boat == Status.BRIGHT &&
leftPriests.Count == 3 &&
leftDevils.Count == 1)
{
next.boataction = Boataction.D;
}
else if (next.boat == Status.BLEFT &&
leftPriests.Count == 3 &&
leftDevils.Count == 2)
{
next.boataction = Boataction.DD;
}
else if (next.boat == Status.BRIGHT &&
leftPriests.Count == 3 &&
leftDevils.Count == 0)
{
next.boataction = Boataction.D;
}
else if (next.boat == Status.BLEFT &&
leftPriests.Count == 3 &&
leftDevils.Count == 1)
{
next.boataction = Boataction.PP;
}
else if (next.boat == Status.BRIGHT &&
leftPriests.Count == 1 &&
leftDevils.Count == 1)
{
next.boataction = Boataction.PD;
}
else if (next.boat == Status.BLEFT &&
leftPriests.Count == 2 &&
leftDevils.Count == 2)
{
next.boataction = Boataction.PP;
}
else if (next.boat == Status.BRIGHT &&
leftPriests.Count == 0 &&
leftDevils.Count == 2)
{
next.boataction = Boataction.D;
}
else if (next.boat == Status.BLEFT &&
leftPriests.Count == 0 &&
leftDevils.Count == 3)
{
next.boataction = Boataction.DD;
}
else if (next.boat == Status.BRIGHT &&
leftPriests.Count == 0 &&
leftDevils.Count == 1)
{
int turn = randomValue();
if (turn == 1) next.boataction = Boataction.D;
else next.boataction = Boataction.P;
}
else if (next.boat == Status.BLEFT &&
leftPriests.Count == 2 &&
leftDevils.Count == 1)
{
next.boataction = Boataction.P;
}
else if (next.boat == Status.BLEFT &&
leftPriests.Count == 0 &&
leftDevils.Count == 2)
{
next.boataction = Boataction.DD;
}
else if (next.boat == Status.BLEFT &&
leftPriests.Count == 1 &&
leftDevils.Count == 1)
{
next.boataction = Boataction.PD;
}
}
return next;
}
接下来,执行一些动作,以便到达下一个状态。不过,这次需要自动执行上船,开船,和下船动作。其中,上船和下船,可以复用前面实现的代码。
上船,需要判断船员情况,所以稍微复杂一些。
public void nextOnboat()
{
nextPassenger next = getNextPassenger();
if (next.boat == Status.None) return;
if (next.boataction == Boataction.D)
devilOnBoat();
else if (next.boataction == Boataction.DD)
{
devilOnBoat();
devilOnBoat();
}
else if (next.boataction == Boataction.P)
priestOnBoat();
else if (next.boataction == Boataction.PD)
{
priestOnBoat();
devilOnBoat();
}
else if (next.boataction == Boataction.PP)
{
priestOnBoat();
priestOnBoat();
}
else { }
if (next.boat == Status.BLEFT)
Invoke("boatLeftToRight", 0.5f);
else {
Invoke("boatRightToLeft", 0.5f);
}
instatus = true;//这是用来判断何时下船的一个辅助变量
}
接下来,就是下船的实现了。这个非常简单。
public void nextOffBoat()
{
if (nextStatus == true)//nextStatus = true 点击了next,一切处于自动运行状态。需要实现自动上下船。
{
while(boatSize() != 0)
{
int pos = boatSize() - 1;
if (boatPassName[pos] == "devil")
devilOffBoat(pos);
else
priestOffBoat(pos);
}
}
instatus = false;
nextStatus = false;
}
那么,最后一步,更新IUserAction接口和OnGUI函数,实现界面。在OnGUI中只要新增加了Next按钮和自动上下船。具体代码如下:
以上是本次学习的全部内容,希望对大家有所帮助。