Sunday, November 26, 2023

Acuitas Diary #66 (November 2023)

This month has been all about the Text Parser. I'm pushing to get this latest revision done, and that has crowded out other work for the moment. The big thing I cracked this month - the thing this Parser revision was mostly aiming at - was the ability to nest branches and dependent clauses inside each other.

Photo by Ed Vaile ("Edric") from Palmpedia

What I call "branching" takes place when there is a coordinating conjunction in the sentence (like "and" or "but"). Branching can produce simple compounds, as in "Cats and dogs are animals." But sentences can also divide at any point and continue along two separate paths, as in "I fed the dog his dinner and gave Sarah her book." Or start out divided at the beginning and merge, as in "Are you or are you not a man?" Adding conjunction processing and branch management was one of my major accomplishments from last year. But this first version only really supported conjunctions in the uppermost layer of the sentence - not inside or between dependent clauses. Any interaction between branching and that other vital feature - nesting - had the potential to confuse the parser horribly. Not to mention that the code was a huge mess.

I'm a big believer in the design process that goes "Make a sloppy version that works; refine it later." Later became now and it was time to refine. I'm happy to report that I think I got clearer and better-organized code out of this month's work, in addition to enabling some sentences I couldn't manage before. Such as:

What you need and what you want are different things.
If you want or need the box, I can get it.
I don't know how you and Jack plan to find food.

There's still a ton of polishing to do and other small features to reintroduce, but this was the big hurdle.

Until the next cycle,
Jenny

Monday, November 20, 2023

Atronach's Eye 2023

I last made a blog about this project early last year, and now I'm back to it, being nearly done with the next iteration! It was a long, meandering process to get here, so I'll try to describe that (including all the things that didn't work) for everyone's edification.

The 3d-printed mechanical eyeball that is the topic of this post, with its top panels off so the motors and motor drivers are showing.

At the end of my last round of development, I identified several problems that still needed to be solved. I chose to concentrate on the electro-mechanical issues, specifically:

*Eyeball rolling motion not as smooth as I would like - too much friction, eyeball catches on socket
*Greater range of motion desired
*Slack in tendons when ball is near center of motion
*Limit sensing needed to provide feedback on eyeball angle and prevent motor stall

The friction issue was the only one on this list that turned out easy, because I had most of the solution already. It happens partly because 3d-printed objects just aren't smooth (no matter what, they'll have some layer lines) and partly because there's a seam in the middle of the eyeball - the two halves are printed separately and fit together so that I can put the camera inside. No amount of sanding and rounding off edges seemed to be good enough, but what did help was tying some extra pieces of nylon fishing line across the seam, from the tendon attachment holes to the back of the eye. So in the next design iteration, I added more holes - all around the "pupil" and the opening in the back for the "optic nerve" (camera cord). I used one of my thicker fishing lines and ran lengths of it, like lines of longitude on a globe, between twelve pairs of these holes. This proved a consistent solution, even when I further enclosed the eyeball by adding the shroud (more on that later). The nylon lines are very smooth and slippery and put a low-friction surface between the ball and the socket.


To address "range of motion" and "slack in tendons," I decided to move the tendon attachment points from the front of the eyeball to the back. The original design was drawn from biological inspiration: human eye muscles attach to the sides of the eyeball toward its front, well forward of the ball's "equator." But human eye muscles also automatically manage slack by getting shorter as they pull! My motor and differential tendon pair setup can't do that.

An illustration (using the 3d model of eyeball version 3) of how the distance from a tendon guide hole in the cradle, to a tendon attachment point next to the iris, differs from a straight line due to the curvature of the eyeball itself.

I always installed my tendons by putting the eye at a limit, tying the "fully extended" tendon on, then spinning the motor shaft until that tendon was "fully contracted" and tying on the other. And then, when I returned the eye to its center position, there would be slack in both tendons. The explanation is that the tendons when fully extended were following the curvature of the eyeball (which is longer than a straight line), but when the eye was at center, they were angled straight away from the eyeball toward their guide holes in the socket.

I considered various tricks to solve this. Moving the attachment points closer to the eyeball's equator would reduce that curvature effect, but also reduce range of motion. Moving the guide holes farther away would change the angle of the lines and keep them away from the eyeball even at its limit, but then I wouldn't be able to put the top panels on the eyeball case, because the tendons would no be able to run down through that central hole. It finally seemed that the best solution was to put some nubs or spars on the back of the ball as tie-on points for the tendons, and run the tendons *up* through the guide holes in the cradle, then back down to the motor shafts.

