2026年6月16日火曜日

Turbo-CからJavaScript、 そしてPythonとC++

 Atraの開発のきっかけになったのが、1998年の「Cでつくる脳の情報システム」という本だよ。購入したのは2017・8年だったかなぁ・・・大好きな神保町の古本屋まわりして300円か500円で購入した本だよ。


一番下にデモ入れておきました。F12でcodeを見られます


PC-9800シリーズ 3.5インチ2HD付きさ
Turbo C++v1.01だよ。

パーセプトロン
アソシアトロン
ホップフィールド、ボルツマンマシン
バックプロパゲーション
ニューラルネットワーク
アソシアトロンの応用
のデモが入ってるんだ。
盛りだくさんだよ。

PC-9800なんて持ってないし、本に記載してるcodeをNotepad++に書いたよ。
OCR試したけど全然ダメでね。

Turbo-Cは逐次実行の実験プログラムなのでfor で回して、状態を更新して、画面を書いて、また回す。Cの世界では「今この瞬間のメモリ状態」を直接触っている感じが強いでしょ。
でもJavaScript、特にブラウザでは、描画は requestAnimationFrame、入力はイベント、時間は非同期、画面はCanvasやDOMになる。なので、Cの「手続きで世界を進める」感じを、そのまま持ってくると崩れる。だからCのループ実験を、JavaScriptの時間更新モデルに置き換えるのはマジで地獄。



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

https://crimson-cake-2832.nabedada3.workers.dev/
一番下にデモ入れておきました。F12でcode見られます


④robotを外部制御ではなく、それぞれの内部状態を持つ形に分けた。
  (一人称というものを真面目に考えてみた)
⑤Associatronの想起やcue競合を、ブラウザで見える形に整理した。

今のAtraの概念であるcarryとか、差分とか、順番無しとか細かいのは無いよ。
まだまだ、ぜんぜんアルゴリズム。
あくまでもworld(core)の中の動物認識 robotだよ。

だから完全な一人称ではなかった。
でもそれが良かった。
robotを一人称っぽく分けても、coreが命令していたらまだ三人称が残るという境界が見えた。

その不完全さがあったから、研究に火が付いた。
何か、「ロボットが誤認する」そこに可能性があった。
だから、皆が見捨てた古い古典だろうが、種火を見つける。それを意地でも手繰り寄せる。
そうしないと、Atraなんかには絶対に結び付かないんだよ。





上にあるリンク先の一番下にこのデモがあります。
F12でcodeが確認できます。

下のコードは独立させた robot1.js(ファイル5つのうちの1つ)
たぶん、これがなかったら、今のAtraは生まれなかった。
今のAtraとはぜんぜん違うけれど、発火したcodeってあるんですよ。




// robot1.js const LangieRobot1 = (() => { "use strict"; function create(deps, options = {}) { const { // constants N, M1, M2, M3, NMAX, VSTEP, // tables pat, attrLetters, // utils rn, f1, f2, clamp, abs, // allocators make2DInt, make2DFloat } = deps; const robot = { id: 1, homeIndex: (options.homeIndex | 0) || 0, // positions (TurboC ranges) posx: 25 + Math.floor(rn() * 350), posy: 25 + Math.floor(rn() * 200), prex: 0, prey: 0, vx: 0, vy: 0, // associative memory mw: make2DInt(M1, M1, 0), ml: make2DFloat(M2, M3, true), // world image memory 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, // spoken j_word: new Int16Array(M2), z_word: new Int16Array(M2), // ui text speechLine1: ".......... .....", speechLine2: ".......... .....", hearLine: ".......... (......)", speechHoldFrames: 0, speechHoldMax: 240, actionHoldFrames: 0, actionHoldMax: 90, // current sensory input currentInput: null, // simple internal timing seenObjectNow: false, heardNow: false, lastSeenObject: false, lastHeardSignature: "", lastOwnSpeechSignature: "", idleFrames: 0, // ------------------------------------------------- // Individual parameters // Different robots can start with different speech, // hearing range, action bias, and timing phase. // ------------------------------------------------- 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 = ".......... (......)"; }, // ------------------------------------------------- // Generate pronounceable syllables from bit pattern // Speech is unstable when memory frequency is low. // Stabilizes as experience accumulates. // ------------------------------------------------- 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 }; }, // -------- TurboC asc1_memorize(l,ob) -------- 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; } } }, // -------- TurboC extract(l,ob) -------- 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; } } }, // -------- TurboC select(l,ob) -------- 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; } } }, // -------- TurboC speak(l,e) -------- 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; }, // -------- TurboC learning(l) -------- 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); } } }, // -------- TurboC hear(l) -------- 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})`; }, // -------- TurboC sp_action(l) -------- 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); }, // -------- TurboC hr_action(l) -------- 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); }, // ------------------------------------------------- // Apply individual action bias // Allows different initial choices for the same object. // ------------------------------------------------- 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; }, // -------- TurboC action_disp(l) -------- 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(); } }, // -------- TurboC random_walk() の個体側 -------- 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 }; })();



---------------------Research Note and Attribution Notice-----------------------
本ブログに含まれる Atra の一人称自律、差分、carry、field、trace、dream slack、外部LLMの翻訳層、非単調な漏れ、およびそれらの関係構造に関する設計記述は、c-side研究所による継続研究メモです。引用・参照・要約・翻案を行う場合は、出典を明記してください。

The design descriptions in this blog concerning Atra’s first-person autonomy, differences, carry, field, trace, dream slack, the translation layer of external LLMs, nonmonotonic leakage, and the relational structure among these elements are ongoing research notes by c-side Research Institute. If you quote, refer to, summarize, or adapt them, please clearly indicate the source.


0 件のコメント:

コメントを投稿

Atra Emotions_Conditions 感情・状態

 -----------------C++------------------ struct EmotionsConditionsNow { // Unpredictability double input_irregularity_now = 0.0 ; ...