Bad ideas for NaNoWriMo

I’m not a novelist. I can write, and with sufficient editing, I can even write passable English text, but it’s not really something that I’ve put a lot of effort into, and so not something that I’m good at. My real time investment in writing has been writing software, which is read only by compilers and interpreters, and so what it lacks in excitement, narrative, and plausible dialog, it makes up for in conciseness and precision.

My ideas for National Novel Writing Month (November, for those as don’t know) then, are coding a program that writes novels, and coding a program that takes novels as inputs and generates interactive fiction (text adventure games) from them. Both of these are problems with infinite hair, and are arguably AI-Hard problems (that is, problems whose solution is on par, difficulty-wise, with creating a human-level general-purpose AI). On the other hand, writing a good novel is probably also quite hard.

I think the most reasonable approach for the novel generator would be a recursively-defined novel description language, which selects from tropes and plot stubs, generates characters, and so forth, based on relatively simple rules. The complexity would come from applying the rules over and over, so a simple quest to throw a ring into a volcano grows branches on branches on branches until it is One Damn Thing After Another Until All The Orcs Are Dead. The goal of the program would be to use generative content and emergent behavior to do most of the writing, and leave me to fill out the turns of phrase and details (or generate them a la Dwarf Fortress, which menaces with spikes of ivory). Done badly, this would read like a Mad Lib. Done well, it would read like a Mad Lib filled out by people who don’t say “dongs” every time they are asked for a noun.

Making interactive fiction (IF) out of novels would be substantially harder. The novel parser would have to read English, which is actually quite a trick. English has multiple words for one meaning and multiple meanings for one word, highly flexible structure, and counts on the reader to sort it all out. On top of that, most of the awesome tricks one can pull in English are  more a matter of exploiting shared cultural context with your reader than they are particular sequences of words. If I wrote such a parser in one month, or at all, a lot of linguistics researchers would be out of work.

Assume I went for one tiny part of the problem: identifying the locations in the novel. The same place might be described as “where that party was”, “Joe’s house”, “the darkened house”, and “a pit of iniquity”. Only the events of the novel link them, and so the program would have to determine that these totally different words referred to the same place. The best approximation I could likely come up with is identifying all the things in the novel that sound like places, and then performing some sort of clustering based on what words are mentioned close to mentions of those places. This would likely lead to a bunch of spurious places getting generated, and real places getting overlooked.  There is an entire company, called Metacarta, that did this sort of analysis on much more constrained data sets, and even then it was a difficult problem for a team of people who were likely smarter than me.

However, doing a good job of adapting novels to interactive fiction might not be the best approach. It might be better to get a rough cut of the software together to do anything at all, and gradually improve it until it writes things that are playable curios, rather than detailed simulations of well-loved novels. It wouldn’t be a matter of playing through “A Game of Thrones” so much as it would be “poking around in a demented dreamscape based loosely on ‘A Game of Thrones'”.

This is actually related to another idea that I had, which is sort of a rails shooter based on the consequences of shooting things. You play through the game, riding the rails and shooting enemies of varying levels of craftiness and menace. When you reach the end of the level, you just loop through it again, passing the bodies of everything you killed, and getting another shot at everything you missed. This repeats until you have killed everything in the game world, whereupon you continue to loop, passing through scenes of slaughter as the heroic music fades and is replaced with silence and the buzzing of flies. Perhaps, if you let it run long enough, the dead bodies would rot to skeletons. Now, of course, I’ve spoiled it for you, but I’ll probably never get around to writing it, so at least you’ve had the idea.

Chatbot is done

I finished writing the chatbot that I was working on. It consists of a set of scripts to prepare the data, and another script that listens for incoming messages and responds. You can get the code and an overview of how it works here.

Obviously, I’m not publishing my chat logs. Use your own. It is designed to work with Pidgin’s HTML-like format for chat logs, but it could be modified to work on almost any corpus. I really should clean up things like the string cleaning routines, but it worked for class, and that’s what actually matters.

Python Pidgin Dbus Monitor

This is a script that registers a callback with Pidgin’s Dbus interface, and then sends a message in response whenever anyone sends an IM. I’m using it for a chatbot, but it could easily be extended to do things like switch X10 light controllers on and off, get the state of hardware connected to the target computer, and so forth. Pidgin has to be running and configured to use Dbus, but it does that by default.

'''
Created on Nov 18, 2011
Watches pidgin over Dbus and responds to incoming messages.
'''

