Turbo-Cで 1139行 JavaScriptに変換して3400行
Turbo-Cって、そもそもコンパイルが速いとか、エディタ・コンパイラ・リンカが一体化していて作業が速い!値段が安くPC上で軽快に動くみたいな感じで打ち出していたんだよね。
だから Turbo-C の “Turbo” は、C言語そのものの仕様ではなく、Borland が「速く開発できるC環境」として付けた商品名だったんだ。
Turbo Pascal、Turbo C、Turbo C++、Turbo Basicみたいなシリーズあったでしょ。
昔のPCでは、CPU本体だけだと浮動小数点計算が遅かったので、数値計算・グラフィック・シミュレーション系ではFPU=x87浮動小数点コプロセッサがあると、かなり違ったんだよね。
中野先生のこのデモは「言語発声robot」という名前でね。
最初はロボット2体〇と□が、熊とか、ウサギとか、豚とかライオンとか出現する度に、「なんだろう?」と近づいてその都度発声するんだよ。はじめのうちはライオンは危険じゃないと誤認する。でも、何度か繰り返すうちに「危険だ」に収束していく。ナッツが出ると近寄るみたいな。発声も危険と安全では違うんだよ。robotの発声もそれぞれバラバラ、そのうち1体のrobotが「pa-pu-pu-bu」みたいに叫ぶと、もう一体もつられて避難場所に逃げる。というデモだよ。
でもやっぱりcode的には外部命令的なラベル寄せがあってね
A: Dangerous
B: Edible
C: Hair/Fur
D: White
E: Black
F: Brown
出現する動物にはA D C E、BCE、みたいな感じでラベル寄せさせているんだ。
Lion (A, C, F)
Bear (A, C, E)
Rabbit (B, C, D)
Nuts (B, F)
Wild boar (B, C, E)
こんな感じでね。
でも、一見自律のようなデモに見えても3人称の外部命令系なんだよね。
中野先生の書籍からJavaScriptに変換したのは
①Turbo-Cの逐次ループをJavaScriptの描画/イベントループに直すこと
②Cの配列・メモリ前提を、JSの配列/オブジェクトに置き換えること
③画面描画をDOS/グラフィック処理からCanvas/HTMLに変えること
ここまで。
そして何より、Turbo-Cのコードを1枚のJavaScriptに変換は無理があり過ぎた。
core.jsとrobot1.jsとrobot2.jsのファイルを分け、core.jsから命令を受けないようにする。
じゃなくて「そうしたかった」w
当時自律とか、一人称なんて概念は無いけど、今年になって僕なりに改造追加したんだよね
随分寝かせた。LLMにも手伝ってもらったけど誤認が酷かった。
でも見事な変換もあった。LLMすげーなと思ったよ、マジで。
それでも、出来ないかな・・と何度もあきらめた。
で再び今年の3月にチャレンジしたさ。(最近の話だよ)
で3月10日だったと思う。
なんでかっていうと、別のブログに記載してたものをアソシアトロン研究という
このブログを立ち上げたから、やらなきゃいけないでしょ。という理由ねw
今のAtraの概念であるcarryとか、差分とか、順番無しとか細かいのは無いよ。
まだまだ、ぜんぜんアルゴリズム。
あくまでもworld(core)の中の動物認識 robotだよ。
だから完全な一人称ではなかった。
でもそれが良かった。
robotを一人称っぽく分けても、coreが命令していたらまだ三人称が残るという境界が見えた。
その不完全さがあったから、研究に火が付いた。
何か、「ロボットが誤認する」そこに可能性があった。
だから、皆が見捨てた古い古典だろうが、種火を見つける。それを意地でも手繰り寄せる。
そうしないと、Atraなんかには絶対に結び付かないんだよ。
上にあるリンク先の一番下にこのデモがあります。
F12でcodeが確認できます。
下のコードは独立させた robot1.js(ファイル5つのうちの1つ)
たぶん、これがなかったら、今のAtraは生まれなかった。
今のAtraとはぜんぜん違うけれど、発火したcodeってあるんですよ。
const LangieRobot1 = (() => {
"use strict";
function create(deps, options = {}) {
const {
N,
M1,
M2,
M3,
NMAX,
VSTEP,
pat,
attrLetters,
rn,
f1,
f2,
clamp,
abs,
make2DInt,
make2DFloat
} = deps;
const robot = {
id: 1,
homeIndex: (options.homeIndex | 0) || 0,
posx: 25 + Math.floor(rn() * 350),
posy: 25 + Math.floor(rn() * 200),
prex: 0,
prey: 0,
vx: 0,
vy: 0,
mw: make2DInt(M1, M1, 0),
ml: make2DFloat(M2, M3, true),
wp: make2DInt(NMAX, M1, 0),
wf: new Int16Array(NMAX),
wq: make2DInt(NMAX, M3, 0),
word: make2DInt(NMAX, M2, 0),
sym: new Array(NMAX),
n: 0,
jw: -1,
zw: -1,
recall_num: -1,
action: 0,
j_word: new Int16Array(M2),
z_word: new Int16Array(M2),
speechLine1: ".......... .....",
speechLine2: ".......... .....",
hearLine: ".......... (......)",
speechHoldFrames: 0,
speechHoldMax: 240,
actionHoldFrames: 0,
actionHoldMax: 90,
currentInput: null,
seenObjectNow: false,
heardNow: false,
lastSeenObject: false,
lastHeardSignature: "",
lastOwnSpeechSignature: "",
idleFrames: 0,
speechJitter: (typeof options.speechJitter === "number") ? options.speechJitter : 0.28,
hearingRange: (typeof options.hearingRange === "number") ? options.hearingRange : 80,
actionJitter: (typeof options.actionJitter === "number") ? options.actionJitter : 0.16,
cautionBias: (typeof options.cautionBias === "number") ? options.cautionBias : 0.00,
approachBias: (typeof options.approachBias === "number") ? options.approachBias : 0.00,
startDelay: (typeof options.startDelay === "number") ? options.startDelay : 0,
tickCount: 0,
init() {
this.prex = this.posx;
this.prey = this.posy;
for (let k = 0; k < NMAX; k++) this.sym[k] = "";
return this;
},
reset() {
this.posx = 25 + Math.floor(rn() * 350);
this.posy = 25 + Math.floor(rn() * 200);
this.prex = this.posx;
this.prey = this.posy;
this.vx = 0;
this.vy = 0;
for (let i = 0; i < M1; i++) this.mw[i].fill(0);
for (let i = 0; i < M2; i++) {
for (let j = 0; j < M3; j++) {
this.ml[i][j] = rn() * 20.0 - 10.0;
}
}
for (let k = 0; k < NMAX; k++) {
this.wp[k].fill(0);
this.wq[k].fill(0);
this.word[k].fill(0);
this.wf[k] = 0;
this.sym[k] = "";
}
this.n = 0;
this.jw = -1;
this.zw = -1;
this.recall_num = -1;
this.action = 0;
this.j_word.fill(0);
this.z_word.fill(0);
this.speechLine1 = ".......... .....";
this.speechLine2 = ".......... .....";
this.hearLine = ".......... (......)";
this.speechHoldFrames = 0;
this.actionHoldFrames = 0;
this.currentInput = null;
this.seenObjectNow = false;
this.heardNow = false;
this.lastSeenObject = false;
this.lastHeardSignature = "";
this.lastOwnSpeechSignature = "";
this.idleFrames = 0;
},
quiet() {
this.speechLine1 = ".......... .....";
this.speechLine2 = ".......... .....";
this.hearLine = ".......... (......)";
},
makeSpeechSyllables(bits5, memIndex) {
const out = [];
const freq =
(typeof memIndex === "number" && memIndex >= 0)
? this.wf[memIndex]
: 0;
const instability = Math.max(0, 4 - freq);
const jitterRate = clamp(this.speechJitter * (instability / 4), 0, 0.5);
for (let i = 0; i < M2; i++) {
let bit = bits5[i];
if (rn() < jitterRate) {
bit = -bit;
}
if (bit === 1) {
out.push(rn() < 0.5 ? "ba" : "pa");
} else {
out.push(rn() < 0.5 ? "bu" : "pu");
}
}
return out.join("-");
},
getHome(input) {
if (input && Array.isArray(input.shelters) && input.shelters.length > 0) {
const idx = clamp(this.homeIndex, 0, input.shelters.length - 1);
return input.shelters[idx];
}
return { x: 375, y: 30 };
},
asc1_memorize(jb, ob) {
const mw = this.mw;
const src = jb[ob];
for (let i = 0; i < M1; i++) {
const jbi = src[i];
if (jbi === 0) continue;
const row = mw[i];
for (let j = 0; j < M1; j++) {
const jbj = src[j];
if (jbj === 0) continue;
row[j] += jbi * jbj;
}
}
},
extract(jb, ob) {
const check = new Int8Array(NMAX);
const src = jb[ob];
for (let i = 0; i < M1; i++) {
if (src[i] === 0) continue;
let found = false;
const th = this.mw[i][i] * (rn() * 0.6 + 0.3);
const xbits = new Int16Array(M1);
for (let j = 0; j < M1; j++) {
xbits[j] = f1(this.mw[j][i] * src[i], th);
}
for (let k = 0; k < this.n; k++) {
let err = 0;
const mem = this.wp[k];
for (let j = 0; j < M1; j++) {
if (xbits[j] !== mem[j]) err++;
if (err > 2) break;
}
if (err <= 2) {
if (check[k] === 0) {
this.wf[k] += 1;
check[k] = 1;
}
found = true;
break;
}
}
if (!found && this.n < NMAX) {
const k = this.n++;
this.wp[k].set(xbits);
this.wf[k] = 1;
for (let j = 0; j < M3; j++) {
this.wq[k][j] = (Math.floor(rn() * 2) * 2 - 1);
}
let symStr = "";
for (let ai = 0; ai < N; ai++) {
let err = 0;
for (let j = 0; j < 5; j++) {
if (this.wp[k][8 * ai + j] !== pat[ai][j]) err++;
if (err > 1) break;
}
symStr += (err < 2) ? attrLetters[ai] : " ";
}
this.sym[k] = symStr;
}
}
},
select(jb, ob) {
const src = jb[ob];
const zz = new Int16Array(NMAX);
let m = 0;
this.jw = -1;
this.zw = -1;
for (let k = 0; k < this.n; k++) {
let err1 = 0;
let err2 = 0;
const mem = this.wp[k];
for (let j = 0; j < M1; j++) {
if (src[j] !== mem[j]) {
err1++;
if (mem[j] !== 0) err2++;
}
if (err2 > 2) break;
}
if (err2 <= 2) {
this.wf[k] += 1;
if (err1 <= 1) {
this.jw = k;
} else {
zz[m] = k;
m++;
}
}
}
let max = 0.0;
for (let i = 0; i < m; i++) {
const k = zz[i];
const temp = this.wf[k] * rn();
if (temp > max) {
max = temp;
this.zw = k;
}
}
},
speak(e) {
if (this.speechHoldFrames > 0) {
this.speechHoldFrames--;
return;
}
let line1 = "..........";
let attr1 = ".....";
if (this.jw >= 0) {
const k = this.jw;
for (let i = 0; i < M2; i++) {
let u = 0.0;
for (let j = 0; j < M3; j++) {
u += this.ml[i][j] * this.wq[k][j];
}
const v = f2(u);
this.j_word[i] = v;
this.word[k][i] = v;
}
line1 = this.makeSpeechSyllables(this.j_word, k);
attr1 = `(${this.sym[k]})`;
} else {
this.j_word.fill(0);
}
let line2 = "..........";
let attr2 = ".....";
if (this.zw >= 0) {
const k = this.zw;
for (let i = 0; i < M2; i++) {
let u = 0.0;
for (let j = 0; j < M3; j++) {
u += this.ml[i][j] * this.wq[k][j];
}
const v = f2(u);
this.z_word[i] = v;
this.word[k][i] = v;
}
if (e === 0) {
line2 = this.makeSpeechSyllables(this.z_word, k);
attr2 = `(${this.sym[k]})`;
} else {
this.z_word.fill(0);
}
} else {
this.z_word.fill(0);
}
this.speechLine1 = `${line1} ${attr1}`;
this.speechLine2 = `${line2} ${attr2}`;
this.lastOwnSpeechSignature = `${this.speechLine1}|${this.speechLine2}`;
this.speechHoldFrames = this.speechHoldMax;
},
learning(otherRobot) {
const alpha = 0.25;
const beta = 0.15;
if (this.jw < 0) return;
for (let i = 0; i < M2; i++) {
for (let j = 0; j < M3; j++) {
let jdm = 0.0;
let zdm = 0.0;
let wdm = 0.0;
if (otherRobot && otherRobot.jw >= 0) {
jdm = alpha * otherRobot.j_word[i] * this.wq[this.jw][j];
}
if (otherRobot && otherRobot.zw >= 0) {
const zsrc = (this.zw >= 0) ? this.zw : this.jw;
zdm = beta * otherRobot.z_word[i] * this.wq[zsrc][j];
}
for (let k = 0; k < this.n; k++) {
if (k === this.jw) continue;
let inpro = 0;
for (let o = 0; o < M2; o++) {
inpro += this.word[k][o] * this.word[this.jw][o];
}
if (inpro === 5) {
wdm += 0.2 * (inpro * this.word[k][i] * this.wq[k][j]);
}
}
this.ml[i][j] += (jdm + zdm - wdm);
}
}
},
hear(otherRobot) {
this.recall_num = -1;
if (otherRobot && otherRobot.jw >= 0) {
const recall = new Int16Array(M3);
for (let i = 0; i < M3; i++) {
let u = 0.0;
for (let j = 0; j < M2; j++) {
u += otherRobot.j_word[j] * this.ml[j][i];
}
recall[i] = f2(u);
}
const zz = new Int16Array(NMAX);
let m = 0;
for (let k = 0; k < this.n; k++) {
let err = 0;
for (let j = 0; j < M3; j++) {
if (recall[j] !== this.wq[k][j]) err++;
if (err > 2) break;
}
if (err <= 2) {
zz[m] = k;
m++;
}
}
let max = 0.0;
for (let i = 0; i < m; i++) {
const k = zz[i];
const temp = this.wf[k] * rn();
if (temp > max) {
this.recall_num = k;
max = temp;
}
}
}
const k = this.recall_num;
const symStr = (k >= 0) ? this.sym[k] : "??????";
this.hearLine = `.......... (${symStr})`;
},
sp_action(zs, input) {
let k = -1;
let err1 = 1;
let err2 = 1;
if (this.jw >= 0) {
k = this.jw;
} else if (this.zw >= 0) {
k = this.zw;
}
if (k >= 0) {
err1 = 0;
err2 = 0;
const mem = this.wp[k];
for (let j = 0; j < M1; j++) {
if ((mem[j] !== zs[0][j]) && (zs[0][j] !== 0)) err1++;
if ((mem[j] !== zs[1][j]) && (zs[1][j] !== 0)) err2++;
if ((err1 > 1) && (err2 > 1)) break;
}
}
let baseAction = 0;
if (err1 === 0) baseAction = 1;
else if (err2 === 0) baseAction = 2;
this.action = this.biasActionByIndividuality(baseAction, input);
this.actionHoldFrames = this.actionHoldMax;
this.action_disp(input);
},
hr_action(zs, input) {
if (this.actionHoldFrames > 0) {
this.actionHoldFrames--;
this.action_disp(input);
return;
}
if (this.recall_num >= 0) {
const mem = this.wp[this.recall_num];
let err1 = 0;
let err2 = 0;
for (let j = 0; j < M1; j++) {
if ((mem[j] !== zs[0][j]) && (zs[0][j] !== 0)) err1++;
if ((mem[j] !== zs[1][j]) && (zs[1][j] !== 0)) err2++;
if ((err1 > 1) && (err2 > 1)) break;
}
let baseAction = 0;
if (err1 === 0) baseAction = 1;
else if (err2 === 0) baseAction = 2;
this.action = this.biasActionByIndividuality(baseAction, input);
this.actionHoldFrames = this.actionHoldMax;
} else {
this.action = this.biasActionByIndividuality(0, input);
}
this.action_disp(input);
},
biasActionByIndividuality(baseAction, input) {
let action = baseAction;
const obj = input && input.object ? input.object : null;
const distanceBand = obj ? obj.distanceBand : "far";
let fleePull = this.cautionBias;
let approachPull = this.approachBias;
if (distanceBand === "near") {
fleePull += 0.18;
} else if (distanceBand === "mid") {
fleePull += 0.08;
}
const noise = (rn() * 2 - 1) * this.actionJitter;
if (action === 0) {
if ((fleePull + noise) > 0.16) {
action = 1;
} else if ((approachPull - noise) > 0.16) {
action = 2;
}
}
else if (action === 2) {
if ((fleePull + noise) > 0.28) {
action = 1;
}
}
else if (action === 1) {
if ((approachPull - noise) > 0.30 && distanceBand !== "near") {
action = 2;
}
}
return action;
},
action_disp(input) {
const stepToward = (tx, ty) => {
this.prex = this.posx;
this.prey = this.posy;
if (abs(tx - this.posx) > 2) {
this.vx = (tx - this.posx) > 0 ? VSTEP : -VSTEP;
this.posx += this.vx;
} else {
this.vx = 0;
}
if (abs(ty - this.posy) > 2) {
this.vy = (ty - this.posy) > 0 ? VSTEP : -VSTEP;
this.posy += this.vy;
} else {
this.vy = 0;
}
this.posx = clamp(this.posx, 25, 375);
this.posy = clamp(this.posy, 25, 205);
};
if (this.action === 1) {
const home = this.getHome(input);
stepToward(home.x, home.y);
} else if (this.action === 2) {
if (input && input.object && input.object.active) {
stepToward(input.object.posx - 7, input.object.posy - 7);
} else {
this.randomStep();
}
} else {
this.randomStep();
}
},
randomStep() {
this.vx = 0;
this.vy = 0;
this.prex = this.posx;
this.prey = this.posy;
if (Math.floor(rn() * 17) > 13) {
const r = Math.floor(rn() * 15);
if ((r < 2) && (this.posx < 375)) this.vx = VSTEP;
else if ((r < 9) && (this.posx > 25)) this.vx = -VSTEP;
else if ((r < 12) && (this.posy < 205)) this.vy = VSTEP;
else if (this.posy > 25) this.vy = -VSTEP;
}
this.posx += this.vx;
this.posy += this.vy;
this.posx = clamp(this.posx, 25, 375);
this.posy = clamp(this.posy, 25, 205);
},
receiveWorld(input) {
this.currentInput = input;
},
canSeeObject() {
return !!(this.currentInput && this.currentInput.object && this.currentInput.object.active);
},
hasOtherSpeech(input) {
if (!input || !input.otherRobot || !input.otherRobot.id) return false;
const dx = input.otherRobot.dx || 0;
const dy = input.otherRobot.dy || 0;
const dist = abs(dx) + abs(dy);
if (dist > this.hearingRange) return false;
const s1 = input.otherRobot.speechLine1 || "";
const s2 = input.otherRobot.speechLine2 || "";
return (s1 !== ".......... .....") || (s2 !== ".......... .....");
},
shouldSpeakFromSeeing(input) {
if (!input || !input.object || !input.object.active) return false;
if (input.object.distanceBand === "far") return false;
return true;
},
processSeeing(otherRobot) {
const input = this.currentInput;
if (!input) return;
const jb = input.jb;
const ob = input.object.ob;
const zs = input.zs;
const dis_flag = input.dis_flag;
this.asc1_memorize(jb, ob);
if (this.n < NMAX) this.extract(jb, ob);
this.select(jb, ob);
if (this.shouldSpeakFromSeeing(input)) {
this.speak(0);
} else {
this.speak(1);
}
if (dis_flag !== 2) {
this.sp_action(zs, input);
}
this.learning(otherRobot);
},
processHearing(otherRobot) {
const input = this.currentInput;
if (!input) return;
const zs = input.zs;
const dis_flag = input.dis_flag;
this.hear(otherRobot);
if (dis_flag !== 2) {
this.hr_action(zs, input);
}
},
processIdle() {
this.jw = -1;
this.zw = -1;
this.recall_num = -1;
this.hearLine = ".......... (......)";
this.action = 0;
this.randomStep();
if (this.idleFrames > 8) {
this.speechLine1 = ".......... .....";
this.speechLine2 = ".......... .....";
}
},
tick(input, otherRobot = null) {
this.tickCount++;
if (this.tickCount <= this.startDelay) {
this.randomStep();
return;
}
if (input) this.receiveWorld(input);
if (!this.currentInput) return;
this.seenObjectNow = this.canSeeObject();
this.heardNow = this.hasOtherSpeech(this.currentInput);
if (this.seenObjectNow) {
this.idleFrames = 0;
this.processSeeing(otherRobot);
} else if (this.heardNow) {
this.idleFrames = 0;
this.processHearing(otherRobot);
} else {
this.idleFrames++;
this.processIdle();
}
this.lastSeenObject = this.seenObjectNow;
this.lastHeardSignature = this.heardNow
? `${this.currentInput.otherRobot.speechLine1}|${this.currentInput.otherRobot.speechLine2}`
: "";
this.currentInput = null;
},
getIntentText() {
if (this.action === 1) return "flee";
if (this.action === 2) return "approach";
return "wander";
}
};
return robot.init();
}
return { create };
})();
0 件のコメント:
コメントを投稿