Two iterations of eyeball version 3, showing the spars on the back of the eyeball and the new limit switch contacts.

So, I tried it. Those new spars on the back also seemed like a good place to put one half of a limit switch contact. So I wrapped them in copper wire and connected them to the common ground, and put an aluminum foil signal contact on the underside of each quadrant of the cradle. I backed the foil with foam, in hopes that this would allow the spars to sink into the foil a bit and ensure a positive contact. Since the foil proved annoyingly fragile, I used pieces of (aluminum) soda can with the coating sanded off for a second attempt.

Attaching one of the new tendons

Well. Putting the tendons on the back of the eyeball did solve the tendon slack problem without hurting range of motion. BUT - the tension of the tendons against the front of the ball had been the only thing keeping the ball down in the cradle. And the eye isn't designed to be displayed lying on its back, it's supposed to hang on the wall. Whenever I raised it vertically like that, the ball wanted to fall forward out of the cradle. And the limit switches still didn't work well at all. The contact formed by touching wire to foil simply wasn't reliable, no matter how much I wanted it to be.

This was the point when I started doing smaller-scale prototypes, because I just needed to try a bunch of things and taking apart or redesigning the case for each one didn't seem smart. So I designed a smaller version of the eyeball and a little flat base with two motor mounts, all of which would use fewer materials to print - and I made it fairly modular, so that I could mix and match three different limit detection attempts and three different socket designs.

The prototype fully assembled with the mechanical limit switches and three-lobed shroud.

The three limit detection methods were 1) standard mechanical clicky switches installed in the cradle, with paddles attached to the levers, so the eyeball's spars would close a switch no matter where they hit the underside of the cradle, 2) Hall effect sensors on the spars, paired with magnet strips on the underside of the cradle, and 3) rotary encoders fitted on the ends of the motor shafts. I bought the parts to sample all three, and mechanical switches won. When I installed them in the prototype, they pretty much worked reliably on the first try. The encoders were a bust, by comparison. They required a surprising amount of force to turn, and just installing one at the end of a motor shaft caused the motor to struggle at the limits of the eyeball's motion. I'll have to keep them for some project with stronger motors. And the Hall sensors worked, but not as well as I wanted. They responded to small permanent magnets, but did not react to the flat magnetic strips I have; their field strength is apparently too weak. So I would have had to embed a whole bunch of magnets all along the underside of the cradle. Hall sensors are also proximity-based rather than contact-based, which is nice if you can't get a positive contact, but also kind of imprecise.

First version of the limit switches with paddles, assembled for the prototype.

The one tough thing about the limit switches was figuring out how to attach paddles to their levers to get a larger contact area. The levers don't seem designed for this at all; they're about as small as they can be, they don't have enough convenient holes, and they're made of metal. There's also very little clearance between the lever and the switch housing, so if you try to wrap anything around the level, wedge it into a slot, etc. then the switch can't close anymore. My first paddles were made from pieces I cut out of flat plastic lids (coffee-can style). I poked holes in the soft plastic with a needle, and tied the paddles to the switch levers with fine thread. It was janky, but it worked. Eventually I made 3d-printed paddles with built-in holes, and added a sleeve made of thin transparent plastic (the kind used for shell packaging), wired down to the paddle. I can't print something as thin and tight as that sleeve, which is why it's a separate piece. I can slide the switch lever between the sleeve and the paddle, and the sleeve holds it in place without hindering the switch's ability to close.

Final version of limit switches with paddles attached via sheet plastic and wire

To address the problem of the eye falling out of the cradle, I also designed three new sockets. All made use of the original cradle on the bottom/back side of the eyeball. One was a sort of clamshell - I added a second, inverted cradle on the top, with pegs and holes to fit into the bottom cradle, so the eye would be contained between the two. The other sockets both featured shrouds with fixed "eyelids" to wrap a little way around the front side of the eyeball and hold it in the cradle. I made one with a standard pair of eyelids at the top and bottom, as well as a weird one with three lids. I tested this second option specifically for Atronach's sake, since the eye has three-fold symmetry in most respects, and it would be a little weird to give it the ordinary two eyelids. I redesigned the cradle legs to grip the edges of the cradle instead of fitting into holes on its underside, so that the legs wouldn't interfere with the limit switch paddles in any way, and I made prototype versions with four and three legs, also.

