public partial class Form1 : Form
    {
        private const int MapWidth = 25;
        private const int MapHeight = 20;
        private const int CellSize = 50;

        private List<Obstacle> obstacles = new List<Obstacle>();
        private List<Robot> robots = new List<Robot>();
        private Timer simulationTimer;
        private bool simulationRunning = false;

        // 默认障碍(可选)
        private readonly List<Point2D> defaultObstacles = new List<Point2D>
        {
            new Point2D(8, 8), new Point2D(9, 8), new Point2D(10, 8),
            new Point2D(15, 12), new Point2D(15, 13), new Point2D(15, 14),
            new Point2D(5, 15), new Point2D(6, 15)
        };

        public Form1()
        {
            InitializeComponent();
            InitializeComponent1();
            DoubleBuffered = true;
            ResetMap();
        }

        private void InitializeComponent1()
        {
            this.Text = "10机器人往返一次模拟(起点终点显示编号)";
            this.ClientSize = new Size(MapWidth * CellSize + 100, MapHeight * CellSize + 60);
            this.Paint += Form1_Paint;
            this.MouseDown += Form1_MouseDown; // 🔧 保留:鼠标点击处理

            Size size = new Size(MapWidth * CellSize, MapHeight * CellSize);

            var btnStart = new Button { Text = "开始", Location = new Point(50, size.Height + 10), Size = new Size(70, 30) };
            var btnStop = new Button { Text = "停止", Location = new Point(150, size.Height + 10), Size = new Size(70, 30) };
            var btnReset = new Button { Text = "重置", Location = new Point(250, size.Height + 10), Size = new Size(70, 30) };

            btnStart.Click += (s, e) => StartSimulation();
            btnStop.Click += (s, e) => StopSimulation();
            btnReset.Click += (s, e) => ResetMap();

            Controls.Add(btnStart);
            Controls.Add(btnStop);
            Controls.Add(btnReset);

            simulationTimer = new Timer { Interval = 400 };
            simulationTimer.Tick += SimulationStep;
        }

        public struct Point2D : IEquatable<Point2D>
        {
            public int X, Y;
            public Point2D(int x, int y) => (X, Y) = (x, y);
            public bool Equals(Point2D other) => X == other.X && Y == other.Y;
            public override bool Equals(object obj) => obj is Point2D p && Equals(p);
            public override int GetHashCode() => HashCode.Combine(X, Y);
            public static bool operator ==(Point2D a, Point2D b) => a.Equals(b);
            public static bool operator !=(Point2D a, Point2D b) => !a.Equals(b);
        }

        public class Obstacle
        {
            public Point2D Position { get; set; }
        }

        public enum RobotState
        {
            Going,       // 起点 → 终点
            Returning,   // 终点 → 起点
            Finished     // 已完成往返
        }

        public class Robot
        {
            public int Id { get; set; }
            public Point2D Start { get; set; }
            public Point2D Goal { get; set; }
            public Point2D CurrentPosition { get; set; }
            public List<Point2D> Path { get; set; } = new List<Point2D>();
            public RobotState State { get; set; } = RobotState.Going;

            public Point2D CurrentTarget
            {
                get
                {
                    return State switch
                    {
                        RobotState.Going => Goal,
                        RobotState.Returning => Start,
                        _ => CurrentPosition
                    };
                }
            }

            public bool IsFinished => State == RobotState.Finished;
        }

        private void ResetMap()
        {
            StopSimulation();
            obstacles = defaultObstacles.Select(p => new Obstacle { Position = p }).ToList();
            robots = new List<Robot>();

            // 自动生成10个不重叠的机器人
            var used = new HashSet<Point2D>();
            Random rand = new Random(42); // 固定种子便于调试

            for (int i = 0; i < 10; i++)
            {
                Point2D start, goal;
                do
                {
                    start = new Point2D(rand.Next(1, MapWidth - 5), rand.Next(1, MapHeight - 5));
                    goal = new Point2D(rand.Next(start.X + 3, Math.Min(start.X + 8, MapWidth - 1)),
                                       rand.Next(start.Y + 3, Math.Min(start.Y + 8, MapHeight - 1)));
                } while (used.Contains(start) || used.Contains(goal) ||
                         obstacles.Any(o => o.Position == start || o.Position == goal));

                used.Add(start);
                used.Add(goal);

                robots.Add(new Robot
                {
                    Id = i + 1,
                    Start = start,
                    Goal = goal,
                    CurrentPosition = start,
                    Path = new List<Point2D> { start },
                    State = RobotState.Going
                });
            }

            Invalidate();
        }

        private void StartSimulation()
        {
            if (!robots.Any(r => !r.Start.Equals(r.Goal)))
            {
                MessageBox.Show("没有有效的机器人!");
                return;
            }
            simulationRunning = true;
            simulationTimer.Start();
        }

        private void StopSimulation()
        {
            simulationRunning = false;
            simulationTimer.Stop();
        }

        // 🔧 保留:鼠标点击处理障碍物编辑
        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            // 获取点击的格子坐标
            int gridX = e.X / CellSize;
            int gridY = e.Y / CellSize;

            // 检查是否在地图范围内
            if (gridX < 0 || gridX >= MapWidth || gridY < 0 || gridY >= MapHeight)
                return;

            var clickedPos = new Point2D(gridX, gridY);

            // 检查该位置是否被机器人占据
            var robotAtPos = robots.FirstOrDefault(r => r.CurrentPosition.Equals(clickedPos));

            if (robotAtPos != null)
            {
                MessageBox.Show($"不能在机器人位置放置障碍物!机器人 {robotAtPos.Id} 在此处。");
                return;
            }

            // 检查是否是机器人起点或终点
            var isStartOrGoal = robots.Any(r => r.Start.Equals(clickedPos) || r.Goal.Equals(clickedPos));
            if (isStartOrGoal)
            {
                MessageBox.Show("不能在机器人起点或终点放置障碍物!");
                return;
            }

            // 切换障碍物状态
            var existingObstacle = obstacles.FirstOrDefault(o => o.Position.Equals(clickedPos));
            if (existingObstacle != null)
            {
                // 移除障碍物
                obstacles.Remove(existingObstacle);
            }
            else
            {
                // 添加障碍物
                obstacles.Add(new Obstacle { Position = clickedPos });
            }

            // 重绘界面
            Invalidate();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.Clear(Color.White);

            // 绘制网格
            for (int x = 0; x < MapWidth; x++)
            {
                for (int y = 0; y < MapHeight; y++)
                {
                    Rectangle rect = new Rectangle(x * CellSize, y * CellSize, CellSize, CellSize);
                    Brush bg = Brushes.White;

                    if (obstacles.Any(o => o.Position.X == x && o.Position.Y == y))
                        bg = Brushes.Gray; // 障碍物
                    else
                    {
                        bool isStart = robots.Any(r => r.Start.X == x && r.Start.Y == y);
                        bool isGoal = robots.Any(r => r.Goal.X == x && r.Goal.Y == y);
                        if (isStart && isGoal) bg = Brushes.LightYellow;
                        else if (isStart) bg = Brushes.LightGreen;
                        else if (isGoal) bg = Brushes.LightPink;
                    }

                    g.FillRectangle(bg, rect);
                    g.DrawRectangle(Pens.Black, rect);
                }
            }

            // 绘制机器人
            foreach (var robot in robots)
            {
                DrawLabeledCircle(g, robot.Start, Color.Green, robot.Id.ToString(), 0.4f); // 🔧 起点显示编号
                DrawLabeledCircle(g, robot.Goal, Color.Red, robot.Id.ToString(), 0.4f); // 🔧 终点显示编号
                Color robotColor = robot.IsFinished ? Color.DarkGray : Color.Blue;
                DrawLabeledCircle(g, robot.CurrentPosition, robotColor, robot.Id.ToString(), 1.0f);
            }

            // 绘制路径(仅运行时或未完成)
            if (simulationRunning || robots.Any(r => !r.IsFinished))
            {
                foreach (var robot in robots.Where(r => !r.IsFinished && r.Path.Count >= 2))
                {
                    Color pathColor = robot.State == RobotState.Returning
                        ? Color.FromArgb(200, 255, 100, 100) // 返程:红
                        : Color.FromArgb(200, 100, 100, 255); // 去程:蓝

                    using (Pen pen = new Pen(pathColor, 2))
                    {
                        pen.DashStyle = DashStyle.Dot;
                        var points = robot.Path.Select(p =>
                            new PointF(p.X * CellSize + CellSize / 2f, p.Y * CellSize + CellSize / 2f)
                        ).ToArray();
                        g.DrawLines(pen, points);
                    }
                }
            }
        }

        private void DrawCircle(Graphics g, Point2D p, Color color, float scale = 1.0f)
        {
            float radius = CellSize * 0.4f * scale;
            float centerX = p.X * CellSize + CellSize / 2f;
            float centerY = p.Y * CellSize + CellSize / 2f;
            g.FillEllipse(new SolidBrush(color), centerX - radius, centerY - radius, radius * 2, radius * 2);
        }

        private void DrawLabeledCircle(Graphics g, Point2D p, Color color, string label, float scale = 1.0f)
        {
            DrawCircle(g, p, color, scale);
            var font = new Font(FontFamily.GenericSansSerif, 7,FontStyle.Bold);
            var size = g.MeasureString(label, font);
            float centerX = p.X * CellSize + CellSize / 2f;
            float centerY = p.Y * CellSize + CellSize / 2f;
            g.DrawString(label, font, Brushes.White, centerX - size.Width / 2, centerY - size.Height / 2);
        }

        private void SimulationStep(object sender, EventArgs e)
        {
            var activeRobots = robots.Where(r => !r.IsFinished).ToList();
            if (!activeRobots.Any())
            {
                StopSimulation();
                return;
            }

            // 检查是否所有活动机器人都到达当前目标
            bool allReached = activeRobots.All(r => r.CurrentPosition.Equals(r.CurrentTarget));

            if (allReached)
            {
                foreach (var robot in activeRobots)
                {
                    if (robot.State == RobotState.Going)
                    {
                        // 到达终点,开始返程
                        robot.State = RobotState.Returning;
                        robot.Path.Clear();
                        robot.Path.Add(robot.CurrentPosition);
                    }
                    else if (robot.State == RobotState.Returning)
                    {
                        // 到达起点,完成任务
                        robot.State = RobotState.Finished;
                        robot.Path.Clear(); // 清空路径(可选)
                    }
                }
                Invalidate();
                return;
            }

            // 防碰撞:收集当前位置
            var occupied = new HashSet<Point2D>(robots.Select(r => r.CurrentPosition));

            var nextPositions = new Dictionary<int, Point2D>();
            foreach (var robot in activeRobots.OrderBy(r => r.Id))
            {
                if (robot.CurrentPosition.Equals(robot.CurrentTarget)) continue;

                var next = PlanNextStep(robot, occupied);
                nextPositions[robot.Id] = next;
                occupied.Add(next); // 占用新位置
            }

            foreach (var robot in activeRobots)
            {
                if (nextPositions.TryGetValue(robot.Id, out var nextPos))
                {
                    if (!robot.CurrentPosition.Equals(nextPos))
                    {
                        robot.CurrentPosition = nextPos;
                        robot.Path.Add(nextPos);
                    }
                }
            }

            Invalidate();
        }

        private Point2D PlanNextStep(Robot robot, HashSet<Point2D> occupied)
        {
            var start = robot.CurrentPosition;
            var goal = robot.CurrentTarget;

            if (start.Equals(goal)) return start;

            // 修复:使用自定义比较器
            var openSet = new SortedSet<(int f, Point2D pos)>(
                Comparer<(int f, Point2D pos)>.Create((a, b) =>
                {
                    int cmp = a.f.CompareTo(b.f);
                    if (cmp != 0) return cmp;
                    cmp = a.pos.X.CompareTo(b.pos.X);
                    if (cmp != 0) return cmp;
                    return a.pos.Y.CompareTo(b.pos.Y);
                })
            );

            var gScore = new Dictionary<Point2D, int>();
            var cameFrom = new Dictionary<Point2D, Point2D>();

            gScore[start] = 0;
            openSet.Add((Heuristic(start, goal), start));

            while (openSet.Count > 0)
            {
                var current = openSet.Min.pos;
                openSet.Remove(openSet.Min);

                if (current.Equals(goal))
                {
                    var path = new List<Point2D>();
                    var curr = current;
                    while (cameFrom.TryGetValue(curr, out var prev))
                    {
                        path.Add(curr);
                        curr = prev;
                    }
                    path.Reverse();
                    return path.Count > 0 ? path[0] : start;
                }

                foreach (var neighbor in GetNeighbors(current))
                {
                    if (neighbor.X < 0 || neighbor.X >= MapWidth || neighbor.Y < 0 || neighbor.Y >= MapHeight)
                        continue;
                    if (obstacles.Any(o => o.Position.Equals(neighbor)))
                        continue;
                    if (occupied.Contains(neighbor))
                        continue;

                    int tentativeG = gScore.GetValueOrDefault(current, int.MaxValue) + 1;
                    if (tentativeG < gScore.GetValueOrDefault(neighbor, int.MaxValue))
                    {
                        cameFrom[neighbor] = current;
                        gScore[neighbor] = tentativeG;
                        int f = tentativeG + Heuristic(neighbor, goal);
                        openSet.Add((f, neighbor));
                    }
                }
            }

            return start; // 无法移动,原地等待
        }

        private int Heuristic(Point2D a, Point2D b)
        {
            return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y);
        }

        private IEnumerable<Point2D> GetNeighbors(Point2D p)
        {
            yield return new Point2D(p.X + 1, p.Y);
            yield return new Point2D(p.X - 1, p.Y);
            yield return new Point2D(p.X, p.Y + 1);
            yield return new Point2D(p.X, p.Y - 1);
        }
    }

posted on 2025-11-06 00:57  魔法乐  阅读(0)  评论(0)    收藏  举报