Simple Game AI
simpsimp> GOAP is more fiddly. There was someone here who made one
for C#, but it was sort of slow to run.
With that glowing review of GOAP out of the way, what's a simple AI for NPC in games? That's right, Dijkstra Maps! Now in one dimension! (Or, technically, two.)
...:...K........@.....
So here we have a tasty treat ":", a Kitten, and a Heroic Hero of Mass Murdering "@". The kitten can be given multiple "goals" with a Dijkstra Map (shown adjacent, below) that has the treat and the hero as "goals" which here would result in the kitten going for the tasty treat, as "3" is a better move than "5". One could also weight the goals by some factor to make more remote goals more desirable than nearer ones.
...:...K........@.....
3210123456654321012345
At this point, however, (remember that time is also a dimension) a crafty crow swoops in and makes off with the tasty treat, whereupon the Dijkstra Map is regenerated, and the best move is to move towards the hero. I kind of ran out of numbers here, which does point to a problem with showing distances in a terminal, unless you allow for two or three characters per level map cell. SDL would give you more options, or even generating a PDF.
.......K........@.....
FEDCBA987654321012345
What a player might see is the kitten going for the treat, and when that fails, changing their goal to be near the player. At worst this is a hallucination of agency on the part of the kitten, which is merely following a sort of heat map to the nearest local minimum. Other tricks to enhance the illusion would be to vary the behavior based on distance (helpfully encoded into the Dijkstra Map), maybe so that far away the Kitten will make a beeline for the player, and closer to them moves more randomly. Or the opposite of that.
Another idea is to have an invisible point moved by drunken walk each turn, and every so many turns to generate a Dijkstra Map to that point. Critters using that map would then seem to wander around the map, as smoothed out by the infrequent updates. If the invisible point can go anywhere, then it might force an entity to take the long way around some obstacle to catch up with it. A problem here is when a critter is trapped in a loop where their best move would lead to what in chess would yield a draw. Avoiding those sorts of glitches may be difficult, and it may require some amount of playtesting to even become aware of them.
With only a few states entity behavior can be enhanced; typical here would be to have "approach" and "flee" Dijkstra Maps, using one or the other depending on whether a target (the player, typically) should be moved towards, or away from. If the flee maps take them to a safe spot, then they could be removed from play as they scamper down some hole or the other, thus providing a solution for the "why are the NPC still at their jobs at 3 AM??" problem. Near sunset one would switch the villagers to their flee map, and off they go!
Such maps (provided that the level is not too large) are fairly efficient to generate, involve code that is not really complicated, and may provide "good enough" behaviors for entities. With a larger world map one would probably be looking at a grid for local movement and a graph to connect larger areas of the world, much like how in the Indiana Jones movies the "sit in a plane" parts involve some stock photography and red lines on a map, while the local action is more detailed.