HOME   

 

 

詰将棋で勉強するプログラミング

 

 

ExcelVBAを使い詰将棋を解くプログラムを作成しました。詰将棋というのは、やるべきことが明確でかつかなり複雑です。プログラミングの教材として最適ではないかと思います。ExcelVBAは駒の動きや効き範囲、攻め手、受け手を図、表で表示することが容易で、楽しくプログラミングできます。また、PythonJavaScriptなど他の言語とも命令が類似しており、プログラミングの考え方を理解するには十分です。詰将棋の全部の機能を勉強するのは大変ですのでここでは、詰将棋のある問題を解くプログラムの製作方法を解説致します。使用する駒の種類も少なく、一連の処理について説明できると思います。ここではプログラムの詳細の解説を省きますが、何をするものなのか「問い」として明確にしており、「答え」としてプログラムを載せました。また勉強用プログラムをダウンロードすることで実際に動かすことができ、理解しやすくなると思います。プログラム未経験の方でも勉強すれば分かるようになると思います。論理的思考の習得や、リタイア後の趣味にいかがでしょうか。

 

勉強用プログラムのダウンロード

 

 

今回勉強する詰将棋 5手詰 

将棋タウンより https://www.shogitown.com/tume/easy/easy5-01.html

 

 

プログラミングの基本

 

アマチュアがプログラムを作ると、だらだら長いものになって、どこで何をやっているのかわからないものが出来上がります。プログラムで最も大事なのは、機能を決めて、小さなプログラムを作ることです。単純な機能、意味がわかる機能で入力、出力を明確にします。この小さなプログラムを組み合わせることで最終的な機能が出来上がります。小さなプログラムで結果が目に見えるものですと、作っていて楽しく作業を進められます。

 

 

1 将棋盤の表示

 

 

プロでしたら、検討し、設計し、製作するのが通常ですが、アマチュアは思いついたものから順に作り、動かしていきます。

当然失敗して、作り直しになることもありますが、作って、動かすのは楽しいものです。小さな機能を決めて、ひとつずつ作っていきます。

詰将棋の機能は複雑で、作る前に設計するのは非常にたいへんですが、少しずつ作って動作確認していくと、何を作ればいいのかがだんだん見えてきます。

 

まず、将棋盤に駒を並べる必要があります。

2四の地点に「金」を表示するにはどうすればいいでしょうか

 

1-1

 

下図で黄色部のセルに「原点11」という名前を付けました。4二の場所に玉方の駒として「金」と表示してください。また2三に攻め方の駒として「と」と表示してください。

 

 

答え

 

Sub 4二玉方()

'y=0 x=0 1一の地点とする。0から始まる方が作り易い

y = 1 '

x = 3 '

Set h = Range("原点1").Offset(1 + y, -(x + 1))

h.Value = ""

h.Orientation = 90 '書式を90度回転させる

h.Font.Name = "@MS ゴシック"  'フォントを90度回転させる

End Sub

         

Sub 2三攻め方()

'y=0 x=0 1一の地点とする。0から始まる方が作り易い

y = 2 '

x = 1 '

Set h = Range("原点1").Offset(1 + y, -(x + 1))

h.Value = ""

h.Orientation = xlVertical

h.Font.Name = "MS ゴシック"

End Sub

 

 

書式とフォントで90度ずつ回転させ、玉方の方向に文字を書くことができました。

この設定はセルに記憶されてしまいますので、攻め方の駒では通常の方向にもどす設定が必要になります。

 

今度は逆に、表示されている駒を、プログラムが扱える形に読み込みたいと思います。

どのように記憶するのがいいでしょうか。

これは、いろいろな方法があると思いますが、下記のように記憶したいと思います。

 

1-2

 

下図で黄色部のセルに「原点12」という名前を付けました。駒の配置を次のように配列ban()に読み込んでください。

1   4二の玉方の「金」 は ban(1,3)="1金金"

2   2三の攻め方「と」 は ban(2,1)="0金と"

ban(Y,X)は Yが縦方向 Xが横方向、今後プログラムを作りやすくするため、  1一の場所をY=0 X=0とします。

