Question

I'm trying to keep this as simple as possible. Basically I want the data to be saved to a file, and the retrieve it so that questor.py works and can "remember" everything it was ever taught on your machine. The original code is available on the web at http://www.strout.net/info/coding/python/questor.py If I'm reading the code right, you end up with an object that looks something like {key:{key:{key:class instance},class instance},class instance} . (rough estimate)

Please ignore the unfished method Save, I'm working on that as soon as I figure out how to pickle the dictionary without losing any of the imbedded instances.

The following is my attempt at trying to save the dict via pickler. Some of the code is unfinished, but you should be able to get an idea of what I was trying to do. So far all I am able to do is retrieve the last question/answer set. Either my pickle isn't saving the imbedded instances, or they're not actually there when I save the pickle. I've followed the spaghetti lines as much as possible, but can't seem to figure out how to set up a way to save to file without losing anything. Also my file doesn't have to be .txt originally I was going to use .data for the pickle.

# questor.py 

# define some constants for future use
kQuestion = 'question'
kGuess = 'guess'
questfile = 'questfile.txt'

## Added
import cPickle as p
# create a file for questor
def questor_file():
    try:
        questor = open(questfile,'rb')
        try:
            q = p.Unpickler(questor)
            quest = q.load()
            questor.close()
            return quest
        except:
            print 'P.load failed'
    except:
        print 'File did not open'
        questor = open('questfile.data', 'wb')
        questor.close()
    return Qnode('python')

# define a function for asking yes/no questions
def yesno(prompt):
    ans = raw_input(prompt)
    return (ans[0]=='y' or ans[0]=='Y')

# define a node in the question tree (either question or guess)
class Qnode:

    # initialization method
    def __init__(self,guess):
        self.nodetype = kGuess
        self.desc = guess

    ##Added
    ## Not sure where I found this, but was going to attempt to use this as a retreival method
    ## haven't gotten this to work yet
    def Load(self):
        f = open(self.questfile,'rb')
        tmp_dict = cPickle.load(f)
        f.close()    
        self.__dict__.update(tmp_dict) 

    ##Added
    # was going to use this as a save method, and call it each time I added a new question/answer
    def Save(self,node):
        f = open(self.questfile,'wb')
        quest = p.pickler(f)


    # get the question to ask 
    def query(self):
        if (self.nodetype == kQuestion):
            return self.desc + " "
        elif (self.nodetype == kGuess):
            return "Is it a " + self.desc + "? "
        else:
            return "Error: invalid node type!"

    # return new node, given a boolean response
    def nextnode(self,answer):
        return self.nodes[answer]

    # turn a guess node into a question node and add new item
    # give a question, the new item, and the answer for that item
    def makeQuest( self, question, newitem, newanswer ):

        # create new nodes for the new answer and old answer
        newAnsNode = (Qnode(newitem))
        oldAnsNode = (Qnode(self.desc))

        # turn this node into a question node
        self.nodetype = kQuestion
        self.desc = question

        # assign the yes and no nodes appropriately
        self.nodes = {newanswer:newAnsNode, not newanswer:oldAnsNode}
        self.save(self.nodes)


def traverse(fromNode):
    # ask the question
    yes = yesno( fromNode.query() )

    # if this is a guess node, then did we get it right?
    if (fromNode.nodetype == kGuess):
        if (yes):
            print "I'm a genius!!!"
            return
        # if we didn't get it right, return the node
        return fromNode

    # if it's a question node, then ask another question
    return traverse( fromNode.nextnode(yes) )

def run():
    # start with a single guess node
    # This was supposed to assign the data from the file
    topNode = questor_file()


    done = 0
    while not done:
        # ask questions till we get to the end
        result = traverse( topNode )


        # if result is a node, we need to add a question
        if (result):
            item = raw_input("OK, what were you thinking of? ")
            print "Enter a question that distinguishes a",
            print item, "from a", result.desc + ":"
            q = raw_input()
            ans = yesno("What is the answer for " + item + "? ")
            result.makeQuest( q, item, ans )
            print "Got it."

        # repeat until done
        print
        done = not yesno("Do another? ")
    # Added
    # give me the dictionary    
    return result

# immediate-mode commands, for drag-and-drop or execfile() execution
if __name__ == '__main__':
    print "Let's play a game."
    print 'Think of something, just one thing.'
    print 'It can be anything, and I will try to guess what it is.'
    raw_input('Press Enter when ready.')
    print
    questdata = run()
    print
    # Added
    # Save the dictionary
    questor = open(questfile,'wb')
    q = p.Pickler(questor)
    q.dump(questdata)
    questor.close()
    raw_input("press Return>")
else:
    print "Module questor imported."
    print "To run, type: questor.run()"
    print "To reload after changes to the source, type: reload(questor)"

# end of questor.py
Was it helpful?

Solution

one way that comes to mind is creating a list of all the nodes and saving that ... they should keep their internal pointers on their own.

declare a list of nodes at the top of your file (and use pickle... just cause Im more familiar with that)

import pickle
kQuestion = 'question'
kGuess = 'guess'
questfile = 'questfile.txt'
nodes = []
....

change your load method to something like

def questor_file():
    global nodes
    try:
        questor = open(questfile,'rb')
        try:
            nodes= pickle.load(questor)
            quest = nodes[0]
            questor.close()
            return quest
        except:
            print 'P.load failed'
            nodes = []

    except:
        print 'File did not open'
        nodes = []
    return Qnode('python')

change your class constructor so that it adds each node to nodes

class Qnode:
    # initialization method
    def __init__(self,guess):
        self.nodetype = kGuess
        self.desc = guess
        nodes.append(self)

at the end where it says #added save dictionary , save your list of nodes

questor = open(questfile,'wb')
q = pickle.dump(nodes,questor)

make sure you exit the program by typing no when prompted ...

you could also save it to a database or whatever but you would still have to store each node and it might be more complicated... this method should really be fine I think , (although there may be a more natural way to save a tree structure) ...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top