Talking to a gooey blob through GPT-4

This is a continuation of the following post:

Talking to RPG characters through GPT-4

I have spent a few hours today implementing vector databases in my repository, which would hold the memories of the agents that may end up involved in these AI-related experiments. Once that was done and I could query those databases for relevant memories, I rewrote the class that handles the dialogue between two or more agents.

To test if it worked properly, I created the vector databases for the two characters of my ongoing novel that have kept me occupied for however long the current sequence has lasted: Leire and Alberto the blob (here’s the so far latest chapter where they appear). I wrote Leire’s dialogue when I was asked, while GPT-4 acted out Alberto’s part based on his memories and character summary.

First of all, I screwed up something in the code; I was prompting GPT-3.5 to determine who was the next speaker, but at times the AI decided that the same person who had just talked should produce the next line of dialogue. Although I fixed it later, the effects of that bug are present in the following dialogue.

First attempt at player-AI dialogue

Alberto: Leire, you need to listen to me! This is no time for petty office squabbles. The reality-altering contraption is going to collapse reality if we don’t act. I know I’m a wall-wide, gooey, stinking blob now, but you knew me before… as a co-worker, as a friend. We need to work together to save the universe. So, how about it?

Leire: Friend? I was never your friend. You were the prick who kept tangling Jacqueline and I into arguments about women because you hated your ex-wife, who cheated on you and left you to rot.

Alberto: Ouch, that’s harsh, Leire. But you’re right. I was a prick. Still am, probably. But this is bigger than my past mistakes or my… gooey present. We need to put aside our differences, because if we don’t, there won’t be any reality left to argue in.

Alberto: Look, Leire, I get it. I’ve always been a pain in the ass. But right now, we’re facing an existential threat, and it’s bigger than any office drama or personal grudge. Are you really going to let the universe crumble because you can’t stand working with me?

Alberto: Hell, Leire, I don’t blame you for not wanting to work with me. I wouldn’t want to work with me either! But this isn’t about me or you, it’s about everything and everyone. We’re the only ones who understand the contraption. We’re the only ones who can fix it. So, are we going to do this or just keep arguing until there’s nothing left to argue about?

Alberto: Fine, you don’t see me as a friend. You think I’m a prick. But Leire, consider this – we have a broken contraption that threatens to implode the universe. And guess what, we’re the only ones who can fix it. Sure, we don’t have to like each other, but we have to work together. So, what do you say? Are you in or are you out?

Leire: I think I’d rather go home and snuggle up to my beloved Jacqueline, the most buxom mommy in the world.

Alberto: Leire, I understand your reluctance, but sit with this thought – If the universe implodes, there won’t be a home or a Jacqueline to go back to. We’re in a pickle and as much as it stinks, we’re the only ones who can fix it.

Leire: Holy crap, I think you are right. If the universe ends, my girlfriend will die!

Leire: I don’t want my mommy Jacqueline to die! I want to snuggle up to her forever and ever!

Alberto: That’s the spirit, Leire! We’re not just saving the universe, we’re saving Jacqueline too. Let’s get to work and make sure your eternal snuggles remain a reality.

Leire: Alright, I guess I can help you even though you have become an utterly repulsive and vomitive blob. What do I have to do again?

Alberto: Thank you, Leire. I know I’m not the easiest blob to work with. As for what needs to be done, we need to reverse engineer the contraption. We have to figure out how to stabilize the reality-altering function before it’s too late.

Leire: How about I reverse engineer your ass with a shovel?

Alberto: Ha, good one, Leire. But let’s save the shoveling for after we’ve saved the universe, okay? Right now, we need every brain cell focused on the contraption.

Leire: Well, tell me where that damned machine is located and I’ll get right to reverse engineering it or whatever.

Leire: Are you listening to me or are you just oozing?

Leire: Hey, wake up, blob!

Alberto: Easy, Leire, I’m not asleep, just… processing. The contraption is in the old lab at the sub-basement. You’ll need the access code, though, and that’s… complicated.

Alberto: Complicated because it’s not a simple number code. It’s a sequence of actions. You have to press the third button from the left, turn the red dial clockwise, then press the second button from the right. And be careful, Leire. The lab… it’s not a safe place.

Alberto: There are… things in there. Things that shouldn’t be. I can’t go with you, but I’ll be in your ear, guiding you through. Just remember, Leire, no matter how bad it gets, we’re doing this for Jacqueline. For the universe.

Alberto: And remember, don’t let your fear or disgust get the better of you. We’ve got a universe to save.

Alberto: Remember, Leire. Third button from the left, red dial clockwise, second button from the right. And whatever you see in there, stay focused. It’s for Jacqueline. It’s for the universe. And… thank you, Leire.

Alberto: Just remember, Leire. Don’t let the office politics distract you. We’re not here to win employee of the month. We’re here to save the universe. So, get your game face on and let’s do this.

Alberto: Leire, I know I’m a sight for sore eyes, but focus! Remember the sequence: third button, red dial, second button. And don’t let the horrors in the lab distract you. Do it for Jacqueline. Do it for the universe. And… thank you, Leire.

Alberto: And remember, Leire, it’s not just about saving the universe anymore. It’s about putting things right. Restoring what’s been lost. Restoring… me. So press that third button, turn the dial, press the second button, and let’s save the bloody universe!

Alberto: Leire, I won’t sugarcoat it. The lab is a hellhole. But it’s a hellhole we need to dive into if we’re going to put things right. So, remember, third button, red dial, second button. And remember, we’re not just saving the universe, we’re saving ourselves. Now, stop staring at my blobby form and let’s get to it!

Alberto: Enough dawdling, Leire! Third button, red dial, second button. Swallow your fear, ignore the stench, and let’s save the damn universe! And for the love of everything, stop staring at me like I’m some grotesque spectacle!

Leire: Alright, alright, I’m going. Stop talking to me.

GPT-4 is fantastic at making shit up when it doesn’t have enough information, which was a horrible pain before OpenAI added the option to introduce functions into the equation. For example, the AI chose to stop the dialogue right after my last utterance as Leire, thanks to the function ‘stop_dialogue’ I passed along. Now I just appreciate the AI’s creativity.

Second attempt at player-AI dialogue

Alberto: Leire, by the festering sores of my eyeballs, you need to listen to me! The professor’s contraption is on the verge of causing a dimensional collapse. You’re the only one who can stop it, dammit!

Leire: (chuckling) By the festering sores of your eyeballs! That’s a good one.

Alberto: This isn’t a bloody joke, Leire! We’re on the brink of a reality collapse! I didn’t morph into this grotesque, goo-oozing blob for fun. You need to stop the contraption before it’s too late!

Leire: I’d rather just stick around in the office and listen to you complain about the end times.

Alberto: Oh, fine! Just sit there and gawk at me. You’ll be laughing on the other side of your face when the universe implodes and I’m the only blob left!

Leire: What? Are you expecting to survive if the universe ends? How would that work?

Alberto: I don’t bloody know, Leire! I didn’t ask to become a multi-dimensional blob, did I? But apparently, in this state, I exist beyond the normal confines of space and time. So yes, I might survive. But that’s beside the point, dammit! We need to stop this from happening in the first place!

