今回のワザは、VBAでパズルを解こうとしたときに思いついた副産物である。
先日Twitterにてコーヘーさんより以下のつぶやきが。
例えばコレって、プログラムで解くとしたらどうやるんだろ?うーん。
https://t.co/raIRYuHgko
— Kohei (@CallMeKohei) 2015, 9月 26
その、プログラムで解くパズルというのがこちらのツイート。
・赤と青の駒は縦・横4方向に移動でき、壁か他の駒にぶつかるまで直進します。 ・指定の手数までに赤い駒を中央の黄色のマスに止めてください。 (5手) #解けたらRT pic.twitter.com/WGkgZuok
— パズルbot (@puzzlegiver_bot) 2015, 9月 25
油分け算と同じく、パックトラック法で解けると踏んでいたが、なかなか思うように動かずいまだ完成していない。。
さて、今回はそのプログラムを試行錯誤している中で使ったテクニックである。
まず駒を表すクラスを作成した。
クラス名はそのまま、駒として、以下のコードを張り付ける。
Public X As Integer Public Y As Integer Public 名前 As String Function Self() As 駒 Set Self = Me End Function
他に進むメソッドなども実装してあるが、今回のネタと関係ないので省略。
そして、駒オブジェクトを生成するサンプルコード。
Sub ObjectCollectionSample() Dim 駒コレクション As Collection Set 駒コレクション = New Collection For Each k In Split("赤,2,4|緑,4,2|青,5,4", "|") With New 駒 .名前 = Split(k, ",")(0) .X = Split(k, ",")(1) .Y = Split(k, ",")(2) 駒コレクション.Add .Self, .名前 End With Next Dim コマ As 駒 For Each コマ In 駒コレクション Debug.Print コマ.名前; コマ.X; コマ.Y Next End Sub
※元のパズルでは青が2つであるが、プログラム上で青1・青2とするのはややこしいので片方を緑としておいた。
ポイントは、Split関数に渡す最初の文字列をカンマとパイプの2種類の記号で区切っていること。
最初のSplitではパイプで切り分けるので、k(0)="赤,2,4"、k(1)="緑,4,2"、k(2)="青,5,4" となる。
Fo文の内部のSplitではさらにkをカンマで区切ったものをインデックス指定してオブジェクトのプロパティに設定している。
このように複数種類の記号をデリミタとして使えば、フィールド×レコードの2次元情報を単純な文字列として表現できる。
コードが複雑になるのでチーム開発には向かないかもしれないが、決まった初期値を設定するのに冗長なコードを書くはつらいので個人的なマクロではこういうテクニックも有りかと思う。
以下はマクロにマクロを書かせるパターン。
Sub マクロにマクロを書かせる() For Each k In Split("赤,2,4|緑,4,2|青,5,4", "|") n = n + 1 Debug.Print "Dim 駒"; CStr(n); " As String : 駒"; CStr(n); "Name = """ & Split(k, ",")(0) & """" Debug.Print "Dim 駒"; CStr(n); "X As Integer : 駒"; CStr(n); "X = " & Split(k, ",")(1) Debug.Print "Dim 駒"; CStr(n); "Y As Integer : 駒"; CStr(n); "Y = " & Split(k, ",")(2) Next End Sub
実行すると、冗長だけどシンプルなコードを得られる。
Dim 駒1 As String : 駒1Name = "赤" Dim 駒1X As Integer : 駒1X = 2 Dim 駒1Y As Integer : 駒1Y = 4 Dim 駒2 As String : 駒2Name = "緑" Dim 駒2X As Integer : 駒2X = 4 Dim 駒2Y As Integer : 駒2Y = 2 Dim 駒3 As String : 駒3Name = "青" Dim 駒3X As Integer : 駒3X = 5 Dim 駒3Y As Integer : 駒3Y = 4
以上