import dbus
from dbus.mainloop.glib import DBusGMainLoop
import gobject

class StupidResponder():
    def getResponse(self, message):
        #This is where you would do something clever to come up with a response
        return "Insect! I cannot bear your words! They are TOO TINY!"

def got_msg_cb(account, sender, message, conversation, flags):
    purple.PurpleConvImSend(purple.PurpleConvIm(conversation), responder.getResponse(message))

if __name__ == '__main__':

    #load a response generator
    responder = StupidResponder()

    #Connect to pidgin on Dbus
    main_loop = DBusGMainLoop()
    session_bus = dbus.SessionBus(mainloop = main_loop)
    obj = session_bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
    purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")

    #Add the callback
    session_bus.add_signal_receiver(got_msg_cb, dbus_interface="im.pidgin.purple.PurpleInterface", signal_name="ReceivedImMsg")

    #Listen
    loop = gobject.MainLoop()
    loop.run()

Some Short Notes

The ToyBrain project has been on hold for a variety of reasons, mostly time and money. I finally have enough money to order the motor driver chips I wanted from Digikey. They are on back order, but should arrive near the end of the month. Once I have those, I’m going to put together a little video of the first boards doing a variety of motor driving tasks. That video will go into a Kickstarter funding round to get the second edition of the boards produced and populated.

At least one of the ToyBrain boards is going to end up hooked to a computer via the serial port at one end, and a vibrating motor at the other end. I’m reviving an old project to add a teledildonics plugin to Pidgin. It will allow a remote user to use commands like /harder and /faster (and of course /softer and /slower) to control the speed of the vibrator. That one may not make it into the Kickstarter video.

I found an interesting post about laser power ratings recently. It covers the relationship between PWM and laser output power, which is going to be useful for the power supply that I’m building for my laser. Once I build that power supply, I’ll be in the rather interesting position of having designed a cutting laser power supply that can be built from easy-to-obtain materials. Hopefully, that will knock the price down enough that more people can do DIY CNC laser builds. I may also make that PCB available as a kit, so people can build their own.

I also looked up TEA Laser plans again, and started wondering about making a dye laser. The TEA laser emits in the UV range. so it could be used to pump a UV-reactive dye. Vitamin b12 (in energy shot drinks) and tonic water both are UV reactive, so it may be possible to make a yellow or blue laser using a dye that is drinkable. Normally, laser dye is a toxic dye in a toxic solvent, so this would be pretty neat for the home experimenter.

 

Behold!

I have HEALED this man’s WOUNDED STEREO!

One of the users of a mailing list that I’m on had a nice stereo receiver with one bad channel. Stereo has two channels, and while one worked, the other simply had no output. I opened it up and found that the signal lines from the amplifier run through a relay to the rear panel connectors. I can think of a couple of reasons for this, but the main one is that when you power it up, there is a period where the circuitry of the receiver stabilizes and effectively “boots up”. During that period, it might put some sort of really loud signal out over the speakers (and blow them inside out) unless they are disconnected. Once everything settles, it is safe to connect the speakers. The other possibility is that if you short the speaker connection, the receiver can detect the excessive current flow and disconnect the speaker hookups.

Whatever the reason for them, one of the relays was DPST, with one pole for each stereo channel, and one set of the poles didn’t close when the system powered up. I figured this out by plugging in an audio source, turning the receiver on, and then tracing back from the connector until I found an audio signal. There was nothing at the connector, nothing where the connector joined the PCB, nothing at one pole of the relay, music at the pole it was supposed to be connected to.

The fix was to remove the board with the relay on it, pop off the case of the relay, clean the contacts, push them a little closer together, and put it all back together. If the relay had really been shot, it was a pretty common size, and would have been easy to replace. Overall, the receiver was well-designed for repair, and the service manual was available online. I will give Onkyo props for using one size of normal philips-head screw throughout their case, but did they really need 41 of them?

 

Notacon Talk on Brain Hacking

This is the talk I gave at Notacon in 2008. It’s kind of goofy, but provides a broad overview of wireheading/neurohacking technologies.

Crazy Talk

This is a program that I wrote about ten years ago, to generate nonsense from a given input text. It builds a statistical model of the input text, which allows it to determine, given any two words, what the most likely third word is. Then it can use the last two words of those three to pick a new word, and so on. Since it doesn’t always pick the most likely word, but selects randomly from all options based on likelihood, it tends to create abrupt switches of concepts around short phrases like “it to” or “in the”.