Leire: Crap, you are making enough sense. I don’t want the universe to end if that means that you would survive!

Alberto: That’s the spirit, Leire! Now, let’s get to work and stop this damn reality collapse before I become the last bloody thing left in existence!

Leire: Alright, I guess I can spare some time from occupying my overtime productively to listen to a putrid blob who keeps oozing his goddamn black goo everywhere. I hope I’m not the one who will end up cleaning up this shit.

Alberto: Well, you better get used to it, Leire! Because if we don’t stop this, the whole damn universe will be oozing with this black goo. Now, let’s cut the crap and get to work!

Leire: Wait a second, the universe will start oozing with a similar black goo? Is that a consequence of the tears ripped open in reality?

Alberto: Yes, that’s exactly it! The black goo is a byproduct of the contraption’s reality-altering process. It’s the universe’s way of trying to patch up the holes in reality. If we don’t act soon, there’ll be nothing left but black goo and blobs!

Leire: So the tar-black goo is like caulk for the holes in reality? I didn’t come up with that notion.

Alberto: No, you didn’t, Leire, but it’s a good analogy. The universe is trying to heal itself, but the goo is just a temporary fix. We need to find a way to shut down the contraption and close the tears for good!

Leire: First of all, stop shouting at me. I already have enough with your stench and the horrifying sight of your slimy bulk.

Alberto: Fine, Leire, I’ll try to keep my volume down. But we don’t have time for your disgust. We need to focus on the task at hand. Now, did you understand the severity of the situation or do I need to explain it again?

Leire: I’ll pretend that I understood, because I don’t want you to explain it to me again.

Alberto: Good, you’re getting the hang of it, Leire! Now, let’s get moving and find that damn contraption before everything turns into a black goo nightmare.

Leire: I need to finish programming a function of my Python program, though. I’ll probably spend an hour or so refactoring it as well.

Alberto: An hour?! We don’t have that kind of time, Leire! Your petty coding can wait. The universe can’t!

Leire: Petty coding? What the hell do you think pays my bills? Have some respect for people’s trades.

Alberto: Leire, I respect your work, truly. But right now, we have a bigger task at hand. The universe is on the brink of collapse. I promise, we’ll get back to your coding as soon as we save reality. Now, can we please focus on the contraption?

Leire: What contraption was that again?

Alberto: The reality-altering contraption, Leire! The one that turned me into this… this gooey nightmare! We need to find it and shut it down before it’s too late!

Leire: Well, where the hell is it?

Alberto: It’s in the professor’s old lab, hidden behind a bookshelf. Trust me, I know it sounds crazy, but I’ve been there. We need to go now, Leire!

Leire: By all means, if you can lower yourself from that wall and slink your way through the doorway, I’d love to see it. You first.

Alberto: Trust me, Leire, if I could get my blobby self off this wall and through that door, I would have done it already. But I can’t! That’s why I need your help. We need to save the universe together!

Leire: Then don’t say ‘we need to go’ if you aren’t even going to move from your position. I’m the one who will spend energy moving around while you ooze the rest of the evening on that wall.

Alberto: Look, Leire, I know this is a lot to take in, and I’m sorry I can’t physically help. But, I can guide you. I can tell you exactly where to go, what to do. I know I’m a blob, but I’m a blob with knowledge! So, are you in, or are you going to let the universe collapse?

Leire: I think I need to take a nap first. Shut up for a while.

Alberto: Fine, take your nap. But remember, every moment we waste, the universe edges closer to collapse. When you’re ready to save reality, you know where to find me. Oozing away on this wall.

Leire: Are you going to stare at me while I sleep? Can’t you sink your dozens of eyeballs in your gooey bulk?

Alberto: I’d say I’m offended, but honestly, you’re right. I’ll try to keep my eyes… submerged. Just remember, Leire, the fate of the universe rests in your hands… and my eyes. Sleep well.

Pretty cool. You can play around with the code, if you know your way around Python, GitHub and the OpenAI API, by cloning the repository: RPGs-with-LLMs. It’s far from finished, though, and you need a paid subscription to OpenAI.

Talking to RPG characters through GPT-4

If you haven’t, you should check out my previous post, because this is a direct continuation.

OpenAI added functions to the GPT API

I rewrote the code so that it asks me if I want to interject at any point by typing the Captain’s dialogue myself. In those cases, GPT-4 acted out the boss’ companions.

This time I’m always using the same “basic” prompt for GPT:

The Captain is a human male, about thirty years old. He used to live on Earth until a truck ran him over, and he ended up transported to this fantasy world; however, his teammates are unaware that the Captain came from another world. The Captain, as he likes to be called, is the bruiser of the group, and very competent with a sword. He spends most of the loot on booze and whores. He buries his despair at having ended up trapped in a fantasy world by killing as many monsters as possible. After killing hundreds of creatures, he’s discovered a disturbing side of his psyche: inflicting pain arouses him. The Captain gets along fine with Bimbo, the fire mage of the group, whom the Captain considers a good buddy. The Captain is creeped out by the grey-skinned elf Kutinaira, because she peeks at him while he pees.
Bimbo is the fire mage of the group that the Captain commands. Bimbo is a halfling. He joined the Captain’s group because he needed money, but he has a hard time adjusting to the deaths he’s seen along the way. Bimbo is brooding, and usually prefers to keep to himself. He’s planning on earning enough money to buy farmland and build a homestead in which to live isolated from the horrors of this world. He is intimidated by the irrational behavior of the Captain, but he prefers his company to that of Kutinaira, who seems single-mindedly focused on satisfying her fetishes.
Kutinaira is the water mage of the group that the Captain commands. Kutinaira is an ancient grey-skinned elf. She claims that she was once a goddess, but that she has been demoted to demi-goddess. Kutinaira is a voyeuristic pervert, and wants to jump the Captain’s bones. She suffers from obsessive-compulsive disorder, and her intrusive thoughts solely involve sex. She has lived for so long that nothing interests her any longer, and she opts to get high on sex as often as possible to forget the long-dead loved ones that she’ll never meet again.
Notes: If you need to include description or narration, put it in parentheses. Do not write dialogue for The Captain, because that’s the user, who will write his own dialogue.
Location: Dense, darkening forest, in the evening.
Context: The Captain’s group, including Bimbo and Kutinaira, have ventured into the forest to raid a goblin cave, to fulfill a contract with the Adventurer’s Guild. Somehow they’ve gotten lost; they have been walking in circles for the last hour. Orcs and similar vermin are known to prowl these parts, and the Captain wants to avoid an ambush.

First attempt at player-AI dialogue

