I'm using Unity for the front-end visualisations, then the simulation & avionics are run on a headless server that currently uses Havok (realtime physics engine for games) with some layers to add aerodynamic forces and some tweaks to try to keep things as realistic as possible. More info & code behind the aerodynamics here: https://avionic.dev/blog/simulating-aerodynamics/
This video: https://youtu.be/GSJPm4uG42M is probably more interesting to the HN crowd than the one I shared in my last comment, and gives a little more context as to what's going on behind the scenes than the abstracted UI dashboard.
1. Unload the wings (usually push the stick forward)
2. Roll shortest path to level (easy to calculate, but needed a bit of tweaking to make a smooth roll input)
3. Set power according to energy state (slow = set full power, fast = set idle power)
4. Pitch back to level (9 out of 10 times this means pulling out of a dive)
Easy to combine 2 and 3 at the same time, more tricky than for humans to get 4 correct because you're often in an overspeed condition where just pulling sharply up will over stress the airframe. But if you take too long you're losing altitude rapidly and may hit the ground.
I think the correct software solution would be to always aim for a min(3.8, stall load) G pull (the maximum is 3.8 for a normal aircraft to not break), calculate the expected altitude loss for current speed and if you're going to hit the ground pull as hard as you need to miss the ground by a small margin and hope the aircraft doesn't break apart. I haven't written any code for that last part.
And I haven't added rudder input to it other than using the standard function to coordinate rudder with ailerons when rolling level (the sim has this built in).
If the airplane gets as far as to enter a spin condition the procedure above will no longer work and you really need rudder. And the power change for a prop driven aircraft or an asymmetric thrust situation in a multi-engine then has to happen at the start, everything idle even at low speed. So here you enter a more complicated area to determine what the state is and adjust the response as opposed to following the standard rules.