Shared Markov Chain Chatterbox

October 17, 2006 at 12:47 AM | categories: python, oldblog | View Comments

I wrote this as an example of a relatively simple, but non-trivial network protocol. It creates a server that sits there waiting for connections. Anything that you type at it updates the markov chain for anyone/everyone connected. The protocol handler itself could be bolted into an IRC bot instead so you could have a deranged bot sitting on a channel which talks vaguely on-topic (but relatively - not totally - incoherently) most of the time. I thought I'd blog about it because it makes quite a nice fun/simple introduction to Kamaelia in it's own special way. The markov chain used is based on the one here. (courtesy of a google search)


import Axon, random
nlnl = '\n', '\n'
key = nlnl

def new_key(key, word):
   if word == '\n': return nlnl
   else: return (key[1], word)

class Chatty(Axon.Component.component):
   data = {}
   def updateChain(self, message):
       key = nlnl
       for word in message.split():
           self.__class__.data.setdefault(key, []).append(word)
           key = new_key(key, word)

   def response(self):
       key, result, word = nlnl, [], None
       while word != "\n":
           word = random.choice(self.__class__.data.get(key, nlnl))
           key = new_key(key, word)
           result.append(word)
       return " ".join(result)

   def main(self):
       while 1:
           if self.dataReady("inbox"):
               message = self.recv("inbox")
               self.updateChain(message)
               self.send(self.response(), "outbox")
           yield 1

if __name__ == "__main__":

from Kamaelia.Chassis.ConnectedServer import SimpleServer
SimpleServer(protocol=Chatty, port=1500).run()

And that's pretty much all there is to it. As you'd imagine (I hope), a Chatty component is created to handle any accepted connection on port 1500, and anything the user types is received on the inbox "inbox", used to update the class's markov chain DB, and then generates a response to send to the outbox "outbox" (meaning it gets sent to the socket). The upshot is the more people who connect, the more the database gets updated.

The nice thing about this is that the bulk of the code here focusses on the logic that's desired, not on any networking details. OK, this example isn't ideal because it misses some important things like shutdown and what happens if the connection disappears, but it also is interesting because you can test the component in isolation as well:
Pipeline(
    ConsoleReader(),
    Chatty(),
    ConsoleEchoer(),
).run()
Which is a nice thing to be able to do! If you wanted to train the markov chain server you could also do that as follows:
Pipeline(
    ReadFileAdaptor("SomeTrainingMaterial"),
    TCPClient("127.0.0.1", 1500), # assuming localhost
    ConsoleEchoer(), # May as well see the deranged output :)
).run()
The fun thing about this trainer is that you can see the output from the markov chain during testing as well :-)

blog comments powered by Disqus