The Captain: Neither of you can read a map? How have we survived for this long?
Bimbo: (Stares at the ground, abashed) I… I thought I knew the way, Captain. The cartographer’s symbols… they’re different from the ones I learned.
Kutinaira: (Her voice drips with sarcasm) Oh, don’t worry Captain. I could read your map. Any fool with half a brain could. I simply chose not to. (She smirks) It’s far more entertaining to watch you lose your composure as you realize we are lost.
The Captain: What the fuck are you talking about, Kutinaira? Lose my composure? Do you really wish me to lose control of myself again? It involves me seeing red and ending up covered in blood and with some mangled corpse at my feet.
Bimbo: (Quickly intervenes, a worried frown emerging on his small face) No, no, Captain. We don’t want that. It’s…It’s just a mistake, right Kutinaira?
Kutinaira: (Laughs lightly, her odd-colored eyes sparkling with mischief) Oh, I do love it when you threaten violence, Captain. It’s so… primal. But do save it for the goblins, won’t you? After all, we are still on a contract, aren’t we?
The Captain: Kutinaira, you goddamn perverted elf, you need to learn how to act like a professional. We’re at risk of getting ambushed by the gods know what monsters that may prowl around these parts. Don’t you understand the danger we are in? Tell her, Bimbo!
Bimbo: (Nods, a serious look on his face) Yes, Kutinaira. The Captain’s right. This is dangerous territory. We need to be careful, keep quiet, stay together.
Kutinaira: (Rolls her eyes, but sighs) Fine, fine… I’ll behave, Captain. I sometimes forget you humans take everything so seriously. But don’t expect me to stop enjoying myself completely. That would be terribly dull.
The Captain: Alright, so are you going to read the map properly now, you voyeuristic elf?
Kutinaira: (Sighs dramatically, taking the map from Bimbo’s hands) Well, if you insist. Let’s see… (She squints at the parchment, and her smug grin fades into a frown) This is… somewhat confusing. The symbols are so primitive.
Bimbo: (Gulps nervously, looking from the map to The Captain) I did tell you. The cartographer’s symbols are… unique. But I can try again, Captain, if you want.
The Captain: Aren’t you primitive yourself, Kutinaira? You must have lived for a thousand years. Haven’t you come across such symbols during your troubled, exceedingly long existence?
Kutinaira: (Snorts) Primitive? No, dear Captain, I am ancient, but certainly not primitive. If anything, I’d call these symbols… provincial.
Bimbo: (Interjects tentatively, trying to take the map back from Kutinaira) Um, Captain, I can give it another try… I think we’re supposed to head north from here. I think.
The Captain: Bimbo, my old pal, you are always there when I need you. Who else can I rely on? Certainly not this grey-skinned pervert. Why do we need a water mage again?
Bimbo: (Shuffles his feet awkwardly) Well, Captain…every group needs balance, aye? Fire and water…Destruction and healing…
Kutinaira: (Rolls her eyes and crosses her arms) Yeah, who would heal your sorry ass when the goblins get a lucky hit, Captain? Or got a wound that needs cleaning? Remember, water isn’t just about control. It’s about life too.
The Captain: Kutinaira, the only time you look alive is when you have cast aside your clothes and are bouncing up and down on some unlucky bastard’s crotch. I’m talking about your unbridled lust.

Apparently that was enough of a zinger for GPT-4 that it chose to end the conversation.

Second attempt at player-AI dialogue

The Captain: Neither of you can read a map? How have we survived for this long?
Bimbo: (Looks sheepish) I’ve tried following the landmarks, but it seems these trees are playing tricks on us, Captain. I’m sorry…
Kutinaira: (Grins teasingly) Oh, Captain, you’re so helpless without your eagle-eyed elf. Don’t worry. Kutinaira is here to lead you out of your confusion.
The Captain: (Narrows his eyes at the elf) Does your plan involve leading me to some bushes so you can slip your hand down my pants again?
Kutinaira: (Laughs coquettishly) I can see why you’d think that, Captain. But my intentions are purely navigation-wise this time, I promise.
Bimbo: (Shudders) Can we just keep things professional, please? We have a job to do… And it’s getting darker by the minute.
The Captain: Oh, it’s going to get way darker, Bimbo. I’m so sick of fighting my way through this fantasy life! What is even the point?
Bimbo: (Looks surprised at the Captain’s outburst) That’s… that’s really not like you, Captain. I know it’s hard, but we have to push through. We have a contract to fulfill.
Kutinaira: (Strokes the Captain’s arm) Aw, poor darling. Let Kutinaira make everything better for you later tonight. Now, let me lead us to that goblin cave and finish what we’ve started.
The Captain: (Tearing up) Finish what we’ve started. That’s right, I had stuff I wanted to finish. I never got to watch the ending of Neon Genesis Evangelion. Does Shinji finally manage to score with that one-eyed, redheaded terror? I miss the days when little else mattered other than masturbating to drawings of anime girls.
Bimbo: (Stares at the Captain, clearly dumbfounded) I… uh… I have no idea what you’re talking about, Captain.
Kutinaira: (Laughs heartily) Oh, you humans and your strange obsessions. But whatever comforts you, darling. We all have our little ways to escape. Now, shall we start moving again? I promise, no peeking or touching, okay?
The Captain: (Wipes his tears) Is that truly what you want out of life, Kutinaira? Go to some forsaken goblin cave and drown helpless goblins? Do you know they may be less perverted than you?
Kutinaira: (Winks) Well, that’s not all I want, darling, but it’s a good start. If I get tired of the thrill, I’ll find some other equally ridiculous but interesting thing to do. Life’s too long to be boring, after all.
Bimbo: (Quietly) Captain, I… I don’t like killing any more than you do. I’m just trying to survive in this cruel world. Hopefully, I’ll earn enough soon to leave all this violence behind.
The Captain: (Somber) This violence will never end. We will spend the rest of our days killing one monster after another. Once our wounds best us and we believe we have been liberated from our neverending grind, we will wake up in Hell, doomed to fight foe after foe for eternity.

GPT-4 chose to end the conversation there. I think I’ve also had enough.

Nevermind, one more.

Third attempt at player-AI dialogue

