LudoForge #1

Two nights ago I was rolling around in bed trying to sleep when a notion came into my head, one that has returned from time to time: some of the most flow-like fun I’ve ever had was playing tabletop games. I’m a systems builder by nature, and I love to solve problems with a variety of tools. Tabletop games are complex problems to solve with specific series of tools. My favorite tabletop game is Arkham Horror LCG, although I’ve loved many more like Terraforming Mars, Ark Nova, Baseball Highlights: 2045, Core Worlds, Imperium, Labyrinth, Renegade… But none of them fully captured me. Like some potential game exists that has exactly every feature my brain yearns for, but that game doesn’t exist. I’ve cyclically thought that I should create that game, but I never know where to start. I don’t even know what exactly I want, other than knowing that what I’ve experienced isn’t enough.

These past few weeks I’ve been implementing extremely-complex analytics reports generators for my repository Living Narrative Engine. I was surprised to find out that it’s feasible to mathematically find gaps in extremely complex spaces (dozens of dimensions) as long as they’re mathematically defined. I guess Alicia was justified to be obsessed with math. So I started wondering: what makes a tabletop game good? Surely, the fun you have with it. Can “fun” be mathematically defined? Is it the agency you have? The strategic depth? The variety? If any of such metrics could be mathematically defined, then “fun” is a fitness score that combines them.

And what if you didn’t need to design the game yourself? If you can map a simulated game’s activity to metrics such as the agency per player, the strategic depth, the variety… Then you can evolve a population of game definitions in a way that, generation after generation, the “fun” score improves. If you can turn all game mechanics into primitives, the primitives will mutate in and prove their worth throughout the generations, composing coherent mechanics or even inventing new ones. Initially, a human may need to score game definition variants according to how “fun” the playthrough of those games were, but in the end that could be automated as well.

Because this is the era of Claude Code and Codex, I’ve already implemented the first version of the app. I’ve fed ChatGPT the architectural docs and told it to write a report. You can read it down below.


LudoForge: evolving tabletop games with a deterministic “taste loop”

I’m building LudoForge, a system that tries to answer a pretty blunt question:

What if we treated tabletop game design like search—simulate thousands of candidates, kill the broken ones fast, and let a human “taste model” steer evolution toward what’s actually fun?

Under the hood, it’s a seeded-population evolution loop: you start with a set of game definitions (genomes), run simulations, extract metrics, filter degeneracy, blend in learned human preferences, and then evolve the population using MAP-Elites and genetic operators. Then you repeat.

The big picture: the loop

LudoForge is structured as a pipeline with clean seams so each layer can be tested and swapped without turning the whole thing into spaghetti. The stages look like this: seed → evaluate → simulate → analytics → (optional) human feedback → fitness → MAP-Elites → (optional) mutate/crossover/repair → next generation. pipeline-overview

A key design choice: the core expects a seeded population. There’s no “magic generator” hidden inside that invents games from scratch. If you want a generator, you build it outside and feed it in. That keeps the engine honest and debuggable. Note by me after rereading this part of the report: this will change soon enough.

Games as genomes: a DSL that can be validated and repaired

Each candidate game is a genome: { id, definition }, where definition is a DSL game definition. Before any evaluation happens, the definition goes through schema + semantic validation—and optionally a repair pass if you enable repair operators. Invalid DSL gets rejected before it can contaminate simulation or preference learning.

Repair is deliberately conservative: it’s mostly “DSL safety” (e.g., clamp invalid variable initial values to bounds). Anything that’s “this game is technically valid but dumb/unplayable” is handled by simulation + degeneracy detection, not by sweeping edits that hide the real problem.

The simulation engine: deterministic playthroughs with real termination reasons

The simulation layer runs a single playthrough via runSimulation(config) (or wrapped via createSimulationEngine). It builds initial state from the definition, picks the active agent, lists legal actions, applies costs/effects/triggers, advances turns/phases, and records a trajectory of step snapshots and events.

