前回のあらすじ。
ショートストーリー:「メモリの戦場」
主人公
佐藤涼(さとう りょう)は、東京で働くプログラマ。彼は日々の喧騒から逃れるように、深夜の静けさの中でコードに没頭するのが好きだった。ある夜、彼はデスクに座り、ノートパソコンの前でコーヒーをすすりながら、ふと不思議な考えに取り憑かれた。
「プログラムが意志を持ったらどうなるだろう?」
佐藤は長年、抽象的なアイデアを駆使してシステムを作り上げてきた。しかし、コード同士が戦い、競い合うようなプログラムを書いたことはなかった。そこで、彼は試しに小さなゲームを作ってみることにした。それは、二つのプログラムが同じメモリ領域を奪い合うシミュレーション。
彼はまず「ブループログラム」と「レッドプログラム」を書き始めた。ブループログラムはメモリを支配しようとし、次々と「2」を書き込んでいく。対してレッドプログラムは「1」をメモリに書き込み、ブルーに対抗しようとする。両者は互いに、メモリの領域を巡って絶え間なく戦う。
実行結果。(ちょっとだけ知的なゲームです。)
プログラムブルー は、メモリに 2 を順番に書き込み、さらに自身のコードをメモリ内にコピーしていく動作を行います。
プログラムレッド は、メモリに 1 を順番に書き込み続けます。
プログラムバトルゲームの実行マシンの仕様を以下に示します。このマシンは、2つの異なるプログラム(プログラムブルーとプログラムレッド)が共有メモリに対してアクセスし、指定された操作を実行する仕組みです。(とりあえずの完成。)
実行マシンの仕様
-
メモリ
メモリサイズ: 100バイト
メモリは100個のスロットを持ち、各スロットは整数値(0~255)のデータを格納できる。
初期値はすべて 0。
プログラムブルーとプログラムレッドは、このメモリに対して読み書きを行う。 -
レジスタ
各プログラムには1つのレジスタが与えられます。レジスタは1つの整数値を保持し、メモリへの読み込みや書き込みに使用されます。
レジスタの役割:
メモリからの読み込み (LOAD)、メモリへの書き込み (STORE) に利用。
INC によってポインタを操作する際、レジスタはメモリ位置の計算に使われます。 -
命令セット
以下は実行マシンでサポートされている命令の一覧です。
命令 説明
LOAD <値> 指定した値(メモリ内のアドレスまたは即値)をレジスタにロードします。
STORE レジスタの値をメモリの現在のアドレスに書き込みます。
INC メモリアドレスポインタを1増やします。アドレスが100に達したら、次は0に戻ります。
JUMP <ラベル> 指定したラベルにジャンプして、そこからプログラムを再開します。
COPY_START コピー開始アドレスを設定します。プログラムの増殖などに使用されます。
COPY メモリ内の指定範囲をコピーします。プログラム自体の増殖やデータの複製を行う際に使われます。
- プログラムの動作
各プログラムは独自の命令セットを使用して、メモリ上で動作します。
プログラムブルー: メモリに 2 を書き込み、次に自分のコードをメモリ上にコピーして増殖します。
プログラムレッド: メモリに 1 を書き込み続けます。
-
プログラムの実行
各プログラムは1命令ずつ実行され、交互に処理されます。
時間ステップ: プログラムブルーとプログラムレッドは、1秒ごとに1命令を実行します。時間が経過するにつれて、メモリの状態が変化します。
終了条件: バトルは停止ボタンが押されるまで実行され続けます。 -
メモリアクセスと競合
両プログラムは同じメモリ空間を共有します。これにより、メモリ上のデータが競合する可能性があります。
例: プログラムブルーが 2 を書き込んだ直後に、プログラムレッドが 1 に上書きする可能性がある。 -
インターフェースの構成
プログラムエディタ:
プログラムブルーおよびプログラムレッドのコードは、テキストエリアに編集可能な形で表示され、ユーザーがプログラムを修正できます。
メモリの表示:
メモリの状態は画面中央に表示され、リアルタイムで変化します。
実行制御:
バトル開始ボタンで両プログラムを実行し、停止ボタンで実行を中止できます。
この仕様に基づいて、プログラムの挙動や競合状態をシミュレートすることができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>プログラムバトルゲーム</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#game {
width: 1000px;
padding: 20px;
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
align-items: center;
}
.program {
width: 30%;
padding: 10px;
margin: 10px;
border: 1px solid #ccc;
background-color: #f9f9f9;
}
#blueProgram {
background-color: #e0f7fa;
}
#redProgram {
background-color: #ffebee;
}
#memory {
margin-top: 20px;
font-family: monospace;
background-color: #eee;
padding: 10px;
white-space: pre-wrap;
overflow-x: auto;
max-height: 200px;
width: 100%;
}
#programs {
display: flex;
justify-content: space-between;
width: 100%;
}
button {
margin-top: 20px;
}
textarea {
width: 100%;
height: 200px;
}
</style>
</head>
<body>
<div id="game">
<h1>プログラムバトルゲーム</h1>
<div id="programs">
<div class="program" id="blueProgram">
<h2>プログラムブルー</h2>
<textarea id="blueCode">START:
LOAD 2
STORE
INC
COPY_START
INC
JUMP COPY
COPY:
LOAD MEM[COPY_START]
STORE
INC
COPY_START = COPY_START + 1
JUMP COPY</textarea>
</div>
<div id="memory"></div>
<div class="program" id="redProgram">
<h2>プログラムレッド</h2>
<textarea id="redCode">START:
LOAD 1
STORE
INC
JUMP START</textarea>
</div>
</div>
<button onclick="runBattle()">バトル開始</button>
<button onclick="stopBattle()">バトル停止</button>
</div>
<script>
let memory = Array(100).fill(0); // メモリ(100番地)
let programBlue = {
name: "プログラムブルー",
register: 0,
pointer: 0, // 現在の命令ポインタ
code: document.getElementById("blueCode").value.split("\n"),
copyStart: 0
};
let programRed = {
name: "プログラムレッド",
register: 1, // レジスタの初期値
pointer: 0, // 現在の命令ポインタ
code: document.getElementById("redCode").value.split("\n")
};
function updateMemoryDisplay() {
// メモリ内容を表示
let memDisplay = '';
for (let i = 0; i < memory.length; i++) {
memDisplay += `メモリ ${i + 1}: ${memory[i]} `;
if ((i + 1) % 10 === 0) memDisplay += '\n';
}
document.getElementById("memory").textContent = memDisplay;
}
function updateProgramDisplay() {
// 各プログラムの状態を表示
document.getElementById("blueCode").value = programBlue.code.join("\n");
document.getElementById("redCode").value = programRed.code.join("\n");
}
function executeInstruction(program) {
let instruction = program.code[program.pointer]; // 現在の命令を取得
let parts = instruction.split(" "); // 命令を分割
switch (parts[0]) {
case "LOAD":
program.register = parseInt(parts[1]); // レジスタに値をロード
break;
case "STORE":
memory[program.pointer] = program.register; // メモリにレジスタの値を保存
break;
case "INC":
program.pointer = (program.pointer + 1) % memory.length; // ポインタをインクリメント
break;
case "COPY_START":
program.copyStart = program.pointer; // コピー開始位置をセット
break;
case "JUMP":
let targetLabel = parts[1];
program.pointer = findLabel(program, targetLabel); // ジャンプ先のラベルを探して移動
break;
}
program.pointer = (program.pointer + 1) % program.code.length; // 次の命令へ
}
function findLabel(program, label) {
for (let i = 0; i < program.code.length; i++) {
if (program.code[i].startsWith(label)) {
return i;
}
}
return 0; // ラベルが見つからない場合は最初に戻る
}
let battleInterval;
function runBattle() {
battleInterval = setInterval(() => {
// プログラムブルーを実行
executeInstruction(programBlue);
// プログラムレッドを実行
executeInstruction(programRed);
// メモリ表示を更新
updateMemoryDisplay();
}, 1000);
}
function stopBattle() {
clearInterval(battleInterval);
}
updateMemoryDisplay();
</script>
</body>
</html>