今日午後の授業に行く前にプログラムに機能を追加した。本の第6章にあるように、Kotz’ijに感情モデルを実装した。具体的には特定の言葉によって機嫌が良くなったり、悪くなったりということ。
キチェ語クラスでの単語は150以上位だと思うけど、あまり形容詞を習っていないので今のところ機嫌を良くする言葉(Utz Kotz’i’j=可愛いKotz’i’j)と反対に機嫌を悪くさせる言葉(Chom=太っている)それぞれ一つずつ。
それらはパターン回答集に追加。.rbファイルは5つ。
Tzijonik.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
require './Kotzij' require './Emocion' def prompt(kotzij) return 'Kotz\'ij > ' end puts('Tzijonik: Version 1.4 (20170511)') currentTime = Time.now #get the current time hour = currentTime.hour if hour > 3 && hour < 12 then saludo = 'Saqarik' elsif hour >= 12 && hour < 18 then saludo = 'Xe\'q\'ij' else saludo = 'Xokaq\'ab\'' end flor = Kotzij.new puts(prompt(flor) + saludo) puts(prompt(flor) + 'In al Kotz\'ij') while true print('At> ') input = gets input.chomp! break if input == '' response = flor.dialogue(input) puts(prompt(flor) + response) end |
Kotzij.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
require './Responder' require './Dictionary' class Kotzij def initialize @dictionary = Dictionary.new @emocion = Emocion.new(@dictionary) @responderWhat = WhatResponder.new(@dictionary) @responderRandom = RandomResponder.new(@dictionary) @responderPatterns = PatternResponder.new(@dictionary) @responder = @responderRandom end def dialogue(input) @emocion.update(input) case rand(10) when 0 @responder = @responderWhat else @responder = @responderPatterns end return @responder.response(input, @emocion.mood) end def responderName return @responder.name end def mood return @emocion.mood end attr_reader :name end def randomSelect(ary) return ary[rand(ary.size)] end |
Responder.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class Responder def initialize(dictionary) @dictionary = dictionary end def response(input, mood) return '' end attr_reader :name end class WhatResponder < Responder def response(input, mood) "Jas #{input}?" end end class RandomResponder < Responder def response(input, mood) return randomSelect(@dictionary.random) end end class PatternResponder < Responder def response(input, mood) @dictionary.pattern.each do |patternItems| if m = patternItems.match(input) resp = patternItems.choice(mood) next if resp.nil? return resp.gsub(/%match%/, m.to_s) end end return randomSelect(@dictionary.random) end end |
Dictionary.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
class Dictionary def initialize @random = [] open('dictionaries/list01.txt') do |f| f.each do |line| line.chomp! next if line.empty? @random.push(line) end end @pattern = [] open('dictionaries/patterns02.txt') do |f| f.each do |line| pattern, phrases = line.chomp.split("\t") #split by tab next if pattern.nil? or phrases.nil? @pattern.push(PatternItem.new(pattern, phrases)) end end end attr_reader :random, :pattern end class PatternItem SEPARATOR = /^((-?\d+)##)?(.*)$/ def initialize(pattern, phrases) SEPARATOR =~ pattern @modify, @pattern = $2.to_i, $3 @phrases = [] phrases.split('|').each do |phrase| SEPARATOR =~ phrase @phrases.push({'need'=>$2.to_i, 'phrase'=>$3}) end end def match(str) return str.match(@pattern) end def choice(mood) choices = [] @phrases.each do |p| choices.push(p['phrase']) if suitable?(p['need'], mood) end return (choices.empty?)? nil : randomSelect(choices) end def suitable?(need, mood) return true if need == 0 if need > 0 return mood > need else return mood < need end end attr_reader :modify, :pattern, :phrases end |
で最後に新しく作成したEmocion.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class Emocion MOOD_MIN = -10 MOOD_MAX = 10 MOOD_RECOVERY = 0.5 def initialize(dictionary) @dictionary = dictionary @mood = 0 end def update(input) @dictionary.pattern.each do |patternItems| if patternItems.match(input) adjust_mood(patternItems.modify) break end end if @mood < 0 @mood += MOOD_RECOVERY elsif @mood > 0 @mood -= MOOD_RECOVERY end end def adjust_mood(val) @mood += val if @mood > MOOD_MAX @mood = MOOD_MAX elsif @mood < MOOD_MIN @mood = MOOD_MIN end end attr_reader :mood end |
キチェ語、英語、スペイン語が入り混じってちょっと分かりずらくなってきたかなぁ。
キチェ語コースはまだ挨拶に留まっているけど単語数は増えてきた。動詞はまだ、殆どないけど。
コースの進行と共にKotz’ij(Kotz’i’j)も少しずつ賢くならないと!
単語リストについては後ほど書こうと思うけどGoogle SheetsからLaTex変換する方法があったので、今後はGoogle Sheetsで管理しようと思う。空いた時間に単語を入力するのはブラウザからの方がファイル管理の意味でも楽。バックアップとしては.txtファイルに変換したものをGitで管理する訳だし。
今回のプログラムにはパターン認識を新たに追加。取り敢えず三つの単語に反応する様にした。
パターン認識用の.txtファイルを新たに作成(こちら)。これはdictionariesフォルダ内に。新たにDictionary.rbを作成。
Dictionary.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Dictionary def initialize @random = [] open('dictionaries/list01.txt') do |f| f.each do |line| line.chomp! next if line.empty? @random.push(line) end end @pattern = [] open('dictionaries/patterns01.txt') do |f| f.each do |line| pattern, phrases = line.chomp.split("\t") #split by tab next if pattern.nil? or phrases.nil? @pattern.push({'pattern'=>pattern, 'phrases'=>phrases}) end end end attr_reader :random, :pattern end |
それからKotzij.rbとResponder.rbを編集
Kotzij.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
require './Responder' require './Dictionary' class Kotzij def initialize @dictionary = Dictionary.new @responderWhat = WhatResponder.new('Kotz\'ij', @dictionary) @responderRandom = RandomResponder.new('Kotz\'ij', @dictionary) @responderPatterns = PatternResponder.new('Kotz\'ij', @dictionary) @responder = @responderRandom end def dialogue(input) case rand(5) when 0 @responder = @responderWhat when 1..2 @responder = @responderRandom else @responder = @responderPatterns end return @responder.response(input) end def responderName return @responder.name end attr_reader :name end def randomSelect(ary) return ary[rand(ary.size)] end |
Responder.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class Responder def initialize(name, dictionary) @name = name @dictionary = dictionary end def response(input) return '' end attr_reader :name end class WhatResponder < Responder def response(input) "Jas #{input}?" end end class RandomResponder < Responder def response(input) return randomSelect(@dictionary.random) end end class PatternResponder < Responder def response(input) @dictionary.pattern.each do |patternItems| if m = input.match(patternItems['pattern']) resp = randomSelect(patternItems['phrases'].split('|')) return resp.gsub(/%match%/, m.to_s) end end return randomSelect(@dictionary.random) end end |
Tzijonik.rbは今回も日付とバージョン番号の変更のみ。
以前触れたソロラ県の女性の平均身長が世界一低いという件についてデータを調べてみた。
新聞記事にあった通り確かに平均身長は145CMで低いことは低いけど、ENSMI2014/15のデータによると幾つかの県の女性の平均身長はソロラ県の女性とあまり変わりがない。確かに貧しい県ではあるけど、平均身長が他の県と比べて低いのはやはり先住民率が高いことが主要因だと思う。特に西部の先住民は東部の人たちと比べても小柄そうだし。
Rのコードはこんな感じで。
1 2 3 4 5 |
par(las=1, xaxs="i", yaxs="i") hist(subHeight, freq = FALSE, xlab = "Height", main = "", xlim = c(100, 180), ylim = c(0, 0.1), breaks = 50, col="#165e83") lines(density(subHeight), col = "#ed6d3d", lwd = 3) axis(side=1, at=seq(100, 180, 10)) |
今回は和色を使ってみた。藍色はソロラ県にも関係があると言える色。
前回のプログラムでは4つの単語からひとつをランダムに選択し回答する仕様にした。今回は単語数を93に増やしたため、単語リストを単独に作成した。また、回答もリストから選んだ単語を返すだけではなく、「~って何(Jas….?)」というも再度利用することに。
1/4の確率でJas….?を、残りの3/4の確率でリストからの単語を返す仕様にした。変更点は主にKotzij.rbのこの部分
1 2 3 4 5 6 7 8 9 10 |
def initialize @responderRandom = RandomResponder.new('Kotz\'ij') @responderWhat = WhatResponder.new('Kotz\'ij') @responder = @responderRandom end def dialogue(input) @responder = rand(4) ==0 ? @responderWhat : @responderRandom; return @responder.response(input) end |
と
Responder.rbの
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class RandomResponder < Responder def initialize(name) super @phrases = [] open('dictionaries/dic01.txt') do |f| f.each do |line| line.chomp! next if line.empty? @phrases.push(line) end end end def response(input) return randomSelect(@phrases) end def randomSelect(ary) return ary[rand(ary.size)] end |
まず、回答方法がランダムに決定され、その後単語を返すこととなった場合、単語リストから単語をひとつランダムで選択。
長くなるけどResponder.rbとKotzij.rbの全体のコードはこうなっている。Tzijonikについては日付を変えただけ。
Responder.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class Responder def initialize(name) @name = name end def response(input) return '' end def name return @name end end class WhatResponder < Responder def response(input) "Jas #{input}?" end end class RandomResponder < Responder def initialize(name) super @phrases = [] open('dictionaries/dic01.txt') do |f| f.each do |line| line.chomp! next if line.empty? @phrases.push(line) end end end def response(input) return randomSelect(@phrases) end def randomSelect(ary) return ary[rand(ary.size)] end end |
Kotzij.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
require './Responder' class Kotzij def initialize @responderRandom = RandomResponder.new('Kotz\'ij') @responderWhat = WhatResponder.new('Kotz\'ij') @responder = @responderRandom end def dialogue(input) @responder = rand(4) ==0 ? @responderWhat : @responderRandom; return @responder.response(input) end def responder_name return @responder.name end end |
プログラムを実行してみる。
現在の単語リストはこれ。会話になってないけど、少し前進してるかな。