full visualization ready :)))
This commit is contained in:
+5
-3
@@ -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);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
int run_visual(void);
|
||||
+96
-39
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user