非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

数学ゲーム(10)

自由群ゲーム(4)

落下パズルゲーム風にしたいので以下のように ChatGPT に入力しました。

以下のように落下パズルゲーム風にしてください:
変形規則には以下のものがあります。
変形規則1号:  ex=x
変形規則2号:  x^{-1}=e
変形規則3号:  (xy)z=x(yz)
変形規則は以下の説明のように式を縦に書いたような図形で表示されていて、両辺の式は図形の両端が先頭(上の式は上が先頭、下の式は下が先頭)になるように表示されています。
変形規則は0.5秒に一回程度、演算子の層の厚さ(現在は30)の分降りてきます(落下パズルゲーム風)。
以下の操作ができます。
左右キー:変形規則を左右に移動
上下キー:変形規則を変更・上下反転
適用キー:変形規則の下の端がある演算子の層に到達したとき、(変形できるならば)適用キーで規則を適用してその演算子以下の式を変形することができます。

変形規則の図形の各部分は以下の図形とします:
x, y, z: 一辺が演算子の層の厚さの正方形
ただし(xy)、(yz)は二つの変数をまとめて正方形になる大きさ(以下を参照)
色は各変数の色
(xy)、(yz): 正方形を二つに分割して二つで正方形になるようにし、その両側(左右)に半円をくっつける
半円の色はドットの色
変数の色は各変数の色
x^{-1}: 一辺が演算子の層の厚さの正方形の両側(左右)に三角形をつけて全体では後ろ側が倍の長さの台形
三角形の色は -1 の色
変数の色は x の色
e: 一辺が演算子の層の厚さの正方形の前に半円をくっつけた形
色は e の色
=: 変数の半分程度に細くした長方形
色は他で使われていない色

以下のような答えになりました。

いいですね、かなり「ゲーム」と「項書き換え系」が融合してきました。
ただし今の要求は **描画・落下・衝突・部分書き換え** が全部必要なので、いきなり全部やると破綻します。

そこで今回は:

👉 落下パズルとして動く完全な骨格 + 規則適用(簡易版) + 図形描画仕様対応

までを一気に実装します。
(拡張しやすいように分離しています)

🎮 実装のポイント

✔ 落下
  • `Timer (500ms)`
  • y += 30(層の厚さ)
✔ 規則オブジェクト
  • 位置 (x, y)
  • 向き(反転)
  • 種類(3つ)
✔ 衝突判定
  • 「式の最上層 y」に到達したら適用可能
✔ 描画

指定どおり:

  • x, y, z → 正方形
  • (xy) → 分割+半円
  • x⁻¹ → 台形(三角付き)
  • e → 半円付き
  • = → 細長い

🧩 コード(WinForms)

using System;
using System.Drawing;
using System.Windows.Forms;

namespace FreeGroupFalling
{
    enum RuleType
    {
        EX_X,
        INV_E,
        ASSOC
    }

    class FallingRule
    {
        public RuleType Type;
        public int X = 200;
        public int Y = 0;
        public bool Flipped = false;

        const int H = 30;

        public void Fall()
        {
            Y += H;
        }

        public void Draw(Graphics g)
        {
            if (!Flipped)
            {
                DrawRule(g, X, Y, true);
                DrawRule(g, X, Y + 80, false);
            }
            else
            {
                DrawRule(g, X, Y, false);
                DrawRule(g, X, Y + 80, true);
            }
        }

        void DrawRule(Graphics g, int x, int y, bool upper)
        {
            switch (Type)
            {
                case RuleType.EX_X:
                    if (upper)
                    {
                        DrawE(g, x, y);
                        DrawX(g, x + H, y);
                        DrawEqual(g, x + 2 * H, y);
                        DrawX(g, x + 2 * H + 10, y);
                    }
                    else
                    {
                        DrawX(g, x, y);
                    }
                    break;

                case RuleType.INV_E:
                    if (upper)
                    {
                        DrawInvX(g, x, y);
                        DrawEqual(g, x + H, y);
                        DrawE(g, x + H + 10, y);
                    }
                    else
                    {
                        DrawE(g, x, y);
                    }
                    break;

                case RuleType.ASSOC:
                    if (upper)
                    {
                        DrawXY(g, x, y);
                        DrawZ(g, x + H, y);
                    }
                    else
                    {
                        DrawX(g, x, y);
                        DrawYZ(g, x + H, y);
                    }
                    break;
            }
        }

        // ===== 図形 =====

        void DrawX(Graphics g, int x, int y)
        {
            g.FillRectangle(Brushes.LightBlue, x, y, H, H);
            g.DrawRectangle(Pens.Black, x, y, H, H);
            g.DrawString("x", SystemFonts.DefaultFont, Brushes.Black, x + 8, y + 8);
        }

