some C# cause why not. C# guy might like this? idk. some python too. mostly simulations. WindowsAPI C# is good i guess
This commit is contained in:
316
simulations/astar/astar/Program.cs
Normal file
316
simulations/astar/astar/Program.cs
Normal file
@@ -0,0 +1,316 @@
|
||||
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<Cell> path = new List<Cell>();
|
||||
private HashSet<Cell> openSet = new HashSet<Cell>();
|
||||
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<Cell>();
|
||||
current.Visited = true;
|
||||
var random = new Random();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var neighbors = new List<Cell>();
|
||||
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<Cell> 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<Cell, Cell>();
|
||||
var gScore = new Dictionary<Cell, int>();
|
||||
var fScore = new Dictionary<Cell, int>();
|
||||
|
||||
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<Cell>();
|
||||
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<Cell>();
|
||||
}
|
||||
|
||||
private List<Cell> GetNeighbors(Cell cell)
|
||||
{
|
||||
var neighbors = new List<Cell>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
9
simulations/astar/astar/astar.csproj
Normal file
9
simulations/astar/astar/astar.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -7,7 +7,7 @@ WIDTH = 600
|
||||
ROWS = 20
|
||||
CELL_SIZE = WIDTH // ROWS
|
||||
FPS = 60
|
||||
DELAY = 0
|
||||
DELAY = 0.1
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
|
||||
Reference in New Issue
Block a user