1つの駒を4文字で表現します。 1文字目は 攻め方0 玉方1  2文字目は 駒の機能 (とは金とおなじ機能) 3文字4文字目は駒の表示内容とします。

  "0金成香"   攻め方の 機能が「金」、表示が「成香」

玉の位置の情報はよく使うので、KY KXに出力します。

 

 

答え

 

Sub テスト12()

Dim BAN(8, 8)

Call 駒位置取り込み("原点12", BAN(), KY, KX)

MsgBox ("玉の位置 y=" & KY & " x=" & KX)

End Sub

 

Sub 駒位置取り込み(原点, BAN(), KY, KX)

''  ky kxは玉の位置

Set INS = ActiveSheet

Set GG = INS.Range(原点)

Erase BAN

For YY = 0 To 8

  For XX = 0 To 8

    Set a = GG.Offset(YY + 1, -(XX + 1))

    If a.Value <> "" Then  '駒がある

        fon = Mid(a.Font.Name, 1, 1) 'フォントの1文字目

        If fon = "@" Then = 1 Else = 0

        ''玉の場所を3行目に記入

        If = 1 And a.Value = "" Then

            KY = YY: KX = XX

        End If

        BAN(YY, XX) = & 成り変換(a.Value) & a.Value

    End If

  Next XX

Next YY

End Sub

 

Function 成り変換()

''成りごまの機能が金になるか

成り変換 =

If = "" Or = "成桂" Or = "成銀" Or = "成香" Then

  成り変換 = ""

 Else

  成り変換 =

End If

End Function

Sub 消去13()

Set GG = Range("原点13")

Set 盤範囲 = GG.Offset(1, -9).Resize(9, 9)

盤範囲.Value = ""

盤範囲.Interior.ColorIndex = xlNone '全体の色塗りを消す

End Sub

 

盤上に何が配置されているか順に呼んでいきます。( For yy=0 to 8   for xx=0 to 8 の部分)

gg.Offset(YY + 1, -(XX + 1))の部分はセルgg (原点)から縦の下方向にYY+1、横の左方向にXX+1ずらしたセルを意味します。

見つけたらフォント名の1文字目から、攻め方、玉方を判断します。

配列 ban()に記録していきます。

""  "成桂"  "成銀"  "成香" については 機能を「金」とし、記入します。この変換をFunction 成り変換()で実施しています。

 

次に読み込んだban()を下図に表示してください。読み込んだ図と同じ表示になれば、プログラムが動作したことになります。

 

1-3

 

下図で黄色部のセルに「原点13」という名前を付けました。

ban()に読み込まれた内容を表示してください。

前に作った Sub 4二玉方() Sub 2三攻め方()を応用します。

 

答え

 

Sub テスト13()

Dim BAN(8, 8)

Call 駒位置取り込み("原点12", BAN(), KY, KX)

Call 盤面全表示("原点13", BAN())

MsgBox ("玉の位置 y=" & KY & " x=" & KX)

End Sub

 

Sub 盤面全表示(原点, BAN())

Application.ScreenUpdating = False

Set OUS = ActiveSheet

Set GG = OUS.Range(原点)

Set 盤範囲 = GG.Offset(1 + OFY, -9 + OFX).Resize(9, 9)

盤範囲.ClearContents '値消去

盤範囲.Orientation = xlVertical  '通常の向き 攻め方

盤範囲.Font.Name = "MS ゴシック" '通常のフォント 攻め方

For YY = 0 To 8

  For XX = 0 To 8

    Set h = GG.Offset(YY + 1, -(XX + 1))

    = BAN(YY, XX)

    If <> "" Then

       h.Value = Mid(, 3, 2) '駒表示

       If Mid(, 1, 1) = "1" Then '玉方の駒 180度回転

          h.Orientation = 90

          h.Font.Name = "@MS ゴシック"

       End If

    End If

  Next XX

Next YY

End Sub

 

 

初めに盤面の全範囲を攻め方の標準の書式に変更します。

そのあと、玉方の駒の時に180度回転させる書式に変更しています。

 

テスト13を実行すると元の駒配置が再現されました。

 

 

次に、持ち駒の取込、表示も同じように作ります。

ただし、配列に出力するのではなく、文字列に出力してみます。

例 "銀銀桂"

 

