using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace MazePathfinding { public class Cell { public int Row { get; set; } public int Col { get; set; } public int X { get; set; } public int Y { get; set; } public bool[] Walls { get; set; } = new bool[4]; // top, right, bottom, left public bool Visited { get; set; } public Cell(int row, int col, int cellSize) { Row = row; Col = col; X = col * cellSize; Y = row * cellSize; for (int i = 0; i < 4; i++) Walls[i] = true; Visited = false; } public void Draw(Graphics g, int cellSize) { if (Visited) { using (var brush = new SolidBrush(Color.White)) g.FillRectangle(brush, X, Y, cellSize, cellSize); } using (var pen = new Pen(Color.Black, 2)) { if (Walls[0]) g.DrawLine(pen, X, Y, X + cellSize, Y); if (Walls[1]) g.DrawLine(pen, X + cellSize, Y, X + cellSize, Y + cellSize); if (Walls[2]) g.DrawLine(pen, X + cellSize, Y + cellSize, X, Y + cellSize); if (Walls[3]) g.DrawLine(pen, X, Y + cellSize, X, Y); } } public void Highlight(Graphics g, Color color, int cellSize) { using (var brush = new SolidBrush(color)) g.FillRectangle(brush, X, Y, cellSize, cellSize); } } public class MazeForm : Form { private const int WIDTH = 600; private int rows = 20; private int cellSize; private Cell[,] grid; private Cell start; private Cell end; private List path = new List(); private HashSet openSet = new HashSet(); private System.Windows.Forms.Timer timer; private bool isSearching = false; public MazeForm() { Text = "A* Maze Pathfinding - Press SPACE to solve, UP/DOWN to resize"; ClientSize = new Size(WIDTH, WIDTH); DoubleBuffered = true; KeyPreview = true; FormBorderStyle = FormBorderStyle.FixedSingle; MaximizeBox = false; cellSize = WIDTH / rows; CreateMaze(rows); timer = new System.Windows.Forms.Timer { Interval = 16 }; timer.Tick += (s, e) => Invalidate(); timer.Start(); this.KeyDown += MazeForm_KeyDown; this.Paint += MazeForm_Paint; } private void CreateMaze(int r) { rows = r; cellSize = WIDTH / rows; grid = new Cell[rows, rows]; for (int i = 0; i < rows; i++) for (int j = 0; j < rows; j++) grid[i, j] = new Cell(i, j, cellSize); GenerateMaze(grid[0, 0]); start = grid[0, 0]; end = grid[rows - 1, rows - 1]; path.Clear(); openSet.Clear(); } private void GenerateMaze(Cell current) { var stack = new Stack(); current.Visited = true; var random = new Random(); while (true) { var neighbors = new List(); int r = current.Row, c = current.Col; if (r > 0 && !grid[r - 1, c].Visited) neighbors.Add(grid[r - 1, c]); if (r < rows - 1 && !grid[r + 1, c].Visited) neighbors.Add(grid[r + 1, c]); if (c > 0 && !grid[r, c - 1].Visited) neighbors.Add(grid[r, c - 1]); if (c < rows - 1 && !grid[r, c + 1].Visited) neighbors.Add(grid[r, c + 1]); if (neighbors.Count > 0) { var nextCell = neighbors[random.Next(neighbors.Count)]; stack.Push(current); RemoveWalls(current, nextCell); nextCell.Visited = true; current = nextCell; } else if (stack.Count > 0) { current = stack.Pop(); } else { break; } } } private void RemoveWalls(Cell a, Cell b) { int dx = a.Col - b.Col; int dy = a.Row - b.Row; if (dx == 1) { a.Walls[3] = false; b.Walls[1] = false; } else if (dx == -1) { a.Walls[1] = false; b.Walls[3] = false; } if (dy == 1) { a.Walls[0] = false; b.Walls[2] = false; } else if (dy == -1) { a.Walls[2] = false; b.Walls[0] = false; } } private async void MazeForm_KeyDown(object sender, KeyEventArgs e) { if (isSearching) return; if (e.KeyCode == Keys.Space) { isSearching = true; path = await Task.Run(() => AStar(start, end)); isSearching = false; Invalidate(); } else if (e.KeyCode == Keys.Up) { CreateMaze(rows + 1); Invalidate(); } else if (e.KeyCode == Keys.Down && rows > 5) { CreateMaze(rows - 1); Invalidate(); } } private int Heuristic(Cell a, Cell b) { return Math.Abs(a.Row - b.Row) + Math.Abs(a.Col - b.Col); } private List AStar(Cell startCell, Cell endCell) { var openSetQueue = new SortedSet<(int f, int count, Cell cell)>( Comparer<(int f, int count, Cell cell)>.Create((a, b) => { int cmp = a.f.CompareTo(b.f); return cmp != 0 ? cmp : a.count.CompareTo(b.count); })); int count = 0; openSetQueue.Add((0, count, startCell)); var cameFrom = new Dictionary(); var gScore = new Dictionary(); var fScore = new Dictionary(); for (int i = 0; i < rows; i++) for (int j = 0; j < rows; j++) { gScore[grid[i, j]] = int.MaxValue; fScore[grid[i, j]] = int.MaxValue; } gScore[startCell] = 0; fScore[startCell] = Heuristic(startCell, endCell); openSet.Clear(); openSet.Add(startCell); while (openSetQueue.Count > 0) { var current = openSetQueue.Min.cell; openSetQueue.Remove(openSetQueue.Min); openSet.Remove(current); if (current == endCell) { var pathResult = new List(); while (cameFrom.ContainsKey(current)) { current = cameFrom[current]; pathResult.Add(current); } openSet.Clear(); return pathResult; } foreach (var neighbor in GetNeighbors(current)) { int tempG = gScore[current] + 1; if (tempG < gScore[neighbor]) { cameFrom[neighbor] = current; gScore[neighbor] = tempG; fScore[neighbor] = tempG + Heuristic(neighbor, endCell); if (!openSet.Contains(neighbor)) { count++; openSetQueue.Add((fScore[neighbor], count, neighbor)); openSet.Add(neighbor); } } } try { Invoke(new Action(() => Invalidate())); } catch { } Thread.Sleep(100); } openSet.Clear(); return new List(); } private List GetNeighbors(Cell cell) { var neighbors = new List(); int r = cell.Row, c = cell.Col; if (!cell.Walls[0] && r > 0) neighbors.Add(grid[r - 1, c]); if (!cell.Walls[1] && c < rows - 1) neighbors.Add(grid[r, c + 1]); if (!cell.Walls[2] && r < rows - 1) neighbors.Add(grid[r + 1, c]); if (!cell.Walls[3] && c > 0) neighbors.Add(grid[r, c - 1]); return neighbors; } private void MazeForm_Paint(object sender, PaintEventArgs e) { var g = e.Graphics; g.Clear(Color.White); for (int i = 0; i < rows; i++) for (int j = 0; j < rows; j++) grid[i, j].Draw(g, cellSize); foreach (var cell in openSet) cell.Highlight(g, Color.LimeGreen, cellSize); foreach (var cell in path) cell.Highlight(g, Color.Yellow, cellSize); start.Highlight(g, Color.Blue, cellSize); end.Highlight(g, Color.Red, cellSize); // Redraw walls over highlights for (int i = 0; i < rows; i++) for (int j = 0; j < rows; j++) grid[i, j].Draw(g, cellSize); } [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MazeForm()); } } }