full visualization ready :)))

This commit is contained in:
Magnus Küderli
2025-10-27 22:52:38 +01:00
parent 887e16328a
commit 2b51443179
6 changed files with 221 additions and 85 deletions
+5 -3
View File
@@ -25,9 +25,11 @@ typedef struct AField {
afield_t generate_astar_field(int rows, int columns, int** col_map);
void get_a_star_path(afield_t* field, Vector2 start, Vector2 goal);
Vector2* get_a_star_path(afield_t *field, Vector2 start, Vector2 goal, int max_searches, int* path_length);
void setup_start_field(afield_t* field, Vector2 start, Vector2 target);
Vector2* do_full_a_star_search(int width, int height, int** collisions, Vector2 start, Vector2 target, int max_searches, int* path_len);
void setup_start_cell(afield_t* field, Vector2 start, Vector2 target);
Vector2 get_best_node(afield_t *field);
void collapse_node_at(afield_t *field, Vector2 node_pos);
@@ -35,6 +37,6 @@ int assign_node_as_parent(afield_t *field, Vector2 target, Vector2 node_pos);
void debug_print_field_collisions(afield_t* field);
void debug_print_field(afield_t* field, bool show_parent);
void debug_print_field(afield_t* field);
int adjusted_euclidean(Vector2 a, Vector2 b);
+2
View File
@@ -0,0 +1,2 @@
int run_visual(void);
+96 -39
View File
@@ -41,6 +41,17 @@ afield_t generate_astar_field(int rows, int columns, int** col_map) {
return field;
}
void free_field(afield_t* field) {
if (!field || !field->map)
return;
for (int i = 0; i < field->rows; i++) {
free(field->map[i]);
}
free(field->map);
field->map = NULL;
}
int adjusted_euclidean(Vector2 a, Vector2 b) {
int dx = b.x - a.x;
int dy = b.y - a.y;
@@ -182,40 +193,91 @@ Vector2 get_best_node(afield_t *field) {
return best_pos;
}
void get_a_star_path(afield_t *field, Vector2 start, Vector2 goal) {
// TODO: check if start or end node is obstacle
Vector2* reconstruct_path(afield_t* field, Vector2 start, Vector2 end, int *out_length) {
// entirely chatgpt i had no motivation left
int rows = field->rows;
int columns = field->columns;
anode_t** map = field->map;
if (start.x == goal.x && start.y == goal.y) {
// no path required
return; // TODO: Return valid empty path
}
int max_len = rows * columns; // safe upper bound
Vector2 *path = malloc(max_len * sizeof(Vector2));
if (!path) return NULL;
// setup start node
anode_t *start_node_p = &field->map[(int)start.y][(int)start.x];
start_node_p->g = 0;
int heuristic = octile_heuristic(start, goal);
start_node_p->h = heuristic;
start_node_p->f = heuristic;
start_node_p->is_collapsed = true;
int count = 0;
Vector2 current = end;
// set values of surrounding nodes
assign_node_as_parent(field, goal, start);
while (!(current.x == start.x && current.y == start.y)) {
path[count++] = current;
bool found_target = false;
while (!found_target) {
// get node with best f score
Vector2 best = get_best_node(field);
if (best.x == -1 && best.y == -1) {
printf("Target can not be reached!!");
}
// collase node with best f score and recalculate children
collapse_node_at(field, best);
assign_node_as_parent(field, goal, best);
// repeat until child is target
}
int px = (int)current.x;
int py = (int)current.y;
// bounds check
if (px < 0 || px >= columns || py < 0 || py >= rows) break;
current = map[py][px].parent;
if (count >= max_len) break; // prevent infinite loops
}
path[count++] = start;
// reverse path so it goes start -> end
for (int i = 0; i < count / 2; i++) {
Vector2 tmp = path[i];
path[i] = path[count - 1 - i];
path[count - 1 - i] = tmp;
}
*out_length = count;
return path;
}
void setup_start_field(afield_t* field, Vector2 start, Vector2 target) {
Vector2* get_a_star_path(afield_t *field, Vector2 start, Vector2 goal, int max_searches, int* path_length) {
setup_start_cell(field, start, goal);
collapse_node_at(field, start);
assign_node_as_parent(field, goal, start);
debug_print_field(field);
for (int x = 0; x < max_searches; x++) {
Vector2 best = get_best_node(field);
if (best.x == -1 || best.y == -1) {
// all search paths exausted
debug_print_field(field);
printf("TARGET NOT REACHABLE!\n");
*path_length = 0;
return NULL;
}
// printf("Best node: %d %d\n", (int)best.x, (int)best.y);
int found = assign_node_as_parent(field, goal, best);
collapse_node_at(field, best);
// printf("collapsed node at: %d %d", (int)best.x, (int)best.y);
if (found) {
// debug_print_field(field);
printf("FOUND TARGET!\n");
return reconstruct_path(field, start, goal, path_length);
}
}
printf("All searches wasted, target not found, quitting!\n");
*path_length = 0;
return NULL;
}
Vector2* do_full_a_star_search(int width, int height, int** collisions, Vector2 start, Vector2 target, int max_searches, int* path_len) {
afield_t field = generate_astar_field(height, width, collisions);
Vector2* path = get_a_star_path(&field, start, target, max_searches, path_len);
free_field(&field);
return path;
}
void setup_start_cell(afield_t* field, Vector2 start, Vector2 target) {
int heuristic = octile_heuristic(start, target);
field->map[(int)start.y][(int)start.x].g = 0;
@@ -234,7 +296,7 @@ void debug_print_field_collisions(afield_t* field) {
printf("\n");
}
void debug_print_field(afield_t* field, bool show_parent) {
void debug_print_field(afield_t* field) {
char* arrows[3][3] = {
{"", "", ""},
{"", "", ""},
@@ -252,18 +314,13 @@ void debug_print_field(afield_t* field, bool show_parent) {
// printf("[]");
// }
else {
if (show_parent) {
Vector2 parent = field->map[r][c].parent;
if (parent.x != -1 && field->map[r][c].is_collapsed) {
Vector2 par_offs = Vector2Subtract(parent, (Vector2) {c, r});
printf("%s", arrows[(int)par_offs.y+1][(int)par_offs.x+1]);
}
else {
printf(" ");
}
Vector2 parent = field->map[r][c].parent;
if (parent.x != -1 && field->map[r][c].is_collapsed) {
Vector2 par_offs = Vector2Subtract(parent, (Vector2) {c, r});
printf("%s", arrows[(int)par_offs.y+1][(int)par_offs.x+1]);
}
else {
printf("%2i", field->map[r][c].f);
printf(" ");
}
}
printf("| ");
+15 -43
View File
@@ -4,6 +4,7 @@
#include <assert.h>
#include "../include/astar.h"
#include "../include/visual.h"
int** create_array(int x, int y) {
int rows = y, cols = x;
@@ -12,22 +13,9 @@ int** create_array(int x, int y) {
myArray[i] = malloc(cols * sizeof(int));
}
int values[10][10] = {
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}
};
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
myArray[i][j] = values[i][j];
myArray[i][j] = -1;
}
}
@@ -35,36 +23,20 @@ int** create_array(int x, int y) {
}
int main() {
int** dyn_array = create_array(10, 10);
afield_t field = generate_astar_field(10, 10, dyn_array);
run_visual();
Vector2 start = { 0, 0 };
Vector2 goal = { 9, 0 };
setup_start_field(&field, start, goal);
collapse_node_at(&field, start);
assign_node_as_parent(&field, goal, start);
debug_print_field(&field, true);
for (int x = 0; x < 100; x++) {
Vector2 best = get_best_node(&field);
if (best.x == -1 || best.y == -1) {
// all search paths exausted
debug_print_field(&field, true);
printf("TARGET NOT REACHABLE!\n");
return 0;
}
// printf("Best node: %d %d\n", (int)best.x, (int)best.y);
// int** collision = create_array(20, 20);
// afield_t field = generate_astar_field(20, 20, collision);
int found = assign_node_as_parent(&field, goal, best);
collapse_node_at(&field, best);
// printf("collapsed node at: %d %d", (int)best.x, (int)best.y);
// Vector2 start = { 0, 0 };
// Vector2 goal = { 19, 19 };
// int path_len = -1;
// Vector2* path = do_full_a_star_search(20, 20, collision, start, goal, 100, &path_len);
// if (!path) { printf("no path"); return 0; }
if (found) {
debug_print_field(&field, true);
printf("FOUND TARGET!\n");
return 0;
}
}
// for (int x = 0; x < path_len; x++) {
// printf("node at [%d|%d]\n", (int)path[x].x, (int)path[x].y);
// }
// free(path);
}
+103
View File
@@ -0,0 +1,103 @@
#include <raylib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "../include/astar.h"
#define GRID_SIZE 20
int run_visual(void) {
const int screenWidth = 800;
const int screenHeight = 800;
const int cellSize = screenWidth / GRID_SIZE;
int path_len = -1;
Vector2* path = NULL;
InitWindow(screenWidth, screenHeight, "A*");
bool grid[GRID_SIZE][GRID_SIZE] = {0};
SetTargetFPS(60);
while (!WindowShouldClose()) {
// Handle mouse clicks
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
Vector2 mousePos = GetMousePosition();
int x = mousePos.x / cellSize;
int y = mousePos.y / cellSize;
if (x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE) {
grid[y][x] = true;
}
}
if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) {
Vector2 mousePos = GetMousePosition();
int x = mousePos.x / cellSize;
int y = mousePos.y / cellSize;
if (x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE) {
grid[y][x] = false;
}
}
if (IsKeyDown(KEY_SPACE)) {
int **collision = malloc(GRID_SIZE * sizeof(int*));
for(int i = 0; i < GRID_SIZE; i++)
collision[i] = malloc(GRID_SIZE * sizeof(int));
for(int i = 0; i < GRID_SIZE; i++) {
for(int j = 0; j < GRID_SIZE; j++) {
collision[i][j] = grid[i][j] ? 8 : -1;
}
}
afield_t field = generate_astar_field(GRID_SIZE, GRID_SIZE, collision);
Vector2 start = { 0, 0 };
Vector2 goal = { GRID_SIZE-1, GRID_SIZE-1 };
if (path) { free(path); }
path = do_full_a_star_search(GRID_SIZE, GRID_SIZE, collision, start, goal, 1000, &path_len);
if (!path) { printf("no path"); }
}
BeginDrawing();
ClearBackground(RAYWHITE);
// Draw grid
for (int y = 0; y < GRID_SIZE; y++) {
for (int x = 0; x < GRID_SIZE; x++) {
bool cell_in_path = false;
if (path) {
for (int i = 0; i < path_len; i++) {
if (path[i].x == x && path[i].y == y) {
cell_in_path = true;
}
}
}
Rectangle cell = { x * cellSize, y * cellSize, cellSize, cellSize };
if (cell_in_path) {
DrawRectangleRec(cell, RED);
}
if (grid[y][x]) {
DrawRectangleRec(cell, DARKBLUE);
}
DrawRectangleLines(cell.x, cell.y, cell.width, cell.height, GRAY);
}
}
EndDrawing();
}
CloseWindow();
return 0;
}
BIN
View File
Binary file not shown.