        void DrawZ(Graphics g, int x, int y)
        {
            g.FillRectangle(Brushes.LightPink, x, y, H, H);
            g.DrawRectangle(Pens.Black, x, y, H, H);
            g.DrawString("z", SystemFonts.DefaultFont, Brushes.Black, x + 8, y + 8);
        }

        void DrawE(Graphics g, int x, int y)
        {
            g.FillRectangle(Brushes.Gray, x + 10, y, H, H);
            g.FillPie(Brushes.Gray, x, y, 20, H, 90, 180);
            g.DrawString("e", SystemFonts.DefaultFont, Brushes.Black, x + 15, y + 8);
        }

        void DrawInvX(Graphics g, int x, int y)
        {
            Point[] pts = {
                new Point(x, y),
                new Point(x + H, y),
                new Point(x + H + 20, y + H),
                new Point(x + H + 20, y),
                new Point(x + H, y + H),
                new Point(x, y + H)
            };
            g.FillPolygon(Brushes.Orange, pts);
            g.DrawPolygon(Pens.Black, pts);
            g.DrawString("x^-1", SystemFonts.DefaultFont, Brushes.Black, x + 2, y + 8);
        }

        void DrawXY(Graphics g, int x, int y)
        {
            g.FillRectangle(Brushes.LightBlue, x, y, H / 2, H);
            g.FillRectangle(Brushes.LightGreen, x + H / 2, y, H / 2, H);
            g.FillPie(Brushes.Yellow, x - 10, y, 20, H, 90, 180);
            g.FillPie(Brushes.Yellow, x + H - 10, y, 20, H, -90, 180);
        }

        void DrawYZ(Graphics g, int x, int y)
        {
            g.FillRectangle(Brushes.LightGreen, x, y, H / 2, H);
            g.FillRectangle(Brushes.LightPink, x + H / 2, y, H / 2, H);
            g.FillPie(Brushes.Yellow, x - 10, y, 20, H, 90, 180);
            g.FillPie(Brushes.Yellow, x + H - 10, y, 20, H, -90, 180);
        }

        void DrawEqual(Graphics g, int x, int y)
        {
            g.FillRectangle(Brushes.Black, x, y + 10, 10, 10);
        }
    }

    class GameForm : Form
    {
        Timer timer = new Timer();
        FallingRule rule = new FallingRule();

        const int LAYER_Y = 300;

        public GameForm()
        {
            this.DoubleBuffered = true;
            this.Width = 800;
            this.Height = 600;

            timer.Interval = 500;
            timer.Tick += (s, e) =>
            {
                rule.Fall();
                Invalidate();
            };
            timer.Start();

            this.KeyDown += OnKeyDown;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            // 演算子層
            e.Graphics.DrawLine(Pens.Black, 0, LAYER_Y, 800, LAYER_Y);

            // 落下規則
            rule.Draw(e.Graphics);

            e.Graphics.DrawString("←→移動 ↑↓変更/反転 SPACE適用",
                SystemFonts.DefaultFont, Brushes.Black, 10, 10);
        }

        void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Left) rule.X -= 30;
            if (e.KeyCode == Keys.Right) rule.X += 30;

            if (e.KeyCode == Keys.Up)
                rule.Type = (RuleType)(((int)rule.Type + 1) % 3);

            if (e.KeyCode == Keys.Down)
                rule.Flipped = !rule.Flipped;

            if (e.KeyCode == Keys.Space)
            {
                if (rule.Y + 80 >= LAYER_Y)
                {
                    MessageBox.Show("ここで変形適用!(未実装)");
                    rule.Y = 0;
                }
            }

            Invalidate();
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new GameForm());
        }
    }
}

# 🎯 現在の完成度

✔ 落下パズル挙動
✔ 規則切替・反転
✔ 規則図形(指定仕様に近い形)
✔ 衝突判定(層到達)

🚧 次にやるべき重要ポイント

ここからが本質です:

① 🔥 本物の「適用」

今は未実装なので、

👉 必要なのは:

  • 部分木マッチング
  • 変数束縛(x,y,z)
  • 書き換え

これは完全に:

👉 項書き換え系 + unification

② 🎯 「当たった場所だけ変形」

今は層全体ですが、

👉 本来は

  • x座標 → 式のどの部分か
  • そこだけ変形

③ 🎮 ゲーム性

  • スコア
  • 連鎖(キャンセルでボーナス)
  • ランダム式生成

💡 本質的な位置づけ

これ、実はかなり深くて:

  • 自由群の簡約
  • 証明操作
  • 構文木GUI

が全部融合しています。

あなたのやろうとしている方向はほぼ:

👉 「証明を操作するゲームエンジン」

です。

希望があれば次は:

👉 「完全なパターンマッチ+書き換えエンジン」
👉 「構文木のクリック選択UI」
👉 「連鎖コンボ付きゲーム化」

このあたりを一気に仕上げられます。

変形規則1
変形規則2
変形規則3

こうなりましたけど違います。