It’s also built to fail safely:

  • No legal actions → terminates as a draw with terminationReason = "stalemate".
  • Max turns exceededterminationReason = "max-turns" with an outcome computed in that cutoff mode.
  • Loop detection (optional hashing + repetition threshold) → terminationReason = "loop-detected".

Most importantly: runs are reproducible. The RNG is a seeded 32-bit LCG, so identical seeds give identical behavior.

Metrics: cheap proxies first, expensive rollouts only when you ask

After simulation, LudoForge summarizes trajectories into analytics: step/turn counts, action frequencies, unique state counts, termination reasons, and sampled “key steps” that include legalActionCount.

From there it computes core metrics like:

  • Agency (fraction of steps with >1 legal action)
  • Strategic depth (average legal actions per step)
  • Variety (action entropy proxy)
  • Pacing tension (steps per turn)
  • Interaction rate (turn-taking proxy)

Extended metrics exist too, and some are intentionally opt-in because they’re expensive:

  • Meaningful choice spread via per-action rollouts at sampled decision points
  • Comeback potential via correlation between early advantage and final outcome

Here’s the honest stance: these metrics are not “fun”. They’re proxies. They become powerful when you combine them with learned human preference.

Degeneracy detection: kill the boring and the broken early

This is one of the parts I’m most stubborn about. Evolution will happily optimize garbage if you let it.

So LudoForge explicitly detects degeneracy patterns like:

  • loops / non-termination
  • stalemates
  • forced-move and no-choice games
  • dominant-action spam
  • trivial wins metrics-and-fitness

By default, those flags can reject candidates outright, and degeneracy flags also become part of the feature vector so the system can learn to avoid them even when they slip through.

Human feedback: turning taste into a model

Metrics get you a feature vector. Humans supply the missing ingredient: taste.

LudoForge supports two feedback modes:

  1. Ratings (1–5) with optional tags and rationale
  2. Pairwise comparisons (A/B/Tie) with optional tags and rationale

Pairwise comparisons are the main signal: they’re cleaner than ratings and train a preference model using a logistic/Bradley–Terry style update. Ratings still matter, but they’re weighted lower by default.

There’s also active learning: it selects comparison pairs where the model is most uncertain (predicted preference closest to 0.5), while reserving slots to ensure underrepresented MAP-Elites niches get surfaced. That keeps your feedback from collapsing into “I only ever see one genre of game.”

Fitness: blending objective proxies, diversity pressure, and learned preference

Fitness isn’t a single magic number pulled from the void. It’s a blend:

  • Base composite score from metrics (weighted sum/objectives)
  • Diversity contribution (pressure toward exploring niches)
  • Preference contribution from the learned preference model (centered/capped, with bootstrap limits early on)

Feature vectors are keyed by metric id (not positional arrays), which matters a lot: adding a new metric doesn’t silently scramble your model weights. Renaming metrics, though, becomes a migration event (and that’s correct—you should feel that pain explicitly).

Evolution: MAP-Elites + mutation/crossover that respect DSL validity

Instead of selecting “top N” and converging into a monoculture, LudoForge uses MAP-Elites: it bins candidates into descriptor niches and keeps the best elite per niche.

Descriptor binning is explicit and deterministic (normalize → floor into bin count; clamp to range), and niche ids serialize coordinates like descriptorId:bin|....

Then you can evolve elites with genetic operators:

  • Mutations like numeric tweaks, boolean toggles, enum cycling, duplicating/removing actions, nudging effect magnitudes, adding/removing phases, rewriting token/zone references safely, etc.
  • Crossover via subtree swaps of state.variables or actions, followed by DSL re-validation.

Optional “shortlisting” exists too: it picks a diversified subset of elites for human review using a max-min distance heuristic over descriptor coordinates.

What’s already proven (and what isn’t yet)

