Starting off with the A* The algorithm on an old setup wasn’t my brightest idea. It took me forever to realise I didn’t need to draw every step of the algorithm.
I began by cloning the A* world and refactored it into a class-based structure, anticipating multiple agents on the grid. Using objects from the start seemed the best way to keep things tidy.
After hours of work, the refactor was done; the graphics, path tracing, and visuals all worked perfectly. I thought, “Awesome! I’m a genius.” because the only thing left to be added was an isOccupied variable to handle collisions, but it fell apart! Both agents ended up scanning and rendering at once, causing chaos and overlapping paths. Not what I intended, but the code ran… somehow…it was horrible.
What a letdown! I thought this would be easy. I’d already spent hours debugging but couldn’t figure out the issue. It became clear that sticking to the Ancient Brain A* The setup was a dead end. The worst part? I barely knew p5.js. While I understand game dev concepts but not knowing p5 ‘s syntax made things even more frustrating.
I knew I needed a drastic change, so I decided to start from scratch. First, I had to deeply understand the A* algorithm. I switched to Python with Pygame, a framework I’m comfortable with. I found an old Coding Train live stream where he implemented A* in p5.js. Watching it live gave me a solid grasp of the algorithm. I realised I didn’t need to visualise every step; just the final path was what mattered.
Before diving into coding, I took the time to plan it out. I decided each agent would scan the grid, move if it found a path, then pass the turn to the next agent. This round-robin approach would work even as more agents were added, treating others as obstacles and either moving or waiting.
I ran this plan by the professor the next day. We discussed edge cases in detail, mapping everything out on paper to ensure I had a clear strategy before coding.
I felt confident about this plan: “create an agent with a get_path function using A* to find a path and return it as an array of spots. The agent would then move to the first spot and repaint the canvas.”
Although I had to write the entire Grid and Spot classes, I planned to use The Coding Train’s code as a base, so it seemed manageable. I started rewriting everything, and soon enough, I had a basic grid and a working Agent class that generated a path between spots. Success! I was thrilled.
Core Design
The loop was simple: get the path (an array of spots), then call a function to move the agent to the first spot in the path. If no path was found, the agent wouldn’t move. If there was a valid path, it would take one step; otherwise, it stayed put.
I also added an agent boolean in the Spot class to indicate if a spot was occupied. This helped the A* algorithm check not just for walls but also if another agent was blocking the spot.
So, after all that effort, did it actually work? I needed to verify if A* was treating other agents as walls, so I coloured the spots in the path array blue to visualise the final path returned by the get_path function. I also marked the agent spots in orange for a clearer view of what was happening. Here’s the output.
Awesome! The core algorithm was working and I was really proud of the architecture. It was my own! I used a shared path array where each agent stored its calculated path, which I then used for visualising on the canvas. I also added a function to move the agent one step forward in its path. If no path was found, the agent simply stayed put and passed the turn to the next one.
Next, I wanted to add more agents and make it visually appealing. I downloaded some images; trees, Pac-Man ghosts for the agents, and flags for the destination. It took a bit of trial and error, but most of the effort was just figuring out the correct order to draw each element. Here’s how it turned out.
I was really proud of it at this stage, but there was still more to do. The code was in Python with Pygame, so I needed to port it to p5.js. Plus, I wanted to replace the random walls with streets and roads for a more realistic look. I tackled the conversion first. After a few hours of work, everything was running smoothly, except the images. Despite having transparent backgrounds, they appeared with ugly white edges in p5.js. I solved this by creating custom sprites in Figma, matching the background colour of the streets, which successfully hid the unwanted edges.
After making a lot of tweaks to the code and giving it the look of streets with small houses and trees, I decided to design it with compact blocks resembling actual houses. I got inspired by the typical houses you see here in Ireland, where you often have two or three clusters of homes joined together. That was the main inspiration behind this design choice. I also made sure to change the path colour for each agent, making it easy to visualise where each agent intends to go. Here’s what the world looks like now in p5.js.
Things I am Proud of:
- Grid Design: The block-based grid system, enhanced with trees, creates a natural, residential look.
- Core Algorithm: Efficient use of a global path array with a unified function for agent movement aligns well with game dev principles and project requirements.
- Custom Assets: I created unique assets, solved the white background issue, and added diverse house designs for realism.
- Colour Palette: Thoughtful selection of colours for assets and paths enhances clarity and is visually pleasing.
- Incremental Development: Built the project step by step, ensuring each component worked flawlessly before increasing complexity.
A Weird Problem I Encountered
Calling getPath every loop failed, but using it inside an if block worked in both Python and JS. Likely due to how Pygame/p5.js update the canvas. Not sure why, but it now updates and moves correctly.
A Dope Edge Case!
Three agents get stuck in a loop, blocking each other on the recalculated paths. Clip available.
Things That I want to add things that could be better:
- I originally considered using a cross for symmetry in sideways movement but chose circles instead, as they fit the “agent” vibe, resembling mini UFOs. Now, I want to add rotation functionality so the sprite turns and faces the correct direction (heading).
- I want to add street dividers to improve map visualisation. To do this, I’ll need to write some logic to decide whether to place them vertically or horizontally, which will take some effort. However, it’s definitely worth it as it will enhance the aesthetics of the map.
- I want to make the path colour blocks transparent so they don’t cover the Goal Flags, which currently makes it hard to see the destination. Right now, due to how I’m drawing the path, I can’t use the alpha channel because I’m colouring the spot instead of drawing the path over it. This is something I want to improve.
Link to my github repository (python implementation): Visit
Link to my Figma Assets: Visit
Link to The Coding Train Live Stream which helped a lot: Visit
Leave a Reply