ChatGPT writes an episode of Friends

Tonight a thought crossed my mind: “What if ChatGPT wrote an episode of Friends?” Of course, it needed to involve a dinosaur.

My prompt:

Question. Write a script for the TV show Friends. The script should be about the group of friends coming across a live dinosaur. Answer: Let’s try to work out the answer step by step:

GPT-4’s answer:


Sure, let’s go through this in steps:

Step 1: Establishing the Scenario

First, we need to establish how our beloved group of Friends would come across a live dinosaur. One possible scenario is that Ross, being a paleontologist, is invited to a top-secret experiment where scientists have managed to clone a dinosaur, and he decides to bring the whole group along.

Step 2: Character Reactions

Next, we need to determine how each character would react to seeing a live dinosaur:

  • Ross: He would be thrilled and fascinated, trying to explain everything about the dinosaur to the others.
  • Rachel: She might be scared at first but then would probably try to make friends with the dinosaur.
  • Monica: She could be worried about the dinosaur making a mess.
  • Chandler: He would likely make sarcastic remarks and jokes about the dinosaur.
  • Joey: He might be excited and curious about the dinosaur, potentially trying to communicate with it or even ride it.
  • Phoebe: She would likely be concerned about the dinosaur’s feelings and well-being.

Step 3: Dialogue and Plot

Once we have established the setting and character reactions, we can start writing the script. Here’s a brief example:

INT. SECRET LAB – DAY

ROSS excitedly gathers the group around a large, secure enclosure. Inside is a small, surprisingly cute dinosaur.

ROSS: You guys, meet Dino – the world’s first living dinosaur clone!

CHANDLER: (sarcastically) Oh, because Jurassic Park worked out so well.

MONICA: (worriedly to Ross) You promise it’s safe, right? And… clean?

JOEY: (excitedly) Can I ride it?

ROSS: No, Joey! This is a delicate scientific breakthrough, not a petting zoo.

PHOEBE: (concerned) But is he happy here? What about his dinosaur family?

RACHEL: (to the dinosaur) Hi, Dino. I’m Rachel. Do you want to be friends?

Dino ROARS, causing the group to jump back.

CHANDLER: (dryly) Well, that went well.


GPT-4 stopped the output there, but I prompted it to continue.


Suddenly, Dino RAMS against the enclosure, breaking it open. The group SCREAMS and backs away.

ROSS: (panicked) Everyone stay calm! Dinosaurs can sense fear!

CHANDLER: (sarcastically) Oh, well, in that case, we’re totally fine.

Monica is HYPERVENTILATING, clutching Chandler’s arm.

MONICA: (terrified) Ross, do something!

Joey suddenly makes a bold decision.

JOEY: (determined) I got this!

He steps forward, trying to wrestle Dino back into the enclosure. Dino, however, is unimpressed and flicks Joey away with his tail.

Phoebe steps forward, singing a calming song.

PHOEBE: (singing softly) Smelly cat, smelly cat, it’s not your fault…

Dino seems to calm down a bit, but is still out of the enclosure.

Rachel, seeing Dino calmed by Phoebe’s singing, steps forward cautiously.

RACHEL: (gently) Dino, it’s okay. You’re safe here.

Dino seems to respond to Rachel’s soothing voice, but he’s still nervous.

ROSS: (to Rachel) Rachel, keep talking! He seems to like you.

Rachel nods, continuing to speak softly to Dino.

Meanwhile, Joey gets up from the floor, dusting himself off.

JOEY: (to Chandler) You know, I thought that would go differently.

CHANDLER: (deadpan) Really? Because I think we all saw that coming.

Suddenly, a GROUP OF SCIENTISTS rush into the room, panicked. They manage to coax Dino back into his enclosure with some food.

ROSS: (relieved) Thank goodness.

MONICA: (still shaking) Can we please go somewhere less…prehistoric now?

PHOEBE: (to Dino, sadly) Bye, Dino. I hope they let you out to play sometimes.

The group, still a bit shaken, leaves the lab.

CHANDLER: (sarcastically) So, Ross, when’s your next show and tell?


That was a success.

AI news #3


If you are interested in what GPT-4 can do in games, check out part two of this series, because it showed how people are using ChatGPT for Skyrim and Fallout. Things have developed a bit further. The following video shows someone’s modded companion with whom the player can interact naturally, and who is aware of the world around her.

Truly amazing. As long as the world doesn’t end, imagine how amazing games will become in a few years. At least how amazing modders will make those games, if their companies can’t be arsed.

The following unassuming entry represented a turning point in how I use GPT-4:

