#!/usr/bin/python """ Codelet System by Kris Schnee. A program using the "codelet" architecture described by Prof. Douglas R. Hofstadter et. al. in "Fluid Concepts and Creative Analogies." This program uses the metaphor of a hand of cards. Run by itself, this module demonstrates the basic concept; it does nothing, but does it elegantly. Each time through its main loop (Go), it semi-randomly picks a card (a Python dictionary object), does something based on its listed effect, then "discards" the card. What it does depends on the function refered to by the card's "effect" field. This module by itself has a few dummy functions as examples. It can be built into a larger program for AI or other purposes. This is a Python module, so it can be used either by copying and modifying the code, or by importing and subclassing it. The point of this system is to break a task into many small subtasks that work in simulated parallel, devoting varying amounts of time to different processes while allowing some time even for unlikely ones. This isn't just a crude form of multi-threading, though; the codelet system makes it possible to have little independent judgments going on and deciding what steps need to be taken next, rather than a number of linear processes running at once. DRH's research group uses the technique in its attempts to build AI programs that model human creativity. As a storyteller I find the codelet technique a useful way of looking at the creative process, because I make many small observations and tweaks to a story in a non-linear way, modifying things that have already been decided and setting myself new tasks along the lines of "I need to look at this scene again" or "I see a pattern I hadn't consciously intended, but it won't quite work unless I make the hero do X." If the cards can make observations such as "this character fits the standard model of a 'hero', but feature X detracts from that," then it can begin to make the same kinds of decisions that I make. As implied, I've thought about using something like this for a storyteller program. I've been dissuaded from trying that for now due to the extreme "hollowness" of an AI's knowledge of the things it'd be writing about; that is, its lack of anything resembling the complex set of sensory data and associations that a human has for even a trivial object. Still, this approach is a promising one for storytelling and other creative tasks. If you do anything with this code, I'd appreciate hearing about it at kschnee at xepher net. (Optimization tweaks provided by Dratter; thanks!) """ __author__ = "Kris Schnee" __version__ = "2008.5.20" __license__ = "Public Domain" import random class CodeletSystem: def __init__(self): self.hand = [] ## Set of codelets that might be played. self.table = [] ## Set of copied structures, a form of working memory. self.age = 0 ## How many cycles have I gone through? self.hand_limit = 0 ## Optional limit on # cards in hand. def __repr__(self): """Describe myself.""" return "" def ShowHand(self): text = "--- Cards in hand: ---" for card in self.hand: text += "\n"+str(card) print text def ShowTable(self): text = "--- Data on table: ---" for item in self.table: text += "\n"+str(item) print text def Deal(self,card={}): """Add a new card to the hand.""" if not self.hand_limit or len(self.hand) < self.hand_limit: self.hand.append(card) else: raise "Hand limit exceeded!" def PlayCard(self): """Select a card and run the function it refers to. Card types can do such things as dealing new cards, adding data to the table, modifying that data, or interacting with other parts of the program.""" self.age += 1 if self.hand: """Raffle-ticket system for picking a card to play. Each point of priority a card has gives it one chance to be chosen this turn. High priority cards therefore tend to be chosen quickly, but not always.""" chances=[] for n in range(len(self.hand)): priority = int(self.hand[n].get("priority",1)) chances += [n]*priority card_number = chances[random.randint(0,len(chances)-1)] card = self.hand[card_number] del self.hand[card_number] effect = card.get("effect","") target = card.get("target",None) ## print "Card's Effect: "+str(effect)+"\nTarget: "+str(target) ## Find the appropriate function to call, and run it. try: function = getattr(self,effect) except: raise "Couldn't find a function for card type \""+effect+"\"." function(card) def CopyToTable(self,what): """Store some piece of data so that cards can access it. Data should be in the dictionary format and can contain anything else. Card types can be given references to objects on the table so that several cards can manipulate the same piece of data over time. A "when_added" field is automatically added. A good practice would be to supply a "nature" field explaining the type of data.""" what["when_added"] = self.age self.table.append( what ) ## Sample, silly codelet functions. Add your own. def Examine(self,card): print "Running an 'Examine' function. Put a 'Label' card in play." self.Deal({"effect":"Label","target":card["target"],"priority":card["priority"]}) def Label(self,card): print "Running a 'Label' function. Modifying this card's target to say 'shiny'." target = card.get("target") if target: target["sample label"] = "shiny" else: print "(This card didn't have a target, so nothing happened.)" def Ferret(self,card): print "Running a 'Ferret' function. Adding some data to the table. Dook!" new_data = {"nature":"text","text":"Dook!"} self.CopyToTable(new_data) ## Demo function. def DemonstrateCardLoop(self): """Go through the loop up to 100 times.""" while self.hand and self.age < 100: print "*** Round "+str(self.age)+" ***" self.PlayCard() """ Autorun Run a demo if this module is run by itself. """ if __name__ == "__main__": print "Running a demonstration.\nThis code will run some dummy functions in semi-random order.\n" c = CodeletSystem() ## Give it some sample data for its working memory. c.CopyToTable({"nature":"text","text":"Sample Data"}) ## Give it some sample cards. c.Deal({"effect":"Ferret","priority":30}) ## Note that the "target" field here is an object reference. c.Deal({"effect":"Examine","target":c.table[0],"priority":42}) c.ShowHand() c.ShowTable() print "Demo begins.\n" c.DemonstrateCardLoop() print "\nRan out of things to do after playing "+str(c.age)+" cards.\n" c.ShowTable()