1-4

 

下図で黄色部のセルに「原点14」という名前を付けました。

その下に持ち駒が最大9個表示されます。これを文字列TMCに出力してください。

 

 

答え

 

Sub 駒台置取り込み(原点, TMC)

TMC = ""

Set INS = ActiveSheet

Set GG = INS.Range(原点)

For YY = 0 To 8

    Set a = GG.Offset(YY + 1, 0)

    If a.Value <> "" Then  '駒がある

       TMC = TMC & a.Value

    End If

Next YY

End Sub

 

1-5

 

下図で黄色部のセルに「原点15」という名前を付けました。

持ち駒が 文字列TMCに入っています。これを駒台に表示して下さい。

例 TMC="銀銀桂"

 

答え

 

Sub テスト15()

Call 駒台置取り込み("原点14", TMC)

Call 駒台表示("原点15", TMC)

End Sub

 

Sub 駒台表示(原点, TMC)

Set OUS = ActiveSheet

Set GG = OUS.Range(原点)

Set 駒台範囲 = GG.Offset(1, 0).Resize(9, 1)

駒台範囲.ClearContents '値消去

For I = 1 To Len(TMC)

 GG.Offset(I, 0).Value = Mid(TMC, I, 1)

Next I

End Sub

 

テスト15を実行すると、うまく表示できました。

 

 

これで、詰将棋問題を記憶し、また表示することができるようになりました。

記憶した内容を元に、王手をかける方法を考えます。

 

2 王手をかける 金

 

どうなれば王手をかけることができるのでしょうか

下図で、玉の位置に、玉方として「金」を置いたときの効きを黄色に塗りました。

また、攻め方の「金」の効きを青色に塗りました。

 

 

王手をかけるとは、黄色と青を重ねることです。

黄色と青が重なった箇所()に金を移動させると王手となります。

 

 

 

下図で金は玉から Y=2 X=-1 離れています

王手とするには  上に1または、上に1左に1動かす。数式で書くと、DY=-1 DX=0 または  DY=-1 DX=1 動かせばよい

 

 

 

王手をかけるということをプログラムで表現すると次のようになります。

 

2-1

 

金が玉からY,X離れた位置にいます。どれだけ移動すれば王手となるか、移動量DY DXを求めて下さい

王手が出来ない場合は DX=""(空白) としてください。

(つまり、Y Xをインプットし DY DXを出力するプログラムを作る)

 

答え

 

Sub 金の王手A(y, x, DY, DX)

'玉からY,X はなれた金はDY,DX移動すると王手になる

DY = "": DX = ""

If y = -2 And x = 0 Then DY = 1: DX = 0

If y = -1 And x = -1 Then DY = 0: DX = 1

If y = -1 And x = -1 Then DY = 1: DX = 0

If y = -1 And x = 1 Then DY = 0: DX = -1

If y = -1 And x = 1 Then DY = 1: DX = 0

If y = 0 And x = -2 Then DY = 0: DX = 1

If y = 0 And x = 2 Then DY = 0: DX = -1

If y = 1 And x = -2 Then DY = -1: DX = 1

If y = 1 And x = -2 Then DY = 0: DX = 1

If y = 1 And x = 2 Then DY = -1: DX = -1

If y = 1 And x = 2 Then DY = 0: DX = -1

If y = 2 And x = -2 Then DY = -1: DX = 1

If y = 2 And x = -1 Then DY = -1: DX = 1

If y = 2 And x = -1 Then DY = -1: DX = 0

If y = 2 And x = 0 Then DY = -1: DX = 1

If y = 2 And x = 0 Then DY = -1: DX = 0

If y = 2 And x = 0 Then DY = -1: DX = -1

If y = 2 And x = 1 Then DY = -1: DX = 0

If y = 2 And x = 1 Then DY = -1: DX = -1

If y = 2 And x = 2 Then DY = -1: DX = -1

End Sub

 

 

別のうまいやり方があるかもしれませんが、ここでは、全パターン試してプログラムを自動で作ってしまう方法をやってみました。

簡単なシミュレーションでプログラムを自動作成する方法です。

(作り方は後で説明します)

