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);
}
}