การจำแนกประเภท
คำตอบนั้นไม่ใช่เรื่องง่าย ทฤษฎีเกมมีการจำแนกประเภทสำหรับเกม แต่ดูเหมือนจะไม่มีความชัดเจน 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 และเมื่อเข้าสู่การค้นหา () คุณจะต้องตรวจสอบว่าสถานะนี้ผ่านไปแล้วหรือยังโดยที่ไม่มีผลลัพธ์ที่เป็นบวก ตรวจสอบต่อไป. สำหรับสิ่งนี้คุณจะต้องมีตารางแฮชขนาดใหญ่และต้องยอมรับ "การชนกันของแฮช" ซึ่งอาจทำให้คุณไม่พบโซลูชันที่มีอยู่ แต่ไม่น่าเป็นไปได้มากหากฟังก์ชันแฮชของคุณดีพอและตารางของคุณคือ ใหญ่พอ (เป็นความเสี่ยงของความเสี่ยงที่คำนวณได้)
ฉันคิดว่าไม่มีอัลกอริธึมอื่นที่จะแก้ปัญหานี้ (ตามที่คุณอธิบาย) มีประสิทธิภาพมากกว่าสมมติว่าฟังก์ชั่นการประเมินของคุณเหมาะสมที่สุด ...