Unity學習之P&D 過河遊戲智能幫助實現
根據之前設計好的動作分離版過河遊戲,我們進行一個簡單的狀態圖AI實現。
轉移狀態圖
狀態圖老師已經給出:
該狀態圖只記錄了遊戲過程中左岸的情況。P代表牧師,D代表魔鬼,B代表船。當船在右岸時不記錄。雙箭頭代表兩個狀態可以相互轉化。
構造狀態圖
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;
}
}
搜索路徑
while (queue.Count > 0)
{
temp = queue.Peek();
if (temp == end)
{
while (temp.parent_state != start)
{
temp = temp.parent_state;
}
return temp;
}
queue.Dequeue();
if (temp.pos)
{
if (temp.lp > 0)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = false;
next.lp--;
next.rp++;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.ld > 0)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = false;
next.ld--;
next.rd++;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.ld > 0 && temp.lp > 0)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = false;
next.ld--;
next.rd++;
next.lp--;
next.rp++;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.lp > 1)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = false;
next.lp -= 2;
next.rp += 2;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.ld > 1)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = false;
next.ld -= 2;
next.rd += 2;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
}
else
{
if (temp.rp > 0)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = true;
next.rp--;
next.lp++;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.rd > 0)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = true;
next.rd--;
next.ld++;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.rd > 0 && temp.rp > 0)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = true;
next.rd--;
next.ld++;
next.rp--;
next.lp++;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.rd > 1)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = true;
next.rd -= 2;
next.ld += 2;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
if (temp.rp > 1)
{
GameState next = new GameState(temp);
next.parent_state = new GameState(temp);
next.pos = true;
next.rp -= 2;
next.lp += 2;
if (next.isValid() && !queue.Contains(next))
{
queue.Enqueue(next);
}
}
}
}
return null;
}
最後,更新GUI,以便正確提示輸出即可
void OnGUI()
{
GUIStyle fontstyle = new GUIStyle();
fontstyle.normal.background = null;
fontstyle.normal.textColor = new Color(255, 192, 203);
fontstyle.fontSize = 50;
style = new GUIStyle()
{
fontSize = 16
};
style.normal.textColor = new Color(0, 0, 0);
buttonStyle = new GUIStyle("button")
{
fontSize = 16
};
if (GUI.Button(new Rect (Screen.width/2 - 50 , Screen.height- 50, 100, 40), "Reset", buttonStyle))
{
status = 0;
userAction.Restart();
}
if (status == 1)
{
GUI.Label(new Rect (Screen.width/2- 50, 50, 100, 40), "Lose", style);
}
else if (status == 2)
{
GUI.Label(new Rect (Screen.width/2- 50, 50, 100, 40), "Win", style);
}
GUI.Button(new Rect(Screen.width/2 - 150 , 30, 400, 40), tips, style);
if (GUI.Button(new Rect(Screen.width/2 - 300 , 30, 100, 40), "Tips", buttonStyle))
{
int[] arr = userAction.getStateInfo();
leftPriests = arr[0];
leftDevils = arr[1];
rightPriests = arr[2];
rightDevils = arr[3];
if (arr[4] == 0) boat_pos = true;
else boat_pos = false;
start = new GameState(leftPriests, leftDevils, rightPriests, rightDevils, boat_pos, null);
GameState temp = GameState.BFS(start, end);
int p = leftPriests - temp.lp;
int d = leftDevils - temp.ld;
if (p < 0) p = -p;
if (d < 0) d = -d;
tips = "Move " + p + " priest(s)," + d
+ " devil(s) to another side.";
}
}
GUI更新
最後,我們更新GUI,從而能夠正確提示即可
void OnGUI()
{
GUIStyle fontstyle = new GUIStyle();
fontstyle.normal.background = null;
fontstyle.normal.textColor = new Color(255, 192, 203);
fontstyle.fontSize = 50;
style = new GUIStyle()
{
fontSize = 16
};
style.normal.textColor = new Color(0, 0, 0);
buttonStyle = new GUIStyle("button")
{
fontSize = 16
};
if (GUI.Button(new Rect (Screen.width/2 - 50 , Screen.height- 50, 100, 40), "Reset", buttonStyle))
{
status = 0;
userAction.Restart();
}
if (status == 1)
{
GUI.Label(new Rect (Screen.width/2- 50, 50, 100, 40), "Lose", style);
}
else if (status == 2)
{
GUI.Label(new Rect (Screen.width/2- 50, 50, 100, 40), "Win", style);
}
GUI.Button(new Rect(Screen.width/2 - 150 , 30, 400, 40), tips, style);
if (GUI.Button(new Rect(Screen.width/2 - 300 , 30, 100, 40), "Tips", buttonStyle))
{
int[] arr = userAction.getStateInfo();
leftPriests = arr[0];
leftDevils = arr[1];
rightPriests = arr[2];
rightDevils = arr[3];
if (arr[4] == 0) boat_pos = true;
else boat_pos = false;
start = new GameState(leftPriests, leftDevils, rightPriests, rightDevils, boat_pos, null);
GameState temp = GameState.BFS(start, end);
int p = leftPriests - temp.lp;
int d = leftDevils - temp.ld;
if (p < 0) p = -p;
if (d < 0) d = -d;
tips = "Move " + p + " priest(s)," + d
+ " devil(s) to another side.";
}
}