I made a simple A* path finding algorithm, but it i really messed up and only finds the right path sometimes. I'll post the code since there isn't a lot.
| 1 | void AddTile(Tile *tile, bool addToOpenList = true) |
| 2 | { |
| 3 | if(open && !tile->blocked) |
| 4 | { |
| 5 | if(find(openList.begin(), openList.end(), tile) == openList.end()) |
| 6 | { |
| 7 | openList.push_back(tile); |
| 8 | } |
| 9 | } |
| 10 | else if(!tile->blocked) |
| 11 | { |
| 12 | if(find(closedList.begin(), closedList.end(), tile) == closedList.end()) |
| 13 | { |
| 14 | closedList.push_back(tile); |
| 15 | tile->pathMark = true; |
| 16 | } |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | void FindPath(int startX, int startY, int endX, int endY) |
| 21 | { |
| 22 | bool pathFound = false; |
| 23 | int currentX = startX, currentY = startY; |
| 24 | AddTile(GetTile(startX, startY), false); |
| 25 | GetTile(endX, endY)->end = true; |
| 26 | |
| 27 | while(!pathFound) |
| 28 | { |
| 29 | //add the tiles around the current tile to check them |
| 30 | if(GetTile(currentX - 1, currentY)) AddTile(GetTile(currentX - 1, currentY)); |
| 31 | if(GetTile(currentX, currentY - 1)) AddTile(GetTile(currentX, currentY - 1)); |
| 32 | if(GetTile(currentX + 1, currentY)) AddTile(GetTile(currentX + 1, currentY)); |
| 33 | if(GetTile(currentX, currentY + 1)) AddTile(GetTile(currentX, currentY + 1)); |
| 34 | vector<Tile *>::iterator it = openList.begin(); |
| 35 | int currentLow = 1000000; |
| 36 | Tile *currentTile = NULL; |
| 37 | //find the tile with the lowest F score |
| 38 | while(it != openList.end()) |
| 39 | { |
| 40 | (*it)->h = 32 * (abs((*it)->x / 32 - endX) + abs((*it)->y / 32 - endY)); |
| 41 | (*it)->g = closedList.size() * 32; |
| 42 | (*it)->f = (*it)->h + (*it)->g; |
| 43 | (*it)->checked = true; |
| 44 | //check if following this path would be faster |
| 45 | if((*it)->f < currentLow) |
| 46 | { |
| 47 | currentLow = (*it)->f; |
| 48 | currentTile = (*it); |
| 49 | } |
| 50 | it++; |
| 51 | } |
| 52 | //add the tile to the closed list |
| 53 | AddTile(currentTile, false); |
| 54 | currentX = currentTile->x / 32; |
| 55 | currentY = currentTile->y / 32; |
| 56 | //check if the tile we just added is the destination |
| 57 | if(currentTile->x / 32 == endX && currentTile->y / 32 == endY) pathFound = true; |
| 58 | } |
| 59 | } |
I realize this isn't very optimized, I just want it to work before I try speeding it up. The main problem is that it only makes paths that look like right angles, and sometimes it doesn't work at all when it meets blocked tiles.
EDIT:
Ugh, I guess I should change this so it's more like a linked list where the tiles have parents. I'm not sure how I would do the search though, would I just keep going through the whole list and adding more tiles to check which way it the shortest?
Try the following code
int tempx = (*it)->x / 32 - endX, tempy = (*it)->y / 32 - endY; (*it)->h = tempx*tempx + tempy*tempy;
instead of your own to compute h. When you call
sqrt((*it)->h);
you get the distance between the current tile and the destination tile. But in this case you don't need to do that, especially if you don't want to waste runtime.
Heh, thanks for the help, but I've already fixed it. It works perfectly now, but for some reason the search area is larger than it should be.
I suppose I'll post the updated code, the search issue isn't that much of a problem since it's still very fast, but if anyone can point out something I'm doing wrong that would be nice.
| 1 | struct Tile |
| 2 | { |
| 3 | Tile *parent; |
| 4 | int x, y; |
| 5 | int h, g, f; |
| 6 | bool blocked; |
| 7 | bool onClosedList; |
| 8 | bool start, end; |
| 9 | bool pathMark; |
| 10 | bool checked; |
| 11 | }; |
| 12 | |
| 13 | Tile *GetTile(int x, int y) |
| 14 | { |
| 15 | if(x > -1 && x < 20 && y > -1 && y < 15) |
| 16 | { |
| 17 | int index = (y * 20) + x; |
| 18 | if(index > -1 && index < 300) return &tiles[index]; |
| 19 | } |
| 20 | return NULL; |
| 21 | } |
| 22 | |
| 23 | void AddTile(Tile *parent, Tile *tile, bool open = true) |
| 24 | { |
| 25 | if(open && !tile->blocked) |
| 26 | { |
| 27 | if(!tile->onClosedList) |
| 28 | { |
| 29 | if(find(openList.begin(), openList.end(), tile) != openList.end()) |
| 30 | { |
| 31 | //check if the path if better using that tile |
| 32 | if(parent->g + 32 < tile->g) |
| 33 | { |
| 34 | tile->parent = parent; |
| 35 | tile->g = parent->g + 32; |
| 36 | tile->f = tile->h + tile->g; |
| 37 | } |
| 38 | } |
| 39 | else |
| 40 | { |
| 41 | tile->h = 32 * (abs(tile->x / 32 - endX) + abs(tile->y / 32 - endY)); |
| 42 | tile->g = closedList.size() * 32; |
| 43 | tile->f = tile->h + tile->g; |
| 44 | tile->parent = parent; |
| 45 | tile->checked = true; |
| 46 | openList.push_back(tile); |
| 47 | } |
| 48 | } |
| 49 | } |
| 50 | else if(!tile->blocked) |
| 51 | { |
| 52 | if(find(closedList.begin(), closedList.end(), tile) == closedList.end()) |
| 53 | { |
| 54 | tileAdded = true; |
| 55 | tile->onClosedList = true; |
| 56 | closedList.push_back(tile); |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | void MarkPath(Tile *dest) |
| 62 | { |
| 63 | if(dest->parent != NULL) |
| 64 | { |
| 65 | dest->pathMark = true; |
| 66 | MarkPath(dest->parent); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | void FindPath(int x, int y, int x2, int y2) |
| 71 | { |
| 72 | if(GetTile(x2, y2)->blocked || GetTile(x, y)->blocked) return; |
| 73 | log << "Start: " << clock() << endl; |
| 74 | bool pathFound = false; |
| 75 | startX = x; |
| 76 | startY = y; |
| 77 | endX = x2; |
| 78 | endY = y2; |
| 79 | int currentX = startX, currentY = startY; |
| 80 | AddTile(NULL, GetTile(startX, startY)); |
| 81 | |
| 82 | Tile *currentTile = NULL; |
| 83 | while(1) |
| 84 | { |
| 85 | //find the tile with the lowest F score |
| 86 | vector<Tile *>::iterator it = openList.begin(); |
| 87 | int currentLow = 1000000; |
| 88 | tileAdded = false; |
| 89 | while(it != openList.end()) |
| 90 | { |
| 91 | if(step) DrawTiles(); |
| 92 | if((*it)->f < currentLow && (*it) != currentTile && !(*it)->onClosedList) |
| 93 | { |
| 94 | currentLow = (*it)->f; |
| 95 | currentTile = (*it); |
| 96 | } |
| 97 | it++; |
| 98 | } |
| 99 | //add the tile to the closed list |
| 100 | AddTile(NULL, currentTile, false); |
| 101 | //if the tile was not added, that means that a path could not be found |
| 102 | if(!tileAdded) return; |
| 103 | currentX = currentTile->x / 32; |
| 104 | currentY = currentTile->y / 32; |
| 105 | //check if the tile we just added is the destination |
| 106 | if(currentTile->x / 32 == endX && currentTile->y / 32 == endY) break; |
| 107 | //add the tiles around the current tile to check them |
| 108 | if(GetTile(currentX - 1, currentY) != NULL) AddTile(GetTile(currentX, currentY), GetTile(currentX - 1, currentY)); |
| 109 | if(GetTile(currentX, currentY - 1) != NULL) AddTile(GetTile(currentX, currentY), GetTile(currentX, currentY - 1)); |
| 110 | if(GetTile(currentX + 1, currentY) != NULL) AddTile(GetTile(currentX, currentY), GetTile(currentX + 1, currentY)); |
| 111 | if(GetTile(currentX, currentY + 1) != NULL) AddTile(GetTile(currentX, currentY), GetTile(currentX, currentY + 1)); |
| 112 | //this stuff is just here for stepping through the process |
| 113 | if(step) readkey(), clear_keybuf(); |
| 114 | if(key[KEY_ENTER]) step = false; |
| 115 | } |
| 116 | MarkPath(closedList.back()); |
| 117 | log << "End: " << clock() << endl; |
| 118 | } |