vm2gol v2 製作メモ(36) すべてのセルで繰り返し


前回までで、1つのセルについて 生存セル数をカウントして、 次世代の生死を決定して、 バッファ用の配列に書き込むところまでできました。

今回は、盤面のすべてのセルに対してそれを行うようにしましょう!! (ゴールが近づいてテンション上がってきた)


といっても単にループ回すだけです!!

まず、初期状態をグライダーにしましょう!!!

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -257,9 +257,11 @@
     , ["set", "y", 0]
 
       // 初期状態の設定
-    , ["call", "vram_set", "w", 1, 1, 1]
+    , ["call", "vram_set", "w", 1, 0, 1]
     , ["call", "vram_set", "w", 2, 1, 1]
-    , ["call", "vram_set", "w", 3, 1, 1]
+    , ["call", "vram_set", "w", 0, 2, 1]
+    , ["call", "vram_set", "w", 1, 2, 1]
+    , ["call", "vram_set", "w", 2, 2, 1]
 
     , ["var", "count"]

それから 「 生存セル数をカウントして、 次世代の生死を決定して、 バッファ領域に書き込む 」 部分をループで囲みます。

先に変数宣言をループで囲む範囲の外側に移動させて……

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -264,18 +264,18 @@
     , ["call", "vram_set", "w", 2, 2, 1]
 
     , ["var", "count"]
+      // 注目しているセルの現世代の生死
+    , ["var", "current_val"]
+      // 注目しているセルの次世代の生死
+    , ["var", "next_val"]
 
     , ["call_set", "count", ["count_alive", "w", "h", 2, 2]]
     , ["_cmt", "★ count_alive から戻った直後"]
 
     , ["_cmt", "★次世代の生死決定の直前"]
 
-      // 注目しているセルの現世代の生死
-    , ["var", "current_val"]
     , ["call_set", "current_val", ["vram_get", "w", "h", 2, 2]]
 
-      // 注目しているセルの次世代の生死
-    , ["var", "next_val"]
     , ["call_set", "next_val", ["calc_next_gen", "current_val", "count"]]
 
     , ["_cmt", "★次世代の生死決定の直後"]

ハードコーディングしていた座標を変数に置き換えて……

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -269,18 +269,18 @@
       // 注目しているセルの次世代の生死
     , ["var", "next_val"]
 
-    , ["call_set", "count", ["count_alive", "w", "h", 2, 2]]
+    , ["call_set", "count", ["count_alive", "w", "h", "x", "y"]]
     , ["_cmt", "★ count_alive から戻った直後"]
 
     , ["_cmt", "★次世代の生死決定の直前"]
 
-    , ["call_set", "current_val", ["vram_get", "w", "h", 2, 2]]
+    , ["call_set", "current_val", ["vram_get", "w", "h", "x", "y"]]
 
     , ["call_set", "next_val", ["calc_next_gen", "current_val", "count"]]
 
     , ["_cmt", "★次世代の生死決定の直後"]
 
-    , ["call", "vram_set_buf", "w", 2, 2, "next_val"]
+    , ["call", "vram_set_buf", "w", "x", "y", "next_val"]
     , ["_cmt", "★ vram_set_buf から戻った直後"]
     ]
   ]

二重ループで囲みます! (ループの中の部分のインデントは別のコミットで修正しました)

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -269,7 +269,12 @@
       // 注目しているセルの次世代の生死
     , ["var", "next_val"]
 
-    , ["call_set", "count", ["count_alive", "w", "h", "x", "y"]]
+    , ["while", ["neq", "y", "h"]
+      , [
+          ["set", "x", 0]
+        , ["while", ["neq", "x", "w"]
+          , [
+      ["call_set", "count", ["count_alive", "w", "h", "x", "y"]]
     , ["_cmt", "★ count_alive から戻った直後"]
 
     , ["_cmt", "★次世代の生死決定の直前"]
@@ -282,6 +287,13 @@
 
     , ["call", "vram_set_buf", "w", "x", "y", "next_val"]
     , ["_cmt", "★ vram_set_buf から戻った直後"]
+
+            , ["set", "x", ["+", "x", 1]]
+            ]
+          ]
+        , ["set", "y", ["+", "y", 1]]
+        ]
+      ]
     ]
   ]

どうじゃ?

$ ./run.sh gol.vgt.json 
vgcg.rb:410:in `block in codegen_stmts': Not yet implemented ("stmt_head") ("call_set") (RuntimeError)
    from vgcg.rb:394:in `each'
    from vgcg.rb:394:in `codegen_stmts'
    from vgcg.rb:93:in `codegen_while'
    from vgcg.rb:406:in `block in codegen_stmts'
    from vgcg.rb:394:in `each'
    from vgcg.rb:394:in `codegen_stmts'
    from vgcg.rb:93:in `codegen_while'
    from vgcg.rb:375:in `block in codegen_func_def'
    from vgcg.rb:356:in `each'
    from vgcg.rb:356:in `codegen_func_def'
    from vgcg.rb:398:in `block in codegen_stmts'
    from vgcg.rb:394:in `each'
    from vgcg.rb:394:in `codegen_stmts'
    from vgcg.rb:425:in `codegen'
    from vgcg.rb:437:in `<main>'

修正します! (codegen_func_def() からコピペ)

--- a/vgcg.rb
+++ b/vgcg.rb
@@ -398,6 +398,8 @@ def codegen_stmts(fn_arg_names, lvar_names, rest)
       alines += codegen_func_def(stmt_rest)
     when "call"
       alines += codegen_call(lvar_names, stmt_rest)