I tested both shrouds in the prototype, and they worked out so well that I didn't even try the clamshell. With everything pretty well figured out, I was left to apply all of the design changes to the original model and re-print most of the case. I kept the wall pieces and motor mounts from the original, but all the top and bottom panels had to be redone, since various attachment and connection holes had changed. While I was at it, I made colored inserts for the Hebrew lettering on the top panels, so I don't have to paint them this time (the new version looks a lot cleaner).


So much printing! There's an amazing amount that goes into this one build.






And after all that, it's still going to need software improvements! I hope to report on those next year.

Until the next cycle,
Jenny

Sunday, October 29, 2023

Acuitas Diary #65 (October 2023)

I guess I've done a lot since the last entry. I integrated the new Text Generator into the main body of Acuitas code, so now it's his solution for generating speech. I continued with the Text Parser rework and improved handling of infinitive phrases. And I (tentatively) completed Big Story.

Let's start with Big Story. It is plot-complete and currently stands at 99 lines. It generates 45 character "issues" (problems or subgoals), all of which are resolved in some way by the time the story is complete. Compared to any story I've told Acuitas before, this is dramatically longer and more complex (and it still leaves out a bunch of subplots and secondary characters from the movie this story is based on). I did my best to reproduce character motivation and the major story beats in a way Acuitas can "understand."

I don't want to give the story away yet, but here's a low-detail zoom-out of just part of the narrative diagram, to give an idea of how it's shaping up.

Screenshot of a narrative flow diagram showing part of a complex story. Zoomed out so actual text is largely unreadable.

Once the full plot was established and all the issues were resolving, I started refining the explanation of some points and fixing bugs. I'll continue this work in hopes of having it wrapped up by year's end. Before I demo the story, I'll also need to reinstate Acuitas' reactions to story lines and the ability for Acuitas and the conversant to ask each other questions about the story (things that fell by the wayside during my last Narrative rework). This should be easier now that the new Text Generator is in place. And of course, working on this project has revealed tons of pain points in the way I currently do things, and ways I could improve the Narrative module. Some time after I get done with it, I'll be tilling all those insights back into the soil, as it were. But that will almost certainly have to wait for next year.

One of the fun asides from this round of storytelling was figuring out how to render another new concept. I originally had this line:

[A] wanted to obey [B] because [A] was cowardly.

Problem: Acuitas has no specific grounding for "cowardly." Could I explain cowardice with the existing tools? Well ... yes actually. Like the obsession with power, it's another goal misprioritization. I replaced the line with these two:

[A] wanted to be safe more than [A] wanted any other thing.
[A] wanted to obey [B] because [A] wanted to be safe.

The first line gets this safety obsession flagged as a negative goal, because putting anything but altruism in the top spot is a no-no. Then that taints the motivation for obedience on the second line, which would be fine in isolation. The issue is not that Character A desires safety, but that he will do literally anything to get it (which includes submitting to coercive orders to harm others).

I need breaks from working on Narrative (it's hard), so I've also continued improvements to the Parser. My major accomplishment in the past month was getting infinitives to work again under the new scheme that better supports phrase/clause nesting. While I was at it, I finally dealt with some special constructions that the old parser couldn't handle. Consider these sentences:

1. To live is to exist.
2. Is to live to exist?
3. What is it to live?
4. What is to live?

The previous version of the Parser could handle the first two. The open-ended question form was tricky. I think the most correct way to ask it is Sentence 3; without further context, Sentence 4 means something more like "which things are intended to live." But it's pretty easy for the parser to trip over Sentence 3 (What's the direct object? What role does the phrase play? Does the "what" belong inside the phrase or outside?). This time around I finally put in the effort to get all four variations working - plus some other constructions I hadn't tried before, such as "What is he to do?"

Work on the new Parser continues. I'd also love to have that done by the end of the year, but we'll see.

Until the next cycle,
Jenny

Tuesday, October 10, 2023

Poor Person's First Hydraulic System

