結論から先に言うと、前回のJavaScriptのソースをOOPらしく書き直し、Windowsベースの.NET Framework 4.8ベースで動く

 

「ウィンドウズ版Boids for C# Version 1.0」

 

が出来たみたいです。

 

 

これで最初の方針

 

【Task2】

その上で、私の理解が正しいか否か、自分の言語でそのプログラム構造と実装を再現してみて(「移植」)、初めて「本当に理解した」と言えるのではないか、と考えました。

 

が完了したと言えるでしょう。しかし、結構躓きも多く、結局大分Chat-GPT様にも助けていただいたので、その顛末もネタとして提供させていただくつもりです。(【Task3】はその後で...)

 

前回の通り、「(オリジナル)はブラウザー用に書かれているのでHTML(index.html)とJavaScript(boids.js)により書かれて」いるので先ず、

 

【Task1】

私はHTML、JavaScript共に門外漢で、先ずはそのコードの意味と「何が何をしているのか」の解明をすることが先決となりました。

 

先ずはエントリーポイントとなるHTMLファイルを眺めます。

 

【index.html】

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <title>Boids</title>
    <script src="
./boids.js"></script>    (解説:ここでJavaScriptファイルを実行するようです。)
    <style type="text/css">            (解説:ページ全体にCSS(スタイルシート)を適用する、らしいです。)
      body {
        margin: 0;
        background: #282c34;
        overflow: hidden;
      }

    </style>
  </head>
  <body>
    <canvas id="boids" width="150" height="150"></canvas>
  </body>
</html>

 

