Monday, September 29, 2025

Acuitas Diary #88 (September 2025)

This month I returned to the Text Parser after letting it be for almost a year. My focus was on nailing the final major feature that I needed to handle all the sentences in my three children's book benchmarks: "parenthentical noun phrases." I don't know if that's the technical term, but that's what I'm calling them. They come after another noun and provide further description or elaboration of it, like this:

I was brought to see Philip Erto, the great engineer.
I was brought to see the great engineer, Philip Erto.

In both examples above, the "parenthetical noun phrase" appears at the end of the sentence, and is paired with the direct object of "see." In this case, the noun phrase that acts as the direct object and the noun phrase that acts as the parenthetical elaboration are interchangeable - the order depends on the speaker's desired emphasis.

Notice also that the same meaning can be captured by a dependent adjective clause instead:

I was brought to see Philip Erto, who is a great engineer.
I was brought to see the great engineer whose name is Philip Erto.

So in the Text Interpreter, I can reduce both the parenthetical noun phrases and the dependent adjective clauses to the same output: they produce extra semantic relationships, such as "Philip Erto <is-a> engineer <has-quality> great." But the Parser is the first stage of the text processing chain, and must handle their grammatical differences. So I added new code to pick out parenthetical noun phrases and attempt to distinguish them from other nouns that follow previous nouns (it's complicated).

Three pie charts showing the percentage correct and incorrect for the three test sets: "Magic Schoolbus: Inside the Earth (53%/47%)," "Out of the Dark (54%/46%)," and "Log Hotel(81%/19%)."
Percentage correct and incorrect for the three test sets: "Magic Schoolbus: Inside the Earth": (53%/47%), "Out of the Dark": (54%/46%), and "Log Hotel: (81%/19%).

After adding this feature, I spent some time on cleanup and a few more ambiguity resolution abilities. (See the November 2024 Diary for previous examples of this type of thing.) All in all, I was able to move every sentence in the Out of the Dark and Magic Schoolbus: Inside the Earth test sets into the "Parseable" category! (All sentences in Log Hotel were already parseable as of January 2024.) This just means that I can construct a data structure that represents the ideal parsed version of the sentence, and it's something the Parser is theoretically capable of generating. I still have a long way to go on getting the Parser to produce correct outputs for all the sentences. (For more information on my benchmarking methods and some early results for comparison, refer to the June 2021 and February 2022 diaries.

I've also done new work on Episodic Memory, but I'll save discussion of that for next month.

Until the next cycle,
Jenny

Wednesday, September 10, 2025

Hydraulic Heaven?

Since getting my custom pump to work well earlier this year, I've been pushing ahead on hydraulic actuator designs. There isn't a lot of miniature hydraulic equipment available for hobbyists, at least not at a good price, so I'm trying to make my own. I've settled on inflatable bladders as a fruitful direction to investigate - they don't have the same friction and seal wear issues as cylinders (syringes especially), and they can be customized in all manner of ways. In parallel, I've been working on moving parts designed to contain the bladders and be actuated by them.

Bladder Geometry

In the hydraulic demo from last year, I showcased my very first working bladder, which was a simple "pillow" - two rectangles of plastic film, with a valve stem inserted through the center of one rectangle, sealed together at their four edges. The problem with a bladder like this is that you can't get much range of motion out of it. When filled with fluid, it bulges a little - maybe a centimeter or so, at the small scales where I'm working. That wasn't enough for everything I wanted to do. I needed bladders with more complex geometries that could expand further, while still collapsing flat when fully deflated.

First of all I bought a heat-sealer, so I could melt plastic films together the professional way. (My first bladders were sealed with a soldering iron. The thought of going back to that method gives me the horrors.) This greatly improved the ease of making "pillows." Then I started trying for three-dimensional pouches with accordion folds. Getting all the little pieces of plastic lined up in the heat-sealer before they were attached to each other was too difficult, so I hit on the idea of sewing them together before sealing the seams. I even bought some plastic thread so that it could melt and be incorporated into the seal.

Two bladders sewn together from sheet plastic to make a "wedge" shape. There are visible seams with stitching, and each bladder has an inlet valve on the broad top side.

In short, I gave it a good try, but it was such a struggle. Sewing the little bladders together was a lot of work, and I never got one that was leak-free. It was particularly hard to get good seals where three seams met at a corner; no matter how much I might practice and refine the process, trying to make that on the heat-sealer was just plain awkward. Given how little success I had making wedge-shaped bladders with just one fold, I shuddered to think about my dreams of piston drivers with five or more. Manufacturing the bladders this way simply wasn't realistic.

Five plastic rectangles sealed together in their centers but not sealed at the edges yet, laid out on a table. The "Thriftbooks" logo side of the plastic is up. A two-pillow bladder with a tube attached to its inlet valve, fully inflated with water. The visible plastic is silver in color.

So I found a better way. You can make accordion folds by sealing the sides of two pillows together at the center, cutting a tiny hole in the middle of the seal, and then sealing the edges of both pillows. This avoids a lot of fiddly cutting and sewing, and more importantly, there are zero three-seam corners. All the seals are two-dimensional even if the bladder as a whole is not!

Photo of a bladder being made, showing creation of the center join on a heat sealer (model PFS-200).Photo of a heat gun being aimed at some plastic. All but a small circular region is protected with aluminum foil and a stack of large washers.
Methods of creating central seals 

I made my first center seals by folding the plastic around a strip of cardboard and putting it in the end of the heat-sealer, once on each side. This was non-ideal; it created a very small sealed area, the crease where the film bent around the cardboard got melted too much and could develop holes, etc. A better method is to shield all but the circular region you want to want to seal together, then blast that area with a heat gun. You might think I should be using an insulator (like cardboard) for the shielding, but from my experience so far, metallic objects that will soak up the heat actually work better! So you can fuse your two pillows in the middle, then snip the very center of the sealed area with scissors to allow fluid through.

Bladder Materials

Throughout the process of figuring this out, I had lots of general quality problems. I seemed to need a double seal at each edge (seal once, fold the edge over, seal again) to have decent chances of a working bladder, and they still sometimes leaked. Well ... I think the printed layer on one side of my film was interfering with a good seal. You may recall that I was using plastic cut from salvaged Thriftbooks poly mailers. I started noticing that the green side with the company logos didn't seal as well. In fact, when I'm doing center seals with the heat gun, those must be made silver side to silver side; the printed sides simply won't fuse (though I can get them to fuse, somewhat poorly, on the heat sealer). And then the pillow edges have to be sealed green side to green side, which doesn't work so well! I also lost at least one bladder because a tiny flaw or strain in the plastic popped open the first time I put it under pressure. These flaws are present because the plastic has already been wrapped around a book and beaten up in transport.

So I finally bit the bullet and got some pristine plastic films to try out. When you buy materials, you have more control over what you're getting, so I sampled two different thicknesses: a 2 mil painter's dropcloth (transparent) and a 4 mil plant bed cover (black). Neither has any coating or printing on it, and they both heat-seal wonderfully. Now I can get good edge seals on the first try, without folding the edge over and doubling the seal.

Bladder Inlets

The last element I need to talk about is the attachment of the valve stems. After some early experiments with silicone gel that didn't have much success, I used cyanoacrylate (Super Glue) for my first working bladders. Eventually I also tried a polyurethane sealer intended for use on nylon tents and such. The brand I got is Gear Aid Seam Grip+WP. The cyanoacrylate works okay, but in my tests (sample size = 3 each), the polyurethane proved more likely to make a good seal. It is necessary to respect its long cure time; I actually consider that a positive, since I cannot seem to use Super Glue without getting it on my fingers and possibly other things nearby. One thing I have NOT tried yet is two-part epoxy.

The hinge joint described in the text, fully extended with a fully inflated two-pillow bladder inside. A syringe is attached to the bladder via a tube that emerges through a hole in the joint's backshell.

So, okay, the glues are tolerable. But for real quality and durability, I would love to melt the valve stem and the plastic film together. So far all my attempts to heat-seal this part have been unsuccessful; they are two different materials and don't want to adhere. The option that remains is some sort of chemical weld. But it's hard to find anything that is safe and available for in-home use that will dissolve HDPE/LDPE film. I tried acetone on the off chance that it might work. It did smooth the surface of my PLA valve stems, but it never seemed to get them to a tacky or gooey state, and it did absolutely nothing to the films. So this may be a non-starter, unless I hear tell of a magical solvent that can do it.

Hydraulic Actuators

So how can we use all these bladders, exactly? After pondering how I might get one of them to operate my existing quadruped joint designs, I decided it would make more sense to come up with a new joint that was optimized to hold a bladder. Meet my hydraulic spade joint. A wedge-shaped bladder fits into one side of the backshell and pushes the spade upward (or forward, depending on the joint's orientation) when inflated. There are attachment holes for a tension element to return the spade to its original position when the bladder is depressurized. The spade's edge is constrained to remain in contact with the center of the backshell by a string tied through a hole in the spade and the backshell.

The cylinder taken apart, showing three pieces (the cylinder shell, the rod with its pressure-plate base, and the cap), and the fluid bladder installed inside the bottom of the shell. The bladder outside the cylinder, attached to the syringe, and partially inflated, showing all five pleats

The other actuator I tried out was this simple cylinder. To produce its relatively large linear motion, I made a bladder with five accordion folds, my most ambitious one to date. I hadn't worked out the quality problems yet at that time, so this bladder leaked in multiple places ... but if I inflated it quickly enough, I could still practice driving the cylinder. So it worked as a proof-of-concept.

Conclusion

I think I have all the components I need now: the pump, the valves, and the actuators are working well enough that it's about time to see how things play in an integrated system. I might be ready to start the process of designing and budgeting full projects that use these parts. Look out for more of my hydraulics work next year (if not sooner).

Until the next cycle,
Jenny

Tuesday, August 26, 2025

Acuitas Diary #87 (August 2025, Allergic Cliffs demo)

It's ready! Acuitas can play the text version of Allergic Cliffs and is often able to determine one of the secret rules by which the cliffs operate. Watch the video watch the video

In a previous blog I discussed how Acuitas chooses moves based on a simple heuristic that tries to replicate successes and avoid repeating failures. The part I haven't fully discussed yet is the final version of rule formation. Acuitas attempts to generalize from the results of past moves to derive rules of cause and effect that indicate when the cliffs sneeze. At this time all possible move results are reduced to the two goal-relevant outcomes (succeed or fail), and generalizations are made by looking at commonalities among all actions that share the same result. I started by just comparing pairs of actions, then upgraded the algorithm to look at the entire pool of past actions for feature combinations common across two or more of them.

For the moment, all rules are given in positive form. So, supposing the rules for the current game round divide the zoombinis onto the lefthand bridge if they have a blue nose or the righthand bridge if they do not, you'll never hear Acuitas say, "If a guide puts a zoombini who does not have a blue nose on a lefthand bridge, a guide fails," or "If a guide puts a zoombini does not have a blue nose on a righthand bridge, a guide succeeds." Instead you would get "If a guide puts a zoombini who has a blue nose on a righthand bridge, a guide fails," or any of "If a guide puts a zoombini who has a [red, orange, green, purple] nose on a lefthand bridge, a guide succeeds."

I ended up not having time (in my completely self-imposed schedule) to implement experiments (purposely choosing moves that will falsify or confirm tentative rules) or rule-following (informing moves by rules to increase chances of success). So Acuitas still plays the whole game using the "similar to previous move" heuristic. At the end of the game, he scores all tentative rules that have not yet been falsified and selects the one with the highest score to announce out loud. The scoring system is of interest.

Rules with more evidence behind them (more moves which had those combinations of features in common) score higher, naturally. But I found that I needed another trick to more successfully pick out which tentative rule was among THE rules of the current round. Any action which is the cause in one of THE rules tends to have a counterpart which differs by one feature and produces a different effect. So if you have a rule like "Put a zoombini with an orange nose on the lefthand bridge and you will succeed," then if THE rules are about orange noses, there ought to be opposing rules such as "Put a zoombini with an orange nose on the righthand bridge and you will fail," or "Put a zoombini with a blue nose on the lefthand bridge and you will fail." If these opposite counterparts don't exist, then the orange nose + lefthand bridge = success association is probably incidental; some other feature is driving the cliffs' behavior, and it just *happened* that all the zoombinis allowed to cross the lefthand bridge also had orange noses.

There's a lot more fun I could have with this and many directions to extend it, obviously, but for now I'm finished. I'm toying with the idea of possibly sharing the Allergic Cliffs text adventure (not any part of Acuitas, just the independent game script) for others to use. I would want to polish it first, add documentation, and possibly implement the higher difficulty levels, so I can't say when that might happen.

Until the next cycle,
Jenny

Tuesday, August 12, 2025

ACE the Quadruped 2025

It's about time I blog about ACE (Ambulatory Canine Emulator). This project got neglected while I focused on completing Atronach and getting started with hydraulics, but I've been poking at it every now and then. My most recent conclusion is that I do, in fact, need to replace the PF35T-48 motors. Those bargain-basement unipolar steppers are just not good enough.

My last major overhaul of ACE was - gulp - three years ago. The quick summary is that I solved enough rigidity and tolerance problems that the skeleton could be posed standing at its full height, and repositioned the motors so that none of their weight had to be carried on the legs. Since then, I've been doing motion tests and refining the design of the ankle/hock joint.

Once I started trying to make parts of ACE *move*, it quickly became obvious that, out of the box, the PF35Ts didn't have enough torque to accomplish much. It was difficult for one of them to even swing the upper leg back and forth when it was hanging free - a relatively easy motion, since the only load was the weight of the leg itself. So I detoured into designing gearboxes, taking my motor cradle as a starting point and adding mounting tubes for additional drive shafts. A single gear designed to mesh with the little one that comes attached to the motor was good enough to enable upper leg motion (see the first video, above). But there was still no way that was going to be enough to operate the hock joints. My efforts to *really* gear these motors down led to the creation of the little beauty in the video below, with three gear pairs. I'm still quite proud of it.

I also fiddled with the hock joint design and some of the tendon routing, trying to optimize the mechanical advantage and reduce the strain on the motors as much as possible. In this new version, the tendons are more exposed, but I think I get better leverage out of the deal. So I tried the new joint prototype with the new gearbox, and no gravity loading the leg - it's just moving in a horizontal plane. How did it go? Not well.

It only managed to do as well as it did for that demo because I was over-volting the motor (I typically operate these at 5-6V.) Careful study of this test setup convinced me that the main problem was with the tendons binding against various corners they have to go around. But they're made of nylon fishing line, which presents a very low-friction surface, so they shouldn't be binding that *hard*. I think the latest joint design is about as good as it can get, and the gearbox is doing fine as well - I saw no evidence of the gears jamming or anything like that.

So with no weight on the leg, and the motor geared down so much that it's moving miserably slow, it's still stymied by a little friction. My guess is that I'm not getting the higher torque I expected because losses in the gearbox are consuming it; the amount available was so low to start with that I might not be able to make the situation much better, no matter how many gear pairs I add. Although there are some small adjustments I could make to reduce how tightly the tendons are bent around corners, I'm taking this test result as a sign that the motors are just not right for this project.

Fortunately, there are options with a much better torque-to-weight ratio out there. They do cost a little more, but I'm not budgeting as tightly as I was back in my college days. I've already selected some new models to try, and redesigned the motor cradles to hold them. (It's so nice to have a somewhat modular design.) Here's hoping for somewhat more progress when I get a chance to try them.

Until the next cycle,
Jenny

Tuesday, July 29, 2025

Acuitas Diary #86 (July 2025)

This month I continued work on trial-and-error learning for playing Allergic Cliffs. If you haven't read my introduction to this Acuitas sub-project and the subsequent progress report, I recommend taking a look at those. What I've done since has been debugging and enhancing the "feedback-informed actions" and "rule formation" features discussed in the progress report, and getting them to actually work. It turned out to be a fairly big job!

A complex assembly of colorful gears slowly turning. Public domain image by user Jahobr of Wikimedia Commons.

Now that "feedback-informed actions" is functional, though, I'm a little surprised by how well it works. Its essence is that, in the event of a success, Acuitas tries to make his next move as similar as possible; in the event of a failure, he makes certain his next move is different. This heuristic only considers feedback from the move immediately previous, so it's a reactive, barely intelligent behavior. It still enables Acuitas to win the game about 90% of the time! Granted, he is playing on the easiest difficulty level, and at higher levels it is quite possible this would not work. It's still a huge improvement over purely random move selection.

Candidate cause-and-effect rules are also being formed successfully, and marked invalid when violated by an example. What I need to do next is implement higher levels of generalization. Right now rule formation only looks at positive commonalities between pairs of examples, and I need to also consider commonalities across larger groups, and commonalities based on the absence of a feature rather than its presence. In some cases I can see the algorithm *reaching* toward discovery of the hidden rule that defines the Allergic Cliffs' behavior, but we're not quite there yet.

After getting that far, I decided to walk away for a bit to look at game-playing with fresh eyes later, and worked on narrative understanding some more. What I wanted to add was the concept of a role or job. It's important for Acuitas to be aware of character goals, but those goals aren't always explicitly stated. If I told you somebody was a detective, you would automatically assume that this person wants to solve crimes, right? You wouldn't need to be told.

Acuitas had an existing system that allowed the semantic memory for a concept (like "detective") to contain goals that override parts of the default "agent" goal model. But here's the tricky part: the goal model specifies *intrinsic* goals, and goals associated with a role aren't necessarily intrinsic! Adoption of a role is often derived from some instrumental goal, like "get money," which eventually ties back to an intrinsic goal like survival or altruism. The meaning of anything a character does in a role is shaded by how invested they are in performing that role, and why. So it became evident to me that role-related goals need to be nested under a goal that encompasses the role as a whole, which can then be tied to an intrinsic goal.

So I tweaked the semantic memory's goal definition format a bit, to include a way to distinguish role-related goals from intrinsic goals, and provided the Narrative engine with a way to pull those into the scratchboard when a character is said to have a role. For now, all roles have to be sub-categories of the concept "professional," but I can imagine other types of roles in the future.

Until the next cycle,
Jenny

Sunday, July 13, 2025

A New Project?

I've been wanting to add some kind of physics experiment to my rotation of hobby projects, and I think I've picked one out. But I don't want to go into that just yet, because I'll be concentrating on the equipment prerequisites first. The most interesting thing I'll need is a way to measure tiny amounts of force - on the order of mN (milliNewtons) or even μN (microNewtons). Weighing scales are the most common force-measuring tools out there, so it makes sense to convert this force to a weight or mass. The amount of mass that produces a μN of force under standard Earth gravity is 0.000102 grams, or 0.102 milligrams.

Representation of a black hole, courtesy NASA.

Digital postal scales tend to have a resolution of 0.1 oz (2.8 g), which simply won't do. But there are also cheap digital scales intended for weighing jewelry, powders, etc. which claim resolution of up to 0.001 g (1 mg). Scales like this are all over sites like Alibaba and Amazon for less than $25 ... but they're short of the sensitivity I might need to see the effect. Going up an order of magnitude in price will get me an order of magnitude more precision, from this laboratory scale for example. For the really serious scales with a resolution of 1 μg, I would have to lay out over $10K. Technically that's within my grasp, but I'm not that invested in this experiment ... and I don't think I could claim to operate this blog on a "shoestring budget" anymore if I made such a purchase!

But wait! There's one more option: "build your own." Dig around a bit, and you can find some how-tos for building a scale that "is easily able to get around 10 microgram precision out of a couple of bucks in parts." The crux of the idea is to repurpose an old analog meter movement, adding circuitry that measures the electric current needed to return the needle to a neutral position after a weight is placed on it. The author of that Hackaday article "can’t really come up with a good reason to weigh an eyelash," but I can ... so now I'm really tempted by this build. It seems challenging but doable for someone with electronics knowledge. I don't really believe the budget would be 2 bucks ... any Digikey order costs more than that ... so let's figure $25.

To sum up, my options are as follows:

Consumer jewelry scale (Resolution = 0.001 g): ~$25
Laboratory scale (Resolution = 0.0001 g): ~$250
Home-built needle scale (Resolution = 0.00001 g): ~$25 + blood/sweat/tears
Professional microbalance (Resolution = 0.000001 g): ~$12000

Ideally, I'm thinking I should make the needle scale and buy the laboratory scale, so I can cross-check them against each other. As a home-made piece of equipment prone to all sorts of unexpected errors, the needle scale will benefit from some degree of corroboration, even if it can technically achieve a higher resolution than the lab scale. And either of these would put me in the right range to measure effects of a few μN, without breaking the bank. I really don't need the microbalance, thank goodness.

I'm not sure where I'll fit this into my schedule, but I've already got some analog meter movements, courtesy of my dad's extensive junk collection. So stay tuned to see if I can weigh an eyelash.

Until the next cycle,
Jenny

Monday, June 30, 2025

Peacemaker's First Strike

My short story of the above title will be LIVE and free to read in the 3rd Quarter 2025 issue of Abyss & Apex tomorrow! It's about a professional curse-remover who gets in a little over her head on an unconventional case; it's got mystery, magic, barbarians, and something to say about the consequences when defense of one's own goes too far. Ef Deal (at that time the Assistant Fiction Editor) was kind enough to tell me, "It has been a very long time since I read a sword & sorcery I enjoyed as much as this tale." So don't miss it!

Equestrian statue of a burly man with a sword in his right hand and some kind of banner made from an animal hide rising over his left shoulder. (It happens to be Decebalus, but that's not relevant.) The horse has all four feet planted on the plinth, and their head bowed forward.

I put something of myself in all my stories, but this one is more personal than most. It would be impossible for me to explain where it came from without airing some things that are better kept private, but in a roundabout and strange way, it reflects something I went through. So it feels particularly fitting that "Peacemaker's First Strike" should be my first paying publication credit. Turning this story loose means healing for me as well as the characters.

Abyss & Apex has been great to work with, so I'd love it if you would check out my writing and the rest of the issue, and support the zine with a donation if you are so inclined.

Until the next cycle,
Jenny

Tuesday, June 10, 2025

Acuitas Diary #85 (June 2025)

This month I have a quick demo for you, showcasing Acuitas' upgraded semantic memory visualization. My goal for this was always to "show him thinking" as it were, and I think I've finally gotten there. Nodes (concepts) and links (relationships between concepts) are shown as dots and lines in a graph structure. Whenever any process in Acuitas accesses one of the concepts, its node will enlarge and turn bright green in the display. The node then gradually decays back to its default color and size over the next few seconds. This provides a live view of how Acuitas is using his semantic memory for narrative understanding, conversations, and more.


You can see a previous iteration of my memory access visualization work in Developer Diary #4. Wow, that's ancient. The original access animations were only activated by "research" behavior (ruminating on a concept to generate questions about it), and were often hard to see; if the concept being accessed was one of the "smaller" ones, it was impossible to detect the color change at a reasonable level of zoom. The upgraded version of the animation is called from the semantic memory access functions, such that it will be activated if a concept's information is retrieved for any reason. And it enlarges the node by an amount proportional to its default size and the display's current level of zoom, such that it will always become visible.

I would have liked to make the links highlight when used as well. The problem is that links in Acuitas' memory storage aren't really distinct things anymore. A link is indirectly defined by endpoints included in the data structures for all the concepts it connects to. So there isn't a low-level function that determines when a particular link is being accessed; a node gets accessed, and then the calling function does whatever it pleases with the returned data structure, which might include following a link to another node. Keeping track of every time that happens and connecting those events with the correct lines on the display would have become very messy, so I opted not to. I think just highlighting the concept nodes yields an adequate picture of what's happening.

I haven't showcased the memory display in a long time because it's been a mess for a long time. The node placement is generated by a custom algorithm of my own. As more concepts were added to the graph and certain important concepts got "larger" (i.e. acquired more links), the original algorithm started to generate spindly, ugly graphs in which the largest nodes were surrounded by excess empty space, and the smallest nodes crowded too close together. I managed to work out a new placement method that generates attractive, proportional clusters without blowing up the computation time. Creating a new layout is still computation-intensive enough that the visualization can't be updated to add new nodes and links as soon as they are created; it must be regenerated by me or (eventually) by Acuitas during his sleep cycle.

And that's about the size of it. I'll be on vacation for the second half of this month, which means there probably won't be much Acuitas development happening until I get back. Enjoy the video, and I'll see you all later.

Until the next cycle,
Jenny

Saturday, May 31, 2025

Acuitas Diary #84 (May 2025)

A couple months ago I described my plans to implement trial-and-error learning so Acuitas can play a hidden information game. This month I've taken the first steps. I'm moving slowly, because I've also had a lot of code cleanup and fixing of old bugs to do - but I at least got the process of "rule formation" sketched out.

A photo of High Trestle Trail Bridge in Madrid, Iowa. The bridge has a railing on either side and square support frames wrapping around it and arching over it at intervals. The top corner of each frame is tilted progressively farther to the right, creating a spiral effect. The view was taken at night using the lighting of the bridge itself, and is very blue-tinted and eerie or futuristic-looking. Photo by Tony Webster, posted as public domain on Wikimedia Commons.

Before any rules can be learned, Acuitas needs a way of collecting data. If you read the intro article, you might recall that he begins the game by selecting an affordance (obvious possible action) and an object (something the action can be done upon) at random. In the particular game I'm working on, all affordances are of the form "Put [one zoombini out of 16 available] on the [left, right] bridge," i.e. there are 32 possible moves. Once Acuitas has randomly tried one of these, he gets some feedback: the game program will tell him whether the selected zoombini makes it across the selected bridge, or not. Then what?

After Acuitas has results from even one attempted action, he stops choosing moves entirely at random. Instead, he'll try to inform his next move with the results of the previous move. Here is the basic principle used: if the previous move succeeded, either repeat the move* or do something similar; if the previous move failed, ensure the next move is different. Success and failure are defined by how the Narrative scratchboard updates goal progress when the feedback from the game is fed into it; actions whose results advance at least one issue are successes, while actions that hinder goals or have no effect on goals at all are failures. Similarity and difference are measured across all the parameters that define a move, including the action being taken, the action's object, and the features of that object (if any).

*Successful moves cannot be repeated in the Allergic Cliffs game. Once a zoombini crosses the chasm, they cannot be picked up anymore and must remain on the destination side. But one can imagine other scenarios in which repeating a good choice makes sense.

Following this behavior pattern, Acuitas should at least be able to avoid putting the same zoombini on a bridge they already failed to cross. But it's probably not enough to deliver a win, by itself. For that, he'll need to start creating and testing cause-and-effect pairs. These are propositions, or what I've been calling "rules." Acuitas compares each new successful action to all his previous successes and determines what they share in common. Any common feature or combination of features is used to construct a candidate rule: "If I do <action> with <features>, I will succeed." Commonalities between failures can also be used to construct candidate rules.

The current collection of rule candidates is updated each time Acuitas tries a new move. If the results of the move violate any of the candidate rules, those rules are discarded. (I'm not contemplating probability-based approaches that consider the preponderance of evidence yet. Rules are binary true/false, and any example that violates a rule is sufficient to declare it false.)

Unfortunately, though I did code all of that up this month, I didn't get the chance to fully test it yet. So there's still a lot of work to do. Once I confirm that rule formation is working, future steps would include the ability to design experiments that test rules, and the ability to preferentially follow rules known with high confidence.

Until the next cycle,
Jenny

Sunday, May 11, 2025

Further Thoughts on Motion Tracking

Atronach's Eye may be operating on my wall (going strong after several months!), but I'm still excited to consider upgrades. So when a new motion detection algorithm came to my attention, I decided to implement it and see how it compared to my previous attempts.

MoViD, with the FFT length set to 32, highlighting and detecting a hand I'm waving in front of the camera. The remarkable thing is that I ran this test after dusk, with all the lights in the room turned off. The camera feed is very noisy under these conditions, but the algorithm successfully ignores all that and picks up the real motion.

I learned about the algorithm from a paper presented at this year's GOMACTech conference: "MoViD: Physics-inspired motion detection for satellite image analytics and communication," by MacPhee and Jalai. (I got access to the paper through work. It isn't available online, so far as I can tell, but it is marked for public release, distribution unlimited.) The paper proposes MoViD as a way to compress satellite imagery by picking out changing regions, but it works just as well on normal video. It's also a fairly simple algorithm (if you have any digital signal processing background, otherwise feel free to gloss over the arcane math coming up). Here's the gist:

1. Convert frames from the camera to grayscale. Build a time series of intensity values for each pixel.
2. Take the FFT of each time series, converting it to a frequency spectrum.
3. Multiply by a temporal dispersion operator, H(ω). The purpose is to induce a phase shift that varies with frequency.
4. Take the inverse FFT to convert back to the time domain.
5. You now have a time series of complex numbers at each pixel. Grab the latest frame from this series to analyze and display.
6. Compute the phase of each complex number - now you have a phase value at each pixel. (The paper calls these "phixels." Cute.)
7. Rescale the phase values to match your pixel intensity range.

The result is an output image which paints moving objects in whites and light grays against a dark static background. I can easily take data like this and apply my existing method for locating a "center of motion" (which amounts to calculating the centroid of all highlighted pixels above some intensity threshold).

My main complaint with the paper is its shortage of details about H(ω). It's an exponential of φ(ω), the "spectral phase kernel" ... but the paper never defines an example of the function φ, and "spectral phase kernel" doesn't appear to be a common term that a little googling will explain. After some struggles, I decided to just make something up. How about the simplest function ever, a linear function? Let φ(ω) = kω, with k > 1 so that higher frequencies make φ larger. Done! Amazingly, it worked.

Okay, math over. Let me see if I can give a more conceptual explanation of why this algorithm detects motion. You could say frequency is "how fast something goes up and down over time." When an object moves in a camera's field of view, it makes the brightness of pixels in the camera's output go up and down over time. The faster the object moves, the greater the frequency of change for those pixels will be. The MoViD algorithm is basically an efficient way of calculating the overall quickness of all the patterns of change taking place at each pixel, and highlighting the pixels accordingly.

It may be hard to tell, but this is me, gently tilting my head back and forth for the camera.

My version also ended up behaving a bit like an edge detector (but only for moving edges). See how it outlines the letters and designs on my shirt? That's because change happens more abruptly at visual edges. As I sway from side to side, pixels on the letters' borders abruptly jump between the bright fabric of the shirt and the dark ink of the letters, and back again.

The wonderful thing about this algorithm is that it can be very, very good at rejecting noise. A naive algorithm that only compares the current and previous camera frames, and picks out the pixels that are different, will see "motion" everywhere; there's always a little bit of dancing "snow" overlaid on the image. By compiling data from many frames into the FFT input and looking for periodic changes, MoViD can filter out the brief, random flickers of noise. I ran one test in which I set the camera next to me and held very still ... MoViD showed a quiet black screen, but was still sensitive enough to highlight some wrinkles in my shirt that were rising and falling with my breathing. Incredible.

Now for the big downside: FFTs and iFFTs are computationally expensive, and you have to compute them at every pixel in your image. Atronach's Eye currently runs OpenCV in Python on a Raspberry Pi. Even with the best FFT libraries for Python that I could find, MoViD is slow. To get it to run without lagging the camera input, I had to reduce the FFT length to about 6 ... which negates a lot of the noise rejection benefits.

But there are better ways to do an FFT than with Python. If I were using this on satellite imagery at work, I would be implementing it on an FPGA. An FPGA's huge potential for parallel computing is great for operations that have to be done at every pixel in an image, as well as for FFTs. And most modern FPGAs come with fast multiply-and-add cells that lend themselves to this sort of math. In the right hardware, MoViD could perform very well.

So this is the first time I've ever toyed with the idea of buying an FPGA for a hobby project. There are some fairly inexpensive FPGA boards out there now, but I'd have to run the numbers on whether this much image processing would even fit in one of the cheap little guys - and they still can't beat the price of the eyeball's current brain, a Raspberry Pi 3A . The other option is just porting the code to some faster language (probably C).

Until the next cycle,
Jenny

Sunday, April 27, 2025

Acuitas Diary #83 (April 2025)

I'm eager to get started on trial-and-error learning, but in the spirit of also making progress on things that aren't as much fun, I rotated back to the Conversation engine for this month. The big new feature was getting what I'll call "purposeful conversations" implemented. Let me explain what I mean.

An old black-and-white photograph of what looks like a feminine mannequin head, mounted in a frame above a table, with a large bellows behind it and various other mechanisms visible.
Euphonia, a "talking head" built by Joseph Faber in the 1800s.

A very old Acuitas feature is the ability to generate questions while idly "thinking," then save them in short-term memory and pose them to a conversation partner if he's unable to answer them himself. This was always something that came up randomly, though. A normal conversation with Acuitas wanders through whatever topics come up as a result of random selection or the partner's prompting. A "purposeful conversation" is a conversation that Acuitas initiates as a way of getting a specific problem addressed. The problem might be "I don't know <fact>," which prompts a question, or it might be another scenario in which Acuitas needs a more capable agent to do something for him. I've done work like this before, but the Executive and Conversation Engine have changed so much that it needed to be redone, unfortunately.

Implementing this in the new systems felt pretty nice, though. Since the Executive and the Conversation Engine each have a narrative scratchboard with problems and goals now, the Executive can just pass its current significant issue down to the Conversation Engine. The CE will then treat getting this issue resolved as the primary goal of the conversation, without losing any of its ability to handle other goals ... so greetings, introductions, tangents started by the human partner, etc. can all be handled as usual. Once the issue that forms the purpose of the conversation gets solved, Acuitas will say goodbye and go back to whatever he was doing.

I also worked on sprucing up some of the conversation features previously introduced this year, trying to make discussion of the partner's actions and states work a little better. Avoiding an infinite regress of either "why did you do that?" or "what happened next?" was a big part of this objective. Now if Acuitas can tie something you did back to one of your presumed goals, he'll just say "I suppose you enjoyed that" or the like. (Actually he says "I suppose you enjoyed a that," because the text generation still needs a little grammar work, ha ha ha oops.)

And I worked on a couple Narrative pain points: inability to register a previously known subgoal (as opposed to a fundamental goal) as the reason a character did something, and general brittleness of the moral reasoning features. I've got the first one taken care of; work on the second is still ongoing.

Until the next cycle,
Jenny

Saturday, April 12, 2025

Pump and Hydraulics Progress

If you've been following for a while, you may know I've been working on pump designs for a miniature hydraulic system. The average commercially available water pump appears to be optimized for flow rate rather than pressure, and small-scale hobby hydraulics are barely a thing ... so that means I'm custom-making some of my own parts. Last year I got the peristaltic pump working and found it to be a generally better performer than my original syringe pump, but I always wanted to get a proper motor for it.

The new pump sitting atop a pair of reusable plastic food containers, pumping water from one into the other. A power supply connected to the pump's motor is visible in the background.

The original motor powering all the pumps was an unknown (possibly 12 V) unipolar stepper and gear assembly from my salvage bin. But the precision of a stepper motor truly wasn't necessary in this application, and was costing me some efficiency. For the upgrade, I wanted a plain gearmotor (DC motor + gear box assembly) with a relatively high torque and low RPM. I settled on this pair of motors, both of which are rated for 6 V input:

SOLARBOTICS GM3 GEAR MOTOR (4100 g-cm, 46 rpm)
Dagu HiTech Electronic RS003A DC Motors Gearhead (8800 g-cm, 133 rpm)

You can tell from the torque and speed ratings that the Dagu HiTech was always going to be the better performer. I included the Solarbotics motor in my order because its gearbox and housing are plastic, which may reduce durability but also means it weighs less. In practice, it also draws less current than the Dagu motor, which might mean the power source can weigh less ... these things are important when thinking about walking robot applications!

The peristaltic pump with its lid off, showing the latex tubing, rotor, and rollers, sits on a table next to a cat for scale. It's smaller than the cat's head.

The next step was to reprint the pump. I left the main pump design essentially unchanged - all I did was correct the geometry errors from the previous iteration. So this time it worked after assembly without an extra shim, and I could put the lid on properly without needing zip ties to hold it closed. The motor housing and coupler were always separate pieces, so I designed two new versions of each, one for the Dagu motor and another for the Solarbotics motor. This is where the 3D printers reeeeaally show off their value. Compared with both the old stepper and each other, the new motors have completely different sizes, shapes, drive shaft designs, and mounting options, but I was able to produce custom parts that mated them to the pump in only a few hours of actual work.

And the test results were amazing. The Dagu is obviously more powerful and delivers a higher flow rate, but both motors have enough torque to drive the pump at the 6 V they're rated for.

Watch to the end for a surprise appearance by the Lab Assistant.

I pressure-test my pumps by dropping a piece of tubing from my second-story window to the back patio, and measuring how high the pump can lift water in the tube. From this it is possible to calculate PSI. I have published results from the previous pump designs. Well: Peristaltic Pump Version 3 can lift water all the way past the window with either motor. Given the water level etc. in this particular test, that's a total lift height of 170 inches. So the pump is producing at least 6 PSI, and I can't measure any higher than that. This makes it competitive with the syringe pump for pressure (at least as far as I can tell - the syringe pump also exceeded my maximum ability to measure), and MUCH better for flow rate.

When I was testing the syringe pumps last year, I used to go read a book for a little bit while I waited for the water to climb to its maximum height! I timed Peristaltic V3 with the Dagu motor, and it can get the water all the way up the tube (standard 1/4" aquarium tubing) in about 24 seconds. So this is a dramatic improvement on where I was when I started.

A window with a set of blinds in front of it, and a piece of transparent silicone tubing hooked through the blind cords up high. Water is visible extending nearly to the end of the tubing, and there are visible water drips below it on many of the blind panels.
Hydraulics testing: it gets messy

One little problem remains: I've noticed that, with these more powerful motors, the friction between the latex pump tubing and the rollers gradually pulls the tube through the pump. It'll keep shortening on the intake side and eventually lift out of the water. So I need something to hold it in place without clamping it and blocking the flow. Piercing the tube seems like the only solution for this. I could do it below the water line, OR, the tube is thick-walled enough that I bet I could put a very thin thread or wire through the wall without creating a leak.

I've also started on new actuators, but that is mostly a story for another day. I did get a "knee" style of joint working to the point of a basic demo. Once I started trying to adapt my existing quadruped hinge joint for hydraulic power, I realized it would be less complicated to make an entirely new design that naturally incorporates the hydraulic bladder. Next I need better bladders ... I'm working on that!

Until the next cycle,
Jenny

Tuesday, March 25, 2025

Acuitas Diary #82 (March 2025)

This month I've made a long-awaited return to the Game Engine (see the demo from June 2023) with ambitious plans to improve Acuitas' reasoning and agency. Specifically, I want to introduce some experimental learning abilities. And I'm hoping to do that by getting him to play Allergic Cliffs.

What is that? There's a reason I wrote a game showcase on Logical Journey of the Zoombinis earlier this month. "Allergic Cliffs" is the first puzzle in every journey. It's a hidden information game, with some underlying ties to Set Theory.

A screenshot of the Logical Journey of the Zoombinis puzzle "Allergic Cliffs," showing a whimsical painting of a chasm with two plank bridges across it. There are a pair of stone faces in the cliffs on the right side of the chasm. A number of zoombinis - little round blue creatures with various types of hair, eyes, and locomotion devices - are clustered on the grassy lawns on both sides of the chasm.
An example of the original Allergic Cliffs. It appears the righthand/foreground cliff is allergic to "two wide eyes" in this scenario. Screenshot by jdl on the Wonderland Forum.

In brief, there's a chasm with two bridges spanning it, and you want to get all your zoombinis across. The complicating factor is the presence of two stone faces in the cliffs on the chasm's far side. These beings are quite literally allergic to zoombinis with (or without) certain attributes. Putting a zoombini on the bridge that passes over the wrong cliff will cause the face to sneeze violently, shaking the bridge and tossing the hapless zoombini back to the near side. After enough mistakes, both bridges fall, and you have to leave behind any zoombinis who didn't make it over. Here's a video of someone playing Allergic Cliffs.

Since you're only allowed a limited number of failures, you can't brute-force the puzzle by setting every zoombini on both bridges. You need to be trying to learn which zoombini features make the cliffs sneeze. And to be sure of learning this before the bridges fall, you have to perform targeted experiments. Why did this zoombini get across? Why did that one get sneezed at?

Acuitas can't play the original Zoombinis game, obviously; it's far too visuo-spatial. He needs a text version. The author of the Storeroom Blog has kindly written up a full breakdown of the Allergic Cliffs game mechanics. I will only be expecting Acuitas to play on the easiest difficulty level, for the time being. So every round will require choosing a zoombini feature that one cliff will be allergic to and the other cliff will be immune to (i.e. allergic to all zoombinis who don't have the feature). This rule is kept secret, but must be used to inform descriptions of what the cliffs do when zoombinis are placed on the bridges. Each scenario also includes the group of sixteen zoombinis, with known characteristics, whom the player is trying to get across the chasm.

In my first demo of the Game Engine, I behaved as a "game master"; I initiated the game during a conversation with Acuitas, then kept interacting with him to describe the setting and the results of his in-character actions. But I don't want to do that in this case. Do you know how much effort it takes to fully describe a group of sixteen zoombinis? Lots, actually. I am not going to type that up and paste one line at a time into Acuitas' text box every time we play! This game needs to be automated.

So I wrote an independent, interactive Python program that implements a "text adventure" version of Allergic Cliffs. When launched, the game program generates sixteen zoombinis with randomized names and attributes, and a set of rules for the cliffs. Then it outputs a text description of the scene, the player character and their goal, and all the zoombinis. Acuitas, using the "Play" action, can run this script and connect the IO to his Game Engine. The script includes a very dumb parser that scans Acuitas' speech outputs for signs that he's moving a zoombini, and replies "you can't do that" to anything else. If Acuitas puts a zoombini on a bridge, the Allergic Cliffs script tells him the results. It also keeps track of the game's state; if it reaches either the win condition (all zoombinis on the far side) or the loss condition (fallen bridges), it will tell Acuitas "Game Over" and terminate.

This is something of a milestone, since it's the first time Acuitas has been able to launch and use a subordinate software tool. It was also more difficult than I expected. It's easy to launch another executable from a Python program using subprocess, but getting them to interact is another matter; by default, the main program expects the subprocess to run to its end, produce one burst of output, and be done. I ended up using the temporary file trick (described in the second answer). Both the Acuitas side and the independent program side send outputs, then wait for a response to appear in either the PIPE or the file, then formulate and send new outputs ...

Recreating Allergic Cliffs without all the graphics ended up being fairly simple, though. The game script is under 250 lines of code. Here's the boilerplate it uses to set the scene at the beginning of any game run:

"You are a guide."

The player character in LJotZ is referred to as the "guide" of the zoombinis; that's it. We don't even know what species this person is.

"You are at the Allergic Cliffs."
"There is a chasm."
"The chasm has a near side."
"The chasm has a far side."
"The far side has a lefthand cliff."
"The far side has a righthand cliff."
"There is a lefthand bridge over the chasm."
"There is another righthand bridge over the chasm."

Thanks to my January work on adjectives and distinct instances, the lefthand bridge and the righthand bridge can be distinguished. I'm using these words instead of "left" and "right" to avoid tricky sense disambiguation issues for now.

"You can put a zoombini on the lefthand bridge."
"You can put a zoombini on the righthand bridge."

Here the game gives the player affordances - that's a fancy name for obvious possible actions. In the original, these are communicated visually. Clicking on a zoombini "picks them up" (they start following the cursor and their locomotion devices dangle). Two patches of ground next to the bridges flash if the zoombini is moved over them, as a sign that the zoombini can be set down there.

"If a zoombini crosses a bridge, the zoombini will be on the far side."
"If you put a zoombini on a bridge, the zoombini will try to cross the bridge."

A couple of inference rules specific to this setting, to aid in problem-solving. I'm not sure whether I'll keep them in the final version, or have Acuitas learn this mechanic for himself too.

"You want all zoombinis to be on the far side."

This sentence establishes a goal for the player character. In the original, this would have been implicit in the narration and premise.

"Six pegs hold the bridges up.",
"If all pegs pop loose, the lefthand bridge will fall.",
"If all pegs pop loose, the righthand bridge will fall.",
"If a bridge falls, a zoombini cannot cross the bridge."

A warning about the loss condition, which thwarts the goal.

And that's it. Even a lot of the descriptions of the scene are just pretty nothings; the important part is the existence of the two bridges and the fact that the player can put zoombinis on them.

Here's an example description of a zoombini:

Oosebeek is a zoombini.
Oosebeek is on the near side.
Oosebeek has scruffy hair.
Oosebeek has eyelids.
Oosebeek has an orange nose.
Oosebeek has wheels.
Oosebeek does not have a ponytail ...

That's right, I also have the game script tell Acuitas every possible zoombini feature that this zoombini does not have. If not told that something is true, Acuitas doesn't assume it's false. Maybe this zoombini has wheels and a propeller, and nobody mentioned the propeller! Another way I could handle this would be to establish mutual exclusivity rules, like "if a zoombini has wheels, the zoombini does not have a propeller," and let him infer all the negatives. But this is an easier way to start.

I had to tweak some of the code behind the Narrative scratchboard for this as well. I introduced "temporary concepts" so Acuitas won't memorize the names of all sixteen zoombinis every time he plays a round. That would junk up the semantic database quickly. Playing this game much can easily lead to interactions with hundreds of zoombinis, and their names are procedurally generated strings. They're made to be loved, but not remembered.

If Acuitas puts a zoombini on the safe bridge, the game will tell him what happens afterward:

Oosebeek crosses the righthand bridge. (Acuitas has to infer that Oosebeek is now on the far side.)

If Acuitas puts a zoombini on the bridge over the cliff that's allergic to them, he gets this kind of response instead:

Oosebeek tries to cross the lefthand bridge.
The lefthand cliff sneezes.
Oosebeek is thrown to the near side.
Oosebeek cannot cross the lefthand bridge.
The first peg pops loose.

My goal for this month was to get Allergic Cliffs set up and make it possible for Acuitas to play. He's not any good at the game yet. His Game Engine is aware of the goal but has no idea how to reach it - so it falls back on trying the actions offered by the affordances. He'll keep putting a randomly chosen zoombini on a random bridge until he either loses or wins by dumb luck. After he's finished, the game-playing action generates a flow diagram from the game's narrative scratchboard for me; between that and the game outputs written to the temp file, I can see what happened.

Later this year I'll work on trial-and-error learning so he can actually win. This is a brand new area for me, and I'm excited.

Until the next cycle,
Jenny

Monday, March 10, 2025

Foundations Part II: They're Bluuuuue

Yeah, this series has a Part I - bet you thought I'd never finish it! It's time for me to showcase my other favorite edugame from childhood, Logical Journey of the Zoombinis. Like the Doctor Brain games, this one was so good for me that I wanted to keep playing it as an adult. I still have the original CD and can run the game if I set up one of my older Windows computers. (If you'd like to play it on a modern PC, there is a remake, though they redid all the art for some silly reason). Unlike the Doctor Brain series, Logical Journey does not teach any encyclopedic knowledge or technical skills. It is 100% about how to think - reasoning, experimenting, and planning. All the puzzles sorta have a basis in mathematics or programming, but you wouldn't realize that unless you were told (or knew what to look for).

Four zoombinis, which look like little blue orbs with hair, eyes, nose, and locomotion devices attached, are gathered around a hole dug in the ground, looking conspiratorially at each other.
Get in the hole, we're escaping!

Let's start with the obvious: this game is CUTE. What little kid doesn't love small round innocent creatures with big eyes? The ridiculous supporting characters range from an anthropomorphic ringtail cat to sapient rocks and trees. There's a thin but motivating plot: the Zoombinis were a happy nation of craft workers [1] until the Bloats tricked them into a bad trade agreement and basically enslaved them. The player character is a "guide" who helps bands of Zoombini refugees travel to a distant land where they can be free. That means getting them through bizarre obstacles, which I guess they're not smart enough to manage on their own.

To get a group of Zoombinis through the entire trek, you have to solve nine puzzles out of the twelve available. A common theme across seven of the puzzles is a hidden rule you must deduce. Sometimes the rule is about categories, sometimes it's about mapping (functions), sometimes it's about spatial relationships ... regardless, it can only be found through trial and error, and you get a limited number of tries. So this game excels at teaching the player not only how to generalize from limited experience, but also how to choose experiments that judge between competing possibilities and maximize information gained. Other puzzle elements I can think of include resource allocation under constraints, prediction, and sequencing.

An ethereal light grid is projected above a deep chasm in an underground cavern. Several zoombinis are floating above the grid inside bubbles. The bubble-making machines are visible on the ledges that border the chasm. Various symbols that resemble arrows, buttons, etc. are suspended within the grid.
Bubblewonder Abyss, the last obstacle, became my favorite puzzle once I figured out how it worked. It's all about sequencing (you solve it by sending Zoombinis across in the correct order) and imitates computer program elements like toggles and if/else statements. Screenshot obtained from zoombinis.fandom.com. 

This is also the only edugame I can think of that had a serious difficulty curve - it enabled me to watch myself get smarter. When I played for the first time, the easiest level was manageable (mostly - I had no clue what was going on in the Fleens puzzle), but the upper levels were too hard. When I got older and came back to it, I found myself mysteriously able to figure out parts that once seemed impenetrable.

I can remember having big feelings about this game - mostly frustration. Failing a puzzle means you "lose" some Zoombinis. They never die (the designers weren't brutes), but they do go back to the closest available campsite. And then, unless you lose more Zoombinis, you can never get back to moving perfect groups of sixteen around. You'll always have a "remainder" stuck at one of the camps. I adore round numbers enough that this ticked me off. It's tough to choose the puzzle I had the fiercest love-hate relationship with. "Mirror Machine" probably drove me crazy the longest, but I have to give the award to "Titanic Tattooed Toads," because failing at this one really made me feel dumb. It's quite easy if you meticulously pay attention: just make sure your lily pad feature of choice forms an unbroken trail across the marsh. If you aren't meticulous and miss one incorrect lily pad, boom, the scenario becomes unwinnable. Being impatient and launching more than one toad at once can also wreck you. Thanks loads, toads!

An overhead view of a river with an orderly grid of lily pads spanning it. Each lily pad is decorated with a colorful flower. Giant toads and crabs are sitting on some of the lily pads; the toads have zoombinis riding on their heads.
The same-color paths were always easier for me to find than same-shape, so I wonder if this puzzle is extra torture for colorblind kids. Screenshot obtained from zoombinis.fandom.com.

My late-blooming achievement was not intelligence, but discipline. In early playthroughs, I lost interest after a few journeys at the highest difficulty level. It wasn't until I was older ... I forget when, probably my late teens ... that I "beat the game" by transferring a full complement of 625 Zoombinis from Zoombini Isle to Zoombiniville. That involves playing the nine puzzles at least forty times, even if you do it perfectly. Was it worth it? I don't know, but I feel strangely satisfied. I guess it means I cared. (If you leave the game running in Zoombiniville for very long, the narrator starts harassing you about all the Zoombinis still living under tyranny. Tell him I finally committed and went back for every last one.)

There might be a particular reason why I decided to talk about this game this month, but you'll just have to wait and see. :)

Happy cogitations,
Jenny

[1] How they managed this without hands or other manipulators, I don't know. Don't overthink it.

Sunday, February 23, 2025

Acuitas Diary #81 (February 2025)

I've been on a real productive streak, so I did two major things this month. One enhances conversation abilities; the other is about gerunds. Don't worry, I'll explain those.

An abstract logo of an eye accompanied by the words "seeing is believing."
A famous phrase that uses gerunds. Image from https://commons.wikimedia.org/wiki/File:SiB_Logo.jpg

First, I went back to the conversation features that I introduced last September and worked on getting them solid - ironing out the remaining bugs and moments of weirdness. After spending about a week on that, I was pretty happy with the state of the Conversation Engine. Then I added another type of "topic tree." The one from last September guided responses to the conversation partner's states of being; this one reacts to actions that the conversation partner says they took or are taking. Possible threads include ...

*Try to infer whether the speaker liked doing that or not, and comment accordingly
*Ask for motivation ("Why did you ...") or announce what he suspects the motivation was
*Guess what the results were (if he can make any inferences)

This needs a lot more polishing, but it's starting to increase the complexity and variability of conversations. You can now go down "rabbit holes" which start with talking about a personal state, then lead into what you did to cause it, and so on. Which also means it's harder to keep everything straight, and I haven't really set Acuitas up to clearly indicate when he's jumping topics, yet. Always more to do.

My next project was to add support for gerunds to the Text Interpreter and Generator. What's a gerund, you might say? It's a present participle verb form (the kind that ends with -ing) used as a noun. Gerunds can be used to make statements about the concept of an action, such as the following:

I enjoy dreaming.
Exercising is good for the body.

Like other verbs, gerunds can have objects and adverbs, forming a gerund phrase - a group of words which, as a unit, acts like a noun in the full sentence.

[Reading books] makes me happy.
I see that you didn't care for [John's clumsy handling of that situation]. Did [my smoothing it over] satisfy you?

If a gerund has a "subject" that is performing the action, as in the final example, it's supposed to be in the possessive; it modifies the whole gerund phrase, instead of acting as a true subject.

I already added code to identify some gerund phrases to the Text Parser back in 2023, but the later stages of the text processing chain didn't know what to do with them if they came out of the parser, and Acuitas couldn't use them in his own speech. I wanted to get these capabilities in, because gerunds are so useful for expressing sentiments about actions. They're often used for expressing sentiments about states, too:

I dislike being wet.
Being warm is a pleasure.

I had to work around the absence of gerunds when I was putting in the latest conversation features, and it was giving me some pain. But thanks to this month's work, they're now more fully supported. I defined some new "fact" structures to function as the distilled version of statements about actions, added code to the Interpreter to map incoming sentences to those, and added code to the Generator to produce output sentences from those. So Acuitas has a bunch of new ways to say he likes or doesn't like something, in addition to a path for "comprehending" a wider range of written sentiments.

Until the next cycle,
Jenny

Tuesday, February 11, 2025

Atronach's Eye 2025 (Complete?)

As of last year I had worked all the major bugs out of my motion-tracking eyeball, but I still couldn't consider it finished. I wasn't ready to hang it on the wall and let it operate yet, due to one last issue: thermal concerns.

The mechanical eyeball in its colorful case, mounted on a beige wall. There's a couch in the foreground, and the eyeball's power cord is visible hanging down behind the couch. Also visible is the back half of a tricolor tabby cat, who is trying to burrow in between two layers of blankets on the back of the couch.
It's installed on the wall! It's operating continuously! It's done! The Lab Assistant gets to take a nap!

The eyeball's motion is driven by two unipolar stepper motors, each of which is powered from one of my dirt-simple stepper motor controllers. These are custom PCBs that I got manufactured many years ago. They were very inexpensive, and they only have two control wires (a nice thing for connecting motors to embedded processors with a limited number of general purpose IO). But that also means they don't have a power enable. Even if the control inputs are static and the motors aren't moving, some of their coils remain energized. That means they can exert their holding torque, which keeps the drive shaft in place and resists any external forces that try to turn it. But that wasn't something the eyeball needed (it naturally stays in position when unpowered). And during tests, I noticed that the motors got quite hot: enough to be uncomfortable to touch and make the plastic case smell.

I didn't like the thought of leaving the eye turned on with the motors baking themselves and everything else in the case in their own heat day in and day out. I doubt it would qualify as a fire hazard or anything serious like that, but in addition to wasting a small amount of electricity, it could reduce the lifetime of the parts. So I wanted to be able to cut all power to the motors when the eye wasn't active.

My solution was a standard relay. I bought some of these nice little breakout boards from DIYables that have the relay already mounted with connectors and a couple of indicator LEDs, and spliced one into the motor power input path of both motor controllers.

Then I added code to the eyeball software to turn the relay on and off. I wanted to strike a balance between letting the motors heat too long, and clicking the relay on and off constantly. So the software turns the motors off if no moving object to track has been seen in view for a certain amount of time. I also threw in darkness detection; the eye will stop trying to track motion if the average brightness of the scene is too low to see properly. (So it won't move around and make little noises in the middle of the night because the camera picked up some low-light noise.) Both features worked very well. The eye reliably deactivates when I leave the room (so long as there isn't something else moving around, like the flames in the fireplace) and when the lights are out or too dim.

In the dark, the indicator LEDs on the Raspberry Pi and the relay board diffuse through the white parts of the case and turn the eye into a low-key nightlight. I wasn't really expecting this, but hey: it still has a function when it's not looking at stuff.

In the end, I got to do what I always wanted: hang the eye on the wall and let it be an always-on fixture in the house. Further tweaks and upgrades to the software are possible, but I'm calling it finished because it's finally operating. It has been on the wall trying to look at me, the Lab Assistant, and other motion sources for at least a couple weeks of total up-time. Hearing it "wake up" as I pull open the curtains has become part of the morning routine.

There will almost certainly be another version, because there are plenty of things I could improve. I already applied a bunch of little enhancements to the 3D models of the case parts late last year. But this is the first Atronach's Eye iteration that could be said to realize my original vision (cough) for the project, if imperfectly. I'm also thinking about what else I can do with it. Atronach could theoretically tell Acuitas whether there's somebody in the room, for instance. Mmmm.

Until the next cycle,
Jenny