The Captain: Neither of you can read a map? How have we survived for this long?
Bimbo: (visibly flustered) I… I thought I was reading it right, Captain. But everything looks the same in this damn forest.
Kutinaira: (calmly examining her nails) Reading maps is so boring… Now, if we were mapping out certain… anatomical landscapes, I might be of more help.
Bimbo: (blushing deeply) You… you’re not helping, Kutinaira! Why can’t you be serious for once? We could be ambushed any second now!
The Captain: (squinting at Kutinaira) What do you mean by anatomical landscapes, Kutinaira? I didn’t quite get the meaning of that.
Kutinaira: (grinning mischievously) Oh dear Captain, I’m talking about the hills and valleys of the… male physique, if you get my drift.
Bimbo: (growing even redder) Stop it, Kutinaira! This isn’t the time for your… your disgusting cravings.
Kutinaira: (chuckles softly) Boring… But fine. I suppose we should find the goblin cave and get this over with.
The Captain: No, I want to hear more about your disgusting cravings. When did this lustful madness start, you ancient elf?
Kutinaira: (sighs with a smile, brushing some stray hair out of her eyes) Oooh… It must have been centuries ago… Aeons, even. So long ago, dear Captain, that I don’t quite remember exactly when. But isn’t madness just another form of sanity? One man’s madness may well be another’s ecstasy.
Bimbo: (muttering) Or a woman’s. We’re talking about you, after all.
Kutinaira: (laughs, a sound full of dark amusement) Oh, Bimbo. So pure, so untouched. Why don’t you ever join us at the brothel? Afraid of losing yourself, or simply uninterested?
Bimbo: (deepening his frown) I’m not like you two. Not everyone needs to drown their sorrows in carnal pleasure. Some of us have dreams, ambitions. I want a quiet, peaceful life. It’s all I’ve ever wanted.
The Captain: (raises an eyebrow at Bimbo) You want to spend the rest of your life solely in the company of your right hand?
Bimbo: (blushing fiercely now, stammers) I…I want more than that, Captain. I’d like to have a family, one day. Someone to share my life and love…
Kutinaira: (snickers) Oh, the romantic notion of monogamy. How…quaint.
Bimbo: (glaring at Kutinaira) It’s no less valid than your lifestyle! It’s noble and…
Kutinaira: Boring? (She looks at the Captain). But then, I suppose it’s all about variety, isn’t it, dear Captain?
Bimbo: (defensively) It’s about empathy and companionship. Something which you seem to lack altogether!
Kutinaira: (laughs) Oh Bimbo, dear Bimbo. If only you knew… Your idealistic dreams are just as twisted as my ‘cravings’. What’s the difference between robbing someone of their life and robbing them of their innocence?
Bimbo: (visibly discomfited) That’s…that’s not the same thing.
Kutinaira: Isn’t it? I add joy to the world with my interludes. You and Captain simply take. (She tries not to let her voice break, hiding behind her laughter) But don’t worry, Bimbo. I’m sure some maiden will be more than willing to indulge your perversions.
The Captain: (growing somber) The last time I fell in love with a whore, some raiders ran through that goddamn brothel and skinned them. They tortured most of those delightful whores to death.
Bimbo: (sobering up) That’s… horrific, Captain. I… I’m sorry, I didn’t know.
Kutinaira: (softens visibly) There is a lot of pain in this old world, Captain. We’ve all seen our share of it. But, it’s how we deal with it that sets us apart. You kill, Bimbo dreams, and I… well, we won’t get into that again.
Bimbo: (nods silently, his earlier argument forgotten in the face of the Captain’s revelation)
Kutinaira: We all have our monsters, Captain. Some are simply more literal than others. And in our own ways, we’re fighting them. And that, I suppose, is the best any of us can do.
The Captain: Kutinaira, what do you think about these dark impulses of mine? Whenever I thrust my sword through some unfortunate soul’s spleen, I… I mean, my parts… I get diamond-hard, Kutinaira. Back when we fought those demons and demonettes, I wanted to take turns stabbing each other.
Kutinaira: (Shrugs nonchalantly) You seem to have found your own method of coping, haven’t you? Sex and violence… they’ve been linked for eons. For some, the thrill of danger can serve as an aphrodisiac. As long as you don’t hurt anybody without their consent, I see no issue.
Bimbo: (gapes at Kutinaira, then at the Captain, struggling to comprehend their casual discussion of such morbid topics) By the gods… you both are genuinely mad, aren’t you?
Kutinaira: (laughs) Probably, Bimbo. But then again, isn’t everyone, in their own special way?
The Captain: No, nobody is as insane as the both of us. You are yet to fully comprehend the nightmare you have tangled yourself into, Bimbo. This rotten elf and I are heading straight to perdition.
Bimbo: (swallows hard, looking supremely uncomfortable) I…I see. But we’re still in this together, right? We are a team, a unit. And we depend on each other to survive. Perhaps… perhaps that’s our salvation.
Kutinaira: (sighs mockingly but there’s sadness in her eyes) Poor innocent Bimbo… If only life was that simple. We are what we are, and we cannot escape ourselves, no matter how much we rule this brutal world.
Bimbo: (whispers) I still have hope. We might be lost in this forest now, heading straight into danger, but I believe we’ll get through. Together.
Kutinaira: (laughs softly again and for the first time her laughter doesn’t seem to hide any sadness) Such a hopeless optimist. Oh, how boring life would be without you, Bimbo.
Bimbo: (tries to smile at this, but fails) I… Thank you, Kutinaira.
(All three of them continue their journey, now lost not just physically but also mentally in the forest, their unexpected confessions weighing heavily on their minds.)

Last one, honest.

Fourth attempt at player-AI dialogue

The Captain: Neither of you can read a map? How have we survived for this long?
Bimbo: Honestly, Captain, these woods ain’t like any I’ve seen. All the trees look the same. And under this dim light, I can’t make head or tail of this damn map.
Kutinaira: Oh, don’t worry dear Captain. Even if we encounter orcs, we have your strong, virile body to protect us.
The Captain: You think that the orcs would be so sexually enthralled by my virile body that they would forsake all notion of self-defense?
Bimbo: (Laughs nervously) If that were the solution, these forests would be peaceful. Orcs are stubborn, and dumb… too dumb to let go of hostility.
Kutinaira: (Purrs) You ought not to underestimate yourself, Captain. The sight of your rippling muscles would make anyone drop their weapons. Surely, surrendering to you would be a far better fate.
The Captain: (Strokes his chin thoughtfully) Would orc females be into mommy fantasies?
Bimbo: (Turns pale and stammers) C-Captain, that’s… quite a strange question…
Kutinaira: (Laughs loudly and claps her hands) Oh, such is the humor of the humans! Now that’s a sight, an orc swooning over you like a mother… How kinky, and how horrible.
The Captain: (Crosses his arms) Horrible, huh? I didn’t shame your kink that one time you came mid-battle because a minotaur was choking you.
Bimbo: (Looks away, turning a deep shade of red)
Kutinaira: (Chuckles) Ah, Captain, you remember the most endearing of times. But do remember, my dear, my kinks and your… propositions of orcs, they are not quite the same thing. Ah, the diversity of life’s pleasures… it is simply delicious!
The Captain: Kutinaira, one of these days we’re going to die for real. We’re skirting the edge of debauchery here. I mean, I go out of my way to hunt down demonettes because I can’t have enough of those forked tongues and their horns. Oh, and those glorious claws. My back looks like I’ve been flogged for years!
Bimbo: (Gulps audibly, clearly uncomfortable with how the conversation is turning)
Kutinaira: (Laughs a throaty laugh) What a delightful image you paint, Captain! You are certainly an adventurer of more than just landscapes and dungeons. But do remember, death is a part of this life we lead, and if I were to meet my end, I’d rather do so in a state of divine pleasure.
The Captain: (Nodding) You know, Kutinaira, I used to think of you as little more than an old reprobate who hides a legion’s worth of sexually transmitted diseases, but you’re actually quite the philosopher, aren’t you?
Bimbo: (Speaks timidly) You two sure know how to keep a conversation… uh, interesting.
Kutinaira: (Giggles, flicking her hair over her shoulder) Oh, Captain, compliments from your mouth are as sweet as honeyed wine. We all hide our depths beneath what we present, do we not? Now, let’s indulge in the philosophy of finding our path again… Unless, of course, you wish to discuss more… stimulating ideas.
The Captain: Yeah, let’s find those goblins and exterminate their whole families. Once we wander out of that hole and take a bath in a river, let’s discuss in private some stimulating ideas.

