はてなキーワードしりとりを改造・その2

前回のエントリに引き続きコメントでご教示いただいた内容を元にコードを書きなおしてみました。

# coding: utf-8

import urllib
import urllib2
from xml.dom.minidom import parse

def input_reader(text):
    """入力を受け取ってunicodeオブジェクトにして返す"""
    
    word = raw_input(text)
    
    return unicode(word, "utf-8")

class HatenaFuriganaDict(dict):
    """入力された単語とそのふりがなの組み合わせを辞書として管理する
       はてなキーワードに登録されていなかった単語の値はNone
       APIから正しいxmlが受信できない単語の値はFalseとする
    """
    @staticmethod
    def _ask_hatena(word):
        """__missing__から呼ばれてwordをはてなキーワードAPIに問い合わせる"""
        
        #URLエンコード
        encoded_word = urllib.quote(word.encode("utf-8"))
        
        #URLを作成
        requestURL = ("http://search.hatena.ne.jp/keyword?word="
                       + encoded_word
                       + "&mode=rss&ie=utf8&page=1")
        
        return urllib2.urlopen(requestURL, None)
    
    def __missing__(self, word):
        """この辞書に登録されていないwordが参照された場合に呼ばれ
           APIに問い合わせを行い返答する。さらにその結果を辞書に登録する
        """
        try:
            dom = parse(self._ask_hatena(word))
        except:
            self[word] = False
            return self[word]
        
        items = dom.getElementsByTagName("item")
        hatenaNS = "http://www.hatena.ne.jp/info/xmlns#" #XMLのNS
    
        if items and (items[0].getElementsByTagName("title"))[0].firstChild.data == word:
            self[word] = (items[0].getElementsByTagNameNS(hatenaNS, "furigana"))[0].firstChild.data
        else:
            self[word] = None
        
        return self[word]
    
    

def main():
    furiganadict = HatenaFuriganaDict() # API問い合わせた単語をキー読み仮名を値として管理する
    usedwords = {} # 正答の単語をキー、何番目に使ったかを値として管理する
    
    initial = u"は" #しりとりの最初の文字
    
    print (u"最初のもじは「" + initial + u"」です")
    
    while True:
        wordU = input_reader(u"次の単語を入力してください。次は" + str(len(usedwords) + 1) + u"語目です。")
        
        if wordU in usedwords.keys():
            print u"この単語は" + str(usedwords[wordU]) + u"番目に使いました。"
            continue
        
        furigana = furiganadict[wordU]
        
        if furigana is None:
            print u"この単語ははてなキーワードに登録されていませんでした。"
            continue
        
        if furigana is False:
            print u"xmlデータを正しく受信できませんでした。他の単語を試してください。"
            continue
        
        if not furigana.startswith(initial):
            print u"次の文字は「" + initial + u"」です!"
            continue
        
        if furigana.endswith(u"ん"):
            print u"「ん」がついたよ。"
            break
        
        print wordU
        
        if furigana.endswith(u"ー"):
            initial = furigana[-2:-1]
        else:
            initial = furigana[-1:]
        
        toRep = {u"ゃ":u"や", u"ゅ":u"ゆ", u"ょ":u"よ", u"を":u"お", u"っ":u"つ"}
        
        if initial in toRep.keys():
            initial = toRep[initial]
        
        usedwords[wordU] = len(usedwords) + 1
    
    print u"終了です"

if __name__ == "__main__":
    main()

前回のコードには「最初の文字が前の単語のお尻の文字ではない単語」を入力した場合にその単語が「使用済み単語」と判断されてしまい、その後使えなくなってしまうという明らかなバグがあったのでまずその問題を修正しました。
バグの原因ははてなキーワードAPIに問い合わせた単語はそれがしりとりのルール上適格であったか否かに関わらず単一の辞書で管理していた事でした。そこでAPIに問い合わせた結果のみを管理する辞書furiganadictと、しりとりのルール上「使用済み」の単語のみを管理する辞書usedwordsに分けました。

HatenaFuriganaDictがAPIに問い合わせを行うメソッドは特殊メソッドである__missing__を使用し、辞書にその単語が既に存在するかどうかを判定する処理は削除しました。
_ask_hatenaメソッドはstaticmethodに。
turn変数によるターン管理はやめusedwords辞書の要素数でそれを判断するようにしました。