I've been taking tentative steps into the field of hydraulics, and I'm probably done with those for the year, so I want to write them up. My starting goal was pretty simple: get a minimum viable system together and make it cycle a cylinder. And I got there, mostly.

Why am I doing hydraulics now?

The robots I've showcased here so far have all been pretty simple. The eyeball has two motors; ACE in theory has at least eight. But in the back of my mind there's always a lurking desire for something with a *ton* of degrees of freedom. And once you add an actuator for every possible direction of movement around a joint, the weight of all those actuators starts to become prohibitive. Long-time readers of this blog will recall that I experimented with artificial muscles, specifically the ones made out of nylon. They disappointed me. My home-made fishing line muscles were wondrously cheap and, yes, lightweight ... but slow, power-hungry, and worst of all, not durable. So I'm beginning to explore other options.

The attraction of a fluid-driven actuation system is that you can have *one* motor - in the pump or compressor - which you locate near your robot's center of mass, and a panoply of lighter and smaller actuators that control fluid flow to the appendages. The full power of the central motor can be applied in any part of the robot; instead of having some smaller motors as useless dead weight when their joints aren't currently moving, you can use all the available motive force for whatever the current motion is. And with the right design, the system doesn't need to expend any energy to hold a joint in position either - contained fluid pressure will just do that. A choice remains between pneumatics (gas as working fluid) and hydraulics (liquid as working fluid).

Issues with pneumatics:

Noisy
Compressed air is a potential safety hazard
Motion tends to be fast and jerky - precision control is difficult
Leaks are harder to locate

Issues with hydraulics:

Messy (leaks can cause damage or contaminate environment)
Working fluid adds weight
Motion tends to be slow
System must be primed to remove air

Pneumatics are somewhat common in FRC robots, so I have experience with them from mentoring one of the local FRC teams. We tended to use them for binary motions, like gripper operation (the gripper can be either open, or closed as far as the held object will allow). See video of one of these robots. You can tell when the pneumatics are operating by the hissing sounds. Another year, we used pneumatics to power a throwing arm, which was expected to move quickly through its full range. We stuck to motors for applications that called for positional control.

And that, I think, was the main reason why I decided to go with hydraulics for my own recent experiments. I want that option of precision control. Concerns about the safety of compressed air were also a big factor. I want the freedom to work with unrated home-made or repurposed parts, without worrying about whether my system might explode and skewer me with shrapnel.

The parts

Now for the first hurdle: miniature hydraulics barely seem to be in use among other robotics hobbyists. In my research, I discovered that there's a small market for them among people who build working scale models of earthmoving equipment. Huh. (I took a look at some of their stuff - it felt too expensive for a first try.) But the maker community at large doesn't seem to have an interest, and that makes parts hard to find. The best prior project example I uncovered was this little hydraulic quadruped, which doesn't seem to be documented anywhere.

Fortunately, there is basically no issue with repurposing a lot of pneumatic equipment for hydraulics. I got a pair of solenoid "air" valves from Adafruit, and a bag of cheapo check and regulator valves meant for use with aquarium air pumps. My tubing is all aquarium tubing also.

My "cylinders" are Monoject medical syringes, which I already had a collection of because I save them whenever they come to me (usually with cat medicine). They're a bit of a problem. There is so much friction between the seal and the inside of the cylinder that moving the plunger takes serious effort, even without a load on the cylinder. (If you've never noticed, it's because you don't know your own strength, you big muscley human, you.) And lubrication doesn't help all that much. So I may need to find another solution for the next phase of this project, but I don't know what yet.

The pump was the biggest issue. It's easy enough to get a tiny water pump - aquarium suppliers and CPU cooling system suppliers are both good places to look. But these pumps are built for *moving* water, not pressurizing it, and I suspect they're all built with a poor speed-to-force tradeoff. They don't even advertise maximum pressure in their specs; the closest you'll usually get is a measure of the pump's "head," which is the height to which it can raise water in a vertical pipe. (This is not a pressure measurement, but you can convert it to one.) I included a little 3V water pump in my Adafruit order, hoping it would be enough ... and no way could that poor thing push water into one of my syringes. I'll have to use it for a decorative fountain or something.

So I built my own pump, following the well-known "syringe pump" design. A linkage converts the rotary motion of the pump motor to linear motion that draws a syringe plunger back and forth. Above and below the intake of the syringe are two one-way valves (check valves). When the volume inside the syringe is expanding, water is drawn in through the upper valve; when the volume inside the syringe is shrinking, water is expelled through the lower valve. Control over the connection between the motor and the pump mechanicals means I can theoretically use any gear ratio I want.

