Built a tower defense in a hackathon and the enemy waves were either trivial or a sudden brick wall on wave 6. The balance was hand-tuned garbage.
I tried to get the model to design the wave spawner and it just gave me count = wave * 10 which is exactly the linear-then-impossible curve I wanted to avoid. The insight was to make the prompt specify the difficulty math explicitly: a smooth budget function, enemy composition rules, and a breather every N waves.
No answers yet, but I'm mostly happy. Curious if anyone has a cleaner formula than the one I forced.
Build a single-file tower defense on 2D canvas, vanilla JS, 60fps. Fixed path (waypoint list), 3 tower types (rapid/low-damage, slow/high-damage, splash), place towers on grid tiles with the mouse, gold economy, 20 waves.
Difficulty design (this is the important part, implement exactly):
1. Each wave has a 'threat budget' B(w) = round(30 * w^1.35). Spend the budget buying enemies from a catalog with per-enemy threat costs; do NOT just multiply a count.
2. Enemy roster: grunt (cost 3, medium hp/speed), runner (cost 5, low hp/high speed), tank (cost 12, high hp/low speed), healer (cost 15, heals nearby). Tanks unlock at wave 4, healers at wave 8.
3. Composition rule: no wave is more than 60% one enemy type by budget; runners appear in bursts of 4-8.
4. Breather: every 5th wave costs 0.8x budget and drops bonus gold, so the curve has relief valleys, not a monotonic wall.
5. Boss every 10th wave: single enemy with 40% of the wave budget as hp and a telegraphed slam.
Game feel: towers recoil on fire, hit-flash on enemies, floating damage numbers, a screen-edge pulse when the base takes damage, and a satisfying coin-collect blip. Show a wave-preview panel listing what's coming. Framerate-independent movement along the path. Print the B(w) table for waves 1-20 before the code.