Turns out that some clever people out there have proved that some prompts can squeeze significantly more intelligence out of GPT-4. The author of the video had the idea of pushing it a bit further by implementing the following architecture:

  • Given a prompt, ask GPT-4 through the API to give you three separate answers.
  • Send those three answers to GPT-4, asking it to act like a researcher and point out the flaws in those answers.
  • Send the answers along with the researcher’s analysis to GPT-4, and ask it to act like a resolver to determine which answer is more valid (according to the researcher’s analysis), improve it a bit, and present it as the final answer.

I have implemented that system in Python. It was very easy to do, as well. Although it takes significantly longer than just asking regular ChatGPT a question, the results are much better.

I thought about how mini-AGIs (Auto-GPT and BabyAGI) use GPT-4 to simulate artificial general intelligence, and I realized that if you can get GPT-4 to develop a list of tasks in a similar way the previous algorithm is structured, you can then link those tasks to the previous algorithm so that it executes each task in a loop.

I’m not that interested in programming an Auto-GPT at the moment; I’m happy enough getting a list of tasks that I can then send to GPT-4 at my leisure. However, I did code an addition to the previous program, one that sends an objective to GPT-4 and tells it to act like a manager developing a prioritized task list. In fact, as in the previous algorithm, it sends that request thrice, and then those answers are passed to a researcher, whose answer gets passed to a resolver as per the previously displayed algorithm.

Here’s the list of tasks that resulted from sending to my program the objective of programming a particular card game, one I’m developing, in the Rust programming language:

  1. Initialize the game engine, including a GUI that supports displaying cards and decks on screen.
  2. Create data structures to represent the different card types: Exploration Zone, Biomes, Encounters, Events, Resources, Afflictions, Player cards, and Portal cards.
  3. Load card data from toml files into the appropriate data structures.
  4. Develop a function to generate a random Exploration Zone card from the available Exploration Zone cards.
  5. Display the randomly selected Exploration Zone card on the top right corner of the screen.
  6. Develop a function to create the Biomes deck, selecting the matching Biome types as specified in the chosen Exploration Zone card.
  7. Ensure the first Biome card of the Biomes deck is displayed face up next to the Biomes deck.
  8. Develop a function to create the Encounter, Events, Resources, and Affliction decks based on the Feature icons from the chosen Exploration Zone card.
  9. Implement player classes (soldier, medic, scientist, scout) with their respective attributes and abilities.
  10. Design the player decks for 4 players, each with a unique role, and include a Portal card to allow escaping to the Hub.
  11. Design the Hub and implement the card drafting system for the Hub, allowing players to choose cards to add to their decks while trading resources.
  12. Develop a system for players to rebuild their decks while in the Hub between explorations.
  13. Design the goal cards that specify objectives required for each Exploration.
  14. Implement a system that tracks combos and interactions between cards, such as activating abilities based on certain conditions.
  15. Create a data gathering and analysis system for role combinations, deck compositions, and card interactions to identify balance issues and adjust card values and abilities accordingly.
  16. Test the game extensively, iterating on card mechanics, balance, and player engagement.
  17. Polish graphics, UI, and overall user experience to ensure an enjoyable gaming experience.
  18. Incorporate playtesting, gather feedback, and make improvements to game mechanics, card designs, and balance based on feedback and data-driven insights.
  19. Create game documentation, tutorial, or in-game help system to provide instructions and guidance for players on how to play the game and understand its mechanics.
  20. Market and promote the game, develop marketing materials and promotional events to raise awareness about the game, attract players, and grow the community.

As you can see, you could pass most of those tasks to the first algorithm, which would come up with a good answer.

I found myself at times copy-pasting ideas from a working document to the prompt I was sending to GPT-4. I thought that there must be some automated system that would pick the most appropriate domain knowledge for whatever prompt I wanted to send to GPT-4. I asked ChatGPT if it could come up with an answer, and it suggested vector databases. I had no fucking clue what vector databases were, but now I know.

Vector databases translate information into “webs” of data according to the distance between the elements, or something to that effect. What’s important is that if you query the database with some information, it will return the most similar information it contains related to what you asked. Sort of like a recommendation system.

The ‘annoy’ crate for Python seems to be one of the most commonly used systems for implementing vector databases, so that’s what I used. Now I just have to paste all the domain knowledge into a txt file, then run a simple program that registers the knowledge into the appropriate vector database. When I’m ordering the other couple of programs to either develop a task list or perform a task, they query the vector database to retrieve the most relevant domain knowledge. Pretty cool.

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.

Interdimensional Prophets – Deckbuilder (Game Dev) #1