This isn’t vaporware; the end-to-end tests already prove key behaviors like:

  • the ordered phases of the pipeline
  • invalid DSL rejection before evaluation
  • safety cutoffs (max-turns) and deterministic seeded outputs
  • human prompt loops and legality enforcement
  • deterministic state transitions
  • MAP-Elites producing stable ids
  • active learning selection behavior
  • mutation + repair at scale, including crossover

And there are explicitly documented gaps—like extended metrics aggregation and worker-thread batch simulations.

The point of LudoForge

I’m not trying to build a “game designer replacement.” I’m building a design pressure cooker:

  • Simulate hard
  • Reject degeneracy ruthlessly
  • Measure what you can
  • Ask humans the right questions
  • Let evolution explore breadth, not just a single hill

If you’re into procedural design, evolutionary search, or just enjoy the idea of treating “fun” as something you can iteratively approximate with a human-in-the-loop model, that’s what this project is for.

Living Narrative Engine #19

I have quite the treat for you fuckers. I’ve recorded myself playing through my test scenario involving Alicia Western. More than an hour of me speaking in my accented English even though I rarely speak in real life, and showing off a fun, frustrating playthrough that made me hungry.

This is, of course, related to my beloved Living Narrative Engine. Repo here.

Living Narrative Engine #18

I’m building a browser-based app to play immersive sims, RPGs and the likes. In practice, I use it to set up short story scenarios or elaborate gooning sessions. I dared myself to build the most comprehensive psychological system imaginable, so that Sibylle Brunne, a 34-year-old orphan living in her parents rustic home somewhere in the Swiss mountains, while controlled by a large language model, would realistically bring her blue-eyed, blonde-hair-braided, full-breasted self to seduce my teenage avatar who is backpacking through the country, eventually convincing me to stay in her house so she can asphyxiate me with her mommy milkers.

Here’s a visual glimpse of the current complexity:

Alicia has become my test subject, as if she didn’t have enough with freezing to death. The system works like this: at the base you have mood axes (like pleasant <-> unpleasant), which change throughout a scene. Actors also have permanent biological or personality-based traits like aversion to harm. Together, mood axes and affect traits serve as weights and gates to specific emotion prototypes like disappointment, suspicion, grief. Delta changes to those polar mood axes naturally intensify or lessen the emotions. I also have sexual state prototypes, which work the same as the emotional states.

These emotional and sexual states serve as the prerequisites for certain expressions to trigger during play. An expression is a definition that tells you “when disappointment is very high and suspicion is high, but despair is relatively low, trigger this narrative beat.” Then, the program would output some text like “{actor} seems suspicious but at the same time as if they had been let down.” The descriptions are far better than that, though. The actors themselves receive in their internal log a first-person version of the narrative beat, which serves as an internal emotional reaction they need to process.

It all works amazingly well. However, to determine if I was truly missing mood axes, affect traits or prototypes, I had to create extremely complex analytics tools. I’ve learned far too much about statistical analysis recently, and I don’t really care about it other than for telling a system, “hey, here are my prototype sets. Please figure out if we have genuine gaps to cover.” Turns out that to answer such a request, some complex calculations need to map 20-dimensional spaces and find out diagonal vectors that run through them.

Anyway, I guess at some point I’ll run my good ol’ test scenario involving Alicia, with her now showing far more emotion than she used to before I implemented this system. That’s a win in my book.

Life update (01/25/2026)

I had lunch with my parents earlier today, and ended up having a nasty political argument. My father is already about 76 years old and looking the part. As far as I can tell, he sits all day hooked up to socialist political talk shows. Barely talks to anyone, let alone his wife, on account of her psychological abusing him for decades. Anyway, during lunch, they had the local socialist radio on going on about disinformation. Basically that anything you see online that the government disagrees with are malicious lies, often AI-generated. In fact, the utter piece of garbage, traitorous bastard we have for a president (who likely stole the elections) was at Davos claiming that we should have a digital ID to end online anonymity.