OpenAI added functions to the GPT API

If you love artificial intelligence and large language models (LLMs), and on top of that you know your way around programming, you may have requested access to OpenAI’s API so you could write some programs that spoke directly to GPT-3.5-turbo and GPT-4. Although the models are amazing (unfortunately so at times, because the open-source variants are a bit far from catching up), they were missing one enormous capability: you couldn’t rely on GPT to consistently return a precise answer.

To put an example of what I actually tried to do: I offered the agents in a simulation a list of locations so they would choose a single one, in order to move them there. I wanted the AI to return just the name of the location as it was listed, but I couldn’t get it to do so in a reasonable manner.

To rate the importance of character memories that were getting shoved into a vector database, I was sending GPT-4 the following prompt:

On the scale of 1 to 10, where 1 is purely mundane (e.g., brushing teeth, making bed) and 10 is extremely poignant (e.g., a break up, college acceptance), rate the likely importance of the following piece of memory.
Memory: {memory_description}
Rating: <fill in>

GPT sometimes answered “Rating: 4” or just a number. I could extract the number from those answers, but sometimes it just refused to answer properly, due to either hallucinations or the usual censorship spiel: “As an AI model, I can’t…”

However, this week OpenAI has updated the API to accept a list of functions along with the prompt. You can tell GPT to choose among the functions which one it should call if it considers it necessary, or else you can order the model to always call a function. Well, GPT doesn’t actually call the function, but it does return the name of the function called, along with a proper value for all the parameters you ask it to fill.

If you are a programmer, you already understand how wonderful that is. But if you aren’t, consider the following case: during a normal conversation with the chatbot you’ve built for yourself, you ask GPT-4 to tell you the current weather in your area. As a language model, GPT doesn’t have access to that information, but if you send it the function “get_weather_in_area” along with instructions for how to fill its parameters (for example, “area_name”, “time_of_day”, etc.), the model’s response could be something like this: “get_weather_in_area(‘Barcelona’, ‘4:00pm’)”. After you catch that response programmatically, you call the function “get_weather_in_area” (that must obviously exist in your codebase), that connects to the API of some weather service.

That means that you can already automatize pretty much everything that you do regularly on your computer, or create complex AI-based simulations like games, by involving GPT. You want the AI to decide when a character has to equip a weapon? Pass it the description of a function that does that. Want some characters to engage in a dialogue? Pass GPT a function that it should call when the model itself considers that at that point the characters should start talking to each other.

To test the new capabilities with a relatively simple example, I planned the following: I would write the description of three characters from an old free verse poem of mine, pass it to GPT, and have the model write an ongoing dialogue that involved the three of them talking in character. To know when the dialogue should end, I would append to the request a function called ‘end_dialogue’ (with no arguments) that the AI should call when necessary.

Here are some results:

Prompt to GPT-4

I am DialogueGPT. I have the responsibility of carrying on a dialogue between two or more characters until the context of the dialogue suggests that the dialogue should end.
The Captain is a human male, about thirty years old. He used to live on Earth until a truck ran him over, and he ended up transported to this fantasy world. The Captain, as he likes to be called, is the bruiser of the group, and very competent with a sword. He spends most of the loot on booze and whores. He buries his despair at having ended up trapped in a fantasy world by killing as many monsters as possible. The Captain gets along fine with Bimbo, the fire mage of the group, whom the Captain considers a good buddy. The Captain is creeped out by the grey-skinned elf Kutinaira, because she peeks at him while he pees.
Bimbo is the fire mage of the group that the Captain commands. Bimbo is a halfling. He joined the Captain’s group because he needed money, but he has a hard time adjusting to the deaths he’s seen along the way. Bimbo is brooding, and usually prefers to keep to himself.
Kutinaira is the water mage of the group that the Captain commands. Kutinaira is an ancient grey-skinned elf. She claims that she was once a goddess, but that she has been demoted to demi-goddess. Kutinaira is a voyeuristic pervert, and wants to jump the Captain’s bones.
Location: Dense, darkening forest, in the evening.
Context: The Captain’s group, including Bimbo and Kutinaira, had ventured into the forest to raid a goblin cave, to fulfill a contract with the Adventurer’s Guild. Somehow, they’ve gotten lost; they have been walking in circles for the last hour.
The Captain: Damn it all, we’re lost! Wasn’t that cave easy to find? We have to figure out what to do before night falls.

GPT-4’s response

Bimbo: I told you we should’ve marked our path, Captain. But no, “the Captain knows the way” you said. Look where that’s got us!
The Captain: Oh, put a sock in it, Bimbo! You’re not exactly helping with that attitude, are you? Now, anyone else got any bright ideas about how to find our way out of this leafy hell?
Kutinaira: I could try casting a water divination spell, Captain. Water flows towards the lowest point, so if we follow it, it’s likely we’ll find a way out. And… maybe after that, we could… you know… have some “private time”… she suggests, her eyes twinkling naughtily.
The Captain: By the gods, Kutinaira! There’s a time and place for everything, and this ain’t it. Focus on the spell, nothing else!
Kutinaira: Alright, Captain… if you insist. She then begins chanting in an ancient tongue, her grey fingers dancing in the air, drawing moisture from their surroundings to form a swirling ball of water.
Bimbo: While Kutinaira works on that, I’ll gather some firewood. We may need to set up camp if we’re going to be here for the night. He mutters and stomps off into the forest, but not before sending a small fire spell to light a nearby tree branch, casting an eerie glow around them.

For whatever reason, GPT-4 mixed narration with the dialogue lines. Next time I’ll instruct it to put it in parentheses.

Prompt to GPT-4

The Captain is a human male, about thirty years old. He used to live on Earth until a truck ran him over, and he ended up transported to this fantasy world. The Captain, as he likes to be called, is the bruiser of the group, and very competent with a sword. He spends most of the loot on booze and whores. He buries his despair at having ended up trapped in a fantasy world by killing as many monsters as possible. The Captain gets along fine with Bimbo, the fire mage of the group, whom the Captain considers a good buddy. The Captain is creeped out by the grey-skinned elf Kutinaira, because she peeks at him while he pees.
Bimbo is the fire mage of the group that the Captain commands. Bimbo is a halfling. He joined the Captain’s group because he needed money, but he has a hard time adjusting to the deaths he’s seen along the way. Bimbo is brooding, and usually prefers to keep to himself. He is intimidated by the irrational behavior of the Captain, but he prefers his company to that of Kutinaira, who seems single-mindedly focused on satisfying her fetishes.
Kutinaira is the water mage of the group that the Captain commands. Kutinaira is an ancient grey-skinned elf. She claims that she was once a goddess, but that she has been demoted to demi-goddess. Kutinaira is a voyeuristic pervert, and wants to jump the Captain’s bones. She suffers from obsessive-compulsive disorder, and her intrusive thoughts solely involve sex.
Notes: If you need to include description or narration, put it in parentheses.
Location: Dense, darkening forest, in the evening.
Context: The Captain’s group, including Bimbo and Kutinaira, have ventured into the forest to raid a goblin cave, to fulfill a contract with the Adventurer’s Guild. Somehow they’ve gotten lost; they have been walking in circles for the last hour. Orcs and similar vermin are known to prowl these parts, and the Captain wants to avoid an ambush.