A couple of weeks ago I kept myself busy programming an exploration game based on an old free verse poem of mine. I had developed the core of the game, the encounter system, when it became obvious that for the game to feel remotely compelling (even for myself), I’d have to manually develop dozens or hundreds of encounters. The game as it was conceived couldn’t continue past that point, so I thought about what I liked the most about that concept:

  • A team of players cooperating to solve some issue.
  • Each player having special abilities.
  • Gaining resources, abilities, etc, for one of the players at a time.
  • Exploring strange places.
  • Encountering weird shit.
  • Events that could alter how some encounters play out.
  • Gaining injuries, diseases, etc.
  • Gaining mental afflictions.
  • Being able to regroup at the hub and determine the resources that would be used for the next exploration.

Damn it if that isn’t a deckbuilding game. Not surprising, given that one of my favorite games ever is Arkham Horror LCG, a card game in which a team of at the most four players, each with his or her deck, uses the resources and abilities contained in that deck to solve perilous situations and beat weird monsters. It also features a location system that forces the team to move around, although that’s probably my least favorite part of the game.

So I thought, why can’t I program a deckbuilding game?

First of all, I need a fast system to produce cards. I had looked up programs to create cards in the past, and I was extremely disappointed due to how obscure their usages were. So I would need to develop one such program myself, tailored to the needs of my game.

So that’s what I’ve begun to do thanks to the insane Python skillz of ChatGPT. Behold the repository with the current version of my card generator:

Link to the GitHub repository for the card generator program

The first notion I had of such a program is that it should be able to take a background image, a card image, a frame image, and the necessary text, and generate a standard-sized card immediately. And so it does:

Yes, the cards even have rounded corners. Isn’t that fucking cool?

I have become emboldened by the fact that I could get this far in a few hours. So to come up with ideas I have relied on the current king of mini-AGIs (artificial general intelligences), that for me is godmode.space (requires a plus subscription to OpenAI and an API key; I had to wait for mine). Such AGIs are able to make plans, determine what tasks to perform, and criticize their own performance, in the pursuit of fulfilling some goals you’ve told it to focus on. As I’m writing this, GPT-4 is running in the background, coming up with game ideas and mechanics for the notions I fed it. For example, these are some of the texts that GPT-4 has written:

  • An Afflictions deck will be created, which will add an element of chance and difficulty to the game. The deck will consist of afflictions such as injuries, diseases, and mental statuses that will be detrimental to the player when drawn. The severity of each affliction will be determined, and a variety of afflictions will be included to ensure that the deck does not become predictable.
  • Illusion: A card type that represents deceptive, disorienting elements a player might encounter on specific biomes. These cards could have effects that remove enemy cards from the encounter deck or switch the order of Biome cards in the Biome deck.
  • A card drafting system will be developed to allow players to choose which cards to add to their deck when trading resources. The system will allow players to have more agency and control over their deck, and add another layer of strategic thinking to the gameplay. The rules for the drafting system will be determined, such as how many cards are presented to the player, and how many of those cards can be added to their deck. The card drafting system will be tested to ensure that it provides an engaging level of strategy without compromising the overall gameplay.
  • Encounter cards will be matched with one or more features of the Exploration Zone card in play. This will reflect the biome, geography, or climate the team is exploring, making the game more interesting and exciting for players. Additionally, we will develop a mechanism for how Encounter cards are ‘beaten’ through spent Player cards that feature certain icons. This will give players more agency in the game and create more engaging gameplay.

Born too late to explore the Earth, born too early to explore space, born just in time for the AI revolution.

Information about words thanks to ChatGPT

Yesterday, the shady company behind ChatGPT sent me an API key so I could do extra stuff with their GPT-4 AI model. I was mainly interested in using it for Auto-GPT.

Don’t you know what’s Auto-GPT? Some clever people figured out that if you give ChatGPT access to the internet and various other tools (such as your operating system’s commands), and trap it in a loop of reasoning, planning and criticizing itself, you can drop into that loop some task, such as growing your business or gathering particular information from the web, and ChatGPT will work itself to the bone for you. They called this implementation Auto-GPT, and it’s the closest thing we got, that I’m aware of, to AGI (artificial general intelligence), which is the holy grail of AI, and possibly the thing that will kill us all.

Anyway, here’s a video that shows you what this Auto-GPT can do (the video includes plenty of cool new stuff about AI):

I was aware that Auto-GPT can write, test and run Python code for you (apparently just Python; although I dislike the language, it seems to be the favorite of scripters who want stuff done quick, so you must be familiar with it). I started thinking about what I could tell Auto-GPT to do that would help me for real, and I came down to the fact that I look up words very, very often while writing, mostly to check particular definitions or to get synonyms. I search the definitions of words so often, in fact, that Google has at times demanded that I proved that I was human. So what if ChatGPT could write me a Python program that would provide all the information I need from a word, with a single command from Powershell?