For bonus points, it posts to a Blogger blog, assuming they haven’t changed their API.

It’s written in Perl, and I assume still works. If not, updating it to Python would be fun, and a good excuse for me to practice my Python.

Read more

Adding smarts to common materials

I found a couple of neat sites, and figured I’d link them here so they get more google juice, or link sauce, or whatever combination of a wet thing and food seems appropriate.

The first is the MIT High-Low Tech Lab’s Kit of No Parts. This site has suggestions for ways to incorporate electronics into a lot of other materials, such as wood and cloth, to make more engaging interactive objects. Things like speakers built out of seashells are a lot closer to art than useful products, but that’s part of the intent. It also inspires me to build a device that records and plays sound, and hide it inside a very large seashell as an art project.

The second site is called How To Get What You Want, but that’s rather predicated on you wanting fabric sensors, odd conductive materials (felting wool!), hacked toys, and so forth. There are a lot of good ideas here, waiting to be remixed into other cool stuff.

More Deskewing Rectangles with OpenCV

Someone recently posted to a mailing list that I read, asking for a program that can rotate and crop a lighter-colored rectangle on a black background. Since this fits in with the program I’m working on to locate, rotate, and deskew an image of a card in a photo, I figured I’d give it a shot. I’m planning to do both rotation and deskewing, but for now I’m only doing the rotation.

I’ve installed the latest OpenCV (version 2.3, as of this writing), and started learning the new Python bindings.  There seem to be a lot of cases where functions want a numpy array as an argument, but the functions don’t return numpy arrays, so there is a lot of fiddling around with the results of functions to get something that can be passed to another function.

On top of that, the return values of functions are poorly or rarely documented. Python’s duck-typing can help out a little in these cases, but if you call cv2.minAreaRect, you get something like ((233, 432), (565, 420), -0.2343), which is described in the documentation with the single word “retval”.  It would be helpful to have a way to find out that the first tuple is the center, the second is the width and height, and the third is the tilt (in degrees) of the “first” edge off of horizontal. That tuple makes just as much sense, but is wrong, when interpreted as the top left and bottom right corners and a tilt measured in radians.

Also, the “first” edge is seemingly arbitrary, or at least I can’t find any documentation describing it. This means that the same rectangle could be off by 0.4 degrees or by -89.6 degrees, depending on if the first edge is a horizontal or vertical edge. One thing that may be helpful is this stackoverflow post. Since the rectangle is defined as points, I can reshuffle the points to get them in a consistent order, and then get the angle off of horizontal for a consistent edge. That then goes into producing the transformation matrix for affine transforms (e.g. rotation and deskewing).

The minAreaRect() call gets me the angle I can use to rotate the image, and this trick should get me the four corners of the perspective-skewed image that can be straightened out to get the squared image.

OpenCv and finding rectangles

I have been working, on and off, on a computer vision application that will recognize a card from a certain game in an image, figure out what the card is, and add it to a database. I had some luck in earlier versions of the code looking for template matches to try to find distinctive card elements, but that fails if the card is scaled or skewed, and it rapidly becomes too processor-heavy if there are many templates to match. Recently, at work, I have had even more opportunity to play with OpenCV (a computer vision library), and have found a few blogs and tricks that might help me out.

The first blog shows how to pick out a Sudoku puzzle from a picture. The most important part is finding the corners of the puzzle, as after that, it can be mapped to a square with a perspective transform. I can do a similar trick, only I’ll be mapping to a rectangle. Since corner-finding is kind of scale-invariant (the corner of something is a corner at any scale), this will let me track a card pretty easily.

I think that I can actually use OpenCV’s contour finding to get most of the edges of the card, and then the Hough transform to get the corner points. I may even be able to get away with using just contour finding, getting the bounding rectangle of each contour, and checking that it has something like the proper aspect ratio. This will work in the presence of cards that are rotated, but fails on perspective-related skewing.

This StackOverflow post has a nice approach to getting the corners of a rectangle that has some rotation and perspective skew.

Once I have the card located, I’m going to throw a cascade of classifiers at it and try something like AdaBoost to get a good idea of which card it is. Some of the classifiers are simple, things like determining the color of the front of the card. Others may actually pull in a bit of OCR or template-based image recognition on (tiny) subsections of the card. Since I will actually know the card border at this point, I can scale the templates to match the card, and get solid matches fast.