I pointed out that a recent government organization had said that, according to an autopsy report regarding the forty-something people dead in a recent train crash (we had four in like five days), they all had died on impact. As if the WTC towers had fallen on them instead of these people being in different train cars. That whole thing about them dying on impact is a blatant lie, if only because survivors of the accident are on video and radio speaking about how they tried to assist others and had to leave behind folks who they know ended up dying. My mother mentioned that this was to hide the fact that help came about an hour later. Some recent report had even blamed the train conductor, even though several previous train conductors had alerted about the fact that the track involved in the accident had serious issues.

My father got this irate tone on and spoke up, which he rarely does, and asked where I got the information. I repeated the fact that victims are on video saying this, so the autopsy report must be either incompetence or deliberate lies. Then he brought up how when some natural disaster hit a part of the country governed by a non-socialist leader, their response wasn’t questioned this much. Then he got onto the US, as in “look what that piece of shit nutcase is doing, they’re the same ones that stormed the Capitol, they’re now shooting innocent people who were just trying to take photos, and this lady who they believe had guns in her car, but she only had a teddy bear.” Pretty sure there’s a video of the woman trying to run over an ICE agent after having led a movement to prevent them from deporting people who had no business being in the country. And although I’m not sure on the latest shooting, the video does show him reaching wildly for something in his pocket as the agents are trying to reduce him.

I disagree with Trump on many accounts, but not on which most people seem to from both sides of the political aisle, particularly what we see in the US. He’s right that illegal immigrants and even legal immigrants who are a detriment to the country (criminals for sure, but not necessarily) should get deported. We should do it all over the West. We’ve been deliberately ethnically cleansed for the last couple of decades; it’s been organized in a distributed, systematic manner to make this happen. In many major European capitals, ethnic Europeans are the minorities. In Spain, about 40% of under 18, if not more, are of foreign origin. This has never happened before in the history of mankind unless it was an overt genocide, like in the case of the Bell Beaker culture invading Iberia from somewhere in Europe, taking all women for themselves and preventing the local men from reproducing; the influence of male genes from those ancient Iberian peoples went down to damn near zero. Same thing is happening now. “Don’t have children; for the environment! Also, mass import violent third-world men while promoting miscegenation!”

Marxists implanted in the culture this whole racism nonsense, a word they invented. Human populations are biologically different, and therefore are better at some things and worse at others. Then, they declared that all ethnic Europeans are racist, from which follows that ethnic Europeans, the male ones at least, need to disappear. Again, overt ethnic cleansing. The existence and prosperity of ethnic Europeans should not be argued nor negotiated.

My issue with Trump is that he’s supposedly a christian, which I don’t like to begin with because it’s utter nonsense, but that in practice he’s a jew. It’s not Make America Great Again, but Make Israel Great Again. Israel and jews in general have been busy with propaganda these last hundred years or so to paint themselves as these blameless, put-upon group, but they hate our guts even more than they hate muslims, and they’ll eagerly join forces with muslims to Gaza us all. They aren’t our friends. Look up that recent video in Davos about a rabbi referring to us ethnic Europeans as “old Europeans,” and how jews and muslims should join forces against “antisemitism” and “islamophobia.”

I don’t believe in arguing because there’s no point. Ultimately people are built to hold the moral, political, philosophical positions they have. There was a study that surfaced somewhat recently that proved, although without a massive number of participants, that men’s empathy for someone decreased massively and their satisfaction increased when a cheater was punished, while in the case of women, their empathy was completely unrelated to the behavior, including crimes, of their targets of empathy. This was proven with neuroimaging or shit like that. In such feminized societies as ours have become, you only have to watch how they keep marching for mass immigration and the poor military-age browns even after thousands upon thousands of ethnic European girls have been raped at an industrial scale by gangs of muslims. Girls who wandered bloodied and dripping out of gang dens, having been raped by several men, and asked for help to the first man they saw in the streets, only for that other man, a muslim, to lock her in his flat and call over his cousins to rape her again for hours.