For this first attempt, I grabbed a random stepper motor and gear assembly out of my pile of salvage, hooked it to one of my driver boards and got it running under control from the Arduino, whipped up a 3d model of a compatible coupler and syringe cradle, and somehow made the whole thing go. It'll draw the pump syringe back and forth if powered with 6V, and run the hydraulic system well enough to push another syringe at 10V. (I can't seem to find specs for this motor, so I have no idea what voltage it's supposed to run at.)

The system

Diagram of an extremely basic hydraulic system

A basic hydraulic system is a closed loop in which fluid flows from the reservoir (not pressurized) into the pressure chamber, and back to the reservoir again. Valves admit fluid from the pressure chamber to the cylinders, and permit fluid to drain from the cylinders back into the reservoir. A relief valve or bleed valve allows fluid to move directly from the pressure chamber to the reservoir if the pressure in the chamber exceeds the relief valve's limit (this keeps the pump from stalling if the pressure is not being "used up" to drive the cylinders at the moment).

I was going for a single three-way valve that would connect my cylinder to either the pressure chamber or the reservoir - with a passive load on the cylinder, so that it would automatically drain back into the reservoir if connected thereto. (The pump could not help pull the cylinder closed because my reservoir was open to the air.) That's what the rubber band on the end of the syringe is for - I could wrap that around the plunger to create elastic tension that would empty the unpressurized syringe. In practice, my system was using up so much power just overcoming the friction in both syringes that there was virtually none left to drive a load. So I let it fill my syringe and then applied a dynamic load (with my fingers) to empty it. If you watch the video you will also notice that it is waaayyy toooo sloooow. I need more power and more fluid flow all around. So ... okay for a proof of concept, but this has a long way to go before I put it in a robot and it does anything useful.

Future work and thoughts

I already mentioned that the friction losses in the system are far too high. I probably need something that is better designed for easy motion than these syringes are. And despite how high-friction the plunger seal is, my pump syringe eventually started leaking out the back.

The check valves work well enough, but as I mentioned they are not designed for use with liquids. They tend to retain some air inside their housing; getting it all out when priming the system is difficult. And then that air soaks up some of the pump power. A part of its strength during each cycle goes into temporarily compressing the retained air bubbles instead of driving the cylinder.

The silly little aquarium air regulator valves are pretty imprecise. But I looked and looked for an actual relief valve (hydraulic or pneumatic) without finding one that could operate in an adjustable low psi range, for a reasonable amount of money.

So in principle this "works," but I need better parts all around. I wanted to demonstrate a poor man's hydraulic system and I'm reasonably satisfied that I have done so, but now I have to trade out all the pieces in ways that get the performance up.

Until the next cycle,
Jenny

Wednesday, September 27, 2023

Acuitas Diary #64 (September 2023)

As of this month the new Text Generator is done - all that remains is to integrate it into Acuitas, replacing the old Generator. Quick review: the Text Generator is the component that transforms abstracted facts or communication goals (aka "the gist") into valid, speakable sentences. For any given "gist" there may be multiple ways to render it into speech, and that's why this translation step is necessary. The information carried in speech is distilled into a compact, standard form for all of Acuitas' reasoning purposes; then, anything he wants to express is expanded back out into varied, human-comprehensible speech.

A black-and-white drawing of an old scroll

This version of the Generator is quite a bit more streamlined and flexible than the old one. It accepts what I now use as the common format for abstract data structures, instead of having its own special input format that the other modules would have to translate to. The calling function can request that output sentences be "flavored" in a variety of ways (different verb tense, modifying adverbs, etc.) without the need for me to create a whole new sentence template in the Generator. Nested clauses are supported to an arbitrary depth.

Here are some examples of the sentences the new Generator can create:

What is a hammer used to do?
I have remembered that a cat is an animal.
Do I know what a cat is?
Cold is the opposite of hot.
You intended to eat a cookie.

I've also kept moving ahead on the Parser cleanup, and while I'm at it I am introducing adjective clauses. Part of the goal of this rework was to fully support nesting/recursion in clauses, so it seemed a natural time to add support for the last major type of clause. Example sentences the new parser can manage (with the adjective clause highlighted) include these:

The man who came from Denver is here.
I will bring the computer which was broken.

Lastly, I am still plugging away at Big Story. The innovation I needed for narrative comprehension this month was a theory-of-mind thing: if an agent believes one of their goals is already fulfilled, they will stop trying to fulfill it. If this is a false belief then it constitutes a problem for that agent/obstacle to the resolution of that goal. This opens the way for processing of various plot points based on deception or confusion, such as "decoy version of item" or "character fakes own death."

Will I finish Big Story by the end of the year? I don't know, but I'm going to try. After that it might take a while longer for me to get a demo together the way I want.

Until the next cycle,
Jenny

Sunday, September 17, 2023

Sunless Sea: A Matter of Sacrifice

It's been many years now since I finished playing Sunless Sea, yet the experience still reverberates. In Part I of this game retrospective, I focused on my interactions with Salt and how they helped me really understand something from The Chronicles of Narnia. In this article I'm going to talk about the game's ending, and how it, too, helped me understand something … also, coincidentally, from the Narnia series.

Or perhaps it's not quite a coincidence. The Voyage of the Dawn Treader features a ship sailing for unknown islands, so its ties to Sunless Sea are immediately obvious (though most of Dawn Treader isn't nearly so alarming and gloomy). The voyage has a primary goal of recovering exiled nobles, but one crew member – the talking mouse Reepicheep – becomes obsessed with going all the way to the eastern edge of the world. To quote him,

“My own plans are made. While I can, I sail east in the Dawn Treader. When she fails me, I paddle east in my coracle. When she sinks, I shall swim east with my four paws. And when I can swim no longer, if I have not reached Aslan's country, or shot over the edge of the world in some vast cataract, I shall sink with my nose to the sunrise …”

I accepted this plan of Reepicheep's when I read the book, but I don't think I grasped the why of it. What could make anybody want to behave like this? Then Sunless Sea managed to give me that particular bug.

In-game art from Sunless Sea showing Irem's dock, with its many-headed serpent statue and ground cover of fallen rose petals.
Tell me a secret

It started when I got my steamship to Irem. Measured from the player's home base in Fallen London, Irem is one of the most distant islands. It isn't threatening … just otherworldly. You spend your time there thinking in the future tense. You can purchase a temporary passage to the dreamworld. You can exchange stories like currency, or spend a fistful of precious Secrets to buy the soul of a star. And there's a store called Threshold which accepts some of the most rare and valuable items in the game in return for mundane supplies – food and fuel. It advertises itself with the line, “None sail East from Irem. This shop is for those who sail East from Irem.”

I decided, after only being there once or twice, that *I* was determined to sail East from Irem. My formal Ambition (the goal your player character has to meet before retiring) was to become London's greatest explorer and write a book about it … but after seeing Irem, I discovered I didn't hope for a comfortable retirement in London. I wanted to end the game by sending my boat off the right-hand edge of the map and never coming back.

It might be hard for me to verbalize why I hoped for this. The game will tell you very little about what lies in the mythical East. There's a vague promise of final frontiers and wonders beyond the imagination, but that's about all you get. The Sunless Sea itself is mighty fascinating – why not stay with it? And yet, Irem gave me a hint of something more. Something not of Earth. Without ever learning what I wanted, I wanted it very badly.

To my everlasting delight, the game anticipated this desire. But Reepicheep had it easy compared to me. In Sunless Sea, you can't just point your boat's prow eastward and expect to get somewhere. Going East will cost you. You are not told how high the cost will be.

By the time I had finished experiencing the terrors and glories of the Zee and felt strong enough to work on the arcane preparations for my Easterly voyage, I had probably put a good 40 hours of time into my character. And remember, if you play it as intended, Sunless Sea has permadeath. It was then that the game design reached one of its crowning triumphs: it stopped just frightening my character, and started frightening me.

This is not a safe place. Partly because you'll meet yourself.