だらたらしたプログラムですが、何をやっているのか明確で、処理速度もループで作るより早いものになります。

 

このプログラムが動作するか試してみます。

 

2-2

 

下図で黄色部のセルに「原点22」という名前を付けました。

金がどこに動けば王手となるか、色を塗って下さい。

駒の位置の取込は 前に作ったCall 駒位置取り込み("原点6", BAN(), KY, KX) を使用します。

 

 

答え

 

Sub テスト22()

Dim BAN(8, 8)

Call 駒位置取り込み("原点22", BAN(), KY, KX)  '駒を調べ BAN()に入れる 例 "0金金" "1金と"

Call 金の王手表示("原点22", BAN(), KY, KX)

End Sub

 

Sub 金の王手表示(原点, BAN(), KY, KX)

Set OUS = ActiveSheet

Set GG = OUS.Range(原点)

Set 盤範囲 = GG.Offset(1, -9).Resize(9, 9)

盤範囲.Interior.ColorIndex = xlNone '全体の色塗りを消す

 

''  ky kxは玉の位置

For YY = 0 To 8

  For XX = 0 To 8

    If Mid(BAN(YY, XX), 1, 2) = "0" Then '金が見つかった

        y = YY - KY

        x = XX - KX

        Call 金の王手A(y, x, Dy, Dx) '玉から Y X 離れた金は DY DX移動すると王手

        If Dx <> "" Then

           '移動する場所を求める

           YYA = YY + Dy

           XXA = XX + Dx

           GG.Offset(YYA + 1, -(XXA + 1)).Interior.Color = 65535  '移動すべき場所 色塗り

        End If

    End If

  Next XX

Next YY

End Sub

 

色を消す命令は.Interior.ColorIndex = xlNone 黄色をつける命令は.Interior.Color = 65535です。

(Excelの操作でマクロ自動記録をすると、いろいろな色でどのようにすればいいかが表示できます。)

 

テスト22を実行してみます。

 

 

1か所塗られていない点があります。6五の地点です。

金の玉に対する位置は、Y=+2 X=+1です

Sub 金の王手(y, x, DY, DX)では

If y = 2 And x = 1 Then DY = -1: DX = 0

If y = 2 And x = 1 Then DY = -1: DX = -1

となっており、確かに2か所王手になると指摘しています。

しかし、最初の DY = -1: DX = 0は後から実行されたDY = -1: DX = -1に消されてしまい、塗りつぶしが出来ない状況となっていました。

複数の結果を残すよう、プログラムを修正する必要があります。

複数の結果を残し、かつ、後でプログラムが利用しやすいような形とするよう検討します。

ちょっと複雑ですが今後のことも考え、下図の配列に記入することにします。

 

 

大きく、手ごとに記憶します。0手から始め、1手、2手と記憶します。

行方向には、複数の手の候補を並べます。

横方向には必要となる項目を並べます。

項目としては、駒(今まで使用している4文字で表現)、現在の位置YX、移動後の位置YX5項目となります。

例  1手目、0行の0項目  TE(1,0,0) "1玉玉"が入っている

 

先ほどの王手となる手をこの配列に登録してみましょう。

 

2-3

 

下図で黄色部のセルに「原点23」という名前を付けました。

金がどこに動けば王手となるか、配列TE()に出力して下さい。

配列TE(A,B,C) Aが何手目か、Bが何行目(手の候補)Cは 0のとき駒、14は現在のYX、移動後のYXとなります。

Sub 金の王手(y, x, DY, DX)を改良して製作して下さい。

 

 

答え

 

Sub 金の王手登録(BAN(), KY, KX, TE(), TEC())

''  ky kxは玉の位置

For YY = 0 To 8

  For XX = 0 To 8

    If Mid(BAN(YY, XX), 1, 2) = "0" Then '金が見つかった 2文字目は 金の機能

       Call 金の王手(BAN(YY, XX), BAN(), YY, XX, KY, KX, TE(), TEC())

    End If

  Next XX

Next YY

End Sub

 

Sub 金の王手(, BAN(), YY, XX, KY, KX, TE(), TEC())

y = YY - KY: x = XX - KX

If y = -2 And x = 0 Then Call 可否判断(, BAN(), YY, XX, 1, 0, TE(), TEC())

