There’s been a big dip in the development schedule. Family urgencies and other commitments sapped a lot of my time and energy, but I’m back to developing again.
Time to look at progress for the previous week, and maybe a few other treats, besides.
There’s been a big dip in the development schedule. Family urgencies and other commitments sapped a lot of my time and energy, but I’m back to developing again.
Time to look at progress for the previous week, and maybe a few other treats, besides.
Progress was slower this week. Stuff got in the way, as stuff often does.
Let’s see what got done anyway.
Generally felt unmotivated and without a lot of creative juice. I still got a lot done, through effort rather than inspiration, though somewhat less of it was narrative, and more of it was code.
Almost all of the code work this week happened in the story-compiler.
You see, I had this epiphany – which I should have had quite a lot sooner.
My, what a productive week it has been! Many bugs fixed, and many new bugs created (and also fixed).
If we were just working off bugs-fixed, this would have been a banner week, indeed, since I was briefly creating them as fast as I resolved them. However, there was a whole lot more going on.
Almost let the day get away from me without writing the update for Week 19. Project board says I’ve got 147 commits for the last 7 days, and closed all of my bugs (though a couple cosmetic ones turned up near the end of it).
Mostly this was an engine-work week again, so mostly working on Argus, rather than SNAFU, but SNAFU got some time as well.
One of the more involved aspects of the Argus story engine is how characters learn and express information about each-other. Some of that knowledge is just used for conditional narrative, but the most important part (currently) is how characters address each-other.
Names, titles, nationality, differing levels of formality, age, gender, and more, all get churned through sets of constructs provided by the story, which ultimately result in some form of address. That’s what I call the reference model. It’s updated as characters learn new information about each-other, and gaps in the knowledge-structure are filled in with assumptions, where no direct knowledge exists.
And annoyingly, way down in scene 360, one of the characters seemed suddenly unable to create a valid form of address for the character she was speaking to.
Earlier on in the development of Argus, I made the decision to not bother checking if a character in the story already knew a piece of information before they ‘learned’ it.
My thinking at the time was that the act of looking up the information was essentially the same cost in time and resources as learning it. So, we may as well just learn it. If it altered what they knew, fine. If they already knew it, still fine. Nothing changed.
Then the knowledge inferences/assumptions system got more advanced. Story characters learn about each-other as the narrative progresses. Knowledge about the gender and approximate age-group of speakers is acquired by listeners, when the character speaks, and information is combined with other pieces of knowledge to determine, for example, just how characters should refer to each-other during the narrative.
Eventually the process of looking up lots of disparate pieces of knowledge, filling in with inferences and assumptions, and then producing a reference-model for referring to other characters (in a variety of levels of formality) started to get a bit expensive.
So, I finally bit the bullet, and added a method to the knowledge graph to test if something was already known before we tried to get a character to learn it. That way we can assess if knowledge the character consists of any new knowledge that might require updating their assumptions/inferences.
That means double the workload for any learning a character does (which happens a lot), but the reduction in reference-model updates actually pays back that cost almost fifty times over. The story can be processed even faster than before, with fewer spikes in CPU usage.
Overall, that’s a win.
It did lead me to code this peculiar little construct, though:
KnStore* KnStore::find(statement_t &statement, const std::wstring &p) { if (statement.size() == 0 && this == nullptr) return (KnStore*)1; ...
DO NOT TRY THIS AT HOME!
Yes, I’m intentionally creating a bad pointer. I already had a find() method that is used to locate knowledge insertion points. Producing a contains() method (does the knowledge already exist) could use exactly the same logic (that is, call find() and test the returned pointer), but always ran off the end of the tree to a null KnStore instance, if and only if it was successful.
That was easy to subvert, and I can return a dummy pointer that no code path ever dereferences (I could also have thrown an exception, but at an additional cost).
Honestly, if I ever saw code like this in someone else’s software, that would immediately raise questions and not a few red flags. Surprisingly, in this case, it’s the perfect solution for a very specific problem.