エラトステネスのふるいゲーム(5)
GUIの部分を作ってもらうため ChatGPT に入力してみました。
GUIの部分を以下のような仕様でC#のフォームとして作ってください:
X68000や小さい画面のデバイスで実行できるようにするため
- 作業場と道具箱は切り替え
- 説明やコマンドなどの文字はグラフィックス画面の上に重ねて表示
とします。
これを昔のアクションゲーム風にします。
移動キーには左キー・右キー・上キー・下キーがあります。
「プロトタイプ」の中で部品はプログラミング言語のコードのようになっていて、引数と戻り値を選択キーで選択することができます。
選択すると道具箱に切り替わります。
その引数または戻り値に使える部品だけが選択できるようになっていて、選択すると作業場に戻ります。
選択された部品はプログラミング言語として正しくなるように元の部品の上または下に挿入されます。(引数のとき上、戻り値のとき下のように)
移動キーの左キー・上キーで「プロトタイプ」の外に出ると上位を選択できます。(ここは少し変更)
そこで繰り返しの部品を選択すると引数の対応を指定できます。
これは一回の実行後の引数・戻り値を次の回の実行前の(別の)引数・戻り値に対応させるものです。
これによって「ユークリッドの互除法」のように引数を別の引数に引き継ぐことが実現できるようになります。
次の回は複数にすることができます。
これによって「クイックソート」のように次の回を分割することができるようになります。
以下のような答えになりました。これを改造してできるかどうか検討してみます。
了解です。ここまでの仕様を踏まえて、
👉 「昔のアクションゲーム風・低解像度対応・ビジュアルプログラミングGUI」
を WinFormsで動く最小実装として作ります。
(X68000風=低解像度・ダブルバッファ・キー操作中心)🎮 設計のポイント(今回実装する範囲)
✅ 実装する要素
- 画面モード切替
- WORKSPACE(作業場)
- TOOLBOX(道具箱)
- カーソル移動(矢印キー)
- 選択キー(Enter)
- 部品の簡易AST構造
- 引数・戻り値のスロット
- 部品挿入(上/下)
❗ 今回の割り切り
フル仕様は大きいので、まず:
- 単一プロトタイプ
- 単純なノード構造
- 1段階の接続
👉 「動く骨格」を作ります
🧩 データ構造
enum SlotType { None, Argument, Return, Parent } class Node { public string Name; public List<Node> Children = new(); public Node Parent; public Node(string name) { Name = name; } }🎮 メインフォーム
using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; public class GameForm : Form { enum Mode { Workspace, Toolbox } Mode currentMode = Mode.Workspace; Node root; Node cursorNode; int toolboxIndex = 0; List<Func<Node>> toolbox = new() { () => new Node("min(X)"), () => new Node("add(P, p)"), () => new Node("removeMultiples(X, p)"), () => new Node("while (...)"), () => new Node("for (...)"), () => new Node("if (...)") }; string overlayText = ""; public GameForm() { this.Width = 640; this.Height = 480; this.DoubleBuffered = true; this.BackColor = Color.Black; this.KeyPreview = true; root = new Node("p = min(X)"); cursorNode = root; this.KeyDown += OnKeyDown; } // 🎮 入力処理 void OnKeyDown(object sender, KeyEventArgs e) { if (currentMode == Mode.Workspace) { HandleWorkspaceInput(e); } else { HandleToolboxInput(e); } Invalidate(); } void HandleWorkspaceInput(KeyEventArgs e) { if (e.KeyCode == Keys.Right) { if (cursorNode.Children.Count > 0) cursorNode = cursorNode.Children[0]; } else if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Up) { if (cursorNode.Parent != null) cursorNode = cursorNode.Parent; } else if (e.KeyCode == Keys.Down) { if (cursorNode.Children.Count > 0) cursorNode = cursorNode.Children[0]; } else if (e.KeyCode == Keys.Enter) { // 部品選択 → 道具箱へ currentMode = Mode.Toolbox; overlayText = "Select part"; } } void HandleToolboxInput(KeyEventArgs e) { if (e.KeyCode == Keys.Up) { toolboxIndex = (toolboxIndex - 1 + toolbox.Count) % toolbox.Count; } else if (e.KeyCode == Keys.Down) { toolboxIndex = (toolboxIndex + 1) % toolbox.Count; } else if (e.KeyCode == Keys.Enter) { InsertNode(toolbox[toolboxIndex]()); currentMode = Mode.Workspace; } else if (e.KeyCode == Keys.Escape) { currentMode = Mode.Workspace; } } // 🧩 ノード挿入(戻り値→下に接続) void InsertNode(Node newNode) { newNode.Parent = cursorNode; cursorNode.Children.Add(newNode); overlayText = $"Attached: {newNode.Name}"; } // 🎨 描画 protected override void OnPaint(PaintEventArgs e) { var g = e.Graphics; if (currentMode == Mode.Workspace) DrawWorkspace(g); else DrawToolbox(g); DrawOverlay(g); } // 🧱 作業場描画 void DrawWorkspace(Graphics g) { g.DrawString("WORKSPACE", Font, Brushes.White, 10, 10); DrawNode(g, root, 50, 50, 0); } void DrawNode(Graphics g, Node node, int x, int y, int depth) { Brush brush = node == cursorNode ? Brushes.Yellow : Brushes.White; g.DrawString(node.Name, Font, brush, x + depth * 20, y); int offsetY = 20; foreach (var child in node.Children) { DrawNode(g, child, x, y + offsetY, depth + 1); offsetY += 20; } } // 🧰 道具箱描画 void DrawToolbox(Graphics g) { g.DrawString("TOOLBOX", Font, Brushes.White, 10, 10); for (int i = 0; i < toolbox.Count; i++) { Brush brush = (i == toolboxIndex) ? Brushes.Yellow : Brushes.White; g.DrawString(toolbox[i]().Name, Font, brush, 50, 50 + i * 20); } } // 🧾 オーバーレイ void DrawOverlay(Graphics g) { g.DrawString(overlayText, Font, Brushes.Lime, 10, 400); } }▶ 実行コード
using System; using System.Windows.Forms; class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.Run(new GameForm()); } }🎮 操作方法
キー 動作 ↑↓ 移動 ← 親へ → 子へ Enter 選択 Esc 戻る 🧠 このGUIで実現できていること
✔ アクションゲーム風
- カーソル移動
- リアルタイム描画
- 即時反応
✔ ビジュアルプログラミング
- ノード接続
- 構文木編集
✔ 低スペック対応
- WinForms
- 単純描画
- 軽量
🚀 次にやるべき拡張(重要)
ここからが本番です:
## ① 🔥 引数スロットを明確化
class Slot { public string Name; public Type ExpectedType; public Node Connected; }② 🔥 上/下挿入の厳密化
InsertAbove(node) InsertBelow(node) InsertIntoArgument(node)③ 🔥 ループの「対応付け」
あなたの重要アイデア:
次の回の引数 ← 前回の戻り値実装:
class LoopMapping { public string From; public string To; }④ 🔥 分岐ループ(クイックソート用)
List<Node> NextIterations;⑤ 🔥 推論表示(p8)
"17 is prime because not divisible by any smaller prime"🧠 本質的にすごい点
このGUIは:
👉 構文木エディタ + 証明システム + ゲーム
しかも:
- ユークリッド互除法 → 変数の引き継ぎ
- クイックソート → 分岐再帰
まで同じ仕組みで表現できる
👉 次にやるなら
希望があれば:
- 🔥 引数スロット付き完全版
- 🎮 アニメーション(ノードが落ちる・光る)
- 🧠 推論エンジン完全実装(p1〜p8)
まで一気に作れます。