If y = -1 And x = -1 Then Call 可否判断(, BAN(), YY, XX, 0, 1, TE(), TEC())

If y = -1 And x = -1 Then Call 可否判断(, BAN(), YY, XX, 1, 0, TE(), TEC())

If y = -1 And x = 1 Then Call 可否判断(, BAN(), YY, XX, 0, -1, TE(), TEC())

If y = -1 And x = 1 Then Call 可否判断(, BAN(), YY, XX, 1, 0, TE(), TEC())

If y = 0 And x = -2 Then Call 可否判断(, BAN(), YY, XX, 0, 1, TE(), TEC())

If y = 0 And x = 2 Then Call 可否判断(, BAN(), YY, XX, 0, -1, TE(), TEC())

If y = 1 And x = -2 Then Call 可否判断(, BAN(), YY, XX, -1, 1, TE(), TEC())

If y = 1 And x = -2 Then Call 可否判断(, BAN(), YY, XX, 0, 1, TE(), TEC())

If y = 1 And x = 2 Then Call 可否判断(, BAN(), YY, XX, -1, -1, TE(), TEC())

If y = 1 And x = 2 Then Call 可否判断(, BAN(), YY, XX, 0, -1, TE(), TEC())

If y = 2 And x = -2 Then Call 可否判断(, BAN(), YY, XX, -1, 1, TE(), TEC())

If y = 2 And x = -1 Then Call 可否判断(, BAN(), YY, XX, -1, 1, TE(), TEC())

If y = 2 And x = -1 Then Call 可否判断(, BAN(), YY, XX, -1, 0, TE(), TEC())

If y = 2 And x = 0 Then Call 可否判断(, BAN(), YY, XX, -1, 1, TE(), TEC())

If y = 2 And x = 0 Then Call 可否判断(, BAN(), YY, XX, -1, 0, TE(), TEC())

If y = 2 And x = 0 Then Call 可否判断(, BAN(), YY, XX, -1, -1, TE(), TEC())

If y = 2 And x = 1 Then Call 可否判断(, BAN(), YY, XX, -1, 0, TE(), TEC())

If y = 2 And x = 1 Then Call 可否判断(, BAN(), YY, XX, -1, -1, TE(), TEC())

If y = 2 And x = 2 Then Call 可否判断(, BAN(), YY, XX, -1, -1, TE(), TEC())

End Sub

Sub 可否判断(, BAN(), YY, XX, Dy, Dx, TE(), TEC())

''  現在の位置 YY XX 移動する量 DY DX

'' 盤をはみ出していない  行先に自分のコマがいたら不可

ny = YY + Dy

NX = XX + Dx

If ny >= 0 And ny <= 8 And NX >= 0 And NX <= 8 Then

   a = BAN(ny, NX) '行先

   If Mid(a, 1, 1) <> "0" Then '自分の駒がいないこと

        TE(, TEC(), 0) =

        TE(, TEC(), 1) = YY: TE(, TEC(), 2) = XX

        TE(, TEC(), 3) = ny: TE(, TEC(), 4) = NX

        TEC() = TEC() + 1  '次の行に移る

   End If

End If

End Sub

 

Sub 金の王手で、

If y = 2 And x = 1 Then DY = -1: DX = 0  の部分を

If y = 2 And x = 1 Then Call 可否判断(, BAN(), YY, XX, -1, 0, te(), TEC())

に変更しました。

難しい処理をSUB 可否判断に任せました。

プログラム製作ではこのように難しい処理があったら、入力、出力をはっきり決めて、別のプログラムに任せるようにすると、頭が整理できます。

SUB 可否判断では、駒がその場所に進む事が出来るかどうかの判断、配列TE()への登録を実施します。

盤からはみ出たり、自分の駒がいる場所には移動できません。

何行まで登録したのかを記憶するため、配列 TEC()に登録件数を持ちます。

 

この処理結果を表示するプログラムを作ってみます。

 

2-4

 

配列 TE() 、登録件数TEC()の内容をExcelシート 「候補」に出力してください。1つの手で8列使用します。(登録するのは5)

 

答え

 

Sub 手シート出力(TE(), TEC())