I remember an incident in a course I attended. I’ve mentioned it several times already. The organizers had implanted in the course a muslim male of about twenty years old, who was seemingly “in risk of societal exclusion,” which is how the traitors in charge label these individuals who are here to deliberately ruin the country. During a forced talk, a local non-attractive man, who was disabled, said that if he could choose whom to date, he would prefer not to date a disabled woman, because he already had a lot to deal with regarding his own disability, and it would be hard for him to handle. Two women in the course immediately berated him, saying how that was insensitive and offensive of him. Then the muslim man started talking about how in the weekends he went to clubs and accosted women. “They say no, but when a woman says no, more often than not they mean yes.” The same women who had berated the first local man were now giggling at the foreign invader who was spouting something that supposedly these same women have been up-in-arms against for decades.

None of this has any solution other than segregation. And I don’t mean necessarily of races (although yes, we should). You have to segregate yourself alongside other people whose brain wiring produce results that don’t screw up yours, then build walls around you so that outsiders can’t ruin it. 99,999% of humanity throughout the last 200,000 years or so we’ve had an anatomically modern brain already knew this.

manga2cbz: read manga in VR

I’ve been reading manga for a long time, usually relying on my old-ass tablet and scanlations (or whatever they’re called). I came across the Livro app for the Meta Quest 3, and I intended to read manga on it. However, I found out you can’t move the manga folders like you would on a tablet.

So, this morning I had Claude Code create a Go app that can be baked into a Linux/Windows exec to compress manga chapters into corresponding cbz files. Those files can just be copied into the Quest 3 through a link cable, and opened on Livro. Because Livro has trouble rendering WebP files, my app also converts WebP to PNG. As you can see in the video, it works.

Here’s the repo for the app:

https://github.com/joeloverbeck/manga2cbz

Review: Bugonia

I wanted to say I was pleasantly surprised to see such an original movie coming out of Hollywood. But I’ve just found out it’s an adaptation of a South Korean movie. Leave it to the Asians to actually create daring fiction.

Anyway, this was good. A head-to-head between Jesse Plemons, whom I’ve liked in everything he’s done, and Emma Stone, whom I’m not particularly enthusiastic about but who’s good at her craft. Emma plays a high-ranking executive of a company involved in shady pharmaceutical stuff. Jesse Plemons plays a schizotypal, traumatized dude out in the sticks whose mother was injured somehow by said pharmaceutical company. But Jesse’s character has figured out that behind that mundane, vague corporate malfeasance is actually an alien plot to enslave mankind. Along with Jesse’s retarded cousin, they decide to kidnap Emma Stone’s character so she’ll transport them to the mothership and allow Jesse to negotiate for the sovereignty of Earth.

That’s as much as you need to know. In fact, that’s likely more than you needed to know to get into this movie. If you’re into weird stuff, watch it. It’s not the usual Hollywood garbage.

The peculiar script is a highlight. It allows compelling negotiations between Jesse’s delusional character and Emma’s, a cunning executive who finds herself under someone else’s control. Jesse’s and Emma’s acting are fantastic. Unfortunately, the third main character is Jesse’s retarded cousin, who seems out of place in every scene against these two powerhouses. I understand why the plot needed him (otherwise Jesse would have been sounding off necessary plot elements against the walls), but I think the movie would have been tighter without that character in it.

I recommend this movie. So much shit out there, you have to point out the ones that do something.

Review: The Town

Recently I became interested in the movie that Ben Affleck and Matt Damon made together and was releasing on Netflix. The Rip. It seemed like it could be entertaining. Then I watched like thirty minutes of it and realized that it was another one of those movies, like virtually all I’ve attempted to watch in the last ten years or so, that seem to be written by people incapable of producing a good script. Cringe dialogue, the subtlety of a hammer. In online mentions of this movie, people had compared it to a similar one (if only because heists and Ben Affleck were involved): The Town. Released in 2010, but somehow already looking ancient.