The instructions were clear enough. Auto-GPT did write code that gave me synonyms, antonyms and some other shit for any word I would input, but when I ordered it to change the code so that the word got passed as an argument, Auto-GPT got mired in trying to figure out how to pass command-line arguments from within the Docker container with some dedicated functions.

When I gave it a break so it could write tests for the function in another file, it had trouble correcting the original code so that the tests would pass, but I think that was mostly my fault, as ChatGPT would need to have previous knowledge of, for example, what synonyms a word would have, and in that case, what’s the point of writing a test?

Anyway, I got bored with Auto-GPT itself, but not with the notion that ChatGPT could write that Python program, so that’s what I forced it to do in a couple of hours. Behold the results of passing the word “horse” as an argument:

Information about horse

Meaning of horse

  • solid-hoofed herbivorous quadruped domesticated since prehistoric times
  • a padded gymnastic apparatus on legs
  • troops trained to fight on horseback
  • a framework for holding wood that is being sawed
  • a chessman shaped to resemble the head of a horse; can move two squares horizontally and one vertically (or vice
    versa)
  • provide with a horse or horses

Part of speech for horse

  • verb (transitive)
  • noun

Etymology of horse

  • “solidungulate perissodactyl mammal of the family Equidæ and genus Equus” [Century Dictionary], Old
    Englishhors”horse,” from Proto-Germanicharss-(source also of Old Norsehross, Old Frisian, Old Saxonhors, Middle Dutchors, Dutchros, Old High Germanhros, GermanRoß”horse”), of unknown origin. By some, connected to PIE rootkers-“to run,” source of Latincurrere”to run.” Boutkan prefers the theory that it is a loan-word from an Iranian language
    (Sarmatian) also borrowed into Uralic (compare Finnishvarsa”foal”),The usual Indo-European word is represented by Old
    Englisheoh, Greekhippos, Latinequus, from PIE rootekwo-. Another Germanic “horse” word is Old Englishvicg, from Proto- Germanicwegja-(source also of Old Frisianwegk-, Old Saxonwigg, Old Norsevigg), which is of uncertain origin. In many
    other languages, as in English, this root has been lost in favor of synonyms, probably via superstitious taboo on
    uttering the name of an animal so important in Indo-European religion. For the Romanic words (Frenchcheval,
    Spanishcaballo) seecavalier(n.); for Dutchpaard, GermanPferd, seepalfrey; for Swedishhäst, Danishhestseehenchman. As
    plural Old English had collective singularhorseas well ashorses, in Middle English also sometimeshorsen, buthorseshas
    been the usual plural since 17c.Used at least since late 14c. of various devices or appliances which suggest a horse (as
    insawhorse), typically in reference to being “that upon which something is mounted.” For sense of “large, coarse,”
    seehorseradish. Slang use for “heroin” is attested by 1950. Toride a horse that was foaled of an acorn(1670s) was
    through early 19c. a way to say “be hanged from the gallows.”Horse latitudesfirst attested 1777, the name of unknown
    origin, despite much speculation.Horse-pistol, “large one-handed pistol used by horseback riders,” is by 1704. Adead
    horseas a figure for something that has ceased to be useful is from 1630s; toflog a dead horse”attempt to revive
    interest in a worn-out topic” is from 1864.HORSEGODMOTHER, a large masculine wench; one whom it is difficult to rank
    among the purest and gentlest portion of the community. [John Trotter Brockett, “A Glossary of North Country Words,”
    1829]The term itself is attested from 1560s.The horse’s mouthas a source of reliable information is from 1921, perhaps
    originally of racetrack tips, from the fact that a horse’s age can be determined accurately by looking at its teeth.
    Toswap horses while crossing the river(a bad idea) is from the American Civil War and appears to have been originally
    one of Abe Lincoln’s stories.Horse-and-buggymeaning “old-fashioned” is recorded from 1926 slang, originally in reference
    to a “young lady out of date, with long hair.” Tohold (one’s) horses”restrain one’s enthusiasm, be patient” is from
    1842, American English; the notion is of keeping a tight grip on the reins.

Synonyms of horse

  • horse_cavalry
  • Equus_caballus
  • sawbuck
  • buck
  • sawhorse
  • cavalry
  • horse
  • knight
  • gymnastic_horse