+    when "call_set"
+      alines += codegen_call_set(fn_arg_names, lvar_names, stmt_rest)
     when "set"
       alines += codegen_set(fn_arg_names, lvar_names, stmt_rest)
     when "case"

実行!!

================================
18992: reg_a(0) reg_b(1) reg_c(0) zf(0)
---- memory (main) ----
      00   ["call", 1116]
pc => 02   ["exit"]
      03 ["label", "to_vi"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "[bp+2]"]
      16   ["mult_ab"]
      17   ["cp", "reg_a", "[bp-1]"]
      20   ["sub_sp", 1]
      22   ["set_reg_a", "[bp-1]"]
      24   ["set_reg_b", "[bp+3]"]
      26   ["add_ab"]
      27   ["cp", "reg_a", "[bp-2]"]
      30   ["set_reg_a", "[bp-2]"]
      32   ["set_reg_b", "[bp+5]"]
---- memory (stack) ----
         41 0
         42 0
         43 5
         44 5
         45 5
         46 5
         47 49
         48 2
sp bp => 49 0
---- memory (vram) ----
.@... .....
..@.. @.@..
@@@.. .@...
..... .@...
..... .....
exit

グワーッ 謎! ナンデ??

(↓こうなるのが正しいのです)

---- memory (vram) ----
.@... .....
..@.. @.@..
@@@.. .@@..
..... .@...
..... .....

ふーむ。

おかしいのは (2,2) のときの動作なので、x, y を (2,2) に固定して コメントを追加したりして (このときにいじったコードは割愛します) がんばってデバッグして、

================================
905: reg_a(0) reg_b(0) reg_c(0) zf(1)
---- memory (main) ----
      1283   ["_cmt", "call_set~~count_alive"]
      1285   ["call", 451]
      1287   ["add_sp", 4]
      1289   ["cp", "reg_a", "[bp-5]"]
      1292   ["_cmt", "★~count_alive~から戻った直後"]
      1294   ["_cmt", "★次世代の生死決定の直前"]
      1296   ["push", "[bp-4]"]
      1298   ["push", "[bp-3]"]
      1300   ["push", "[bp-2]"]
      1302   ["push", "[bp-1]"]
      1304   ["_cmt", "call_set~~vram_get"]
      1306   ["call", 119]
      1308   ["add_sp", 4]
      1310   ["cp", "reg_a", "[bp-6]"]
pc => 1313   ["_cmt", "★~current_val~をセットした直後"]
      1315   ["push", "[bp-5]"]
      1317   ["push", "[bp-6]"]
      1319   ["_cmt", "call_set~~calc_next_gen"]
      1321   ["call", 279]
      1323   ["add_sp", 2]
      1325   ["cp", "reg_a", "[bp-7]"]
      1328   ["_cmt", "★次世代の生死決定の直後"]
      1330   ["push", "[bp-7]"]
      1332   ["push", "[bp-4]"]
      1334   ["push", "[bp-3]"]
      1336   ["push", "[bp-1]"]
      1338   ["_cmt", "call~~vram_set_buf"]
      1340   ["call", 84]
      1342   ["add_sp", 4]
---- memory (stack) ----
         32 0
         33 15
         34 47
         35 1308
         36 5
         37 5
         38 2
         39 2
sp    => 40 2
         41 0 ... current_val
         42 2 ... count
         43 2 ... y?
         44 2 ... x?
         45 5
         46 5
   bp => 47 49
         48 2
---- memory (vram) ----
.@... .....
..@.. .....
@@@.. .....
..... .....
..... .....

vram_get() で (2,2) の現世代の生死を取得して ローカル変数 current_val にセットした直後です。 最初の世代の (2,2) は生きているので current_val が 1 になっているべきなのに 0 になっています。

ということは vram_get() がおかしい? とさらに追いかけて……

, ["call_set", "current_val", ["vram_get", "w", "h", "x", "y"]]

ここがミスってました。 vram_get の引数は w, x, y が正しい (h は不要)。 いやー、プログラムが大きくなってきたのでデバッグが大変……。

前々回(第34回) のときに気づいて修正していればここで苦労しなくてよかったのでした。 動作確認大事。 あと、関数呼び出し時の引数チェックがあれば……とか考えてしまいますね。 )

というわけで修正。

-    , ["call_set", "current_val", ["vram_get", "w", "h", "x", "y"]]
+    , ["call_set", "current_val", ["vram_get", "w", "x", "y"]]

再度実行!

================================
18959: reg_a(0) reg_b(1) reg_c(0) zf(0)
---- memory (main) ----
      00   ["call", 1116]
pc => 02   ["exit"]
      03 ["label", "to_vi"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "[bp+2]"]
      16   ["mult_ab"]
      17   ["cp", "reg_a", "[bp-1]"]
      20   ["sub_sp", 1]
      22   ["set_reg_a", "[bp-1]"]
      24   ["set_reg_b", "[bp+3]"]
      26   ["add_ab"]
      27   ["cp", "reg_a", "[bp-2]"]
      30   ["set_reg_a", "[bp-2]"]
      32   ["set_reg_b", "[bp+5]"]
---- memory (stack) ----
         41 0
         42 0
         43 5
         44 5
         45 5
         46 5
         47 49
         48 2
sp bp => 49 0
---- memory (vram) ----
.@... .....
..@.. @.@..
@@@.. .@@..
..... .@...
..... .....
exit

やりました!!!! (最後まで全部実行した状態です)

総ステップ数がめちゃくちゃ増えましたね。 ステップ実行なしでも13秒くらいかかります。

次回でとうとう完成します!!!