今度はJavaScriptファイルを眺めますが、英語表記なので(今後C#用にする可能性もあり、)拡張子を".cs"にしてコメントを日本語で書いてみます。(最初は意訳でしたが、その後少しこんなもので調べて内容を確かめました。)

 

【boids.cs

//////////////////////////////////////////////////
//    Boids algorithm demonstration (in JavaScript)
//    Original program Copyright by Ben Eater
//    日本語コメント文責:Y-sama
//    https://eater.net/boids
//////////////////////////////////////////////////
//描画領域の幅、高さ(ブラウザーの大きさに合わせて更新される)

let width = 150;        //HTMLファイルの描画領域canvas定義の初期値
let height = 150;        //HTMLファイルの描画領域canvas定義の初期値

//定数
const numBoids = 100;        //擬鳥の数
const visualRange = 75;        //視野

var boids = [];                //擬鳥配列(解説:変数の型は不問)

//擬鳥初期化(解説:構造体の宣言がありませんが、ループを使った初期化で構造体の変数としているようです。)
function initBoids() {
    for (var i = 0; i < numBoids; i += 1) {

        //解説:構造体の要素は描画にx, yが使われることから整数の様ですが、randome()は倍制度実数なのでそうなのかも?
        boids[boids.length] = {            //配列を代入するとlengthが自動的に+1される(解説:{}括弧内が要素のようです。
            x: Math.random() * width,    //x座標(Math.random()は静的メソッドで、 0 以上 1 未満の範囲で浮動小数点の擬似乱数を返す)
            y: Math.random() * height,    //y座標
            dx: Math.random() * 10 - 5,    //x移動距離(-5~5-左右へ5)
            dy: Math.random() * 10 - 5,    //y移動距離(-5~5-上下へ5)
            history: [],                //履歴(x、y座標の履歴配列-↓を見て後で分かりました。)
        };
    }
}

//擬鳥間の距離計算(√(x軸距離 ^ 2 + y軸距離 ^ 2))(解説:boids1, 2の方が整数なのか、実数なのかわかりません。)
function distance(boid1, boid2) {
    return Math.sqrt(    //解説:Math.sqrt()の引数は倍精度実数となります。
        (boid1.x - boid2.x) * (boid1.x - boid2.x) +
            (boid1.y - boid2.y) * (boid1.y - boid2.y),
    );
}

//TODO: 未熟で非効率的なので要改善
function nClosestBoids(boid, n) {
    //擬鳥のコピー作成(.slice()に引数を与えないと全ての要素となる)
    const sorted = boids.slice();    //配列のメソッドで、配列の一部を start から end (end は含まれない)までの範囲で、選択した新しい配列オブジェクトにシャローコピーして返す。
    //自分からの距離に応じて他の擬鳥をソートする
    sorted.sort((a, b) => distance(boid, a) - distance(boid, b));
    // Return the `n` closest
    return sorted.slice(1, n + 1);    //自分自身が0になるので、次(1)からn迄のシャローコピー配列となる
}

//初期化及びサイズ変更時に描画領域とwidth/heightフィールドを変更する。
function sizeCanvas() {
    /* 解説:以下は私の参照用に入れていました。

        getElementById(id)はDocumentインターフェイスのメソッドで、idで指定された文字列に
        一致するElementオブジェクトを返します。
        【HTMLファイルの関連表記】
        "<canvas id="boids" width="150" height="150"></canvas>"
        <canvas>:描画領域(キャンバス)を定義するHTMLタグ
        id="boids":このキャンバスをJavaScriptで操作・特定するための固有の識別名。
        width:キャンバスの横幅
        height:キャンバスの縦幅
    */

    const canvas = document.getElementById("boids");    //HTMLからidが"boids"のcanvas要素を取得する
    width = window.innerWidth;        //ウィンドウ幅をwidthに取得(グローバル変数のwindowは、スクリプトを実行しているウィンドウ)
    height = window.innerHeight;    //ウィンドウ高さをheightに取得
    canvas.width = width;            //"boids"canvasの幅設定
    canvas.height = height;            //"boids"canvasの高さ設定
}

//擬鳥を描画領域内に止め、境界に接近すると反対方向へ向かわせる(dx、dyは移動方向・距離)
function keepWithinBounds(boid) {
    const margin = 200;        //余白(HTMLファイル上では初期値は0)
    const turnFactor = 1;    //方向変換要素

    if (boid.x < margin) {            //x軸左方向
        boid.dx += turnFactor;        //右方向(加算)
    }
    if (boid.x > width - margin) {    //x軸右方向
        boid.dx -= turnFactor        //左方向(減算)
    }
    if (boid.y < margin) {            //y軸上方向
        boid.dy += turnFactor;        //下方向(加算)
    }
    if (boid.y > height - margin) {    //y軸下方向
        boid.dy -= turnFactor;        //上方向(減算)
    }
}

//擬鳥の集団の中央を探し、そこへ向かう速度を少し調節する
function flyTowardsCenter(boid) {
    const centeringFactor = 0.005; //速度変更係数(解説:型指定はないが実数)

    let centerX = 0;
    let centerY = 0;
    let numNeighbors = 0;

    for (let otherBoid of boids) {    //解説:C#なら"foreach(Boid otherBoid in boids)"的ループ?
        //視野内の擬鳥のx、y座標とその数を加算
        //otherBoidにboidが含まれる為、if (otherBoid !== boid) {}が必要か?
        if (distance(boid, otherBoid) < visualRange) {
            centerX += otherBoid.x;    //解説:整数で可
            centerY += otherBoid.y;    //解説:Ditto
            numNeighbors += 1;
        }
    }

    if (numNeighbors) {    //解説:視界内に擬鳥がいれば
        //視野内の擬鳥のx、y座標の平均値を求める
        centerX = centerX / numNeighbors;    //解説:整数で可(切り捨てになるが)
        centerY = centerY / numNeighbors;    //解説:Ditto
        //自分のdx、dyに集団の中心との平均距離に速度変更係数を乗じたものを加える
        boid.dx += (centerX - boid.x) * centeringFactor;    //解説:計算式部分は実数で、代入は整数とすることが必要
        boid.dy += (centerY - boid.y) * centeringFactor;    //解説:Ditto
    }
}

//衝突しそうな近くの擬鳥から離れる
function avoidOthers(boid) {
    const minDistance = 20;        //離反する境界距離
    const avoidFactor = 0.05;    //速度変更係数(解説:型指定はないが実数)
    let moveX = 0;
    let moveY = 0;

    for (let otherBoid of boids) {
        if (otherBoid !== boid) {    //解説:ここでは自分自身を除外している(↑flyTowardCenter参照)
            //境界距離以内の擬鳥のx、y座標を自分の座標から減算
            if (distance(boid, otherBoid) < minDistance) {
                moveX += boid.x - otherBoid.x;    //解説:moveX -= "otherBoid.x - boid.x;"に等しい
                moveY += boid.y - otherBoid.y;    //解説:Ditto
            }
        }
    }

    //自分のdx、dyに離反座標に速度変更係数を乗じたものを減じる(マイナスを加算する)
    boid.dx += moveX * avoidFactor;
    boid.dy += moveY * avoidFactor;
}

//他の擬鳥の速さ(速度と方向)を求め、それへ自分を調整する
function matchVelocity(boid) {
    const matchingFactor = 0.05;    //速度変更係数(    //解説:型指定はないが実数)

    let avgDX = 0;
    let avgDY = 0;
    let numNeighbors = 0;

    for (let otherBoid of boids) {
        //視野内の擬鳥のx、y移動距離を加算し、その数を記録
        if (distance(boid, otherBoid) < visualRange) {
            avgDX += otherBoid.dx;
            avgDY += otherBoid.dy;
            numNeighbors += 1;
        }
    }
    //移動距離の合計を総数で除算して平均を求め、自分の移動距離との差に速度変更係数を乗じたもので調整する
    if (numNeighbors) {    //解説:視野内に擬鳥がいれば
        avgDX = avgDX / numNeighbors;    //解説:整数で可
        avgDY = avgDY / numNeighbors;    //解説:Ditto

        boid.dx += (avgDX - boid.dx) * matchingFactor;    //解説:計算式は実数で、整数で代入
        boid.dy += (avgDY - boid.dy) * matchingFactor;    //解説:Ditto
    }
}

//集合速度は自然と変化するが、現実には意図的に変化させてはいない
function limitSpeed(boid) {
    const speedLimit = 15;    //限界速度(解説:整数で可)

    //x、y軸の移動距離から求められる速度(speed)が
    const speed = Math.sqrt(boid.dx * boid.dx + boid.dy * boid.dy);    //解説:Mathsqrt()の引数は倍制度実数
    //限界速度を超えたならば、x、y軸の移動距離を限界速度で頭打ちにする
    if (speed > speedLimit) {
        boid.dx = (boid.dx / speed) * speedLimit;    //解説:整数不可(整数だと0か1になる)
        boid.dy = (boid.dy / speed) * speedLimit;    //解説:Ditto
    }
}

const DRAW_TRAIL = false;    //飛行跡

function drawBoid(ctx, boid) {    //擬鳥の描画
    const angle = Math.atan2(boid.dy, boid.dx);    //Math.atan2(y, x) に対して点 (0, 0) から点 (x, y) までの半直線と、正の x 軸の間の平面上での角度(ラジアン単位)を返す(C#の Math.Atan2

    //解説:以下の描画表記はOpenGLを彷彿とさせます。
    ctx.translate(boid.x, boid.y);        //x、y座標へ移動
    ctx.rotate(angle);                    //angle分回転
    ctx.translate(-boid.x, -boid.y);
    ctx.fillStyle = "#558cf4";            //キャンバス 2D API のプロパティで、図形の内側を塗りつぶすために使用する色、グラデーション、またはパターンを指定
    ctx.beginPath();
    ctx.moveTo(boid.x, boid.y);            //以下座標移動し、三角形を描画し、塗り潰す
    ctx.lineTo(boid.x - 15, boid.y + 5);
    ctx.lineTo(boid.x - 15, boid.y - 5);
    ctx.lineTo(boid.x, boid.y);
    ctx.fill();
    ctx.setTransform(1, 0, 0, 1, 0, 0);

    if (DRAW_TRAIL) {    //飛行跡が真であれば一定の長さの飛行痕(糸のような線)を残す
        ctx.strokeStyle = "#558cf466";
        ctx.beginPath();
        ctx.moveTo(boid.history[0][0], boid.history[0][1]);
        for (const point of boid.history) {
            ctx.lineTo(point[0], point[1]);
        }
        ctx.stroke();    //キャンバス 2D API のメソッドで、現在のあるいは渡されたパスを、現在の線のスタイルで描画
    }
}

//Mainループ
function animationLoop() {
    //すべての擬鳥を更新
    for (let boid of boids) {
        //速さをそれぞれの規則に基づいて更新
        flyTowardsCenter(boid);    //群れへの参集
        avoidOthers(boid);        //近すぎる擬鳥からの離反
        matchVelocity(boid);    //速度調整
        limitSpeed(boid);        //限界速度制限
        keepWithinBounds(boid);    //枠内制限

        //現在の速さに基づき位置(x、y座標)を更新
        boid.x += boid.dx;
        boid.y += boid.dy;
        boid.history.push([boid.x, boid.y])        //配列の末尾に指定された要素を追加
        boid.history = boid.history.slice(-50);    //slice() は Array インスタンスのメソッドで、配列の一部を start から end (end は含まれない)までの範囲で、選択した新しい配列オブジェクトにシャローコピーして返す
        //引数が負の場合、配列の末尾からさかのぼって数えるので、キューとして使っている

    }

    //描画領域を一旦消去し、擬鳥を新しい位置で描画
    const ctx = document.getElementById("boids").getContext("2d");
    ctx.clearRect(0, 0, width, height);
    /* サンプル(解説:自分用の備忘です。)
        const canvas = document.getElementById("canvas");    //HTMLから canvas 要素を取得する
        const ctx = canvas.getContext("2d");                //平面(2次元)描画を指定
        ctx.beginPath(); // 新しいパスを開始
        ctx.rect(10, 20, 150, 100); // 矩形を現在のパスに追加
        ctx.fill(); // パスを描画
    */

    for (let boid of boids) {
        drawBoid(ctx, boid);
    }

    //次の描画フレームを予約
    window.requestAnimationFrame(animationLoop);
    /*    アニメーションを実行したいことをブラウザーに指示します。
        このメソッドは、次回の再描画の前に、ユーザーが指定した
        コールバック関数を呼び出すようブラウザーに要求します。
        コールバック関数への呼び出し頻度は、通常、ディスプレイの
        リフレッシュレートと一致します。 最も一般的なリフレッシュ
        レートは 60Hz(60 サイクル/フレーム毎秒)ですが、75Hz、
        120Hz、144Hz も広く使用されています。
    */

}

window.onload = () => {    //WM_CREATE処理
    //描画領域が何時もウィンドウサイズとなるように、サイズ変更された場合にsizeCanvasを呼び出す
    window.addEventListener("resize", sizeCanvas, false);
    sizeCanvas();

    //擬鳥の初期化を行う
    initBoids();

    //アニメーション動画の予約
    window.requestAnimationFrame(animationLoop);
};

 

JavaScriptのプログラム、如何でしたか?私も初めてなのですが、何かOOP(Object Oriented Program)というよりも、手続型のC言語にOpenGLを混ぜたような感じがしませんか?

 

所で、JavaScriptのファイルの拡張子wo".cs"にしたので、このままではHTMLファイルが動きません。冒頭↑で参照したのHTMLファイルも次のように修正します。

 

    <script src="./boids.cs"></script>
 

こんなことしてJavaScriptが動くのかって?

 

index.htmlファイルをダブルクリックすると、何事もなかったかのように

 

 

擬鳥が飛びます。

 

さーて、

 

どういうプログラムかは大体わかったけれど、言語はJavaScript、でもプログラム自体は完成している(このまま楽しむなら弄る必要はない)し、

 

今後どうしましょうか?(要すればこの間書いた【Task2】と【Task3】をやるか否か、ということです。)

 

ps. まずはどうあれC#に移植してから、プログラム拡張を考えるってことかなぁ?

 

麺好きの私はいつもスパゲッティやペンネといったパスタを常備しています。ある時、こんなもの

 

 

があったので好奇心から買ってみましたが、エンジェルヘアーのように細く、長さも短いので、逆に中途半端な感じがして使っていませんでした。

 

が、

 

又もや早朝の寝床でアイデアが降ってきて

 

「そうだっ!これで焼き米粉を作ろう!」

 

ということで、

 

(1) なんかお肉(細切れ、挽肉、ソーセージやハム等なんでもOK-今回は余ったソーセージと鶏の笹身にしました)

(2) 野菜1(今回は玉葱 - Must、人参、ピーマン、長ネギ)

 

を下炒めし、軽く塩胡椒してからお水少々と鶏ガラ出汁(私は李錦記一択)を加え、

 

(3) 野菜2(今回はモヤシ、キャベツ。小松菜やほうれん草もアリだと思います)

 

を加えて「あともう少し」で火を止め、↑のパスタ用に沸かしておいた湯にパスタを投入、

 

たった一分

 

で野菜炒めにぶち込み、ササっと強火で炒めます。そして...

 

 

「見た目、味共にどう考えても焼きビーフン」

 

という

 

ショートパスタ

 

が出来上がりです。(添えているのは中華スープです。)

 

普通にスパゲッティを作るなら麺に9分、ソースに20分は最低でもかかるので、手軽に調理するには、

 

アリ

 

かもです。

 

さて、この間ここで触れたように「ボイド(”Bird-oid”)(人工生命シミュレーションプログラム)」という語と概念を知り、そのプログラミングの話なども平静に読みましたが、この姿

 

 

を見て一種の衝撃が走り、次のことを悟りました。

 

(1)所謂「ライフゲーム」は「過密と過疎」がファクターとなり、植物のような静的な存在でシミュレート

(2)私の「Cell」は(人間をイメージした)動的な存在が「親密性と敵対性」をファクターとしてシミュレート

のですが、既に書いたように

(3)ボイド(Boids)は「参集と離散)」がファクターとなったシミュレーション

