8 Array.prototype.removeAllObject = function(anObject){
9 //Array中にある全てのanObjectを削除し、空いた部分は前につめる。
10 //戻り値は削除が一回でも実行されたかどうか
12 for(var i = 0; i < this.length; i++){
13 if(this[i] == anObject){
21 Array.prototype.removeByIndex = function(index){
22 //Array[index]を削除し、空いた部分は前につめる。
23 this.splice(index, 1);
26 Array.prototype.intersectionWith = function(a, b, fEqualTo){
28 //fEqualToは省略可能で、評価関数fEqualTo(a[i], b[j])を設定する。
30 for(var i = 0, len = b.length; i < len; i++){
31 if(this.isIncluded(b[i], fEqualTo)){
37 Array.prototype.unionWith = function(a, b, fEqualTo){
39 //fEqualToは省略可能で、評価関数fEqualTo(a[i], b[j])を設定する。
41 for(var i = 0, len = b.length; i < len; i++){
42 if(!this.isIncluded(b[i], fEqualTo)){
46 return this.concat(r);
48 Array.prototype.isIncluded = function(obj, fEqualTo){
49 //含まれている場合は配列内のそのオブジェクトを返す
50 //fEqualToは省略可能で、評価関数fEqualTo(array[i], obj)を設定する。
51 if(fEqualTo == undefined){
52 for(var i = 0, len = this.length; i < len; i++){
58 for(var i = 0, len = this.length; i < len; i++){
59 if(fEqualTo(this[i], obj)){
66 Array.prototype.pushUnique = function(obj, fEqualTo){
67 //値が既に存在する場合は追加しない。評価関数fEqualTo(array[i], obj)を設定することができる。
68 //結果的に配列内にあるオブジェクトが返される。
69 var o = this.isIncluded(obj, fEqualTo);
76 Array.prototype.stableSort = function(f){
77 // http://blog.livedoor.jp/netomemo/archives/24688861.html
78 // Chrome等ではソートが必ずしも安定ではないので、この関数を利用する。
80 f = function(a,b){ return a - b; };
82 for(var i = 0; i < this.length; i++){
85 this.sort.call(this, function(a,b){
88 return (a.__id__ > b.__id__) ? 1 : -1;
93 for(var i = 0;i < this.length;i++){
94 delete this[i].__id__;
99 String.prototype.replaceAll = function(org, dest){
100 //String中にある文字列orgを文字列destにすべて置換する。
101 //http://www.syboos.jp/webjs/doc/string-replace-and-replaceall.html
102 return this.split(org).join(dest);
104 String.prototype.compareLeftHand = function (search){
106 for(var i = 0; search.charAt(i) != ""; i++){
107 if(search.charAt(i) != this.charAt(i)){
113 String.prototype.splitByArray = function(separatorList){
114 //リスト中の文字列それぞれで分割された配列を返す。
115 var retArray = new Array();
118 for(var i = 0; i < separatorList.length; i++){
119 var tmpArray = new Array();
120 for(var k = 0; k < retArray.length; k++){
121 tmpArray[k] = retArray[k].split(separatorList[i]);
122 if(tmpArray[k][tmpArray[k].length - 1] == ""){
123 tmpArray[k].splice(tmpArray[k].length - 1, 1);
124 if(tmpArray[k] && tmpArray.length > 0){
125 for(var m = 0; m < tmpArray[k].length; m++){
126 tmpArray[k][m] += separatorList[i];
130 for(var m = 0; m < tmpArray[k].length - 1; m++){
131 tmpArray[k][m] += separatorList[i];
135 retArray = new Array();
136 retArray = retArray.concat.apply(retArray, tmpArray);
141 String.prototype.trim = function(str){
142 return this.replace(/^[ ]+|[ ]+$/g, "").replace(/\n$/g, "");
144 //http://d.hatena.ne.jp/favril/20090514/1242280476
145 String.prototype.isKanjiAt = function(index){
146 var u = this.charCodeAt(index);
147 if( (0x4e00 <= u && u <= 0x9fcf) || // CJK統合漢字
148 (0x3400 <= u && u <= 0x4dbf) || // CJK統合漢字拡張A
149 (0x20000 <= u && u <= 0x2a6df) || // CJK統合漢字拡張B
150 (0xf900 <= u && u <= 0xfadf) || // CJK互換漢字
151 (0x2f800 <= u && u <= 0x2fa1f)){ // CJK互換漢字補助
156 String.prototype.isHiraganaAt = function(index){
157 var u = this.charCodeAt(index);
158 if(0x3040 <= u && u <= 0x309f){
163 String.prototype.isKatakanaAt = function(index){
164 var u = this.charCodeAt(index);
165 if(0x30a0 <= u && u <= 0x30ff){
170 String.prototype.isHankakuKanaAt = function(index){
171 var u = this.charCodeAt(index);
172 if(0xff61 <= u && u <= 0xff9f){
184 this.input = new AI_Input(this);
185 this.wordRecognition = new AI_WordRecognition(this);
187 this.outputTimer = null;
188 this.messageBox = null;
189 this.messageBoxBuffer = "";
190 this.maxMessageStringLength = 0xffffff;
191 this.debugBox = null;
192 this.debugBoxBuffer = "";
193 this.maxDebugStringLength = 0xffff;
197 sendToAI: function(str){
198 this.debug("**** Start thinking ****\n");
199 this.debug("input:[" + str + "]\n");
200 this.input.appendInput(str);
202 var s = this.input.getSentence();
206 this.message(s + "\n");
208 this.wordRecognition.sortCandidateWordListByWordCount();
209 this.wordRecognition.computeEachWordLevel();
210 this.wordRecognition.sortCandidateWordListByWordLevel();
211 this.wordRecognition.debugShowCandidateWordList();
212 this.debug("**** End thinking ****\n");
214 setMessageBoxDOMObject: function(mBoxObj){
215 this.messageBox = mBoxObj;
216 this.setOutputTimer();
218 setDebugBoxDOMObject: function(dBoxObj){
219 this.debugBox = dBoxObj;
220 this.setOutputTimer();
222 message: function(str){
224 this.messageBoxBuffer += "AI> " + str;
227 debug: function(str){
229 this.debugBoxBuffer += str;
232 outputShowTick: function(){
233 if(this.messageBox && this.messageBoxBuffer != ""){
235 var str = this.messageBox.innerHTML + this.messageBoxBuffer;
236 this.messageBoxBuffer = "";
237 if(str.length > this.maxMessageStringLength){
238 str = str.slice(str.length - (this.maxMessageStringLength >> 1));
240 this.messageBox.innerHTML = str;
241 this.messageBox.scrollTop = this.messageBox.scrollHeight;
243 if(this.debugBox && this.debugBoxBuffer != ""){
245 var str = this.debugBox.innerHTML + this.debugBoxBuffer;
246 this.debugBoxBuffer = "";
247 if(str.length > this.maxDebugStringLength){
248 str = str.slice(str.length - (this.maxDebugStringLength >> 1));
250 this.debugBox.innerHTML = str;
251 this.debugBox.scrollTop = this.debugBox.scrollHeight;
254 setOutputTimer: function(){
255 if(!this.messageBox && !this.debugBox){
256 //すべて無効だったらタイマーの動作自体を止める
257 window.clearTimeout(this.outputTimer);
258 this.outputTimer = null;
259 } else if(!this.outputTimer){
260 //どれかが有効でかつタイマーが止まっていたらスタートさせる
262 this.outputTimer = window.setInterval(function(){that.outputShowTick();}, 50);
271 function AI_WordRecognition(env){
273 this.candidateWordList = new Array();
275 AI_WordRecognition.prototype = {
276 slideLookUpCandidateWordByHistory: function(input){
277 var h = this.env.input.historyList;
278 var cList = new Array();
279 for(var i = 0, iLen = input.length; i < iLen; i++){
280 //input character loop
281 var iStr = input.substr(i);
284 for(var j = 0, jLen = h.length; j < jLen; j++){
287 for(var k = 0, kLen = hStrBase.length; k < kLen; k++){
288 //history character loop
289 var hStr = hStrBase.substr(k);
290 var m = hStr.compareLeftHand(iStr);
291 if(m > cLen && m != iStr.length){
297 cList.pushUnique(new AI_WordTag(iStr.substr(0, cLen))).wordCount++;
301 this.filterCandidateWordList00(cList);
302 this.filterCandidateWordList01(cList, 2);
304 this.mergeCandidateWordList(cList);
306 appendCandidateWordList: function(strTag){
307 var s = this.candidateWordList.isIncluded(strTag, function(a, b){ return (a.str == b.str); });
311 strTag.wordCount = 1;
312 this.candidateWordList.push(strTag);
315 mergeCandidateWordList: function(strTagList){
316 for(var i = 0, iLen = strTagList.length; i < iLen; i++){
317 this.appendCandidateWordList(strTagList[i]);
320 debugShowCandidateWordList: function(){
321 this.env.debug("candidateWordList:\n");
322 var c = this.candidateWordList;
323 for(var i = 0, iLen = c.length; i < iLen; i++){
324 this.env.debug(c[i].wordCount.toString() + " :" + c[i].wordLevel.toString() + " :" + c[i].str + "\n");
326 this.env.debug("candidateWordList end\n");
328 filterCandidateWordList00:function(cList){
329 //00:長い単語に含まれており、かつ出現頻度が長い単語と等しい単語を削除
330 //cList内の候補単語に対して、フィルターをかける。
331 var iLen = cList.length;
335 var baseStrTag = cList[0];
336 for(var i = 1; i < iLen; i++){
338 if(baseStrTag.str.indexOf(c.str) != -1){
339 //c.strはbaseStrTag.strに含まれている
340 if(baseStrTag.wordCount == c.wordCount){
342 //後で削除する。出現回数を0にマークする。
347 //単語は削除されなかった、つまり異なる単語なので、baseStrTagを更新
352 for(var i = 1; i < iLen; i++){
354 if(c.wordCount == 0){
355 cList.removeByIndex(i);
361 filterCandidateWordList01:function(cList, minLen){
362 //01:minLenに満たない文字数の候補を削除
364 var iLen = cList.length;
365 for(var i = 0; i < iLen; i++){
366 if(cList[i].str.length < minLen){
367 cList.removeByIndex(i);
373 filterCandidateWordList02:function(cList, minCount){
374 //02:minCountに満たない出現回数の候補を削除
376 var iLen = cList.length;
377 for(var i = 0; i < iLen; i++){
378 if(cList[i].wordCount < minCount){
379 cList.removeByIndex(i);
385 sortCandidateWordListByWordCount: function(){
386 this.candidateWordList.stableSort(function(a, b){
387 return a.wordCount - b.wordCount;
390 sortCandidateWordListByWordLevel: function(){
391 this.candidateWordList.stableSort(function(a, b){
392 return a.wordLevel - b.wordLevel;
395 computeWordLevel: function(strTag){
399 strTag.wordLevel = 0;
401 for(var i = 0; i < iLen; i++){
402 if(s.isHiraganaAt(i)){
404 } else if(s.isKanjiAt(i)){
406 } else if(s.isKatakanaAt(i)){
408 } else if(s.isHankakuKanaAt(i)){
414 for(var i = 0; i < 5; i++){
420 strTag.wordLevel = 1 / strTag.wordLevel;
423 computeEachWordLevel: function(){
424 var iLen = this.candidateWordList.length;
425 for(var i = 0; i < iLen; i++){
426 this.computeWordLevel(this.candidateWordList[i]);
431 function AI_WordTag(str){
437 function AI_Input(env){
439 this.historyList = new Array();
440 this.sentenceList = new Array();
442 AI_Input.prototype = {
443 maxHistoryLength: 32,
452 appendInput: function(str){
453 var sList = str.splitByArray(this.sentenceSeparator);
455 this.sentenceList = this.sentenceList.concat(sList)
457 getSentence: function(){
460 if(this.sentenceList.length <= 0){
463 var retv = this.sentenceList[0];
464 this.sentenceList.splice(0, 1);
471 this.env.wordRecognition.slideLookUpCandidateWordByHistory(retv);
473 this.appendHistory(retv);
476 appendHistory: function(str){
477 this.historyList.push(str);
478 if(this.historyList.length > this.maxHistoryLength){
479 this.historyList.splice(0, this.maxHistoryLength >> 1);