5. 8-Puzzle 智慧盤系統

using System.Drawing.Printing;

namespace Q5
{
    public partial class Form1 : Form
    {
        public static int[] goal =
        {
            4,5,6,
            3,0,7,
            2,1,8
        };

        public class State
        {
            public int[] board;
            public int blankIdx;
            public int g, h;
            public int f => g + h;
            public State parent;

            public State(int[] board)
            {
                this.board = board;
                blankIdx = Array.IndexOf(board, 0);
            }

            public State Clone()
            {
                var clone = new State((int[])board.Clone());
                clone.h = h;
                clone.g = g;
                clone.parent = parent;
                return clone;
            }

            public void CalcDist()
            {
                int dist = 0;
                for (int i = 0; i < 9; i++)
                {
                    var v = board[i];
                    if (v == 0) continue;
                    var x = i % 3;
                    var y = i / 3;

                    var targetX = Array.IndexOf(goal, v) % 3;
                    var targetY = Array.IndexOf(goal, v) / 3;

                    dist += Math.Abs(x - targetX) + Math.Abs(y - targetY);
                }
                h = dist;
            }

            public bool IsGoal => board.SequenceEqual(goal);

            public State Move(int dx, int dy)
            {
                int x = blankIdx % 3;
                int y = blankIdx / 3;
                int nx = x + dx;
                int ny = y + dy;
                if (nx < 0 || ny < 0 || nx > 2 || ny > 2) return null;
                var clone = Clone();
                clone.parent = this;
                clone.board[blankIdx] = clone.board[ny * 3 + nx];
                clone.board[ny * 3 + nx] = 0;
                clone.blankIdx = ny * 3 + nx;

                clone.g++;
                clone.CalcDist();

                return clone;
            }

            public override int GetHashCode()
            {
                var hc = new HashCode();
                foreach (var item in board)
                {
                    hc.Add(item);
                }
                return hc.ToHashCode();
            }

            public override bool Equals(object? obj)
            {
                State k = (State)obj;
                return k.board.SequenceEqual(board);
            }

            public override string ToString()
            {
                return string.Join(",",board);
            }
        }

        public Form1()
        {
            InitializeComponent();
            draw();
            drawSub(flowLayoutPanel2, goal);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            Close();
        }

        State s = new State([2,5,1,6,0,4,8,7,3]);

        int lastClick = -1;

        void drawSub(FlowLayoutPanel panel, int[] arr)
        {
            panel.Controls.Clear();
            for (int i = 0; i < 9; i++)
            {
                var lbl = new Label();

                int k = i;
                if (arr != goal)
                {
                    lbl.Click += (z, c) =>
                    {
                        if (lastClick == -1)
                        {
                            lastClick = k;
                        }
                        else
                        {
                            (s.board[lastClick], s.board[k]) =
                            (s.board[k], s.board[lastClick]);
                            draw();
                            lastClick = -1;
                        }
                    };
                }

                lbl.Width = 70;
                lbl.Margin = Padding.Empty;
                lbl.Height = 70;
                lbl.TextAlign = ContentAlignment.MiddleCenter;
                lbl.BorderStyle = BorderStyle.FixedSingle;
                lbl.Text = arr[i] == 0 ? "" : arr[i].ToString();
                panel.Controls.Add(lbl);
            }
        }

        void draw()
        {
            drawSub(flowLayoutPanel1, s.board);
        }

        Random rd = new Random();
        private void button1_Click(object sender, EventArgs e)
        {
            var rdl = new List<int>();
            for (int i = 0; i < 9; i++)
            {
                int k;
                do
                {
                    k = rd.Next(9);
                } while (rdl.Contains(k));
                rdl.Add(k);
            }
            s = new State(rdl.ToArray());
            draw();
        }

        public int[][] dirs ={
[0,-1],
[0,1],
[1,0],
[-1,0]
        };

        public State? solve()
        {
            s.parent = null;
            s.g=0;
            s.CalcDist();

            var pq = new PriorityQueue<State, int>();
            pq.Enqueue(s, s.f);

            var visited = new HashSet<String>();
            while (pq.Count > 0)
            {
                var cur = pq.Dequeue();
                if (cur.IsGoal) return cur;
                if (!visited.Add(cur.ToString())) continue;
                
                foreach (var item in dirs)
                {
                    var next = cur.Move(item[0], item[1]);
                    if (next == null) continue;
                    if (visited.Contains(next.ToString())) continue;
                    pq.Enqueue(next, next.f);
                }
            }
            MessageBox.Show("無解");
            return null;
        }

        public List<State> path(State ans)
        {
            List<State> path = [];
            while (ans != null)
            {
                path.Add(ans);
                ans = ans.parent;
            }
            path.Reverse();

            return path;
        }

        async void run(int delay)
        {
            var ans = solve();
            if (ans == null)
            {
                return;
            }

            var step = -1;
            foreach (var item in path(ans))
            {
                step++;
                adaa.Text = step.ToString();
                s = item;
                draw();
                await Task.Delay(delay);
            }
        }

        private async void button2_Click(object sender, EventArgs e)
        {
            run(100);
        }

        private async void button3_Click(object sender, EventArgs e)
        {
            run(500);
        }
    }
}

Last updated