wikiでは

・分離(Separation)ー鳥オブジェクトが他の鳥オブジェクトとぶつからないように距離をとる。
・整列(Alignment)ー鳥オブジェクトが他の鳥オブジェクトと概ね同じ方向に飛ぶように速度と方向を合わせる。
・結合(Cohesion)ー鳥オブジェクトが他の鳥オブジェクトが集まっている群れの中心方向へ向かうように方向を変える。

としていますが、Cohesionは「集合する」「集結する」だけですが、Alignmentを入れると、予め定まった規則に沿って集まるという意味で「参集」になるかと、考えました。

 

なーるほど!

 

ということで、もっとこのプログラムを知りたくて、作者(BenEaterさん:指定の公開リンク先(GitHub)からファイルをダウンロードしてみました。

この方が参照したPascal風のものは擬似コード(Pseudocode)でかかれています。実際のプログラミング言語の代わりに人間が読んで理解しやすい自然言語とプログラミングの構造を組み合わせて記述した「プログラムの設計図」のようです。

 

当たり前のことですが、これはブラウザー用に書かれているのでHTML(index.html)JavaScript(boids.js)により書かれていました。(

:boids.jsをダブルクリックしたらエラーになりました。PC上で実行するにはindex.htmlをダブルクリックしてください。

 

そして、

 

【Task1】

私はHTML、JavaScript共に門外漢で、先ずはそのコードの意味と「何が何をしているのか」の解明をすることが先決となりました。

 

【Task2】

その上で、私の理解が正しいか否か、自分の言語でそのプログラム構造と実装を再現してみて(「移植」)、初めて「本当に理解した」と言えるのではないか、と考えました。

 

【Task3】

更にその段階で、作者自ら(ダウンロードした)README.mdで語っているように、

 

"There are lots of features you could try adding to the code yourself:
- Add a predator(捕食者の追加) that the boids try to avoid that scatters the flock if it gets too close.
- Add a strong wind (強風など外部環境の追加)or current to see what effect it has on the flock.
- Add "perching" behavior(地上行動の追加). If a boid gets close to the bottom of the screen, have it land and hang out on the ground for a bit before taking off again and rejoining the flock.
- Make it 3D! (三次元映像化)The boids' velocity is currently represented as a 2D vector. You could change them to 3D vectors and update the vector math to work. To draw in 3D, you could just change the size of the boids to represent how far away they are."

 

という今後の発展の姿があるのですが、一番私の心に響いたのはCellと同じく「他種族による攻撃と捕食→異種族間関係<逃走と闘争>→『世界』内の異種族Boidsの存在==初歩的『生物群集」になるのではないか?という発想です。

 

とはいえ、

 

そんな「遠い先」のことを偉そうに言える状態ではないので、このシリーズのロードマップとしては、

 

【Task1】【Task2】

 

を先ずはやってみて、更に発展できるか(またはその前に私がお陀仏になるか?)

 

気長に続けて、様子を見よう

 

と思っています。

 

↑を作ってやろうと、予め生協で買ったナムルを使って「石焼きビビンバ-oid」である「パン焼きビビンバ」を作ってみました。

 

 

先ずご飯を炊いたら、フライパンを熱して胡麻油を敷いてからご飯を底に敷き詰めます。弱火でお焦げができる迄焼きます。(僕のように好きな方はひっくり返して両面を焼きましょう。)

 

「お焦げご飯」を丼に入れ、買っておいたナムル(ゼンマイ、モヤシ、人参、ホウレン草etc)に加え、

 

(1) 予め焼肉のタレで味付けした牛(または豚)挽肉と長ネギ(玉葱でもよいです)の微塵切りの胡麻油炒め

(2) 炒り卵

(3) キムチ(できれば「鶴橋」がベター)

 

をご飯に載せ、

 

(4) 韓国海苔(なければ焼きのり)をちぎったもの

(5) 磨り胡麻

 

を塗(まぶ)して出来上がり、です。

 

ビビンバ」は「(ビビンメンの)ビビン(混ぜる)」+「(キンパの)(ブ-ご飯)」で「混ぜご飯」を意味するので、食べる前にこいつらをしっかりと混ぜてください。そうそう

 

コチジャンは絶対にマスト!

)スーパーのドドメ色のコチジャンは好きになれません。私は真っ赤なコレ↓。

 

 

こういう状態になったら完成です。

 

本格的な食感の「お焦げご飯ビビンバ」ができました。お勧めです。

 

植物の「過密と過疎」環境に対する生存行動をシミュレートするライフゲームから、移動する偽生物(Cell)に発展させたCells for C#を何とか完結しましたが、実行テストを行っていてC++版の時代から感じていた「何か足りないんじゃないの?」というような違和感を再度感じました。

 

何かが足りない?...何が足りない!

 

今朝、(またまた)寝床の中でそれは

 

群れ

 

じゃないかと、気が付きました。そしてその

 

原因は私にある

 

事も併せて気が付きました。

 

私は元々ガキ大将で自分なりに社会性や社交性があると思っていたのですが、小学校の時から担任の女性教師に、クラスの他の女の子()と共に、教室の後ろに離れて座らされていました。その理由は、

:この子については、担任教師から「自閉症」(今でいうASD、自閉スペクトラム症)と説明を受けておりました。

 

勝手に発言したり、場合によっては行動するので他の児童の(教育の)為に良くない

「〇〇君、あなたは授業妨害をしているのよッ!」

 

という(キレられて言われました)ことでした。今でいえば、ADHD症状にある

  • 静かに座っていることができない
  • 絶え間なく喋り続ける
  • 無言で身体を動かさずにいることができない。
  • 無目的に喋り続ける
  • 他者の発言を遮って喋る
  • 自分の話す順番を待つことが出来ない

(近いっちゃー近かったのかな)なるのでしょう。まぁ、この「制裁」を食らった後は(少しの間)おとなしくするので、又クラスの「群れ」に戻るのですが...まぁ、昭和っていうのはこういうのが当たり前で私自身「当たり前」と感じていましたね。

 

いずれにしてもそんなこんなで、「右向け右の集団行動」が苦手で、現在も「同窓会や同期会といったあまりよく知りもしない)多数の集団でワイワイやる共同幻想状態の群れ」は苦手です。

 