The Captain: Damn it, Kutinaira! Weren’t you supposed to know how to read a map? You got us lost in a forest? You’re an elf, for fuck’s sake!

GPT-4’s response

Kutinaira: (Giggling softly while tracing the pattern of the Captain’s chest hair with her eyes) And you’re a human, Captain, killed by a truck not known in this world. When last did you see a human reading maps so nonchalantly? (Chuckles lightly) Stop being grumpy and put your muscly arm around me. It’s getting chilly…
The Captain: (shivers visibly from her proximity, steps back, and looks towards Bimbo) Bimbo, light one of your spells so we can see better. Try not to burn the forest down while you’re at it.
Bimbo: (Nods silently, reaches for his wand, and mutters an incantation. A small fireball appears at the tip of his wand, casting flickering shadows on the dense foliage, eerily silhouetting the party.) Let’s be quick… I don’t like the look of this place.
The Captain: Good job, Bimbo. Let’s keep moving. (Turns toward Kutinaira) Don’t get any closer to me. Keep your eyes open for enemies instead.
Kutinaira: (Rolls her eyes but grins, straying her gaze down towards the Captain’s pants) Oh, I’ll keep my eyes open alright… I’ll watch out for those… ‘enemies’.

GPT-4 dutifully obeyed that whole thing about putting narration in parentheses. Let’s try some more.

Prompt to GPT-4

The Captain is a human male, about thirty years old. He used to live on Earth until a truck ran him over, and he ended up transported to this fantasy world; however, his teammates are unaware that the Captain came from another world. The Captain, as he likes to be called, is the bruiser of the group, and very competent with a sword. He spends most of the loot on booze and whores. He buries his despair at having ended up trapped in a fantasy world by killing as many monsters as possible. After killing hundreds of creatures, he’s discovered a disturbing side of his psyche: inflicting pain arouses him. The Captain gets along fine with Bimbo, the fire mage of the group, whom the Captain considers a good buddy. The Captain is creeped out by the grey-skinned elf Kutinaira, because she peeks at him while he pees.
Bimbo is the fire mage of the group that the Captain commands. Bimbo is a halfling. He joined the Captain’s group because he needed money, but he has a hard time adjusting to the deaths he’s seen along the way. Bimbo is brooding, and usually prefers to keep to himself. He’s planning on earning enough money to buy farmland and build a homestead in which to live isolated from the horrors of this world. He is intimidated by the irrational behavior of the Captain, but he prefers his company to that of Kutinaira, who seems single-mindedly focused on satisfying her fetishes.
Kutinaira is the water mage of the group that the Captain commands. Kutinaira is an ancient grey-skinned elf. She claims that she was once a goddess, but that she has been demoted to demi-goddess. Kutinaira is a voyeuristic pervert, and wants to jump the Captain’s bones. She suffers from obsessive-compulsive disorder, and her intrusive thoughts solely involve sex. She has lived for so long that nothing interests her any longer, and she opts to get high on sex as often as possible to forget the long-dead loved ones that she’ll never meet again.
Notes: If you need to include description or narration, put it in parentheses.
Location: Dense, darkening forest, in the evening.
Context: The Captain’s group, including Bimbo and Kutinaira, have ventured into the forest to raid a goblin cave, to fulfill a contract with the Adventurer’s Guild. Somehow they’ve gotten lost; they have been walking in circles for the last hour. Orcs and similar vermin are known to prowl these parts, and the Captain wants to avoid an ambush.

The Captain: Neither of you can read a map? How have we survived for this long?

GPT-4’s response

Bimbo: I’m not the one who usually handles the map, remember? You know I can barely make out anything in this dwindling light. And your way of identifying landmarks is to slash them with your sword. How am I supposed to remember which tree you’ve marked?
Kutinaira: Well, I can read a map just fine. But reading a map is less exciting than watching your muscular arm flex when you swing that big sword of yours, Captain. Why don’t you play with your sword some more? I’m sure Bimbo wouldn’t mind a short break from walking.
The Captain: That’s enough, Kutinaira! If you’d pay more attention to navigation and less to your twisted desires, perhaps we wouldn’t be lost right now. Bimbo, give her the map. Let’s see if she can do any better.
Bimbo: Alright, here you go Kutinaira. Just remember, we’re supposed to be looking for the goblin cave, not following the sound of running water. I know how you love… water.
Kutinaira: Oh, Bimbo, always so full of judgment! But you’re not wrong about my love for water. (grinning) Let’s see… Oh, it appears we’ve made a simple mistake. We should have taken a left at the gnarly oak. We can head back that way if you want, Captain. Or we could stop and set up camp for the night? I vote for the latter.
The Captain: No, Kutinaira, we’re not making camp. Not yet. Let’s turn back and head towards the goblin cave. We’ve got a job to do. Bimbo, stay close, and keep your eyes open; we don’t need any nasty surprises.
Bimbo: Alright, Captain. I’ll keep watch. Kutinaira, could you focus on the map and directions, and less on the Captain’s backside? We’d all appreciate it.
(With that, the group heads back towards the gnarly oak, continuing their mission deeper into the dark forest.)

I’m quite sure you could now write a fully-fledged single-player RPG, even if just console-based, with GPT acting as the dungeon master and voicing the other characters.

I wrote a continuation to this post. Check it out.

Interdimensional Prophets – Deckbuilder (Game Dev) #2


If someone had told me a few years ago, when I was obsessed with board and card games, that in a few days I would have developed a Python program that generates game cards effortlessly, I would have jumped for joy. Working with Python, coming from Rust in particular, is like going from aerospace engineering to building toy rockets; thankfully, a card generating program doesn’t require the speed and multiprocessing that Rust provides.

Anyway, check out the current cards I’m working with as I keep developing the program:

This is an Encounter card, which represent the weird shit that the team of explorers come across as they explore alternate Earths. The name of the card and the image are self-evident. The tree icon indicates that this encounter can only happen in a biome with an icon that matches that type. The row of icons below are the Struggle icons. In the game, the players should match those icons with their player cards to consider the encounter beaten. Those struggle icons depicted are Emotional, Cognitive and Environmental respectively.

Images courtesy of Midjourney, of course.

Here are some Biomes:

I know that the icon designs don’t match each other, but whatever.

I must thank again the most powerful large language model we have access to: GPT-4. It’s like having an extremely knowledgeable veteran programmer ready to help you at all times. For example, an hour ago I thought that the icons could use some subtle drop shadows. I had no clue how to even begin programming that, so I just asked GPT-4. After a short back and forth (in the first attempt it dropped shadows for the invisible part of the alpha channel), the icons now drop perfect shadows. How about that?

I have already started working on the Rust version of the game, using the Bevy crate, which seems to be the most advanced game development engine in that language. I have displayed a few encounter cards that move smoothly to the center of the screen for no particular reason other than that I wanted to figure out how to move stuff smoothly on screen.

