オセロを作りながらマクロVBAを学ぼう№13
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第12回です。
PC5が最も強い事が確認出来ました。
もう少し強くするために、以下の機能を追加します。
・自分がひっくり返す相手石の数。
・自分がひっくり返す相手石の場所の重み。
・辺で自分の石が全て連続になる場合は、それを優先する。
この機能を考えたのは、実際に自分でPC5を相手にしてやってみたときに、
あっ、そこに置いたんじゃ勝てないよ・・・
と思うような場所に置いてしまう事が多々ありました。
そのような手をを減らす為に、何を判定するかを考えた結果が上記の機能になります。
Private Sub Workbook_Open()
With Sheet1
.Select
.Unprotect
.Cells.Locked = True
.ScrollArea = "B2:I9"
With .cmb1
.Clear
.AddItem "あなた"
.AddItem "PC1"
.AddItem "PC2"
.AddItem "PC3"
.AddItem "PC4"
.AddItem "PC5"
.AddItem "PC6"
End With
With .cmb2
.Clear
.AddItem "あなた"
.AddItem "PC1"
.AddItem "PC2"
.AddItem "PC3"
.AddItem "PC4"
.AddItem "PC5"
.AddItem "PC6"
End With
.Protect
End With
End Sub
プロシージャーレベルのPublic変数を定義します。
Option Explicit
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public TargetSheet As Worksheet
Public 置く石 As Range
Public 相手石 As Range
Public isPCvsPC As Boolean
Public StopMsg As Boolean
Public sht重み As String
「対戦開始」
ここで、初期配置の直前あたりで、
sht重み = "重み"
このようにして、シート名を入れます。
後は、
Worksheets("重み")
↓
Worksheets(sht重み)
このように一括置換します。
Function 次手シミュ5(ByVal myRng As Range) As Long
Dim rng1 As Range
Dim r As Long
Dim c As Long
Dim cnt As Long
arySim2 = arySim
Set rng1 = TargetSheet.Range("盤面")
r = myRng.Row - rng1.Row + 1
c = myRng.Column - rng1.Column + 1
'自分がひっくり返す相手石の数
cnt = is置ける全方向S(r, c, "1")
次手シミュ5 = cnt
End Function
他の、次手シミュ○とほぼ同様の内容になっています。
このような判定であれば、コピペで簡単につくれるようになっています。
これを個別判定して回避するようにします。
Function getOwnWait(ByVal myRng As Range) As Long
Dim rng1 As Range
Dim i As Long
Dim j As Long
Dim r As Long
Dim c As Long
Dim rtn As Long
getOwnWait = 999
arySim2 = arySim
Set rng1 = TargetSheet.Range("盤面")
r = myRng.Row - rng1.Row + 1
c = myRng.Column - rng1.Column + 1
'自分がひっくり返す相手石の場所の重み
For i = 1 To 8
For j = 1 To 8
rtn = is置ける全方向S(i, j, "1", "最小")
If getOwnWait > rtn Then
getOwnWait = rtn
End If
Next
Next
End Function
次手シミュ○とほぼ同様の作りになっています。
プロシージャーを、次手シミュ○にしなかったのは、
判定に使うのが、角の取り合いのみにしか使和ないからです。
従って、実際には、その重みが-15の場合限定で使用します。
他の要素に置いて、特別な理由がない限り、辺は確保するようにします。
しかし、
この白石を取ったほうが良いのか、撮らない方が良いのかは、
他の部分との兼ね合いで、全体を判断しなければ分からないので、
ここでの追加機能では、取る判定にはしないようにします。
Function isSide(ByVal myRng As Range) As Boolean
Dim rng1 As Range
Dim i As Long
Dim j As Long
Dim r As Long
Dim c As Long
Dim flg As Integer
Dim cnt As Long
isSide = False
arySim2 = arySim
Set rng1 = TargetSheet.Range("盤面")
r = myRng.Row - rng1.Row + 1
c = myRng.Column - rng1.Column + 1
Call is置ける全方向S(r, c, "1")
If r = 1 And (c = 1 Or c = 8) Then Exit Function
If r = 8 And (c = 1 Or c = 8) Then Exit Function
If r <> 1 And r <> 8 And c <> 1 And c <> 8 Then Exit Function
cnt = 0
'辺で自分の石が全て連続になる場合は、それを優先する
Select Case True
Case r = 1 Or r = 8
flg = 0
For i = 1 To 8
If arySim2(r, i) = "" Then
If flg = 1 Then
flg = 2
End If
End If
If arySim2(r, i) <> "" Then
If flg = 2 Then
Exit Function
End If
If arySim2(r, i) = "2" Then
Exit Function
End If
flg = 1
cnt = cnt + 1
End If
Next
Case c = 1 Or c = 8
flg = 0
For i = 1 To 8
If arySim2(i, c) = "" Then
If flg = 1 Then
flg = 2
End If
End If
If arySim2(i, c) <> "" Then
If flg = 2 Then
Exit Function
End If
If arySim2(i, c) = "2" Then
Exit Function
End If
flg = 1
cnt = cnt + 1
End If
Next
End Select
If cnt > 1 Then
isSide = True
End If
End Function
完全に、新規ロジックになっています。
縦の辺と、横の辺で分かれています。
自分の石を置いて、相手の石をひっ繰返した後に、辺の状態を確認しています。
行方向と列方向で違いますので、Ifで分けています。
これで制御しています。
0:初期
1:何らかの石が出てきた
2:空白以外が出た後に、空白が出てきた。
相手の石が出てきたら、即終了しています。
結果として、辺の状態が自分の石だけになっていれば優先扱いするようにしています。
Function 次手候補() As Range
Dim ix As Long
Dim ix2 As Long
Dim myRng As Range
Dim aryRng() As Range
Dim aryRng2() As Range
Dim aryWait() As tWait
Dim maxWait(1 To 5) As Long
Dim maxWaitT As Long
Dim cnt As Long
Dim PcName As String
For ix = 1 To 5
maxWait(ix) = -999
Next
maxWaitT = -999
With TargetSheet
If .Range("手番石").Font.Color = .Range("先番石").Font.Color Then
PcName = Sheet1.cmb1.Text
Else
PcName = Sheet1.cmb2.Text
End If
cnt = WorksheetFunction.CountA(.Range("盤面"))
ix = 0
Call 盤面配列化
For Each myRng In .Range("盤面")
If is置ける全方向(myRng, True) Then
ReDim Preserve aryRng(ix)
ReDim Preserve aryWait(ix)
Set aryRng(ix) = myRng
aryWait(ix).wait1 = 次手シミュ1(myRng) '自分の置く石の重み
aryWait(ix).wait2 = 100 - 次手シミュ2(myRng) '相手が置ける場所数
aryWait(ix).wait3 = 100 - 次手シミュ3(myRng) '相手が置ける石の重み
aryWait(ix).wait4 = 100 - 次手シミュ4(myRng) '相手がひっくり返す数
aryWait(ix).wait5 = 次手シミュ5(myRng) '自分がひっくり返す相手石の数
If PcName = "PC6" Then
'辺で自分の石が全て連続になる場合は、それを優先する
If isSide(myRng) And aryWait(ix).wait1 <> -12 Then
aryWait(ix).wait1 = 1
End If
'自分がひっくり返す相手石の場所の重み
If aryWait(ix).wait1 <> 30 And getOwnWait(myRng) = -15 Then
aryWait(ix).wait1 = -15
End If
End If
ix = ix + 1
End If
Next
For ix = LBound(aryWait) To UBound(aryWait)
If maxWait(1) < aryWait(ix).wait1 Then maxWait(1) = aryWait(ix).wait1
If maxWait(2) < aryWait(ix).wait2 Then maxWait(2) = aryWait(ix).wait2
If maxWait(3) < aryWait(ix).wait3 Then maxWait(3) = aryWait(ix).wait3
If maxWait(4) < aryWait(ix).wait4 Then maxWait(4) = aryWait(ix).wait4
If maxWait(5) < aryWait(ix).wait5 Then maxWait(5) = aryWait(ix).wait5
Next
For ix = LBound(aryWait) To UBound(aryWait)
Select Case PcName
Case "PC1"
Case "PC2"
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
Case "PC3"
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
Case "PC4"
Select Case cnt
Case Is <= 32
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 1
Case Is <= 48
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 1
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 3
Case Is <= 52
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 4
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 2
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 4
Case Is <= 56
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 2
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 3
Case Else
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 2
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 4
End Select
Case "PC5"
Select Case cnt
Case Is <= 32
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 1
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
Case Is <= 48
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 1
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 3
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
Case Is <= 52
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 3
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 1
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 5
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
Case Is <= 56
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 1
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 5
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
Case Else
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 1
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 0
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 3
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 5
End Select
Case "PC6"
If cnt >= 27 Then
sht重み = "重み2"
End If
Select Case cnt
Case Is <= 32
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 1
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
If aryWait(ix).wait5 = maxWait(5) Then aryWait(ix).wait5n = 0
Case Is <= 48
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 5
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 1
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 3
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
If aryWait(ix).wait5 = maxWait(5) Then aryWait(ix).wait5n = 0
Case Is <= 52
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 3
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 1
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 5
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
If aryWait(ix).wait5 = maxWait(5) Then aryWait(ix).wait5n = 0
Case Is <= 56
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 1
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 3
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 5
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 0
If aryWait(ix).wait5 = maxWait(5) Then aryWait(ix).wait5n = 0
Case Else
If aryWait(ix).wait1 = maxWait(1) Then aryWait(ix).wait1n = 2
If aryWait(ix).wait2 = maxWait(2) Then aryWait(ix).wait2n = 2
If aryWait(ix).wait3 = maxWait(3) Then aryWait(ix).wait3n = 2
If aryWait(ix).wait4 = maxWait(4) Then aryWait(ix).wait4n = 5
If aryWait(ix).wait5 = maxWait(5) Then aryWait(ix).wait5n = 5
End Select
End Select
aryWait(ix).wait9n = aryWait(ix).wait1n + _
aryWait(ix).wait2n + _
aryWait(ix).wait3n + _
aryWait(ix).wait4n + _
aryWait(ix).wait5n
If maxWaitT < aryWait(ix).wait9n Then
maxWaitT = aryWait(ix).wait9n
End If
Next
ix2 = 0
For ix = 0 To UBound(aryRng)
If aryWait(ix).wait9n = maxWaitT Then
ReDim Preserve aryRng2(ix2)
Set aryRng2(ix2) = aryRng(ix)
ix2 = ix2 + 1
End If
Next
Randomize
ix = Int(Rnd * (UBound(aryRng2) + 1))
Set 次手候補 = aryRng2(ix)
End With
End Function
判定結果のポイント化においても、その比重を調整しています。
そして、新規に追加した機能も全て組み込んでいます。
上段が先手勝ち、下段が後手勝ちです。
PC6が完全に勝ちました。
もちろん、勝つように調整したのですけど。
私も油断すると負けてしまいます。
それでも、しっかり考えながら打てば、まだ負けません。
ですが、本当の初心者だと、とても勝てないレベルになっています。
中盤から終盤にかけては、
自分が打つ→相手が打つ→自分が打つ
このあたりまでは考えながら打ちます。
時には、さらに相手が打つときも少しは考えます。
自分が打つ→相手が打つ
ここまでしか判定していませんので、
私かじっくり考えながら打たば勝てるのは当然の結果だと思います。
それでも、判定結果をポイント化する時をいろいろシミューションしていけば、
もう少しは強くすることが出来るとは思います。
3手先以上を読まなければなりません。
それには、「ゲーム木」の考えを入れて、
3手先以上を読めるようにしなければならないと思います。
ですが、そこまでやって強くしても、本記事の主旨にはそぐわないし、
そもそも、強いソフトをつくる事が目的ならVBAじゃなくても良いわけですし。
それなりの強さにもなりました。
そう、「待った」をしたいですよね。
VBAに負けるのは、さすがに気分が悪い(笑)
全体の目次
ここまでのサンプルファイルのダウンロード
新着記事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.繰り返し処理(For Next)|VBA入門
4.変数宣言のDimとデータ型|VBA入門
5.RangeとCellsの使い方|VBA入門
6.ブックを閉じる・保存(Close,Save,SaveAs)|VBA入門
7.メッセージボックス(MsgBox関数)|VBA入門
8.セルのクリア(Clear,ClearContents)|VBA入門
9.ブック・シートの選択(Select,Activate)|VBA入門
10.条件分岐(Select Case)|VBA入門
- ホーム
- マクロVBA応用編
- マクロVBAサンプル集
- オセロを作りながらマクロVBAを学ぼう№13
このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。