そんな訳で、Cellの設計の中で「他者との関係論」を単純に「愛着と敵対の二元論」に纏めた結果、全てのCellは勝手にウロウロと彷徨し、愛着性は交尾に至りますが結局家族も集団も形成せず、

 

一人勝手に生きて死ぬ

 

だけでした。組織の中で孤立し、出世もしなかった自分の今までの生きざまに酷似していますねぇ...)

 

じゃ、群れってなんだよ?

 

というのが次の問いになります。これについて先ずは「ある区域に生息する全生物を一つの集団とみなしたもの」である「生物群集」という概念が参考になります。(「個体群」は単体なので「全生物」とは別物です。)Cellsでは「異種族」だけの簡素化された群集でした。又これら群集の個体や個体群はCellでも取り上げた「競争(敵対捕食被食(死亡以降、寄生、共生(愛着」の関係性があり、「食物連鎖や食物網のような関係、あるいは物質循環やエネルギー循環」を実現しています。

【ご参考】

生命の階層
生態系 ecosystem
生物群集 community
個体群 population
個体 individual
器官 organ
組織 tissue
細胞 cell
細胞小器官 organelle
分子 molecule
その他
群体 colony
定数群体 coenobium

 

この「群れ」が人間様の話になるとそれは「複数の人間の空間的、目的的、心理的な集まり」である「集団」の概念になります。人間の群れも「規範約束慣習等、集団凝縮性集団圧力集団目標リーダーシップ集団規範」によって支配されます。そうなれば話は自ずと

 

政治、社会

 

に及び、「政治は数、数は力、力は金」という言葉を想起してしまいます。

 

政治は数、数は力、力は金」と言うのは元内閣総理大臣()田中角栄の言葉です。私は決して角栄礼賛者ではありませんが、彼の慧眼が現れている言葉だと思います。

ロッキード事件で受託収賄の罪を問われましたが、本人死亡により公訴は棄却(刑は未確定)されました。


この考えを私なりに解釈し、別の言葉に置き換えれば、

 

「社会的支配力()を得るには、群れを作り、それを率い()なければならず、それに成功すれば社会的権力を得ることができ()、その社会的権力は経済力()を生む(又はへ転換する)ことができる」

:彼は「政治」を学問的一般論としていったのではなく、志向性を持つ課題としての「政治的に成功するには」と言う問いとして言っています。

 

と言う「社会の原理(誰もが認めざるを得ない、経験的事実)を指しているのだと考えられます。

ここで重要なのは、この「群れ」と言う人や動物に備わる性向です。これを「社会性」と捉えることもできますが、

 

現実に自然界で弱い種は「群れ」を作ることにより数をとして生き残ってきた

 

ことを想起すれば、これは生存のための力であると考えざるを得ないでしょう。田中角栄の言葉を生態学的に言えば、

 

生態系は群れ、群れは力、力は食物連鎖の上位化

 

につながると考えられます。ここまで調べたり、考えたりして次に

 

じゃ、群れの原理ってなんだよ?

 

という所に流れてゆき、発見したのが

 

boids(Bird-oid)

 

という「ボイド(人工生命シミュレーションプログラム)((wikiでは「鳥もどき」と書かれていますが、私は勝手に「擬鳥」という語を作っちゃいました!)でした。

 

先ずは"Seeing is believing"ということで

 

これ

 

 

を見てください。次回から(自分自身これから何をしようか未だ定まっていないのですが...)この"boids(擬鳥)"をテーマにブログを書いてゆこうかと考えています。

 

さてさて、【食い物話】ばかり書いていましたが、そろそろこのシリーズを手仕舞するときのようです。今回は最終回として、コンパイルの仕方とテスト実施後の評価を書いてみます。

 

このプログラムは、先ずEmbarcadero  C++とBCCSkeltonベースの

【Cell】シリーズ始動...

から始まり、それをC#へ

【無駄話】取り敢えず...Cell for C#?

単純移植した後、C#用に仕様変更

【無駄話】思いつきとひらめき-Cells

してきては、ということで進めてきました。

 

出来上がりは、

 

BCCFormandBCCSkeltonパッケージのサンプル】

("C: ... \BCCForm_and_BCCSkelton\SampleBCCSkelton\MSCompAss\Debug\Samples\Cells\")

 

のようになっていて、(MSCompAssを使ってもよいのですが、使わなくても)"Cells.bat"を起動すれば自動的にコンパイルされ、Cell_World.dllとCells.exeが出来上がる仕組みになっています。

 

起動すると

 

 

全く変わりのない姿(only except the "Cell" icon on the top of the Window )で現れ、(特に「データを開く」で「世界」の地形図を変更しない限り)ゲーム開始」に進み、

 

 

Cellの数」、「異種族間婚姻許可」、「アバターで参加」という3つのオプションを選択してゲーム開始となります。「アバターで参加」を選択しない限り、C++版と全く同じ展開となり、ゲームは「過疎による全滅

 

 

か、過密による「人口爆発」という結果に終わります。

 

 

仮に「アバターで参加」を選択すると、

 

 

ゲームウィンドウの右隣に「アバターコントローラー」ダイアログが(Max3秒間)現れ、濃いピンクの「アバターセル」を周囲の環境に従って操ることができますが、に書いた通り、

 

このゲームで、俺は主役にはなれない!

直ぐに大衆に飲み込まれてしまう...

という現実を嫌っていう程、味あわされます。

 

まぁ、

 

ライフゲームに代表される環境型のシミュレーションゲームにアバターで参加してみる

 

という発想は面白かったのですが、ゲームの楽しさ、という点では

 

んんんん(爆)

 

という感じでした。悪しからず...

 

Cells for C#を途中でほっぽらかして、食い物話に走っている私ですが、今回も又やらさせていただきます。

 

 

これが何か???

 

私の場合、

 

「何か作る(創作活動)が楽しくて好き」

 

なんですが、子供の時からプログラミングの他、

 

図工(電子回路作成含む)

音楽

絵画

料理 etc

 

なんかが好きでしたが、音楽や絵画は早々に才能がないことで見切りをつけ、現在も続いているのが(手が汚れない)プログラミング料理なのかな、と思っています。

 

今回のテーマは、お昼に「冷やし中華風ソーメン(↑参照-上から時計回りに、「サニーレタス」、「錦糸卵」、「茹でモヤシ

、「炒めた豚肉、ソーセージと玉葱」)」を作る際に、

 

「そういえば韓国冷麺って、どんな味だっけ?」

 

という素朴な疑問を持ったことが始まりで、さっそくwebで「本格冷麺スープ(日本の焼肉屋で出す奴では満足できないので)」のレシピなどを調べてスープの試作品を作ってみました。とはいえ"Risk Management"観点から矢張り、

1. 普通のツユ(保険、ですな)-↑の写真で下のガラス器(ネギ、大葉、生姜)
2. 韓国冷麺ツユ(今回の試作品)-↑の写真で上のセラミック器

の両方を用意。も「普通においしく」食べましたが、大半は2で食べてしまいました。

 

2は調べたところ、牛肉出汁と鶏肉出汁のミクスらしく、代用品としてビーフコンソメ鶏ガラスープを使いました。このツユは冷製スープとしてとても美味しく、食べ終わった後に飲み干してしまいましたが、これが

 

冷麺のスープかというと自信ありません。(笑)

 

まぁ、楽しく調理、食事できたので良しとしましょう。

 

ps. 今回の課題として分かったことは、「先ず、(コロナ禍でここしばらく行っていないので忘れてしまった)本格冷麺のスープの味を思い出す」ことから始めないといけない、ということでした。

 

いつもは体重を気にして夜はしっかりと食べない神さんが、バイトから夕刻に帰ってくるので、久々に夕食の提案をしたら「いいね!」ということで、昨晩神さんが作ったミートソースと私の摘み用にしこんでいた豚肉を野菜と添えて、(これも80gだけ残っていた)ペンネを合体して、ペンネボロネーゼ+豚の生姜焼き+サラダにしました。ペンネは(乾燥重量)40g、豚肉は50gづつ

 

夫婦で分かち合って

 

の夕食。確かに取り合わせに

 

 

は付きますが、でも、結構ハーモニーがあって大変美味しかったです。(「?」ついでに箸で食べちゃいました。こうするとペンネを一つづつ摘まむのでゆっくりと食べられ、満腹感もいっぱいで一種の「ダイエット食」になりました!)

 

 

前回、AvatarControllerをやりながら、このダイアログ

 

 

【拡大図】

 

について全く説明しなかったことに気が付きました!(ゴメンナサイ!)以下に概要を説明します。

 

(1)このダイアログがModal Dialogで、Worldクラスインスタンスを親(thisで呼ぶ)として呼んでいます。

 

(2)しかし、このダイアログは「3秒の命」しかなく、(何もしなくても)3秒経つと自動的に閉じられます

 

(3)↑ご覧のように多くのコントロールがありますが、「選択する動作は一つだけ」(動作はカラープッシュボタンを押すことにより実行しますが、ボタンは一つしか押せません)ので、その「3秒の命」の中で、ひとつだけ選択します。(カラープッシュボタンを押すとダイアログが閉じられます。)敢えて何もしない場合には最下段の「パス」ボタンを押してください。

 

(4)具体的に言うと、「遭遇したCellへの対応」では「親交」を結んだり、「威嚇」、「闘争」や「逃走」したりできます。又、食べ物があれば「食べる(への対応)」ことができ、野原があれば「移動(対応)」することができます。

 

(5)しかし、「ボタンを押しても、できないことがあるということを判断するには3秒間では難しい」だろうということで、「できないことはできない(グループボックスの.Enabledプロパティがfalse」ようにしていますので()、「押せるボタンが、今出来ること」になります。(さすがにそれはボタン状態からわかるでしょうと...)

:↑のイメージでいえば「遭遇したCellへの対応」と「食物への対応」がDisable状態です。

 

(6)また、、「遭遇したCellへの対応」では「異種族間婚姻が許可されているか」「自分と相手の種族、愛着性、敵対性」の数値が表示されるので、「親交を結ぶか、威嚇するか、即戦うか、逃げるか」を判断する素地になるでしょう。

 

(7)「食物への対応」動作も対象が複数ある場合は選択ができることも考えたのですが、余りに面倒にすると「特に反応が劣る高齢者」にはキツイのでPC判断と同じ対処にしました。

 

(8)「移動対応」も移動できる方向だけボタンを押せるよう、特に反応が劣る高齢者に対して「親切設計」にしています。

 

このように、従来のC++版のように単に「世界とCellの行く末を神の視座で眺める」というゲームから、「ユーザー自身がアバターを使って世界に入ってプレイする」というのが今回のC#版の売りなんですが、実際にプレイして思ったことは、

 

このゲームで、俺は主役にはなれない!

 

という

 

厳しい現実

 

でした。

 

Cell数を絞ってプレイする場合でも、採り得る戦略は「ひたすら食べ物を追っかけて、他のセルから逃れる」か、「なるべく戦わないで親交を深めるか」しかなく、「威勢よく他のセルを切り結ぶ」なんてことは考えない方がよい(直ぐに死ぬ)という現実です。

 

これがCellの数を多くすると

 

直ぐに大衆に飲み込まれてしまう...

 

という「しがない一個人の悲しい人生」を

 

嫌っていう程、味あわされます

 

正に「人生ゲーム」。期待した効果とは異なりましたが、それはそれで

 

大成功!!!

 

ps. 今回のプログラミングで初めて遭遇したエラーについて備忘として書いておきます。

 

最初AvatarControllerクラスからWorldクラスのフィールドやメソッドを使おうと思い、Worldクラス中の「Class in Class」にしようかと考えて進めたのですが、それでコンパイルすると、

 

CS0038: 入れ子になっている型 'Cell_World.World.AbatarController' を経由して、外の型 'Cell_World.World' の静的でないメンバーにアクセスすることはできません

というエラーが生じます。これはクラスインスタンスがスタックエリアに作られるために「アドレスが実際の実行まで確定できない」ことから来る問題()と考えました。

:言語設計により、相対距離でアクセスできる場合もあるようですが...一方、クラスの静的メンバーであれば、アドレスが確定しているためにアクセスが可能です。

【ご参考】

 

念のためにChat-GPTに確認すると矢張り、

 

C#でこのエラー(CS0038)が発生するのは、インナークラス(入れ子の型)から、外側のクラスの「静的ではない(non-static)メンバー」に直接アクセスしようとしたためです。C#の入れ子クラスは、Javaのインナークラスとは異なり、自動的に外側のクラスのインスタンス(this)への参照を持ちません。

(例)
csharpnamespace Cell_World
{
    public class World
    {
        public int worldSize = 100; // 静的ではないメンバー(インスタンス変数)

        public class AbatarController // 入れ子になっている型
        {
            public void Move()
            {
                // エラーCS0038:外側の非静的メンバーに直接アクセスできない
                int size = worldSize; 
            }
        }
    }
}

 

ということで、「Class in Class」は役に立たないので止め、コンストラクターの引数でWorldインスタンスを参照渡し(C++であれば「ポインター渡し」ですね)することにしました。ちょっとどんくさいですが...ご参考まで。