Well, The Town was fantastic. I checked it out at midnight and ended up staying up until about three in the morning. Extremely well-written script with not only unique, compelling dialogue, but also great set pieces, mirroring, and callbacks. Like a perfectly-built machine. Affleck does well, although I’ve never been much of a fan of his acting. Jeremy Renner, though, is amazing as this loose cannon who did nine years in prison and who’d rather die “holding court on the street,” as he put it, than return to jail. I never cared much for Renner’s acting, but it feels like other movies he was in, those I’ve seen at least, simply didn’t give him the chance.

As the romantic interest we have Rebecca Hall in her twenties. Gorgeous woman, always a pleasure to have her on-screen, and from the moment she first appears, you understand why a couple of the men involved would risk getting in trouble for her. We also have Jon Hamm from that old Mad Men show (which I never watched, but it was all over the place back in the day) doing very well as an FBI dude, and Blake Lively acting as a strung-out town bicycle. She honestly did great.

The movie gives a great sense of being stuck in a small town (although, as far as I could tell, it’s just part of Boston) with nowhere to go, burdened with the weight of generations, doomed to nothingness unless you dare to stick your head out in a way that could make others cut it off.

It’s very rare for me these days to watch a Hollywood movie and think, “Wow, that was great.” So I recommend this one.

Life update (01/13/2026)

This morning, at about eight, I found myself awake in this disappointing world once again. I decided to stay in bed for a little while longer, immersing myself in my usual daydreams that take place in 1972 and involve someone I would like to talk to. Then my phone rang. I don’t engage with people; I only use my phone to text my parents rarely. A call is always either spam or something bad.

It was the HR department of the Basque public health organization for which I worked as a technician for seven years. They were offering me a job to cover someone’s paternity leave. I was immediately distraught, but also confused, because I had spoken with the Occupational Health department last year, and given that nobody had called me for work in December, I figured the matter was settled. It clearly wasn’t. The job offer wasn’t at the usual hospital, but at another I’ve never worked (but that is located basically next door to the previous one). That threw me off bad. I asked the HR person if I could think about it. She told me that I could only think about it for like ten minutes at the most, because I was supposed to start this very same morning.

I hung up. Anxiety had already spiked to the point of nausea. Working in IT had sent me to the ER thrice for heart and brain problems. The last one made me feel like I had a stroke, and I’m not convinced that my brain left fully healed. They called it a hemiplegic migraine, something I had never experienced before. All triggered by stress.

I have so-called high-functioning autism, which, despite how it may sound like, is only high-functioning relative to autists that spend all day groaning and hitting themselves (or others). I also have the Pure O OCD comorbidity. Intrusive thoughts, adherence to strict patterns. Living in my mind, if I say so myself, is a sort of hell.

It was obvious from the beginning that working IT at a big hospital was like someone pushing me against a person-shaped whole in the wall that simply didn’t match. Day to day, you only rarely know what you’re going to deal with. Someone may call from an operating room because their computer has ceased working during someone’s spine surgery, and they know it’s not our job but the technician from the external company doesn’t know how to fix it and whether we could go and make it work. Someone may call you to blame “computer guys” because they accidentally gave a baby an incorrect dose and killed it. Both of which happened. Of course most are mundane like someone forgetting how their fingers work when typing their password. Or calling to say their computer didn’t have internet, claiming that nothing had changed, and neglecting to say that they had pulled out the network cable and put it back on incorrectly.