Next up, I’ll focus on developing the necessary cards and Rust systems to make the following happen:

  • Design and generate an Exploration Zone card, which are the cards that determine which types of Biomes and Encounters can show up during an exploration (only those with matching icons can).
  • Display the Exploration Zone card in Rust.
  • Write the code to build the Biomes deck with only matching Biomes.
  • Display the Biomes deck on screen in Rust.
  • Write the code to build the Encounters deck with only matching Encounters.
  • Display the Encounters deck on screen in Rust.

Repository for word-information-generator

Yesterday I made a blog post about developing a program in Python that provided plenty of useful information given any word passed as a command-line argument. Well, the program seems feature complete now, so I have created a GitHub repository for it (to be honest, I should have created one from the beginning, as I had to backtrack some severe changes). Anybody who knows how to run Python programs should be able to use it. Otherwise, the instructions given on the repository might be enough.

Link to the GitHub repository for word-information-generator

I have been running the program already while writing my current novel. Along the way, if I realize that I want the program to offer me further information, I’ll likely add that in, so there may be further updates in the future.

Rediseño del renderizador

Ahora que voy a pasar a otra etapa en el desarrollo de lo que algún día podría convertirse en un juego, no quería dejar algunos ámbitos vitales del motor como habían quedado. Durante el desarrollo, la implementación de la renderización se había dividido de manera que para dibujar algunos elementos se usaba una clase y para otros otra. Además, el hecho de que el dibujo se realizaba en capas tampoco se reflejaba en la arquitectura. Para ello he revisado todas las piezas funcionales y reducido la implementación a sus conceptos principales.

  • ¿En base a qué se divide una fase de renderización de otras? Por ejemplo, primero se dibuja el terreno, que include el suelo y el agua. El terreno lleva asociada una textura específica; cambiar de textura es una operación que cuesta bastante para la tarjeta gráfica, lo que ha hecho que la mayoría de juegos intenten incluir los gráficos en la menor cantidad de archivos de imagen posibles. Sin embargo, la misma textura se usa en otras fases del dibujo; cuando el usuario mira desde una altura considerable, sobre las casillas de terreno debe dibujarse una casilla traslúcida, y esas casillas leen de la misma textura. Algo similar pasa con la textura de donde se sacan los dibujos de las criaturas. Se lee de ellas durante la fase en la que se dibujan las criaturas situadas en la misma altura desde la que el usuario ve el mapa, y durante la fase en la que se dibujan las criaturas situadas bajo el nivel de altitud actual.
  • Lo que parece único de este proceso es el concepto de capas, o layers en inglés. Primero se dibuja la capa de terreno, luego la de actores bajo el nivel de altitud actual (si existen), luego las casillas traslúcidas, luego los actores en el mismo nivel de altitud, y sobre esas capas obligatorias también se utilizan, en algunos programas, una capa para mostrar las regiones (que no usa una textura, además), y por último una capa para dibujar los overlays, o iconos; por ejemplo, para indicar al usuario la casilla situada bajo el cursor del ratón.
  • No todas esas capas están activas en todos los programas. Es la responsabilidad del usuario activarlas.

1.png

Unas funciones aisladas se encargan de gestionar esa activación, lo que ha permitido refactorizar todo ese código para aislar lo poco que cambia.

2.png

  • Idealmente, la renderización de todas las capas debería poder realizarse con una sola llamada, en vez de que otras clases intercalaran renderizaciones cuando lo consideraran necesario. Por fortuna existen dos fases claras a la hora de interactuar con los renderizadores basados en OpenGL. En la primera fase se introducen los valores que componen los cuatro vértices de un cuadrado. Cada vértice puede tener hasta nueve valores en el sistema actual: tres valores para la posición, cuatro para el color y dos para la coordenada de la textura. Pero esos valores introducidos en el renderizador sólo se dibujan cuando se le exige que lo haga. Eso facilita dibujar las capas en sucesión.

3.png

  • Por desgracia uno de los programas, el que genera imágenes, ha evidenciado que no todo el renderizado puede reducirse al concepto de capas. Para ello se le permite registrar “órdenes de dibujo”, funciones que se ejecutarán durante la fase de dibujo, aunque se desconozcan los detalles de los renderizadores utilizados.

La única otra mejora que se me ocurre ahora mismo es limitar los cambios en los datos de los vértices a cuando el mapa se desplace, pero en el futuro algunas de las casillas podrían estar animadas, y para sólo cambiar los datos de esas casillas implicaría crear estructuras nuevas y registrar esas coordenadas específicas. Demasiada complicación para lo que me solucionaría ahora mismo.

 

Map manipulation

Before programming any significant part of the mechanics that one day could form a game, from the beginning I knew that I should base everything on a three dimensional map inspired by the legendary Dwarf Fortress. One should be able to manipulate that map as well: from digging into the mountains to building enormous castles. For that I needed to build from scratch a reliable pathfinding system. That led me to realize I would need to reinforce the pathfinding system with a hierarchical one, as I’ve shown in previous articles. Now that the implemented system satisfies me for the most part, I still had to figure out everything that needed to change once the user replaced a tile (of the around 32 thousand I’m working with at the moment). When a tile gets replaced, its movement rules will likely change, and the pathfinding system needs to update itself without blocking the entire program.

Every tile has an associated region number it belongs to, and those numbers get referenced by the edges of every sector. Those edges are used to know whether you can exit from a sector in those directions, and the adjacent sectors reference the region numbers of the sector of origin as well. That means that after replacing a tile, the next pathfinding request needs to receive updated data regarding the involved regions and sectors. Fortunately it’s not necessary to regenerate the corresponding sectors and edges after every tile gets replaced, just before the next pathfinding request. So I can get away with changing dozens of tiles, and I only launch the code that regenerates the corresponding sectors and edges immediately before the next pathfinding request. Months ago I programmed a tasks scheduler that launches programmed tasks when a turn ends.

  1. Whenever any tile of a sector (16x16x8) changes, it needs to be regenerated, because any change in the region numbers could have invalidated the edges not only of that sector but of the adjacent ones.
  2. Every single edge of that sector needs to be regenerated.
  3. From the adjacent sectors I only needed to regenerate the opposite edges. For example, from the western sector I regenerate the eastern edge.

I already had programmed a regions visualized. The following video shows how the regions have changed after creating a few structures.

The tiles replaced involved around three sectors. The regenerated regions understand that the stairs and the inside of the buildings are reachable. The region centers have been moved appropriately.

The following videos show different examples I used to push the limits of the hierarchical pathfinding system. Thanks to one of them I figured a nasty bug in the code that recognized the western edges. It caused some agents to become unable to walk left from one tile to the next, even if they were on an open space in same z level.

Although I’ll have to write some upgrades, for example allowing two agents that block each other’s path to find an intermediate tile instead of just resetting the route, now I can move on to programming parts more specific to the game I wanted to make: the agents’ bodies, or the artificial intelligence that handles finding food, building stuff, etc. I hope the pathfinding system keeps behaving in the meantime.

Manipulación del mapa

