言葉を覚えさせていくために、しりとりを利用しようと考えました。
(とりあえずは、単語だけ。意味を覚えこませるのは次の段階で。)
しりとりのルールは次の通り
- 最初は「しりとり」の「り」から始める。
- 当然、始まりの文字が前の言葉の最後の文字と違っていれば負け。
- もちろん、「ん」で終わる言葉を使っても負け。
- 同じ言葉を2回使うと負け。
- ローカルルールとして、(当面)使うのはひらがなに限る、最後が長音(ー)は省略する。
まずは寂しく一人しりとり
いきなり対話的しりとりは難しいので、一人だけで楽しむしりとりを作ってみる。
shiritori.rb
puts "はじめは「しりとり」の「り」" tail = "り" wordsbank = []
while true word = gets.chomp wordsbank.each do |w| if w == word puts "その言葉はもう出てます。あなたの負けです。" break end end if word[-1] == "ん" puts "最後の言葉が「ん」です。あなたの負けです。" break elsif word[0] == tail puts word tail = word[-1] wordsbank.push(word) else puts tail + "で始まる言葉ではありません。あなたの負けです。" break end end
PCが相手してくれるしりとり1
何となく一人しりとりがうまく行ったので、今度はPCが相手してくれるしりとりを作ってみる。
- 操作者は、言葉を直接入力する。
- PCはあらかじめ用意した言葉のリスト(配列)から、使える言葉を見つけて返す。一度使った言葉はリストから外す。言葉が見つからない場合は降参する。
- 答えた言葉が正しいか間違っているかの判定は、操作者でもPCでも同じ。
と言うことなので、それぞれについてメソッドにする。##人間側の設定 def human_play return gets.chomp end
## pc側の設定 pc_wordsbank = ["ごりら","ぱんだ","ごま","めだか"] wordsbank=[]
def pc_play(pc_wordsbank,tail) pc_wordsbank.each do |w| if w[0] == tail puts w pc_wordsbank.delete(w) return w end end puts "ごめんなさい。「"+tail+"」で始まる言葉を思い付きません。私の負けです。" end
##審判 def judge(wordsbank,word,tail) wordsbank.each do |w| if w == word puts "その言葉はもう出てます。あなたの負けです。" break end end if word[-1] == "ん" puts "最後の言葉が「ん」です。あなたの負けです。" elsif word[0] == tail wordsbank.push(word) return word[-1] else puts tail + "で始まる言葉ではありません。あなたの負けです。" end end
##試合開始 puts "はじめは「しりとり」の「り」" tail = "り"
while true word=human_play tail=judge(wordsbank,word,tail) puts "次は"+tail+"で始まる言葉" word=pc_play(pc_wordsbank,tail) tail=judge(wordsbank,word,tail) puts "次は"+tail+"で始まる言葉" end
一応動くようだ。何やらエラーが出るが。
PCが相手してくれるしりとり2
しりとりで出てきた言葉を別ファイルに保存して、次の試合でPC側が使えるようにしてみました。
実際にやってみると、少しずつ語彙が増えて、しりとりが続くようになります(ただし、そのためには、あえて負けてあげる心の広さも必要です。)。
なお、このプログラムを実行するに当たって、あらかじめ同じフォルダに、「wordsbank.txt」と言うファイル(中身は空で構わない。)を作っておく必要があります。
shiritori_3.rb
#### PC用の言葉の呼出と保存 ## いきなりPCが負けるのも可哀想なので、初めから与えておく言葉。 pc_wordsbank = ["ごりら","ぱんだ","ごま","めだか","かばん","ごりら"] wordsbank=[]
## 単語を覚えこませている外部ファイルを開いて、単語を読み込む。 File.open("wordsbank.txt","r") do |text| text.each_line do |line| pc_wordsbank.push(line.chomp) end end
#### 出てきた言葉を保存するメソッド ## 外部ファイルの単語を配列に入れる。 def store_words(wordsbank) File.open("wordsbank.txt","r") do |text| text.each_line do |line| wordsbank.push(line.chomp) end end
## 同じ単語を重複して保存しないように、外部ファイルの中を一度空にする。 File.open("wordsbank.txt","w") do |text| end
## 配列から重複する単語を削除の上、外部ファイルに保存する。 wordsbank.uniq! wordsbank.each do |w| File.open("wordsbank.txt","a") do |text| text.puts w end end exit end
####人間側の設定(キーボードから入力した文字を返すだけ。) def human_play return gets.chomp end
#### pc側の設定(手持ちのリストから、該当する単語を探し出す。) def pc_play(pc_wordsbank,tail,wordsbank) pc_wordsbank.each do |w| if w[0] == tail puts w pc_wordsbank.delete(w) return w end end puts "ごめんなさい。「" + tail + "」で始まる言葉を思い付きません。私の負けです。" store_words(wordsbank) exit end
####審判 def judge(wordsbank,word,tail)
## すでに使われた単語か判断。 wordsbank.each do |w| if w == word puts "その言葉はもう出てます。あなたの負けです。" store_words(wordsbank) end end
## 「ん」で終わっていないか判断 if word[-1] == "ん" puts "最後の言葉が「ん」です。あなたの負けです。" wordsbank.push(word) store_words(wordsbank) ## 前の単語の最後の文字で始まっていれば正常処理 elsif word[0] == tail wordsbank.push(word) puts "次は「" + word[-1] + "」で始まる言葉" return word[-1] ## 前の言葉の最後の言葉と違う言葉で始まっていれば負け判定 else puts tail + "で始まる言葉ではありません。あなたの負けです。" wordsbank.push(word) store_words(wordsbank) end end
#### 試合開始 puts "はじめは「しりとり」の「り」" tail = "り"
while true word=human_play tail=judge(wordsbank,word,tail) word=pc_play(pc_wordsbank,tail,wordsbank) tail=judge(wordsbank,word,tail) end
気づいたこと
- 毎回「り」から始まる言葉を思いつくのは大変なので、最初は任意の言葉で始められるようにした方がいいかも知れません。
puts "はじめは「しりとり」の「り」" tail = "り"
while true word=human_play tail=judge(wordsbank,word,tail) word=pc_play(pc_wordsbank,tail,wordsbank) tail=judge(wordsbank,word,tail) end
を
word=gets.chomp tail=word[-1]
while true word=pc_play(pc_wordsbank,tail,wordsbank) tail=judge(wordsbank,word,tail) word=human_play tail=judge(wordsbank,word,tail) end
にすれば良いように思います。
- pc_wordsbank配列の順番をランダムにすると多少変化が出てくると思います。
YAMLを利用してみた
「wordsbank.txt」はただのテキストファイルですが、今後他で活用することも考え、YAMLファイルに保存するようにしてみました。
なお、YAML を利用するには、最初に require "yaml" と宣言しておく必要があります。
shiritiri_4.rb
require "yaml"
wordsbank=[]
#### PC用の言葉の呼出と保存 ## いきなりPCが負けるのも可哀想なので、初めから与えておく言葉。 pc_wordsbank = ["ごりら","ぱんだ","ごま","めだか","かばん","ごりら"]
#### wordsbank.yamlを一旦読み込み、配列pc_wordsbankに格納 #### ただし、最初はwordsbank.yamlがないので、例外処理を行う begin load_wordsbank=YAML.load_file"wordsbank.yaml" load_wordsbank.each do |i| pc_wordsbank.push(i) end ## wordsbank.yamlがないとき(中身が空のとき)の処理(と言っても何もしない) rescue end
#### 最後にwordsbank.yamlに(再)保存するために一時保管 temp_wordsbank=pc_wordsbank
#### 出てきた言葉を保存するメソッド def store_words(temp_wordsbank,wordsbank) temp_wordsbank.each do |i| wordsbank.push(i) end wordsbank.uniq! YAML.dump(wordsbank,File.open("wordsbank.yaml", "w")) exit end
####人間側の設定(キーボードから入力した文字を返すだけ。) def human_play print "俺> " return gets.chomp end
#### pc側の設定(手持ちのリストから、該当する単語を探し出す。) def pc_play(pc_wordsbank,tail,temp_wordsbank,wordsbank) pc_wordsbank.each do |w| if w[0] == tail puts "嫁> " + w pc_wordsbank.delete(w) return w end end puts "嫁> ごめんなさい。「" + tail + "」で始まる言葉を思い付きません。私の負けです。" store_words(temp_wordsbank,wordsbank) exit end
####審判 def judge(temp_wordsbank,wordsbank,word,tail)
## すでに使われた単語か判断。 wordsbank.each do |w| if w == word puts "その言葉はもう出てます。あなたの負けです。" store_words(temp_wordsbank,wordsbank) end end
## 「ん」で終わっていないか判断 if word[-1] == "ん" puts "最後の言葉が「ん」です。あなたの負けです。" wordsbank.push(word) store_words(temp_wordsbank,wordsbank) ## 前の単語の最後の文字で始まっていれば正常処理 elsif word[0] == tail wordsbank.push(word) puts "次は「" + word[-1] + "」で始まる言葉" return word[-1] ## 前の言葉の最後の言葉と違う言葉で始まっていれば負け判定 else puts tail + "で始まる言葉ではありません。あなたの負けです。" wordsbank.push(word) store_words(temp_wordsbank,wordsbank) end end
#### 試合開始 puts "はじめはあなたからどうぞ" word=human_play tail=word[0]
while true tail=judge(temp_wordsbank,wordsbank,word,tail) word=pc_play(pc_wordsbank,tail,temp_wordsbank,wordsbank) tail=judge(temp_wordsbank,wordsbank,word,tail) word=human_play end
最初の方で
begin load_wordsbank=YAML.load_file"wordsbank.yaml" load_wordsbank.each do |i| pc_wordsbank.push(i) end ## wordsbank.yamlがないとき(中身が空のとき)の処理(と言っても何もしない) rescue end
とbeginで始まる処理が書いてあります。
これは、プログラムの始めの方でwordsbank.yamlを配列に読み込む処理をしているのですが、初めてこのプログラムを実行するときには、まだwordsbank.yamlというファイルがないため、そのままではエラーで終了してしまいます。
そのため、例外処理で、もしまだwordsbank.yamlがない場合には無視して先に進むようにしてあります。
さて、これでも一応動きますが、よく見ると、store_words、pc_play、judgeと言った複数のメソッドでwordsbankなど同じ名前の変数が何箇所も出てきて、何やらごちゃごちゃしてます。
たとえ同じ名前の変数であっても、別のメソッドで使われていれば、それば別の変数とみなされます。
しかし、このプログラムでは、同じ名前の変数はひとつのものを指しているので、色々と無駄が多すぎますね(まさにこのwikiのタイトル通りの状態に・・・)。
しりとりクラスを作成してみた
メソッドを実行させるときに実体が同じ変数をいちいち引数として明記するのは、余りに無駄だし、修正する度にエラーの原因になってしまいます。
このように共通して使う変数は「インスタンス変数」と言うものにすると良さそうです。そんなわけで、生まれて初めてインスタンス変数をいうものを使ってみました。ついでに生まれて初めてクラスを定義してみました。
require "yaml"
class Shiritori #### 初期化の際、各変数を指定する def initialize
#### 初めからPCが答えられる言葉を用意してあげる。 @pc_wordsbank = ["ごりら","ぱんだ","ごま","めだか","かばん","ごりら"]
#### wordsbank.yamlを一旦読み込み、配列pc_wordsbankに格納 #### ただし、最初はwordsbank.yamlがないので、例外処理を行う begin @load_wordsbank=YAML.load_file"wordsbank.yaml" @load_wordsbank.each do |i| @pc_wordsbank.push(i) end ## wordsbank.yamlがないとき(中身が空のとき)の処理(と言っても何もしない) rescue end
## 最後にwordsbank.yamlに(再)保存するために一時保管 @temp_wordsbank=@pc_wordsbank
## wordsbankの空配列 @wordsbank=[] end
#### 出てきた言葉を保存するメソッド def store_words @temp_wordsbank.each do |i| @wordsbank.push(i) end @wordsbank.uniq! YAML.dump(@wordsbank,File.open("wordsbank.yaml", "w")) exit end
####人間側の設定(キーボードから入力した文字を返すだけ。) def human_play print "俺> " return gets.chomp end
#### pc側の設定(手持ちのリストから、該当する単語を探し出す。) def pc_play @pc_wordsbank.each do |w| if w[0] == @tail puts "嫁> " + w @pc_wordsbank.delete(w) return w end end puts "嫁> ごめんなさい。「" + @tail + "」で始まる言葉を思い付きません。私の負けです。" @store_words exit end
####審判 def judge
## すでに使われた単語か判断。 @wordsbank.each do |w| if w == @word puts "その言葉はもう出てます。あなたの負けです。" store_words end end
## 「ん」で終わっていないか判断 if @word[-1] == "ん" puts "最後の言葉が「ん」です。あなたの負けです。" @wordsbank.push(@word) store_words ## 前の単語の最後の文字で始まっていれば正常処理 elsif @word[0] == @tail @wordsbank.push(@word) puts "次は「" + @word[-1] + "」で始まる言葉" return @word[-1] ## 前の言葉の最後の言葉と違う言葉で始まっていれば負け判定 else puts @tail + "で始まる言葉ではありません。あなたの負けです。" @wordsbank.push(@word) store_words end end
def play #### 試合開始 puts "はじめはあなたからどうぞ" @word=human_play @tail=@word[0]
while true @tail=judge @word=pc_play @tail=judge @word=human_play end end end
#### 実際にオブジェクトを作ってプレイする shiritori=Shiritori.new shiritori.play
pc_playやjudgeなどメソッドにいちいち引数を書かなくて良くなったので、かなりスッキリしました。