'配列 TE()をシート候補に出力する 手ごとに8列確保する

Application.ScreenUpdating = False

Set INS = Sheets("候補")   '入力シート

 

INS.Range("$A4:$h" & INS.Rows.Count).Offset(, * 8).ClearContents 'シートを消す

 

If TEC() > 0 Then

    For I = 0 To TEC() - 1

       For J = 0 To 4

         INS.Range("$A4").Offset(I, * 8 + J).Value = TE(, I, J)

       Next J

    Next I

End If

End Sub

 

INS.Range("$A4").Offset(I, * 8 + J).Value の部分は複雑ですが、A4セルからI行下、手の8倍にJを加えた分、右に移動したセルを意味します。

それでは、どの様な王手がされるのか、見てみましょう

 

2-5

 

図で黄色部のセルに「原点25」という名前を付けました。

金がどこからどこに動けば王手となるか、配列TE()に出力し、Excelシート候補Aに出力して下さい。

手は0手目とします。

さらに、移動先の位置に色を塗ってください。

 

 

答え

 

Sub テスト25()

Dim BAN(8, 8), TE(20, 50, 4), TEC(50)

Call 駒位置取り込み("原点25", BAN(), KY, KX)  '駒を調べ BAN()に入れる 例 "0金金" "1金と"

Call 金の王手登録(BAN(), KY, KX, TE(), TEC())

Call 手シート出力(TE(), TEC())

Call 王手表示("原点25", TE(), TEC())

Sheets("候補").Select

End Sub

 

Sub 王手表示(原点, TE(), TEC())

Set OUS = ActiveSheet

Set GG = OUS.Range(原点)

Set 盤範囲 = GG.Offset(1, -9).Resize(9, 9)

盤範囲.Interior.ColorIndex = xlNone '全体の色塗りを消す

For I = 0 To TEC() - 1  'TEC()は次の登録行

       ny = TE(, I, 3) '移動先Y

       NX = TE(, I, 4) '移動先X

       GG.Offset(ny + 1, -(NX + 1)).Interior.Color = 65535  '移動すべき場所 色塗り

Next I

End Sub

 

テスト25を実行します。王手となる場所が正しく色塗りされました。

 

 

それでは、今回解く詰将棋では、どの様な王手がされるのか、見てみましょう

 

2-6

 

下図で黄色部のセルに「原点26」という名前を付けました。

と金がどこに動けば王手となるか、配列TE()に出力し、Excelシート候補Aに出力して下さい。王手となる場所に色を塗ってください。

 

 

答え

 

Sub テスト26()

Dim BAN(8, 8), TE(20, 50, 4), TEC(50)

Call 駒位置取り込み("原点26", BAN(), KY, KX)  '駒を調べ BAN()に入れる 例 "0金金" "1金と"

Call 金の王手登録(BAN(), KY, KX, TE(), TEC())

Call 手シート出力(TE(), TEC())

Call 王手表示("原点26", TE(), TEC())

End Sub

 

テスト26を実行します。

 

 

 

原点の位置を変えただけでうまく動作しました。

と金も "0金と"で登録しているため、金と同じに処理できます。

同じプログラムで違う動きをするのが面白く感じられます。

 

以上で盤上の金(と金)の位置を探し、王手をかける手を出力するプログラムが完成しました。

 

参考

王手するプログラムの自動作成

 

駒の玉に対する位置を元に、王手のために必要な移動量を求めるプログラムを自動作成する方法を説明致します。

駒の効きの範囲(玉方からみた)に色を塗っておきます。

 

銀の効きの範囲

 

銀を玉の周りに移動させ、銀の効き(攻方から見た)と黄色セルがぶつかったとき、銀の位置と移動量をシートに出力します。

 

 

その出力結果から、計算式を使ってプログラムを作成します。

計算式

="if y="&Y3&" and x="&Z3 &" then call 可否判断(, BAN(), YY, XX, "& AA3&","&AB3&", TE(), Tec())"

 

 

次回、飛車の王手、桂馬、金の打ち込みの王手を考えます。

 

 

西八王子教室にて、レベルに応じてやさしく、または詳しく教えております。興味をお持ちの方、ぜひ参加をお願い致します。

 

HOME