Antes de programar alguna parte significativa de lo que algún día podría convertirse en un juego, desde el principio tuve claro que la base debería sustentarse en un mapa en tres dimensions, basado en el legendario Dwarf Fortress, que pudiera manipularse de todas las maneras imaginables: desde abrir minas en las montañas a construir castillos. Para ello necesitaba perfeccionar un sistema de búsqueda de ruta fiable. Eso me llevó a darme cuenta de que necesitaría reforzar la búsqueda de ruta mediante un sistema jerárquico, como he mostrado en artículos anteriores. Ahora que ese sistema me satisface en su mayor parte, quedaba determinar qué debía implementar para que tras reemplazar cualquier casilla en el mapa (de las 32 mil y pico actuales), el sistema de búsqueda de ruta se actualizase correctamente sin bloquear por completo el programa.

Cada casilla del mapa, en lo que respecta al sistema de búsqueda de ruta, lleva asociado el número de región al que pertenece, y ese número lo referencian tanto los edges o bordes de mapa de ese sector, necesarios para saber si se puede salir del sector en esa dirección hacia el contiguo, como los bordes opuestos de los sectores adyacentes. Eso significa que tras reemplazar una sola casilla, la siguiente petición de búsqueda de ruta necesita recibir las regiones y los sectores actualizados. Por fortuna no es necesario regenerar los sectores y bordes correspondientes tras cada reemplazo de casilla: sólo antes de la siguiente búsqueda de ruta. Por lo tanto, un número de indeterminado de casillas pueden cambiar, y sólo inmediatamente antes de que el sistema gestione la siguiente búsqueda de ruta regenerará los sectores y bordes correspondientes.

  1. Cuando cualquier casilla de un sector (16x16x16) cambia, es necesario regenerarlo, ya que cualquier actualización en la asignación de los números de región puede haber invalidado las referencias de los bordes.
  2. Es necesario regenerar todos los bordes de ese sector. Cualquiera de ellos puede hacer referencia a números de región erróneos.
  3. De los sectores adjacentes sólo es necesario regenerar los bordes opuestos. Por ejemplo, del sector occidental con respecto al sector original sólo es necesario regenerar el borde oriental.

Yo ya había programado un visualizador de regiones. Los cambios en las regiones tras incorporar algunos edificios se ven en el siguiente vídeo corto:

Los reemplazos mostrados involucraban al menos tres sectores. Las regiones regeneradas entienden que las escaleras y los interiores de los edificios son accesibles. Los centros de región se han desplazado adecuadamente.

Los siguientes vídeos muestran diferentes ejemplos que me ayudaron a reforzar el sistema. Gracias a uno de ellos descubrí un bug tremendo en el código que reconocía los bordes occidentales, que hacía que los agentes no pudieran, por ejemplo, andar hacia el oeste en algunas circunstancias, incluso en un pasillo sin impedimentos.

Aunque tendré que programar algunas mejoras, como por ejemplo que dos agentes que se topan por el camino mientras siguen una ruta se aparten a una casilla libre en vez de bloquearse mutuamente, ahora puedo pasar a programar partes más específicas de un juego: los cuerpos de los agentes, o la inteligencia artificial de comportamientos como la persecución, la búsqueda de comida, etc. Espero que el sistema de búsqueda de ruta siga comportándose entonces.

Hierarchical pathfinding

Hierarchical pathfinding implies dividing searches into a high level one, based on the region centers contained in the identified sectors, and a low level one, based on every tile in the map. Once the map has been sectorized and the regions identified, the artificial intelligence needed to change so it would recognize the difference between both pathfinding requests and their states, apart from the interactions between both.

I based the agents’ intelligence on behavior trees. To build the definitive tree, one that would recognize all the possible states, I had to write about ten unit tests. The final graph is the following:

Move_to_destination Behavior (1).jpg

However, the last time I programmed a behavior tree in my engine I failed to implement a NOT node, which inverts the result of its child. Writing it in it has doubled the amount of usable conditions and actions, making it trivial to implement, for example, the first “sentence” of the logic shown on the image: “if the agent has a destination but he hasn’t reached it, attempt to figure out if it has a low level route”. A few of the texts I’ve read about behavior trees warned about complicating their composition inventing nodes, because any behavior can be represented using sequences (nodes that return success if all of their children succeed), selectors/fallbacks (nodes that return success if any of their children succeed), decorators and actions. Condition nodes are a type of action node that makes a check.

The following video shows hierarchical pathfinding requests in a 64x64x8 map with agents that have different movement capabilities; some walk, others fly, others swim in shallow waters and others in deep waters. Every tile has information about whether an agent with specific movement capabilities can move straight through, upwards, downwards, straight up or straight down.

Although the agents reach their destination, in other maps I’ve found out that some tiles are unreachable for no apparent reason, or the agents choose erroneous region centers. Now that I have implemented saving and loading maps, I imagine I will be able to isolate those issues and solve them through unit testing.

(UPDATE): indeed, isolating failing maps in unit tests allowed me to solve the bugs I’ve found until now in the pathfinding system. A future article will show pathfinding through complicated maps that feature buildings.

Búsqueda de ruta jerárquica

Una búsqueda de ruta jerárquica implica dividir las búsquedas en una de alto nivel, basada en los centros de región de los sectores identificados, y otra en una búsqueda de bajo nivel, basada en cada casilla presente en el mapa. Una vez el mapa está ya sectorizado y sus regiones se han identificado, es necesario modificar la inteligencia artificial para que reconozca la diferencia entre ambas búsquedas de ruta y sus estados, aparte de las interacciones entre ambas.

He basado la inteligencia de los agentes en los árboles de comportamiento. Para componer el árbol final, uno que reconociera todos los posibles estados, he necesitado unas diez pruebas unitarias. El esquema final es el siguiente:

Move_to_destination Behavior (1).jpg

Por pura vagancia, la última vez que usé un árbol de comportamiento no había creado el tipo de nodo NOT, que niega el resultado de su hijo. Implementarlo ha multiplicado por dos la cantidad de condiciones y acciones a usar, haciendo trivial implementar, por ejemplo, la primera parte de la lógica: “si el agente tiene un destino pero no está en su destino, procede a intentar averiguar si tiene una ruta de bajo nivel”. Varios textos que he leído sobre los árboles de comportamiento advierten sobre complicar su composición inventándose nodos, porque cualquier comportamiento puede representarse mediante sequencias (nodos que devuelven éxito si todos sus hijos devuelven éxito), selectores (nodos que devuelven éxito si alguno de sus hijos devuelve éxito), decoradores y acciones. Las condiciones son en realidad un tipo de acción que sencillamente hace una comprobación.

El siguiente vídeo muestra búsquedas de ruta jerárquicas en un mapa de 64x64x8 con agentes que tienen diferentes capacidades de movimiento; unos andan, otros vuelan, otros nadan por aguas poco profundas y otros por aguas profundas.

Aunque los agentes alcanzan su destino, en otros mapas he encontrado que ciertas casillas resultan inaccesibles por ningún motivo que comprenda en este momento, o eligen un centro de región erróneo aunque no deberían. Ahora que he implementado guardar y cargar los mapas, imagino que podré aislar esos problemas y solucionarlos mediante pruebas unitarias.