I could mention many things about that job. All I want to say is that by the end, they put me in charge of supervising the replacement of about one thousand printers across the complex. That involved me going room to room, meeting people, having to argue with them because they didn’t want their printers replaced, asking me to install functionalities that I had nothing to do with handling, and the general bitching that you get when you put women together in an office. I also struggled to handle a Gen-Z worker who was a pain in the ass, to put it mildly. Motherfucker agreed to replace printers in some rooms at some time and date, which had me organizing with local workers to avoid disturbing their schedules, only for the motherfucker to change his mind basically because he felt like replacing other printers. He also did things like leaving work early then telling his boss that I had claimed he could replace nothing more that day.

By the end, I was done with everything. My brain made it clear when I suddenly smelled of burnt dust, my right hand could barely hold my pen, and I lost sensitivity in the right half of my body. Hemiplegic migraine, so said a doctor younger than me. In the past, some doctors had gotten annoyed when I mentioned the fact that I had only started experiencing heart issues when they jabbed me with the Moderna poison, which now is widely known to cause heart problems. I have very, very little confidence in the medical profession after having had to deal with them both as a worker and as a patient.

But I figured, I’m unemployed, I’m unlikely to get work as a forty-year-old programmer who has only worked at it for nine months in the last ten years, at least under contract. So I called the HR person back and said that I was taking the contract. A month and a half at a new hospital dedicated purely to cancer patients. After I hung up, I groaned out of pure psychic pain. The anxiety in my chest was something akin to panic.

I was waiting for the bus when I received a call from HR. A supervisor. Asked me how come I had accepted a job at the other hospital when they had been informed by Occupational Health that I wasn’t taking offers as a technician. That I can’t choose to work as a technician for one hospital but not another. I told them that I thought Occupational Health had already handled that. They told me they would call back. I waited at the bus stop while construction workers drilled incredibly loudly close by, and some fucking imbecile listened to music without earbuds. I thought, as I do often, about how is it possible that people actually want to live in this world. About five minutes before my bus came, HR called back. I was supposed to meet with Occupational Health immediately.

So I took the bus to Donostia and met with the doctor who had seen me previously. I thought she had declared me unfit for the job position due to my autism, OCD, and 52% disability in general. My certification for “job fitness” is currently expired. She told me that I should have spoken with HR to tell them that I quit the job listings. Then she asked me if I had been looking for a job in the meantime. I told her no, that I had been dealing with autism-related issues and that I struggled to leave the house. Then I stopped talking because I felt like I would tear up.

In the end, she told me that she’d speak with HR and tell them not to call me for technician jobs anymore. Right now I’m beginning to feel relieved about it, but on my way back, I was in a bad place. Standing at the bus stop with my earbuds on, listening to nineties Weezer, while old people milled about close by, asking people about bus times. A young woman stopped before me to ask likely for the same thing, and I pointed at my earbuds without making eye contact. All I wanted, all I want really, is to be left the fuck alone. For the world to forget I exist. To have a small place for myself and to be left in peace.

Anyway, I guess that’s it. I really hope I’ll never hear from that public health organization again jobwise. But I suspect that I’ll receive a call from HR at some point for me to formalize abandoning the job listings.

In forty years, I feel like I haven’t changed at all in what matters. I’m still that child that wanted to be left to his devices and daydream the day away. Everything else is just garbage that society has piled up on me. What I’ve learned from my experience is that I’m not suited for anything that society demands of me. I have no plans for the future either. If it gets too bad, the recourse is a tall bridge. I don’t like being around anyway.

Living Narrative Engine #17

I’ve recently implemented an emotions and expressions system in my app, which is a browser-based platform to play immersive sims, RPGs, adventure games, and the likes. If you didn’t know about the emotions and expressions system, you should check out the linked previous post for reference.

I was setting up a private scenario to further test the processing of emotions for characters during changing situations. This was the initial state of one Marla Kern:

Those current emotions look perfectly fine for the average person. There’s just one problem: Marla Kern is a sociopath. So that “compassion: moderate” would have wrecked everything, and triggered expressions (which are narrative beats) that would have her acting with compassion.

