オセロを作りながらマクロVBAを学ぼう№14
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第14回です。
一番強いPC6でも、まだまだそれほどの強さとは言えませんが、
初心者の人なら苦戦するでしょうし、私も油断すれば負けてしまうくらいの強さにはなっています。
それには、「ゲーム木」の考えを入れて、3手先以上を読めるようにしなければならないと思います。
ですが、そこまで強いソフトをつくる事が目的ではないので、強さとしては一応の完成とします。
ですが、自分で対戦していて、
ちょっと待て、その手は無しだー・・・
ということは、どうしてもあります。
本来は、勝負に「待った」などと言うものはないんですけど。
とはいえ、VBAに負けるのは、さすがに気分が悪い(笑)
「待った」機能を追加します。
1.自分が打つ→2.相手が打つ→3.自分が打つ→4.相手が打つ→「待った」
相手が打った手を見て、「待った」
この場合は、
3.自分が打つ→4.相手が打つ
この2手を戻したいという事になります。
3.自分が打つ
ここからやり直したいという事です。
しかしですよ、人vs人の場合は、
1.自分が打つ→2.相手が打つ→3.自分が打つ→「待った」
こういう事もあり得ます。
一度石を置いてしまったが、直後に、「待った」
この場合は、
3.自分が打つ
この1手だけを戻したいという事になります。
どっち(先後)の人が「待った」をしたか分かりませんし、それを区別する必要もないと思います。
つまり、人vs人なら、
1手だけ戻せば良いわけで、2手戻したいのであれば、2回「待った」をすればよいという事です。
さすがに、PCは「待った」しないので、「待った」は人の番の時だけです。
そうなると、PCの手も戻す必要があります、つまり2手戻すことになります。
人vs人の場合は、1手戻す
人vsPCの場合は、2手戻す
ということになります。
方法としては、以下のどちらかになるでしょう。
・打った手を棋譜として保存しておく
しかし、最大で60手になるので、それをシートとして保存するのは、どうも無理があります。
横に、a~h
縦に、1~8
この交点の位置を
f5
このように記載します。
そこで、この座標をシートに書き加えます。
シート保護解除とスクロールエリアの削除をしなければならないので、
以下のようなプロシージャーを用意しました。
Sub シート編集()
With Sheet1
.Unprotect
.ScrollArea = ""
End With
End Sub
これを実行すれば、
シート保護解除とスクロールエリアの削除が行われます。
茶色の枠部分に文字を入れてしまうと、盤面の端の判定に問題が出ます。
この中で、
If .Cells(r, c) = "" Then
判定終了の条件となっていますので、
一番隅に行ったときにも使われています。
そこで、この部分を
If .Cells(r, c) = "" Or .Cells(r, c) <> "●" Then
つまり、
セルが空白、または、"●"以外がでてきたら終了とします。
1行目の見出し文字は適当で構いません。
Sub 棋譜保存(ByVal ToRng As Range, ByVal myRng As Range)
Dim i As Long
Dim j As Long
Dim myRng2 As Range
With Worksheets("棋譜")
If ToRng = "" Then
i = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
.Cells(i, 1) = i - 1
.Cells(i, 2) = ToRng.Offset(ToRng.Row * -1 + 1) & ToRng.Offset(, ToRng.Column * -1 + 1)
If Sheet1.Range("手番石").Font.Color = Sheet1.Range("先番石").Font.Color Then
.Cells(i, 3) = "黒"
Else
.Cells(i, 3) = "白"
End If
For Each myRng2 In myRng
j = .Cells(i, .Columns.Count).End(xlToLeft).Column + 1
.Cells(i, j) = RangeTo棋譜(myRng2)
Next
Else
i = .Cells(.Rows.Count, 1).End(xlUp).Row
For Each myRng2 In myRng
j = .Cells(i, .Columns.Count).End(xlToLeft).Column + 1
.Cells(i, j) = RangeTo棋譜(myRng2)
Next
End If
End With
End Sub
Function RangeTo棋譜(ByVal myRng As Range) As String
RangeTo棋譜 = myRng.Offset(myRng.Row * -1 + 1) & _
myRng.Offset(, myRng.Column * -1 + 1)
End Function
RangeTo棋譜
シートのダブルクリックされたセル(Rangeオブジェクト)から、
棋譜用の記号(f5のような記号)に変換しています。
1行目とA列に座標の記号があることを前提としています。
ちょっとややこしいロジックになっていますが、
棋譜シートのD列より右側に、ひっくり返した石の座標も入れているためです。
手を戻すときに、
打った場所だけでは、ひっくり返した石を判定するのがとても大変なので、
ひっくり返した石の座標もいれておくようにしました。
これは、今から石を置く場所になりますので、
これが空白という事は、まだ石を置いていないということです。
つまり、新たな手が打たれたという事です。
この場合は、棋譜出力行を1行進めます。
逆に、Elseになるのは、
既に、石が置かれているという事で、
つまりそれは、その後に相手の石をひっくり返している最中ということです。
棋譜の今の行のd列以降に、ひっくり返す石の座標を入れています。
棋譜保存
これをCallします。
Function is置ける1方向(ByVal Target As Range, _
ByVal i As Long, _
ByVal j As Long, _
ByVal isTry As Boolean) As Boolean
Dim r As Long
Dim c As Long
Dim is置く石 As Boolean
Dim is相手石 As Boolean
Dim myRng As Range
Dim myRng2 As Range
'石を置くセルの行列位置
r = Target.Row
c = Target.Column
Set myRng = Nothing
With TargetSheet
'空白or置く石と同じ石が出てくるまで判定
Do
r = r + i
c = c + j
'空白(石が置かれていない)
If .Cells(r, c) = "" Or .Cells(r, c) <> "●" Then
Exit Do
End If
'自分の石が置かれている
If .Cells(r, c).Font.Color = 置く石.Font.Color Then
is置く石 = True
Exit Do
End If
'相手の石が置かれている
If .Cells(r, c).Font.Color <> 置く石.Font.Color Then
is相手石 = True
If myRng Is Nothing Then
Set myRng = .Cells(r, c)
Else
Set myRng = Union(myRng, .Cells(r, c))
End If
End If
Loop
End With
'自分の石が置かれていて終了した時、それまでに相手の石があるか
If is置く石 = True And is相手石 = True Then
is置ける1方向 = True
If Not isTry Then
Call 棋譜保存(Target, myRng)
Call 石を置く(Target, 置く石)
If Not isPCvsPC Then
Sleep 150
End If
For Each myRng2 In myRng
If Not isPCvsPC Then
Sleep 80
End If
Call 石を置く(myRng2, 置く石)
Next
End If
End If
End Function
Callのタイミングが難しいところですが、
先に作成した、棋譜保存は、ここで呼ぶことを考えて作成したものです。
石を置き、相手の石をひっくり返す。
これを実行する直前で、これらのセルが全て取得できた時点になります。
ただしこれは、石を置いてから8方向に対して行われるので、
先の、棋譜保存では、
If ToRng = "" Then
これで判断して、同じ1手としての棋譜かどうかを判断しています。
いよいよ、「待った」を実装します。
デザインモードの状態で、ボタンをダブルクリッで追加されます。
シート「オセロ」のシートモジュールに以下を追加します。
Private Sub btn9_Click()
If TargetSheet Is Nothing Then
Exit Sub
End If
TargetSheet.Unprotect
Call 待った
TargetSheet.Protect
End Sub
標準モジュールに以下を追加します。
Sub 待った()
Dim i As Long
Dim j As Long
Dim k As Long
Dim myRng As Range
With Worksheets("棋譜")
For k = 1 To 2
i = .Cells(.Rows.Count, 1).End(xlUp).Row
If i = 1 Then
Exit Sub
End If
If .Cells(i, 3) = "黒" Then
Set 置く石 = TargetSheet.Range("後番石")
Else
Set 置く石 = TargetSheet.Range("先番石")
End If
Set myRng = 棋譜ToRange(.Cells(i, 2))
myRng = ""
For j = 4 To .Cells(i, .Columns.Count).End(xlToLeft).Column
Set myRng = 棋譜ToRange(.Cells(i, j))
Call 石を置く(myRng, 置く石)
Next
.Rows(i).ClearContents
Call 手番交代
If Not isPC Then
Exit Sub
End If
Next
End With
End Sub
棋譜の最終行を使い、手を戻しています。
1手戻したら、その棋譜は消去しています。
If Not isPC Then
打つ番が人の場合は、1手戻して終了させています。
PCの場合は、
For k = 1 To 2
これで2回手を戻しています。
そして、「待った」ができるようになりました。
「待った」はしない方が良いですけどね。
1手1手進められるように、1手1手戻せるようにします。
ソフト作成としては、いよいよ総仕上げになります。
全体の目次
ここまでのサンプルファイルのダウンロード
新着記事NEW ・・・新着記事一覧を見る
TRIMRANGE関数(セル範囲をトリム:端の空白セルを除外)|エクセル入門(2024-08-30)
正規表現関数(REGEXTEST,REGEXREPLACE,REGEXEXTRACT)|エクセル入門(2024-07-02)
エクセルが起動しない、Excelが立ち上がらない|エクセル雑感(2024-04-11)
ブール型(Boolean)のis変数・フラグについて|VBA技術解説(2024-04-05)
テキストの内容によって図形を削除する|VBA技術解説(2024-04-02)
ExcelマクロVBA入門目次|エクセルの神髄(2024-03-20)
VBA10大躓きポイント(初心者が躓きやすいポイント)|VBA技術解説(2024-03-05)
テンキーのスクリーンキーボード作成|ユーザーフォーム入門(2024-02-26)
無効な前方参照か、コンパイルされていない種類への参照です。|エクセル雑感(2024-02-17)
初級脱出10問パック|VBA練習問題(2024-01-24)
アクセスランキング ・・・ ランキング一覧を見る
1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.繰り返し処理(For Next)|VBA入門
5.RangeとCellsの使い方|VBA入門
6.ブックを閉じる・保存(Close,Save,SaveAs)|VBA入門
7.セルのクリア(Clear,ClearContents)|VBA入門
8.メッセージボックス(MsgBox関数)|VBA入門
9.条件分岐(Select Case)|VBA入門
10.ブック・シートの選択(Select,Activate)|VBA入門
- ホーム
- マクロVBA応用編
- マクロVBAサンプル集
- オセロを作りながらマクロVBAを学ぼう№14
このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。