Developed core game logic for paddles, ball physics, and game state management from scratch, transforming the standard Pong gameplay into a handheld experience, including analog controls using the Playdate's crank.
Utilized the Playdate SDK to create game objects with finite state machines and implemented custom graphics calls for rendering shapes directly on-screen.
Implemented a debug mode to draw information about enemy AI, frame rate, and other game info, expediting the process of fixing bugs whenever they came up.
Optimized code for hardware compatibility, ensuring a smooth transition from the Playdate simulator to the actual handheld device.
Designed a customizable enemy AI that predicts ball trajectory, with adjustable accuracy settings to dynamically scale difficulty.
I really like the Playdate! It's a very fun and quirky handheld, and the barrier to entry for making games for it is very low. The SDK is publicly available, and the documentation is easy to understand -- especially if you already have experience with Lua. This was a fun, low-stakes project that I still come back to every now and then to mess around with when I have the free time.
Learning how to implement draw calls for the Playdate was the most challenging part of this project. Even though the graphics in this game are pretty simple rectangles and circles, it was still tricky to wrap my head around pushing and popping context to the handheld screen. Looking back, this project actually helped me as I learned how to implement custom OpenGL rendering, since the two use similar graphics architecture.
I'm proud of how my enemy AI turned out for this project. When I started I knew I wanted a decently smart enemy paddle that was challenging, but not unbeatable. I did some research, and implemented a few prototypes, but a big challenge was making it so that the AI would run on the device -- since the desktop simulator runs with more power, optimization was needed to port it onto the handheld. In the end, I decided to use a series of raycasts to predict where the ball would make contact with the other side of the screen. What I like about this implementation is that it's easily adjustable: I can choose to add minor offsets on the angle of the casts, and lengthen the time between casts, to mimic the actual imperfections of a human player.
Implemented A* pathfinding algorithm in C++ for a custom professor-provided engine, as part of a game AI course project.
Integrated five different heuristic weight equations: Octile, Chebyshev, Inconsistent, Manhattan, and Euclidean.
Built custom node and grid data structures that fit within the cashe to support efficient pathfinding operations.
Added path smoothing and rubberbanding techniques for more natural movement in resulting paths.
Optimized performance by replacing the open list with a custom bucket queue, precomputing neighbors, and caching computed weights.
This project was extremely interesting and engaging, and my biggest takeaway was my newfound obsession with A*! It's a very versatile algorithm, and even outside of simple wall avoidance like in this demo, I have a lot of interesting ideas for how to implement it in future projects.
Implementation of the algorithm itself was surprisingly the easiest part of this entire project. It's basically just Dijkstra -- which I already understood pretty well -- with extra bells and whistles, so once I understood the differences between the two debugging went pretty quick. The hardest part was everything that came after: A* as a technique is an inch wide and a mile deep, and there's so much additional complexity and extra features to expand on the algorithm itself.
Optimization on this project was also a beast, and trying to do so probably took as much time as programming the actual features did. The most difficult part was programming my own bucket queue data structure, which is basically an array of priority queues arranged in "buckets" with different ranges of values.
Whodunnit -- The Fighting Game! is a project I made for my DES 212: Systems Design class. The project is a one-dimensional fighting game simulation, made in Unity, where the player "interrogates" several enemies, in an attempt to "catch the killer". The goal of the project was to create a well-balanced fighting game, with a variety of attacks and enemies, and to use telemetry data and play testing to ensure the gameplay wasn't too easy or too hard.
I accomplished this by creating a "fast mode" - triggered by pressing the "F" key - which turns off all graphics and makes the simulation run at 6 times the usual speed, running through hundreds of battles with each enemy type, and testing each player AI mode. The AI modes were:
Randomly spamming attacks
Spamming one attack
Playing intelligently, conserving resources and retreating when necessary