You have to make an excursion off all four edges of the map to have the option of going East permanently. I went North first, and it was a disaster; it could have finished me. But South had the potential to be an even more serious problem. I cheated a bit, if you want to call it that: I consulted the internet and read up on this quest before going. Then I farmed my stats high enough that I could not lose the random number roll; I was guaranteed to survive. But! Even in a best-case scenario, the Southern challenge leaves you with one crewman and one point of ship's hull. You can't take so much as a love tap from a sea monster without plunging to a watery grave. And it's a long way to the nearest port where you can get repairs[1]. Retiring my captain in Fallen London actually became a little tempting – but I shrugged that temptation off, because how insufferably dull, and how insufferably cheap. I couldn't settle for it.

One evening I came home from work knowing this would be the night I put it all on the line. Sick feelings fluttered in my stomach as I prepared to dare the Red River. I made my journey into the heart of the Elder Continent, and I limped back to a place of safety … very carefully. I'd done it. Faced with a case when pursuing the unknown imposed actual risk on me, I chose to do it anyway, and I came through.

But that hazard of my real-life time investment wasn't even the end of it; the roleplaying part of the thing remains important. My captain character brutalized herself in order to pass the gate. She stood in the sun until it made her sick … over and over and over again. She pushed herself to the brink of insanity – twice – to reach the heart of Frostfound. She wrote her name on the wall. The game implies that this action is savagely painful. I didn't experience all these sufferings myself, of course, but I was subtly internalizing the choices.

To crown it all, the passage East demands that you sever ties with the mortal world, allow half your stats to be devoured[2], and sell off all your valuable, hard-to-find items. Hours of shuttling my ship around the trade routes went up in smoke. And I enjoyed it. That was the part that was so, so weird. Going East had become such a consummate triumph that it was a pleasure to demonstrate how I valued it by throwing lesser achievements to the winds. What did I grind up those stats for, after all, except to be strong enough to sail Eastward? Go on, burn it! We're going East! Burn everything if you want to!

I realized when I reached the end of it that I had just done a virtual reenactment of the Pearl of Great Price. I still don't know if I could explain to you why Reepicheep yearned to attain the East, but I know why, in the unspeakable knowledge of experience. The World is not enough … and when you come to understand that, only things from beyond the World will do. You have to go and pursue them with everything you've got. You have to Go East.

Months down the road, the lessons I pulled out of this game – the comprehension of joy taken in suffering for a higher goal – would pay off superbly in Real Life, on more than one occasion. If anybody tries to tell you that video games are empty recreation, or worse, a waste of time, either they're playing the wrong ones or they don't know how to use them.

[1] Belatedly, I read a guide and learned that you can bring Rattus Faber Assistants to get your hull out of the danger zone right away. I didn't think of this because I'd long ago sworn off using Rattus Faber Assistants. They're a consumable item, so I assumed that sending them to fix the ship was always a suicide mission, and my RPG characters don't go in for that sort of crud.

[2] I didn't get the alternate option of losing my sweetheart because I never chose to have one. 

Sunday, August 27, 2023

Acuitas Diary #63 (August 2023)

Not a big update this month, because I've been doing a little of everything and I'm still heavily focused on cleanup and capacity-building.

This is *my* brain, actually. I've been driving it hard. It is tired.

I did more work on Narrative and the Big Story. One thing that's very rewarding is to add features to make understanding of the Big Story work, re-run older stories to check for bugs, and see that the new features have added richness to previous stories - or even fixed things that I had to work around by adding more exhaustive explanations. An example is some of the work I did on this concept: "if certain outcomes of story events are rolled back, things return to their previous state, not a default or unknown state."

Sadly it's still going to be a while before I can share the Big Story. I was hoping to have it done by September, but sometimes project schedules just don't work that way. I want it to be finished and solid before I put it out there, so everyone (including me) will just have to wait.

The new Text Generator, in contrast, is almost ready for primetime, and I'm feeling pretty good about how much easier this will make generating the wide variety of sentences Acuitas is starting to need, varying the tense and other modifiers, etc. It's much cleaner than the old version too, at (so far) 1300 lines of code vs. over 2000.

I've also started cleanup on the Text Parser in the wake of last month's modifications. This is mostly boring refactoring, but along the way I've found a better method for handling compound nouns/proper names, and introduced the ability to support some titles written in title case. So for example, the Parser can now manage sentences like this: "The Place of the Lion is a book." "The Place of the Lion" is correctly perceived as the full title of some work and treated as a unit, but its internal grammatical structure (noun with article and prepositional phrase modifiers) is also still analyzed and parsed.

Until the next cycle,
Jenny