การจำแนกประเภท
คำตอบนั้นไม่ใช่เรื่องง่าย ทฤษฎีเกมมีการจำแนกประเภทสำหรับเกม แต่ดูเหมือนจะไม่มีความชัดเจน 1: 1-match สำหรับเกมนั้นกับทฤษฎีพิเศษ มันเป็นรูปแบบพิเศษของปัญหา combinatorial
ไม่ใช่พนักงานขายที่กำลังเดินทางซึ่งจะตัดสินใจเลือกคำสั่งซื้อที่คุณเข้าชม "nodes" โดยมีค่าใช้จ่ายในการเข้าถึงโหนดถัดไปจากโหนดสุดท้าย คุณไม่สามารถจัดลำดับคิวได้อีกหรือไม่ต้องใช้ทุกฟิลด์บนแผนที่
เครื่องหลังไม่ตรงกันเนื่องจากบางฟิลด์ว่างเปล่าในขณะที่วางบางรายการลงใน "เครื่องเป้" ดังนั้นมันอาจจะเป็นบางส่วนที่ขยายออกไป แต่ส่วนใหญ่อัลกอริธึมอาจไม่สามารถใช้ได้เนื่องจากสิ่งนี้
Wikipedia ให้คำแนะนำบางประการเกี่ยวกับการจัดหมวดหมู่ที่นี่: http://en.wikipedia.org/wiki/Game_theory#Types_of_games
ฉันจะจัดหมวดหมู่เป็น "ปัญหาการควบคุมที่เหมาะสมแบบไม่ต่อเนื่องเวลา" ( http://en.wikipedia.org/wiki/Optimal_control ) แต่ฉันไม่คิดว่าสิ่งนี้จะช่วยคุณได้
อัลกอริทึม
ในกรณีที่คุณรู้จักคิวที่สมบูรณ์จริง ๆ คุณสามารถใช้อัลกอริทึมการค้นหาแบบทรี ดังที่คุณกล่าวความซับซ้อนของปัญหาจะเพิ่มขึ้นอย่างรวดเร็วด้วยความยาวของคิว ฉันแนะนำให้ใช้อัลกอริทึมเช่น "การค้นหาความลึกครั้งแรก (DFS)" ซึ่งไม่ต้องการหน่วยความจำมาก เนื่องจากคะแนนไม่สำคัญกับคุณคุณสามารถหยุดได้หลังจากพบวิธีแก้ไขปัญหาแรก ในการตัดสินใจว่าจะค้นหาสาขาย่อยใดก่อนคุณควรใช้การวิเคราะห์พฤติกรรมแบบสั่งซื้อ นั่นหมายความว่าคุณควรเขียนฟังก์ชันการประเมินผล (เช่น: จำนวนของฟิลด์ที่ว่างเปล่า, อันนี้ซับซ้อนมากขึ้น, ที่ดีกว่า), ที่ให้คะแนนเพื่อเปรียบเทียบว่าการเคลื่อนไหวครั้งต่อไปใดที่มีแนวโน้มมากที่สุด
จากนั้นคุณต้องการเพียงส่วนต่อไปนี้:
- รูปแบบของสถานะของเกมที่เก็บข้อมูลทั้งหมดของเกม (เช่นสถานะของบอร์ด / แผนที่, คิว, ย้ายหมายเลข / ตำแหน่งในคิว)
- ตัวสร้างการเคลื่อนย้ายซึ่งให้การเคลื่อนไหวที่ถูกต้องทั้งหมดสำหรับสถานะเกมที่กำหนด
- ฟังก์ชั่น "do move" และ "undo move" ซึ่งใช้ / เลิกทำการย้าย (ถูกต้อง) ที่กำหนดไปยังสถานะเกม ในขณะที่ฟังก์ชั่น "do move" ควรเก็บ "เลิกทำข้อมูล" สำหรับฟังก์ชั่น "เลิกทำ" การคัดลอกสถานะเกมและการแก้ไขในแต่ละการวนซ้ำจะทำให้การค้นหาช้าลงอย่างมาก! ลองอย่างน้อยที่สุดเพื่อเก็บสถานะไว้ในสแต็ก (= ตัวแปรท้องถิ่นไม่มีการจัดสรรแบบไดนามิกโดยใช้ "ใหม่")
- ฟังก์ชั่นการประเมินผลซึ่งให้คะแนนเทียบเคียงสำหรับแต่ละสถานะของเกม
- ฟังก์ชั่นการค้นหา
นี่คือการดำเนินการอ้างอิงที่ไม่สมบูรณ์สำหรับการค้นหาเชิงลึกครั้งแรก:
public class Item
{
// TODO... represents queue items (FLOWER, SHOVEL, BUTTERFLY)
}
public class Field
{
// TODO... represents field on the board (EMPTY or FLOWER)
}
public class Modification {
int x, y;
Field originalValue, newValue;
public Modification(int x, int y, Field originalValue, newValue) {
this.x = x;
this.y = y;
this.originalValue = originalValue;
this.newValue = newValue;
}
public void Do(GameState state) {
state.board[x,y] = newValue;
}
public void Undo(GameState state) {
state.board[x,y] = originalValue;
}
}
class Move : ICompareable {
// score; from evaluation function
public int score;
// List of modifications to do/undo to execute the move or to undo it
Modification[] modifications;
// Information for later knowing, what "control" action has been chosen
public int x, y; // target field chosen
public int x2, y2; // secondary target field chosen (e.g. if moving a field)
public Move(GameState state, Modification[] modifications, int score, int x, int y, int x2 = -1, int y2 = -1) {
this.modifications = modifications;
this.score = score;
this.x = x;
this.y = y;
this.x2 = x2;
this.y2 = y2;
}
public int CompareTo(Move other)
{
return other.score - this.score; // less than 0, if "this" precededs "other"...
}
public virtual void Do(GameState state)
{
foreach(Modification m in modifications) m.Do(state);
state.queueindex++;
}
public virtual void Undo(GameState state)
{
--state.queueindex;
for (int i = m.length - 1; i >= 0; --i) m.Undo(state); // undo modification in reversed order
}
}
class GameState {
public Item[] queue;
public Field[][] board;
public int queueindex;
public GameState(Field[][] board, Item[] queue) {
this.board = board;
this.queue = queue;
this.queueindex = 0;
}
private int Evaluate()
{
int value = 0;
// TODO: Calculate some reasonable value for the game state...
return value;
}
private List<Modification> SimulateAutomaticChanges(ref int score) {
List<Modification> modifications = new List<Modification>();
// TODO: estimate all "remove" flowers or recoler them according to game rules
// and store all changes into modifications...
if (modifications.Count() > 0) {
foreach(Modification modification in modifications) modification.Do(this);
// Recursively call this function, for cases of chain reactions...
List<Modification> moreModifications = SimulateAutomaticChanges();
foreach(Modification modification in modifications) modification.Undo(this);
// Add recursively generated moves...
modifications.AddRange(moreModifications);
} else {
score = Evaluate();
}
return modifications;
}
// Helper function for move generator...
private void MoveListAdd(List<Move> movelist, List<Modifications> modifications, int x, int y, int x2 = -1, int y2 = -1) {
foreach(Modification modification in modifications) modification.Do(this);
int score;
List<Modification> autoChanges = SimulateAutomaticChanges(score);
foreach(Modification modification in modifications) modification.Undo(this);
modifications.AddRange(autoChanges);
movelist.Add(new Move(this, modifications, score, x, y, x2, y2));
}
private List<Move> getValidMoves() {
List<Move> movelist = new List<Move>();
Item nextItem = queue[queueindex];
const int MAX = board.length * board[0].length + 2;
if (nextItem.ItemType == Item.SHOVEL)
{
for (int x = 0; x < board.length; ++x)
{
for (int y = 0; y < board[x].length; ++y)
{
// TODO: Check if valid, else "continue;"
for (int x2 = 0; x2 < board.length; ++x2)
{
for(int y2 = 0; y2 < board[x].length; ++y2) {
List<Modifications> modifications = new List<Modifications>();
Item fromItem = board[x][y];
Item toItem = board[x2][y2];
modifications.Add(new Modification(x, y, fromItem, Item.NONE));
modifications.Add(new Modification(x2, y2, toItem, fromItem));
MoveListAdd(movelist, modifications, x, y, x2, y2);
}
}
}
}
} else {
for (int x = 0; x < board.length; ++x)
{
for (int y = 0; y < board[x].length; ++y)
{
// TODO: check if nextItem may be applied here... if not "continue;"
List<Modifications> modifications = new List<Modifications>();
if (nextItem.ItemType == Item.FLOWER) {
// TODO: generate modifications for putting flower at x,y
} else {
// TODO: generate modifications for putting butterfly "nextItem" at x,y
}
MoveListAdd(movelist, modifications, x, y);
}
}
}
// Sort movelist...
movelist.Sort();
return movelist;
}
public List<Move> Search()
{
List<Move> validmoves = getValidMoves();
foreach(Move move in validmoves) {
move.Do(this);
List<Move> solution = Search();
if (solution != null)
{
solution.Prepend(move);
return solution;
}
move.Undo(this);
}
// return "null" as no solution was found in this branch...
// this will also happen if validmoves == empty (e.g. lost game)
return null;
}
}
รหัสนี้ไม่ได้รับการยืนยันว่าใช้งานได้และไม่สามารถรวบรวมได้หรือสมบูรณ์ แต่ควรให้ความคิดกับคุณว่าจะทำอย่างไร งานที่สำคัญที่สุดคือฟังก์ชั่นการประเมินผล ยิ่งมีความซับซ้อนมากขึ้นความพยายาม "อัลกอริธึม" ที่ผิดจะลอง (และต้องยกเลิก) ในภายหลัง สิ่งนี้ช่วยลดความซับซ้อนอย่างมาก
หากนี่ช้าเกินไปคุณสามารถลองใช้วิธีการบางอย่างของเกมสองคนเป็น HashTables เพื่อที่คุณจะต้องคำนวณคีย์แฮ (ซ้ำ) สำหรับแต่ละสถานะของเกมที่คุณประเมินและทำเครื่องหมายสถานะที่ไม่นำไปสู่การแก้ปัญหา เช่นทุกครั้งก่อนที่เมธอด Search () จะส่งคืน "null" จะต้องสร้างรายการ HashTable และเมื่อเข้าสู่การค้นหา () คุณจะต้องตรวจสอบว่าสถานะนี้ผ่านไปแล้วหรือยังโดยที่ไม่มีผลลัพธ์ที่เป็นบวก ตรวจสอบต่อไป. สำหรับสิ่งนี้คุณจะต้องมีตารางแฮชขนาดใหญ่และต้องยอมรับ "การชนกันของแฮช" ซึ่งอาจทำให้คุณไม่พบโซลูชันที่มีอยู่ แต่ไม่น่าเป็นไปได้มากหากฟังก์ชันแฮชของคุณดีพอและตารางของคุณคือ ใหญ่พอ (เป็นความเสี่ยงของความเสี่ยงที่คำนวณได้)
ฉันคิดว่าไม่มีอัลกอริธึมอื่นที่จะแก้ปัญหานี้ (ตามที่คุณอธิบาย) มีประสิทธิภาพมากกว่าสมมติว่าฟังก์ชั่นการประเมินของคุณเหมาะสมที่สุด ...