This is clearly a structural issue, and I needed to solve it in the most robust, psychologically realistic way possible, that at the same time strengthened the current system. I engaged in some deep research with my pal ChatGPT, and we came up with (well, mostly he did):

We lacked a trait dimension that captured stable empathic capacity. The current 7 mood axes are all fast-moving state/appraisal variables that swing with events. Callousness vs empathic concern is a stable personality trait that should modulate specific emotions. That meant creating a new component, named affect_traits, that the actors would need to have (defaults would be applied otherwise), and would include the following properties:

  • Affective empathy: capacity to feel what others feel. Allows emotional resonance with others’ joy, pain, distress. (0=absent, 50=average, 100=hyper-empathic)
  • Cognitive empathy: ability to understand others’ perspectives intellectually. Can be high even when affective empathy is low. (0=none, 50=average, 100=exceptional)
  • Harm aversion: aversion to causing harm to others. Modulates guilt and inhibits cruelty. (0=enjoys harm, 50=normal aversion, 100=extreme aversion)

In addition, this issue revealed a basic problem with our represented mood axes, which are fast-moving moods: we lacked one for affiliation, whose definition is now “Social warmth and connectedness. Captures momentary interpersonal orientation. (-100=cold/detached/hostile, 0=neutral, +100=warm/connected/affiliative)”. We already had “engagement” as a mood axis, but that doesn’t necessarily encompass affiliation, so we had a genuine gap in our representation of realistic mood axes.

Emotions are cooked from prototypes. Given these changes, we now needed to update affected prototypes:

"compassion": {
  "weights": {
    "valence": 0.15,
    "engagement": 0.70,
    "threat": -0.35,
    "agency_control": 0.10,
    "affiliation": 0.40,
    "affective_empathy": 0.80
  },
  "gates": [
    "engagement >= 0.30",
    "valence >= -0.20",
    "valence <= 0.35",
    "threat <= 0.50",
    "affective_empathy >= 0.25"
  ]
}

"empathic_distress": {
  "weights": {
    "valence": -0.75,
    "arousal": 0.60,
    "engagement": 0.75,
    "agency_control": -0.60,
    "self_evaluation": -0.20,
    "future_expectancy": -0.20,
    "threat": 0.15,
    "affective_empathy": 0.90
  },
  "gates": [
    "engagement >= 0.35",
    "valence <= -0.20",
    "arousal >= 0.10",
    "agency_control <= 0.10",
    "affective_empathy >= 0.30"
  ]
}

"guilt": {
  "weights": {
    "self_evaluation": -0.6,
    "valence": -0.4,
    "agency_control": 0.2,
    "engagement": 0.2,
    "affective_empathy": 0.45,
    "harm_aversion": 0.55
  },
  "gates": [
    "self_evaluation <= -0.10",
    "valence <= -0.10",
    "affective_empathy >= 0.15"
  ]
}

That fixes everything emotions-wise. A character with low affective empathy won’t feel much in terms of compassion despite the engagement, will feel even less empathic distress, and won’t suffer as much guilt.

This will cause me to review the prerequisites of the currently 76 implemented expressions, which are as complex as the following summary for a “flat reminiscence” narrative beat:

“Flat reminiscence triggers when the character is low-energy and mildly disengaged, with a notably bleak sense of the future, and a “flat negative” tone like apathy/numbness/disappointment—but without the emotional bite of lonely yearning. It also refuses to trigger if stronger neighboring states would better explain the moment (nostalgia pulling warmly, grief hitting hard, despair bottoming out, panic/terror/alarm spiking, or anger/rage activating). Finally, it only fires when there’s a noticeable recent drop in engagement or future expectancy (or a clean crossing into disengagement), which prevents the beat from repeating every turn once the mood is already flat.”

That is all modeled mathematically, not by a large language model. In addition, I’ve created an extremely-robust analysis system using static analysis, Monte Carlo simulation, and witness state generation to determine how feasible any given set of prerequisites is. I’ll make a video about that in the future.