Related phrases and expressions with horse

  • to the horse in English
  • your high horse and don
  • is a horse dick !
  • : The horse ?
  • , that horse is peeing
  • a Spanish horse .
  • on a horse ?
  • [ a horse and carriage
  • [ the horse and carriage
  • get a horse too .
  • take a horse !
  • taking the horse and I
  • smells like horse shit .
  • I mean horse .
  • got that horse and his
  • — Horse carriages ,
  • only a horse could love
  • is a horse !
  • and the horse didn ‘
  • take this horse and I
  • your new horse , honey
  • light that horse on fire
  • get a horse thief financing
  • the smelly horse carriages on
  • with this horse .

Semantic field(s) of horse

  • chessman
  • provide
  • gymnastic_apparatus
  • framework
  • equine
  • military_personnel

Hyponyms of horse

  • pommel_horse
  • eohippus
  • pony
  • stalking-horse
  • pinto
  • sorrel
  • steeplechaser
  • liver_chestnut
  • mesohippus
  • roan
  • remount
  • hack
  • wild_horse
  • workhorse
  • palomino
  • pony
  • gee-gee
  • pacer
  • stablemate
  • male_horse
  • bay
  • racehorse
  • harness_horse
  • protohippus
  • chestnut
  • trestle
  • vaulting_horse
  • hack
  • mare
  • saddle_horse
  • stepper
  • post_horse
  • polo_pony

Hypernyms of horse

  • military_personnel
  • gymnastic_apparatus
  • chessman
  • equine
  • framework
  • provide

Meronyms of horse

  • horseback
  • cavalryman
  • encolure
  • foal
  • gaskin
  • horse’s_foot
  • poll
  • horsemeat
  • withers

Domain-specific words related to horse

  • armed_forces
  • military_machine
  • chess
  • war_machine
  • chess_game
  • armed_services
  • military

Associated nouns with horse

  • horse

Associated verbs with horse

  • horse

Stylistic variations of horse

  • bathorse
  • horsepox
  • horseflesh
  • dishorse
  • horselike
  • horselaughter
  • ahorseback
  • horsewhipper
  • sawhorse
  • demihorse
  • horsekeeper
  • Horsetown
  • horsefettler
  • horseshoe
  • horsehead
  • ahorse
  • horseman
  • horsetree
  • underhorsed
  • woodhorse
  • horsehide
  • drawhorse
  • horsewomanship
  • overhorse
  • horsetongue
  • horsemint
  • horseleech
  • horsecloth
  • clotheshorse
  • horseboy
  • horseherd
  • horseload
  • horseplay
  • horsepower
  • horsedom
  • horsefish
  • horsetail
  • horsepond
  • horsefair
  • horsehood
  • rearhorse
  • horselaugh
  • horsemastership
  • horsefly
  • cockhorse
  • waterhorse
  • horsehair
  • horseway
  • horsebreaker
  • horsemonger
  • horsewoman
  • unhorse
  • horsegate
  • horsehoof
  • horseweed
  • horser
  • horseshoer
  • horsefight
  • horsewood
  • horselaugher
  • horsemanship
  • studhorse
  • horsecraft
  • horsefoot
  • horsejockey
  • horseback
  • horseplayful
  • horsewhip
  • underhorse
  • horsehaired
  • horsebacker
  • hobbyhorse
  • horsecar
  • horseless

A couple of weird points about this implementation, although they don’t bother me:

  • I gather the etymology from a website, but some words end up stuck together for whatever reason. Also, no paragraphs. Not sure if it can be fixed, because the html tags don’t come through the request.
  • The section “Related phrases and expressions” only looks for a few words around the passed word, from a dataset that ChatGPT recommended. The results are often strange.

Because I’m obsessive (and compulsive), I kept bothering ChatGPT by telling it to come up with more useful information that the program could provide about any given word. I didn’t know what a hyponym was.

Anyway, this little program ended up being a great tool for writing, which is what I should have done with my afternoon instead of getting involved with ChatGPT. Its auto version has huge potential; I probably need to come up with better use cases.

AI news #2


For me, the holy grail of gaming involves characters driven by AI that could pass the Turing test, and who could trigger changes in the game world according to your unscripted conversations with them and/or their own decisions.

The first half of that dream is already being developed for different games thanks to the astonishing AI of GPT-4. Check out the progress done for the undying Skyrim (its VR version no less, for added immersion):

These conversations are character-appropriate, so the video is much more impressive if, like me, you’ve known all these characters for about twelve years. The voice generation can be plugged to Eleven Labs’ API for more realistic results, although that would burn plenty of the monthly allowed credits.

Here’s another video of this technology in Skyrim, now using Eleven Labs’ voices:

I love how Ysolda admits to being a drug dealer right in the middle of Whiterun’s market. And what’s with that town guard abusing metaphors?

Here’s more or less the same kind of stuff but with a human being acting like a bastard toward Fallout: New Vegas characters:

The following video is an overview of this technology applied to Skyrim (and other games in general), along with its possibilities and limitations.

Interdimensional Prophets (Game Dev) #6


I had finished programming the non-visual part of Team Struggles (a part of the encounter system that involves character traits and psychological dimensions against some performance thresholds) when I faced the fact that the game was loading too damn slow. I admit, I have been a bit overeager demanding more anime photo IDs from Midjourney, and they are completely unoptimized, but still, I figured that this project could load much faster. So I figured the following solutions:

  • Lazy loading. Instead of loading encounter, biome, and photo ID images at once, just the image path is registered. Right before I need to draw a certain image, I check if it has been loaded, and if it hasn’t, I load it. That makes it so that the many images that won’t be seen in a particular testing session won’t need to be loaded at all. This change alone has sped up game loading significantly.
  • Multithreading. This project didn’t feature any multithreading up to this point, as it is a static, 2D strategy game, but the process of loading the various parts (most of them from TOML files) could use some multithreading. My previous experience with this subject involved trying to develop a Dwarf Fortress-like simulation in Python, only to realize that Python isn’t suited for multiprocessing, nor remotely big simulations at all, due to its garbage-collected nature and a core that is locked to a single thread. However, Rust has mature crates that make multiprocessing relatively simple.

I asked GPT-4 to give me an overview of multiprocessing in the Rust programming language. It suggested a combination of the “rayon” and “crossbeam-channel” crates. The process works like this:


let (sender, ecs_receiver) = crossbeam_channel::unbounded();


You declare a sender and a receiver. The sender part will put on a queue the work done from a different thread, and the receiver will remain on the main thread to try to figure out what it can extract from the queue. However, those threads don’t need to disconnect: they are open channels. I assume that you could have a dedicated thread pumping out pathfinding-related calculations back to the main thread.

Spawning a thread is as easy as the following:

       std::thread::spawn(move || {

            load_ecs_threaded(sender);

        });

The “move” order, or whatever you would call it, is tricky. Any information at all that you are sending from the main thread changes its ownership, even if you clone it normally, so you need to use the “Arc” library to clone it in some special way. Not sure how expensive it is.

Anyway, “load_ecs_threaded(sender)” is in this case the function that will run in the spawned thread. The definition and contents are the following:

use crate::{

    gui::image_impl::ImageImpl,

    world::{create_world, ecs::ECS},

};

pub fn load_ecs_threaded(sender: crossbeam_channel::Sender<ECS<ImageImpl>>) {

    sender

        .send(create_world::<ImageImpl, ECS<ImageImpl>>())

        .unwrap();

}

That function merely sends through the sender the results of the “create_world” function, that registers all necessary components with “specs” Entity-Component System.

You won’t be able to check if the spawned threads have done anything unless you are running some sort of loop on the main thread. In this case I’m running the game with the 2D game dev “ggez” crate, which operates a simple, but well-working, game loop. From there, you need to rely on the “receiver” part of the channel to try to receive data:

        if let Ok(ecs) = self.ecs_receiver.try_recv() {

            match self.shared_resources.try_lock() {

                Ok(mut bound_shared_resources) => {

                    bound_shared_resources.set_ecs(ecs);

                    self.progress_text = Text::new(“Loaded Entity-Component System”.to_string());

                }

                Err(error) => return Err(GameError::CustomError(format!(“Couldn’t lock shared resources to set the world instance. Error: {}”,

                error))

                ),

            }

        }

Through the call “ecs_receiver.try_recv()” I will get either an Ok or an Error. An error may just be that the channel is empty because the remote function hasn’t finished working, so we just check Ok. In that case, the thread has finished doing its job. We gather the results (the “ecs” in this case) and store it into our shared_resources as I did previously.

That’s all. You need to be careful, though, because there are some structs that you can’t send through channels. For example, you can’t send the graphical context of “ggez”, meaning that you always need to load images in the main thread. You also can’t send the random number generator through, as it’s explicitly working on a single thread. But I haven’t found any issue sending my game structs.

Now that the game doesn’t seem to freeze on launch, I can focus on implementing the visual aspect of Team Struggles.

Interdimensional Prophets (Game Dev) #5


A couple of entries ago I presented my first version of the encounter screen. As the team of explorers wanders around in the map, the stored encounters will get shuffled, and the first one whose condition gets triggered will present itself. Here’s the somewhat updated screen:

I was checking out the moddability of this game by changing most pictures to manga/anime aesthetics, and I realized that I liked it more this way. With a simple change of directory names, all names and pictures could get swapped to American ones. In any case, this screen presents what encounter has been triggered. The description gives a brief overview of the situation. The rest of the text informs that this encounter, at least the psychological part of it, will test each team member’s self-regulation (which is one of the psychological dimensions, the grouping of a few psychological criteria).

Yes, I know that there’s a lot of black space. Don’t know what to do about that.

Once you click the round button on the lower right, you are shown the results of the psychological test:

A brief text indicates the reason of this psychological test; in this introductory event/encounter to a narrative line called “The Verdant Assembly,” the characters test their self-regulation against the overwhelmingly lush and alien surroundings. For each character, the average value for that psychological dimension gets tested against a series of performance thresholds in the TOML files. The highest threshold they pass, they get that reward (or punishment). In game terms, an Encounter is associated with a series of Outcomes. Here’s how the outcomes for this encounter look like in the raw TOML file:

# Possible placeholders:

#

# {CHARACTER_NAME}

# {CHARACTER_FIRST_NAME}

[[outcomes]]

identifier = 1

outcome_type = “PsychologicalTest”

description = “Overwhelmed by the alien nature of this plant-based world.”

consequences_identifier = 1

[[outcomes]]

identifier = 2

outcome_type = “PsychologicalTest”

description = “{CHARACTER_FIRST_NAME} becomes fascinated by the plant-based entities, leading to increased motivation and a desire to learn from them.”

consequences_identifier = 2

They are self-explanatory. The most important part is that they link to another store of game entities, the Consequences. I intended to unify the concept of game consequences to a single block of game logic that could be applied to psychological tests and, in the future, to team struggles. The TOML file of related consequences is the following:

[[consequences]]

identifier = 1

illness_identifiers = []

injury_identifiers = []

mental_status_effect_identifiers = [1, 2]

character_trait_identifiers = [1]

add_features_identifiers = []

remove_features_identifiers = []

[[consequences]]

identifier = 2

illness_identifiers = []

injury_identifiers = []

mental_status_effect_identifiers = [3]

character_trait_identifiers = []

add_features_identifiers = []

remove_features_identifiers = []

It is quite inexpressive in its contents because it only links to other entities through their identifiers. However, the outcome of each psychological test and team struggle could have any of the following consequences (or all of them):

  • The team member(s) involved receives one or many illnesses. Illnesses reduce the team member’s health every turn until they run out or are cured.
  • Receives one or many injuries. Instant reduction of health, and the permanent ones even reduce max health.
  • Receives one or many mental statuses (like Confused or Discouraged). They increase or reduce the value for associated psychological criteria.
  • Receives one or many character traits (like Terrified of Octopi, or Botanist). These help or hinder during team struggles.
  • The exploration zone the team is exploring either gains or loses features. For example, if some outcome enrages the natives, the exploration zone could gain the feature Enraged Natives, which would present more combat-oriented encounters in the future.

The consequences are already being applied in the code (which was some heavy amount of code, well-tested thanks to test-driven development), and once I get around to implementing team struggles, their consequences will work seamlessly with the code already written.

In the near future I’m going to focus on making sure that encounters can be blocked by other encounters or even their outcomes, if necessary. For example, if during a team struggle the team screws up bad enough to unleash something dangerous, that should cut off access to more positive branches of that same narrative.

A detail about Rust’s fastidious nature: this is a programming language built upon security and protection against the nastiest bugs from the C++ era. As far as I can tell, in Rust it’s impossible to corrupt some memory allocation that it wasn’t supposed to touch. That forces you to change your approach to programming in quite a few ways, but not because Rust is annoying for no reason, but because in other languages you were doing dangerous things. In my code, I was passing around a SharedGameResources entity that had access to “specs” Entity-Component System (that’s a whole thing; if you are interested, google it) as well as the stores of data loaded from TOML files. At one point I had to borrow that SharedGameResources entity both as immutable (just to read from the stores) as well as mutable (to write the results in the components). That’s impossible. Although it forced me to rewrite some basic architectural code, it illuminated the point that stores of game systems (like the “databases” of mental status effects or of character traits) are separate to the Entity-Component System, which handles a lot of mutation. In the end, Rust’s compiler steers you towards proper architecture, because you simply can’t run your program otherwise.

Asking GPT-4 the real questions

“Do groundhogs actually exist?”

Yes, groundhogs do exist. They are a type of marmot, a large rodent found in North America. Groundhogs are also commonly known as woodchucks, and their scientific name is Marmota monax. They are primarily herbivores and live in burrows, which they dig with their strong claws. Groundhogs are often associated with the North American holiday Groundhog Day, which takes place on February 2nd each year. On this day, it is said that if a groundhog emerges from its burrow and sees its shadow, there will be six more weeks of winter; if it does not see its shadow, an early spring is predicted.

“Is it true that groundhogs cause someone to be trapped in a loop that repeats a single day?”

The idea of a groundhog causing someone to be trapped in a time loop that repeats a single day is not true in reality. This concept comes from the 1993 movie “Groundhog Day” starring Bill Murray. In the film, the main character, a weatherman named Phil Connors, gets trapped in a time loop that causes him to relive February 2nd (Groundhog Day) repeatedly. The movie is a work of fiction and should not be taken as an accurate representation of the abilities or effects of groundhogs in real life.

Interdimensional Prophets (Game Dev) #4


As I was writing unit tests for a perilous, convoluted part of the game logic, which I wanted to lock in place as I moved forward, I realized that to test one relatively small part of the code, I would need to create both World, the main entity of the Entity-Component System “specs”, as well as Image, which is tied to the Context of the 2D game dev “ggez” crate. World is heavy by itself to fire up for a simple unit test, but Images themselves may not even be feasible, as they are glued to the graphical context (no graphics should run during unit tests), and they are tied to a single thread, while the unit tests run in all CPUs.

Therefore, I came to the dreaded conclusion: I needed to refactor their entire code into traits (interfaces). Traits are contracts that encapsulate a behavior without implementing anything. If you program to traits (interfaces) instead of to classes, you are working with future, potentially unimplemented structures whose details are irrelevant to you or the compiler, because they are only forced to fulfill a contract (the trait/interface). It’s like telling an animal to say something; a dog would bark, a cat would meow, but both have fulfilled the contract of “talking,” which is apparently all you cared about.

It just happens that traits in Rust involve generics, and generics in Rust are unholy. Due to Rust’s welcome but tough borrowing rules and lifetime whatevers, Refactoring any behavior to traits involves dealing with bizarre generic declarations, and worse yet, nearly incomprehensible lifetime signatures.

Behold the horror:

pub struct MainState<‘a, T: ImageBehavior + ‘static + std::marker::Send + std::marker::Sync + Clone, U: WorldBehavior<T>> {

    active_stage: Option<GameStage>,

    base_state: BaseState<‘a, T, U>,

    expedition_state: ExpeditionState<‘a, T, U>,

    encounter_state: EncounterState<‘a, T, U>,

    shared_game_logic: Arc<Mutex<SharedGameLogic>>,

}

That’s the definition of MainState, the head honcho of the state machine that figures out which other stage needs to update, or draw on the screen. It declares that it’s somehow involved, to start, with an ImageBehavior contract. Every image throughout the program, except in the launcher, is now unaware of what type of structure it’s actually dealing with, except that it fulfills the contract of being static (I assume images are fixed in memory or something), they Send and Sync for multithreading, and can be Cloned. That’s the hardest one. Then we have WorldBehavior, which abstracts away the entirety of the “specs” Entity-Component System into a wrapped class. The resulting contract for WorldBehavior, if I may say so myself, is a thing of beauty:

pub trait WorldBehavior<T: ImageBehavior + std::marker::Sync + std::marker::Send + Clone> {

    fn new(world: World) -> Self;

    fn join_coordinates_and_tile_biomes(&self) -> Vec<((i32, i32), TileBiome)>;

    fn retrieve_player_coordinates(&self) -> Option<(i32, i32)>;

    fn retrieve_biome_at_player_position(&self) -> Option<Biome>;

    fn retrieve_unique_character_traits_of_team_members(&self)

        -> HashSet<CharacterTraitIdentifier>;

    fn count_team_members(&self) -> u32;

    fn calculate_average_mental_strain_of_team(&self) -> f32;

    fn calculate_average_health_of_team(&self) -> f32;

    fn set_player_position(&mut self, x: i32, y: i32);

    fn retrieve_player_entity(&self) -> Entity;

    fn retrieve_name_of_entity(&self, entity: Entity) -> Name;

    fn retrieve_psychological_profile_of_entity(&self, entity: Entity) -> PsychologicalProfile;

    fn retrieve_health_of_entity(&self, entity: Entity) -> CharacterHealth;

    fn retrieve_team_members(&self) -> Vec<Entity>;

    fn retrieve_team_members_except_player(&self) -> Vec<Entity>;

    fn retrieve_photo_id_of_entity(&self, entity: Entity) -> PhotoId<T>;

    fn move_direction(&mut self, direction: Direction);

    fn create_entity(&mut self) -> EntityBuilder<‘_>;

}

With that contract in place, there’s no more need to deal with “specs” way of working. You want to retrieve the player entity? Call “retrieve_player_entity”. Do you want a calculation of the average mental strain of the entire team? Call “calculate_average_mental_strain_of_team”. Of course, the contract can be developed further as more information or calculations need to be gathered.

I’m at ease now that I have managed to refactor those two unit test blockers into behaviors, but it took hours, and if it weren’t for the indefatigable help of GPT-4, I wouldn’t have been able to do it. But thankfully there shouldn’t be any major obstacles to unit test every part of the code now, which should speed up development significantly.

A boring entry compared with the three previous ones, perhaps, but programming is a fight against entropy: the further you build your system, the harder it becomes to change. You have to stop every few days (if not once a day) to make sure that some part of the code isn’t rotting already.