VBAで将棋をつくろう! その1
VBAで○○をつくろう。
第一弾はオセロでしたが
今回第二段は将棋です!!!
その1ではコードは書きません。
将棋を選んだ理由とどうやって将棋をつくるのかを
できるだけ日本語で書きたいと思います。
じつはこの工程こそがプログラミングの肝なんじゃないかとちょっと感じています。
- なぜ将棋?
それは将棋が数種類の駒を動かす種目だからです。
一文ですが要素が二つ。
まずは数種類の駒。
これは後にキャラクターになりますね。
そして駒を動かす。
これが将棋をつくるうえの肝かなと思っています。
一度にひとつのことしかできないコンピュータにどうやって「駒を動かす」をさせるかというと。
動かす駒を選ぶ。
動かす先を選ぶ。
というふたつの工程を踏むことで行わせます。
。。。
これが将棋のプログラミングにおけるほぼすべてなのかなーとか思ったり。
- どうやって将棋をつくるの?
将棋を一手指すことを分解して考えてみます。
動かす駒を選ぶ(盤上or持ち駒)
動かす先を選ぶ
そこに動かすことができるか調べる
可能な場合は動かす
動かした先に相手の駒があった場合は駒をとる
動かした先が相手の領域だった場合は成る(駒をひっくり返す)
6工程
ちなみにオセロだと。
置く場所を選ぶ
置くことができるか調べる
置いて条件にあったところをひっくり返す
3工程
将棋のほうが工程が多いですね。
プラスで。
ご存知のとおり将棋には駒が数種類あります。
それぞれの駒が指定された場所に移動できるかどうかを調べる処理はどうしてもいくつかの具体的なことを書かないと実装できません。
オセロの場合は「自分の石」と「相手の石」の二種類しかなかったので
駒の種類が増えた場合の対応の仕方を考えないといけませんね。
あー。
オセロをつくろうのどこかのページで書いたのですが。
それを作ることは簡単だがシンプルに作ることは難しい。
と思うんです(たぶんフレーズは違いますが同じ概念のことを書いたと思います)。
将棋の場合はとりあえず駒が40個なので
40個すべてについて処理を書いていけばつくることはできます。
ただ面倒だしシンプルじゃない。
先に行ってしまいますが。
現在の自分のスキルでは将棋をすっげぇシンプルに書くことはできないと思っています。
いかにシンプルにつくるかはもちろん攻めますが。
一度つくってから二週間もすればぜんぜん違う様式でもっとシンプルにつくれると思います。
でも一度つくらないとその成長は約束されないのでつくります。
んー。
なんか日本語っぽくなくなってきたのでこれくらいに。
- どうやってつくるか(もうちょっと詰めて)
上の章で書いた6工程それぞれをどうやって実装するのか。
もうちょっと詰めて書きます。
動かす駒を選ぶ(盤上or持ち駒)
fieldとmotigomaという領域を定義して
それらのうち自分が使用権を持つものがクリックされた場合に
selectという領域にその駒の名前を位置を書き込みます
動かす先を選ぶ
selectに情報が書き込まれている場合に発動します
新たにクリックされた場所を動かす先として認識します
具体的にいうとそのセルをtargetとします
そこに動かすことができるか調べる
selectとtargetからどの駒をどこからどこへ動かすつもりなのかがわかります
なのでそれが可能かどうかを調べます
ここが一番の難所かな
その駒が何かによって異なる処理を選びます
その処理でその駒をtargetへ動かすことができるか調べます
異なる処理。というところをもう少し突っ込みます。
ここをシンプルに書くことが僕がこのタイミングで将棋をつくる一番の理由です。
すべての駒に個別の処理を書く
→80通り(駒が40枚でそれぞれに表裏があるため)
自分の駒20枚の処理で相手の駒の処理もこなす
→40通り(80を2で割って)
王,飛車,龍,角,馬,金,銀,桂馬,香車,歩の処理を書いてそれぞれの駒の処理をこなす
→10通り
(飛車,龍,角,馬,香車),(王,金,銀,桂馬,歩)について()に入った駒はできるだけ同じ処理でこなす
ここだけちょっと説明。。。
()の前者は動線に駒があると動けない駒たち
後者は動線がない(1マスしか進めない)ものか動線に駒があっても関係のない駒たち
実は前者は条件が後者よりひとつ多いんです。
ただその条件を調べる方法がどれも似ている(と感じる)ので()にくくりました。
→2*5通り(ひとつ上のものよりシンプルなはず。。)
これでkomaは10種類でこの工程での分岐は2*5にまで絞れました。
この工程での分岐がもっと削れるんじゃないかと思うんですけど。
まぁ今の自分には思いつかないです。
あとはふるいを上手に設けて処理を簡略化するところで勝負します。
可能な場合は動かす
駒を動かせる場合にselectの情報からその位置にある駒を消して
targetに書き直します
動かした先に相手の駒があった場合は駒をとる
targetが相手の駒でありそこに自分の駒を動かせる場合は
targetに自分の駒を書き換える前に
相手の駒を自分のmotigoma領域に書きます
そしてtargetを書き換えます
動かした先が相手の領域だった場合は成る(駒をひっくり返す)
ここも難しい。
動かした先が相手の領域だった場合はその駒を書き換えるだけなので
上の工程がかけたならそのスキルで何とかできます。
問題はその先。
もしも成った駒が相手に取られた場合。
これは成る前の駒を相手に与えないといけませんよね。
これをどう実装するか。。。
ここは自分にとって面白い(つまり答えを知らない)のでいくつか案を出してみましょう。
えっと前提として駒はkomaという配列を使って表現するつもりです。
簡略化のために駒は「歩」「飛車」「王」の三種類だけとしましょう。
成る駒が2つと成らない駒が1つですね。
array関数を使う場合
一次元で詰め込む場合
(歩,金,飛車,龍,王,王)と詰める
koma(o),koma(2)など基本は偶数のみを扱う
成ったらkoma(0)をkoma(0+1)とする
とられたらkoma(0)はkoma(0)としてkoma(1)はkoma(1-1)とする
んー複雑そう。。
二次元で詰め込む場合
これはこの記事を書きながら思いつきました。
*1と詰める
基本はkoma(1,0),koma(2,0)を使う
成ったらkoma(1,1)にしてとられたらkoma(1,0)にする
んー。上よりはよいか??
シートを使う場合
これも今思いついたやつなのですがトリッキーに感じる。。。
あらかじめkomaシートに書いておく
1列目に駒の表面を2列目に駒の裏面を記入しておく
駒が成る場合にkomaシート参照してそれが一列目であった場合には二列目のものに書き換える
駒がとられた場合にkomaシートを参照してそれが二列目であった場合には一列目のものに書き換える
んー。
残念ながら結論をうまく日本語で表現することができないのですが
現時点では表面と特殊な裏面(龍と馬?)だけarrayで宣言して
成る場合と取られる場合にシート参照するのが良いかなと思います。
正直けっこう感覚で喋っているので実際に書いているうちに気持ちが変わるかもしれません。
- さいごに
はて。
ところどころ意味不明なことを書いている気がしますが。
とりあえずめちゃくちゃ疲れました。
でもプログラミングの道筋はほぼ見えました。
あとはいかにシンプルにするかだけ。
道筋を日本語で書く工程はめちゃくちゃ疲れるけどやはり大事な気がする。
なんだろ。
上位概念からのアクセスな感じがします。
おわり
*1:歩,金),(飛車,龍),(王,王