題目
3、P&D 過河遊戲智能幫助實現,程序具體要求:
- 實現狀態圖的自動生成
- 講解圖數據在程序中的表示方法
- 利用算法實現下一步的計算
- 參考:P&D 過河遊戲智能幫助實現
實現
狀態圖
表示方法
該圖在遊戲中以矩陣的形式表示,使用數字編碼對遊戲數據進行設置,以3P3D爲例,計算公式爲:Pnum * 4 + Dnum。其中,P與D均爲左岸數據。
而圖書局的生成依靠如下代碼:
遍歷所有的點,如果該點是合法的,那麼說明可以計算該點到其他點的關係。否則跳過。
for (int i = 0; i < 32; i++)
{
int P = getP(i);
int D = getD(i);
bool side = getSide(i);
if ((P < D && P != 0) || (P > D && P != MAXP) || (P == MAXP && D == MAXD && !side))
{
continue;
}
else
{
Stack<int> temp = getNextNum(i);
while (temp.Count != 0)
{
matrix[i, temp.Peek()] = 1;
temp.Pop();
}
}
}
計算時,使用函數來計算,分別模擬5種情況,判斷是否能夠完成該情況,如果能夠,就將該操作的結果插入存儲器,最終返回存儲器。
生成矩陣
判斷如下
判斷的代碼如下:
public Stack<int> getNextNum(int now)
{
Stack<int> temp = new Stack<int>();
int P = getP(now);
int D = getD(now);
// 1
if (getSide(now))
{
if ((D == 2 && P == 0) || (P == MAXP - 2 && D == MAXD - 2))
{
temp.Push((P * 4 + D + 8));
}
}
else
{
if ((P == 2 && D == 2) || (P == MAXP && D == MAXD - 2))
{
temp.Push(P * 4 + D - 8 + 16);
}
}
// 2
if (getSide(now))
{
if (MAXD - D >= 2 && (P == 0 || P == MAXP))
{
temp.Push(P * 4 + D + 2);
}
}
else
{
if (D >= 2 && (P == 0 || P == MAXP))
{
temp.Push(P * 4 + D - 2 + 16);
}
}
// 3
if (getSide(now))
{
if (P == D && MAXD > P)
{
temp.Push(P * 4 + D + 5);
}
}
else
{
if (P == D && P > 0)
{
temp.Push(P * 4 + D - 5 + 16);
}
}
// 4
if (getSide(now))
{
if ((MAXP - P == 1 && MAXD - D == 1) || (D == 1 && P == 0))
{
temp.Push(P * 4 + D + 4);
}
}
else
{
if ((P == 1 && D == 1) || (MAXD - D == 1 && P == MAXP))
{
temp.Push(P * 4 + D - 4 + 16);
}
}
// 5
if (getSide(now))
{
if (MAXD - D > 0 && (P == MAXP || P == 0))
{
temp.Push(P * 4 + D + 1);
}
}
else
{
if (D > 0 && (P == 0 || P == MAXP))
{
temp.Push(P * 4 + D - 1 + 16);
}
}
return temp;
}
計算時,使用了DFS算法,遞歸實現,實現較爲簡單,其中設置終點。代碼如下:
public Stack<int> Tips(int start, int end)
{
Stack<int> temp = new Stack<int>();
bool[] dfs = new bool[32];
for (int i = 0; i < 32; i++)
{
dfs[i] = false;
}
bool flag = false;
DFS(dfs, start, temp, end, flag);
return temp;
}
public void DFS(bool[] dfs, int pos, Stack<int> temp, int end, bool flag)
{
dfs[pos] = true;
for (int i = 0; i < 32; i++)
{
if (matrix[pos, i] != -1 && !dfs[i]) {
if (!flag)
{
temp.Push(i);
}
if (i != end)
{
DFS(dfs, i, temp, end, flag);
}
else
{
flag = true;
}
}
}
}
## 其他實現:
使用了一個新的按鈕,當點擊這個按鈕的時候,會進行計算,計算後返回存儲了int的存儲器,需要進行解析,並輸出。
while (newStack.Count != 0)
{
int tempi = newStack.Peek();
newStack.Pop();
bool tempS = (tempi > 16) ? true : false;
int tempP = (tempi % 16) / 4;
int tempD = (tempi % 16) % 4;
if (tempS)
{
Debug.Log("前往右岸,使左岸有" + tempP + "牧師和" + tempD + "魔鬼\n");
}
else
{
Debug.Log("前往左岸,使左岸有" + tempP + "牧師和" + tempD + "魔鬼\n");
}
}
完整代碼
其他代碼與之前相同,只貼上新的控制類與計算路徑類
graph
public class Graph
{
public int MAXP = 3;
public int MAXD = 3;
public int priestNum;
public int demonNum;
public bool boatSide;
int[,] matrix = new int[32, 32];
public Graph()
{
priestNum = 0;
demonNum = 0;
boatSide = false;
for (int i = 0; i < 32; i++)
{
for (int j = 0; j < 32; j++)
{
matrix[i, j] = -1;
}
}
for (int i = 0; i < 32; i++)
{
int P = getP(i);
int D = getD(i);
bool side = getSide(i);
if ((P < D && P != 0) || (P > D && P != MAXP) || (P == MAXP && D == MAXD && !side))
{
continue;
}
else
{
Stack<int> temp = getNextNum(i);
while (temp.Count != 0)
{
matrix[i, temp.Peek()] = 1;
temp.Pop();
}
}
}
}
public Stack<int> Tips(int start, int end)
{
Stack<int> temp = new Stack<int>();
bool[] dfs = new bool[32];
for (int i = 0; i < 32; i++)
{
dfs[i] = false;
}
bool flag = false;
DFS(dfs, start, temp, end, flag);
return temp;
}
public void DFS(bool[] dfs, int pos, Stack<int> temp, int end, bool flag)
{
dfs[pos] = true;
for (int i = 0; i < 32; i++)
{
if (matrix[pos, i] != -1 && !dfs[i]) {
if (!flag)
{
temp.Push(i);
}
if (i != end)
{
DFS(dfs, i, temp, end, flag);
}
else
{
flag = true;
}
}
}
}
public int getP(int num)
{
num = num % 16;
return num / 4;
}
public int getD(int num)
{
num = num % 16;
return num % 4;
}
public bool getSide(int num)
{
if (num >= 16)
{
return true;
}
else
{
return false;
}
}
//1 = 2P; 2 = 2D; 3 = 1P1D; 4 = 1P; 5 = 1D
public Stack<int> getNextNum(int now)
{
Stack<int> temp = new Stack<int>();
int P = getP(now);
int D = getD(now);
// 1
if (getSide(now))
{
if ((D == 2 && P == 0) || (P == MAXP - 2 && D == MAXD - 2))
{
temp.Push((P * 4 + D + 8));
}
}
else
{
if ((P == 2 && D == 2) || (P == MAXP && D == MAXD - 2))
{
temp.Push(P * 4 + D - 8 + 16);
}
}
// 2
if (getSide(now))
{
if (MAXD - D >= 2 && (P == 0 || P == MAXP))
{
temp.Push(P * 4 + D + 2);
}
}
else
{
if (D >= 2 && (P == 0 || P == MAXP))
{
temp.Push(P * 4 + D - 2 + 16);
}
}
// 3
if (getSide(now))
{
if (P == D && MAXD > P)
{
temp.Push(P * 4 + D + 5);
}
}
else
{
if (P == D && P > 0)
{
temp.Push(P * 4 + D - 5 + 16);
}
}
// 4
if (getSide(now))
{
if ((MAXP - P == 1 && MAXD - D == 1) || (D == 1 && P == 0))
{
temp.Push(P * 4 + D + 4);
}
}
else
{
if ((P == 1 && D == 1) || (MAXD - D == 1 && P == MAXP))
{
temp.Push(P * 4 + D - 4 + 16);
}
}
// 5
if (getSide(now))
{
if (MAXD - D > 0 && (P == MAXP || P == 0))
{
temp.Push(P * 4 + D + 1);
}
}
else
{
if (D > 0 && (P == 0 || P == MAXP))
{
temp.Push(P * 4 + D - 1 + 16);
}
}
return temp;
}
}
controller
public class Controller : MonoBehaviour
{
Data instance; //數據塊
GameState states; //當前遊戲狀態
DemonAndPriest.Action act; //動作管理器
bool toBoat; //判斷上船動作是否完成,若完成則改變Data中的狀態,下同
bool turnBack; //是否船已到達對岸,需要掉頭
bool toShore; //是否需要對象上岸
Graph graph;
//初始化
public void Start()
{
instance = gameObject.AddComponent<Data>() as Data;
act = gameObject.AddComponent<DemonAndPriest.Action>() as DemonAndPriest.Action;
graph = new Graph();
states = GameState.Waiting;
toBoat = false;
turnBack = false;
toShore = false;
}
//交互
public void OnGUI()
{
//顯示界面
instance.SetView();
//調整當前狀態,查看是否有已完成動作但未和Data同步
if (!act.Run())
{
states = GameState.Waiting;
if (toBoat)
{
instance.MoveToBoat();
toBoat = false;
} else if(toShore)
{
instance.MoveToShore();
toShore = false;
} else if (turnBack)
{
instance.SetPosition();
turnBack = false;
}
}
//判斷當前遊戲是否結束
if (states == GameState.Waiting)
{
states = instance.Charge();
}
//遊戲進行中
if (states == GameState.Waiting || states == GameState.Moving)
{
if (states == GameState.Waiting)
{
if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 10 * 2f, 100, 30), "提示"))
{
int now = 0;
if (instance.getBoatPosition())
{
now = (3 - instance.GetPriestCount()) * 4 + (3 - instance.GetDemonCount()) + 16;
}
else
{
now = instance.GetPriestCount() * 4 + instance.GetDemonCount();
}
Debug.Log(now);
Stack<int> next = graph.Tips(now , 15);
Stack<int> newStack = new Stack<int>();
while (next.Peek() != 15)
{
next.Pop();
}
while (next.Count != 0)
{
int tempi = next.Peek();
newStack.Push(tempi);
next.Pop();
}
while (newStack.Count != 0)
{
int tempi = newStack.Peek();
newStack.Pop();
bool tempS = (tempi > 16) ? true : false;
int tempP = (tempi % 16) / 4;
int tempD = (tempi % 16) % 4;
if (tempS)
{
Debug.Log("前往右岸,使其有" + tempP + "牧師和" + tempD + "魔鬼\n");
}
else
{
Debug.Log("前往左岸,使其有" + tempP + "牧師和" + tempD + "魔鬼\n");
}
}
}
}
if (GUI.Button(new Rect (Screen.width / 10 * 3.4f, Screen.height / 10 * 2f, 100, 30), "牧師上船"))
{
if (states == GameState.Waiting)
{
if (instance.BoatCount() == 2 || instance.GetPriestCount() == 0)
{
return;
}
instance.PriestOnBoat();
act.Act(instance.getMoving(), instance.getBoatPosition(), instance.BoatCount(), false);
states = GameState.Moving;
toBoat = true;
}
}
if (GUI.Button(new Rect(Screen.width / 10 * 6, Screen.height / 10 * 2f, 100, 30), "惡魔上船"))
{
if (states == GameState.Waiting)
{
if (instance.BoatCount() == 2 || instance.GetDemonCount() == 0)
{
return;
}
instance.DemonOnBoat();
act.Act(instance.getMoving(), instance.getBoatPosition(), instance.BoatCount(), false);
states = GameState.Moving;
toBoat = true;
}
}
if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 10 * 3.5f, 100, 30), "啓程"))
{
if (states == GameState.Moving || instance.BoatCount() == 0)
{
return;
}
act.Act(instance.GetBoat(), instance.getBoatPosition(), instance.BoatCount(), true);
states = GameState.Moving;
turnBack = true;
}
if (GUI.Button(new Rect (Screen.width / 2 - 50, Screen.height / 10 * 4.5f, 100, 30), "下船"))
{
if(states != GameState.Waiting)
{
return;
}
if (instance.BoatCount() != 0)
{
instance.DownBoat();
act.Act(instance.getMoving(), instance.getBoatPosition(), instance.BoatCount(), true);
states = GameState.Moving;
toShore = true;
}
}
if (GUI.Button(new Rect(Screen.width / 4 * 3, Screen.height / 10 * 8f, 100, 30), "重新開始"))
{
instance.Reload();
states = GameState.Waiting;
}
}
//勝利
else if (states == GameState.Win)
{
GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 10 * 3f, 100, 30), "勝利");
if (GUI.Button(new Rect(Screen.width / 4 * 3, Screen.height / 10 * 8f, 100, 30), "重新開始")) {
states = GameState.Waiting;
instance.Reload();
}
}
//遊戲結束
else if (states == GameState.GameOver)
{
GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 10 * 3f, 100, 30), "牧師死亡");
if (GUI.Button(new Rect(Screen.width / 4 * 3, Screen.height / 10 * 8f, 100, 30), "重新開始"))
{
states = GameState.Waiting;
instance.Reload();
}
}
}
}