ExcelマクロVBAサンプル集
オセロを作りながらマクロVBAを学ぼう№11

ExcelマクロVBAの実用サンプル、エクセルVBA集と解説
最終更新日:2019-06-16

オセロを作りながらマクロVBAを学ぼう№11


ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第11回です。


前回の石を置く場所に重みを付けることで、
超々初心者なら勝てるかもしれないというレベルにはなりました。
ですが、
ある程度オセロをやった事のある人なら、まあ負けることはないでしょう

今回は、自分の打つ場所ではなく、
それに対する相手の応手を判断して、
相手が良い応手を打てないような場所に自分が打つようにします。

これには、かなり多くのVBAコードが必要になります。
今回は、かなり多くのマクロVBAコードになります。

自分が打った後に、相手の打てる場所を考察するためには、
自分の石を置いた後に、相手の打てる場所を探して判定することになります。
以下の機能を追加します。

・自分の石を置ける場所全てについて、以下を行う
 ・自分の石を置く
 ・手番交代
 ・相手の石の置ける場所全てについて、以下を行う
  ・置ける場所の数
  ・相手が置ける石の重み
  ・相手がひっくり返す数

なんとなく、何とかなりそうにも感じます。
しかし、ちょっと問題があります。
既に作成してあるプロシージャーでは、
・自分の石を置く
ここで、実際にシートのセルに石を置いてしまいます。
これでは、試行錯誤が出来ません。
つまり、シミュレーションしたいのに、実際に石を置いてしまっては困ります。

そこで、
架空として石を置いたことにして、次の処理に進みたいのです。
その為に必要なのは、
シートに実際に石を置くのではなく、あくまでシミュレーションとして石を置けるようにしなければなりません。

その為には、
シートの盤面を配列に入れて、配列上でシミュレーションできるようにします。

配列については、以下を参照してください。
第111回.静的配列
VBAを学習していくと必ず配列が出てきます。VBA記述自体は簡単なものですが正しく理解せずに使っている場合も見受けられます。ここでは、配列の概要から入り、VBAでの記述方法を解説していきます。配列とは 配列は、値を格納するために多くの区画を持つ1つの変数です、集合住宅やアパートにたとえられますが、
第112回.動的配列(ReDim)
VBAにおける配列の説明として最初に静的配列を解説しました、静的配列では要素数は宣言時点で決められていました。しかし、プログラミングをする上で、実行時点で要素数を決めたい場合や、実行途中で要素数を増減させたい場合が多く出てきます。実行時点で(実行途中で)要素数を増減できるの配列を動的配列と呼びます。
第113回.配列に関連する関数
マクロVBAで配列を使う上で、必要となるVBA関数がいくつかあります。より便利に配列を活用するために必須となるVBA関数、・LBound関数 ・UBound関数 ・Array関数 I・sArray関数 ・Join関数 ・Filter関数 以上のVBA関数を解説します。
第114回.セル範囲⇔配列
セル範囲をVariant型変数に入れる事で、配列を作成することができます。また、配列をセル範囲にまとめて出力する事も出来ます。これは、マクロVBAを高速処理したい時の必須テクニックになります、マクロの処理が遅い場合は、このテクニックが使えないか検討してください。

ここからは、配列を駆使したVBAになっていきます。


Workbook_OpenイベントにPCを追加


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"

    End With
    With .cmb2
      .Clear
      .AddItem "あなた"
      .AddItem "PC1"
      .AddItem "PC2"
      .AddItem "PC3"
      .AddItem "PC4"
      .AddItem "PC5"

    End With
    .Protect
  End With
End Sub



モジュールレベル変数として配列を定義
パプリック変数を定義します。
変数には、その変数をVBA内で使う事ができる範囲が決められています。マクロVBAでは変数の使える範囲を、適用範囲(スコープ)と言います。適用範囲とは、宣言した変数を使う事のできる範囲です。変数を宣言した場所と宣言方法によって、その変数を使える場所が違ってきます。

Option Explicit

Public arySim() As String
Public arySim2

Public Type tWait
  wait1 As Long
  wait2 As Long
  wait3 As Long
  wait4 As Long
  wait1n As Long
  wait2n As Long
  wait3n As Long
  wait4n As Long
  wait9n As Long
End Type

arySimは、シートの盤面を入れる配列です。
arySim2は、arySimをコピーして何度もシミュレーションする時に使います。

Public Type tWait
これは、ユーザー定義型(構造体)になります。
ユーザー定義型は、名前の通りユーザーが定義できるデータ型になります。普通の変数は、1つの値しか入れられませんが、ユーザー定義型は、複数の異なるデータ型を入れる事が出来ます。プログラミング言語での一般的な呼び方としては、構造体とも呼ばれます。
石の置ける場所ごとに、複数の項目が必要なので構造体として定義しました。
「次手候補」の中で使用します。



シートの盤面を配列に入れる
シートの盤面(8×8)を配列に入れます。



Sub 盤面配列化()
  Dim rng1 As Range
  Dim i As Long
  Dim j As Long
  Set rng1 = TargetSheet.Range("盤面")
  ReDim arySim(1 To 8, 1 To 8)
  For i = 1 To 8
    For j = 1 To 8
      If rng1.Cells(i, j).Value <> "" Then
        If rng1.Cells(i, j).Font.Color = 置く石.Font.Color Then
          arySim(i, j) = "1"
        Else
          arySim(i, j) = "2"
        End If
      End If
    Next
  Next
End Sub

その時点の
手番を"1"
相手を"2"
として、配列に入れています。



配列上で置ける場所の判定と石を置いて相手の石をひっくり返す
シート上で置ける場所の判定として、既に作成済の
is置ける全方向
is置ける1方向
これをを改造します。

Function is置ける全方向S(ByVal r As Long, _
            ByVal c As Long, _
            ByVal id As String, _
            Optional ByVal opt As String = "合計") As Long
  Dim i As Long
  Dim j As Long
  Dim cnt As Long
  Dim rtn As Long
  Select Case opt
    Case "合計"
      is置ける全方向S = 0
    Case "最大"
      is置ける全方向S = -999
      cnt = -999
    Case "最小"
      is置ける全方向S = 999
      cnt = 999
  End Select
  '既に石が置いてある場合は置けない
  If arySim2(r, c) <> "" Then
    Exit Function
  End If
  '8方向を順に判定
  For i = -1 To 1
    For j = -1 To 1
      rtn = is置ける1方向S(r, c, i, j, id, opt)
      Select Case opt
        Case "合計"
          cnt = cnt + rtn
        Case "最大"
          If cnt < rtn Then
            cnt = rtn
          End If
        Case "最小"
          If cnt > rtn Then
            cnt = rtn
          End If
      End Select
    Next
  Next
  is置ける全方向S = cnt
End Function

Function is置ける1方向S(ByVal r As Long, _
            ByVal c As Long, _
            ByVal i As Long, _
            ByVal j As Long, _
            ByVal id As String, _
            Optional ByVal opt As String = "合計") As Long
  Dim r2 As Long
  Dim c2 As Long
  Dim cnt As Long
  Dim MinMax As Long
  Dim rtn As Long
  
  is置ける1方向S = 0
  Select Case opt
    Case "合計"
    Case "最大"
      MinMax = -999
    Case "最小"
      MinMax = 999
  End Select
  r2 = r
  c2 = c
  If is置ける1方向S2(r, c, i, j, id) Then
    Do
      r2 = r2 + i
      c2 = c2 + j
      If r2 < LBound(arySim2, 1) Or r2 > UBound(arySim2, 1) Or _
        c2 < LBound(arySim2, 2) Or c2 > UBound(arySim2, 2) Then
        Exit Do
      End If
      '空白(石が置かれていない)
      If arySim2(r2, c2) = "" Then
        Exit Do
      End If
      '自分の石が置かれている
      If arySim2(r2, c2) = id Then
        Exit Do
      End If
      '相手の石が置かれている
      If arySim2(r2, c2) <> id Then
        If id = "1" Then
          arySim2(r2, c2) = id
        End If
        Select Case opt
          Case "合計"
            cnt = cnt + 1
          Case "最大"
            rtn = Worksheets("重み").Range(TargetSheet.Range("盤面").Cells(r2, c2).Address)
            If cnt < rtn Then
              cnt = rtn
            End If
          Case "最小"
            rtn = Worksheets("重み").Range(TargetSheet.Range("盤面").Cells(r2, c2).Address)
            If cnt > rtn Then
              cnt = rtn
            End If
        End Select
      End If
    Loop
    If id = "1" Then
      arySim2(r, c) = id
    End If
    is置ける1方向S = cnt
  End If
End Function

Function is置ける1方向S2(ByVal r As Long, _
            ByVal c As Long, _
            ByVal i As Long, _
            ByVal j As Long, _
            ByVal id As String) As Boolean
  Dim is置く石 As Boolean
  Dim is相手石 As Boolean
  Dim r2 As Long
  Dim c2 As Long
  Dim cnt As Long
  r2 = r
  c2 = c
  '空白or置く石と同じ石が出てくるまで判定
  Do
    r2 = r2 + i
    c2 = c2 + j
    If r2 < LBound(arySim2, 1) Or r2 > UBound(arySim2, 1) Or _
      c2 < LBound(arySim2, 2) Or c2 > UBound(arySim2, 2) Then
      Exit Do
    End If
    '空白(石が置かれていない
    If arySim2(r2, c2) = "" Then
      Exit Do
    End If
    '自分の石が置かれている
    If arySim2(r2, c2) = id Then
      is置く石 = True
      Exit Do
    End If
    '相手の石が置かれている
    If arySim2(r2, c2) <> id Then
      is相手石 = True
    End If
  Loop
    
  '自分の石が置かれていて終了した時、それまでに相手の石があるか
  If is置く石 = True And is相手石 = True Then
    is置ける1方向S2 = True
  End If
End Function

とても長いコードになっています。

is置ける全方向S
もとの、「is置ける全方向」これを配列用に改造しています。
Optional
Subプロシージャー、Functionプロシージャーにおける、引数リストの指定について説明します。引数は、呼び出し先のプロシージャーに渡すデータを指定するものです。Callステートメントでプロシージャーを呼び出すときに指定する引数を、呼び出される側のプロシージャーで受け取る記述についてのVBA記述の説明になります。
については、
プロシージャーの引数
Subプロシージャー、Functionプロシージャーにおける、引数リストの指定について説明します。引数は、呼び出し先のプロシージャーに渡すデータを指定するものです。Callステートメントでプロシージャーを呼び出すときに指定する引数を、呼び出される側のプロシージャーで受け取る記述についてのVBA記述の説明になります。
こちらを参考にしてください。
合計や最大、最小については、
相手の状態を判定する上で、
合計数、最大値、最小値
これらが都度かわるので、引数で指定できるようにしています。

is置ける1方向S
もとの、「is置ける1方向」これを配列用に改造しています。
「is置ける1方向」では、手番の石しか置きませんでしたが、
ここでのシミュレーションでは、
自分の石だけでなく、相手の石を置く場合もありますので、
置く石を、引数「id」で渡しています。

is置ける1方向S2
is置ける1方向Sからの派生で、石を置けるかどうかの判定だけをするものになっています。
ここは、プログラミングの都合でひつようになったもので、
ひつとにまとめることも出来ないことはなさそうですが、
ロジックが複雑にならないように、機能を分散させたということです。


上記のプロシージャーを使い、
自分の石を置けるその場所に、もし石を置いたら、
相手が置ける石ばどこか、それはどの程度の価値があるかを判断します。
とても長くなりますが、同じようなVBAコードが繰り返されます。
作成済と同様処理の場合、コピペして直していく作業をします。
長いコードでも、実際のタイピングはそんなに多くはないものです。



次手候補の改造と、相手の応手の個別判断
作成済の「次手候補」の改造と、
・置ける場所の数
・相手が置ける石の重み
・相手がひっくり返す数
これらのプロシージャーを追加します。

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) '相手がひっくり返す数
        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
    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
      End Select
      aryWait(ix).wait9n = aryWait(ix).wait1n + _
                  aryWait(ix).wait2n + _
                  aryWait(ix).wait3n + _
                  aryWait(ix).wait4n
      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

Function 次手シミュ1(ByVal myRng As Range) As Long
  次手シミュ1 = Worksheets("重み").Range(myRng.Address)
End Function

Function 次手シミュ2(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
  
  arySim2 = arySim
  Set rng1 = TargetSheet.Range("盤面")
  
  r = myRng.Row - rng1.Row + 1
  c = myRng.Column - rng1.Column + 1
  Call is置ける全方向S(r, c, "1")
  
  '相手が置ける場所をカウント
  For i = 1 To 8
    For j = 1 To 8
      If is置ける全方向S(i, j, "2") > 0 Then
        次手シミュ2 = 次手シミュ2 + 1
      End If
    Next
  Next
End Function

Function 次手シミュ3(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 curWait As Long
  
  次手シミュ3 = -999
  arySim2 = arySim
  Set rng1 = TargetSheet.Range("盤面")
  
  r = myRng.Row - rng1.Row + 1
  c = myRng.Column - rng1.Column + 1
  Call is置ける全方向S(r, c, "1")
  
  '相手が置ける場所の重みの最大値
  For i = 1 To 8
    For j = 1 To 8
      If is置ける全方向S(i, j, "2") > 0 Then
        curWait = Worksheets("重み").Range(TargetSheet.Range("盤面").Cells(i, j).Address)
        If 次手シミュ3 < curWait Then
          次手シミュ3 = curWait
        End If
      End If
    Next
  Next
  If 次手シミュ3 = -999 Then
    次手シミュ3 = 100
  End If
End Function

Function 次手シミュ4(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 cnt As Long
  
  arySim2 = arySim
  Set rng1 = TargetSheet.Range("盤面")
  
  r = myRng.Row - rng1.Row + 1
  c = myRng.Column - rng1.Column + 1
  Call is置ける全方向S(r, c, "1")
  
  '相手がひっくり返す数の最大値
  For i = 1 To 8
    For j = 1 To 8
      cnt = is置ける全方向S(i, j, "2")
      If 次手シミュ4 < cnt Then
        次手シミュ4 = cnt
      End If
    Next
  Next
End Function

次手候補
Dim aryWait() As tWait
これで構造体を使う変数を配列で定義しています。
石の置ける場所が複数になるので、それを配列として扱い、
5つの判定項目を構造体を使っています。
wait1~wait5は、4つの判定項目の結果を入れます。
wait1n~wait5nは、その4つの判定の最大値でポイントを付け、
合計ポイントをwait9nに入れています。
そして、合計ポイントwait9nの最大値の場所に石を打つようにしています。

次手シミュ1
もともと、次手候補のなかに記述されていたのですが、
以下で追加するプロシージャーと同列の意味を持つ機能なので、
同じように独立したプロシージャーにしました。
プログラミングでは、このような事は多々行われます。
1行だけでも、本体のプロシージャーに記述せずに、別のプロシージャーにするのは、
全体としての可読性や、今後の拡張性を考慮してのこちになります。
PCごとに、
aryWait
これに入れる数値を調整しています。
つまり、強さの調整です。
PC1が一番弱く、置ける場所に適当におきます。
PC5が一番強く、相手の応手を多角的に判断しています。
(のつもりで作成したということで、実際の強さは何とも・・・)

次手シミュ2
相手が置ける場所の数

次手シミュ3
相手が置ける石の重み

次手シミュ4
相手がひっくり返す数

次手シミュ3、次手シミュ4、次手シミュ5
これらは、かなり同じようなVBAコードとなっています。
全体の流れとしては、
・次手候補で呼んだ盤面配列化で作成された配列をコピー
・自分の石を置いてみる
・相手の石の状態を判断する
このような順で処理を書いています。
このようなロジックでは、戻り値が適正になっているかのテストが結構大変になります。
VBAコードを書くのは、何とかなったとして、それが正しく動作しているかの確認が大変になります。
つまり、デバッグ作業が重要になってきます。
VBA開発での効率的なコーディングと、プログラム作成では避けて通れないデバッグについて説明します。また、陥りやすい間違い、よくやってしまう間違い等も説明しておきます。効率的なコーディング プロシージャーの移動 コール先のSubプロシージャーやFunctionプロシージャーに移りたい場合があります。
イミディエイトウィンドウローカルウィンドウを上手に活用しなければなりません。



Workbook_Openイベントのコンボ設定を追加
Workbook_Openでコンボに値を追加していましたが、
「あなた」と「PC」だけでしたので、
PCの代わりに、PC1~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"
    End With
    With .cmb2
      .Clear
      .AddItem "あなた"
      .AddItem "PC1"
      .AddItem "PC2"
      .AddItem "PC3"
      .AddItem "PC4"
      .AddItem "PC5"
    End With
    .Protect
  End With
End Sub

PCの名称は、何でも構わないのですが、
「次手候補」の中の記述と一致させてください。



以上で完成になります。
とても長いVBAコードですが、1行1行のコード自体は基本的なVBAコードでしかありません。
頭の中で考えたことを、いかにVBAコードで表すかという事になります。

まずは、

・実現したい事を書き出す。
・それを実現するために必要な部品は書き出す
・その部品を作る為に必要な部品を書き出す
・・・繰り返し・・・

例えば、
やりたい事は、
相手が置ける場所をカウント
必要な部品は、
盤面の空いている場所全てで石を置けるかの判定が必要
その為には、
石を置けるかの判定が必要

このように順に掘り下げていきます。
「相手が置ける場所をカウント」ここで、どうしたらよいかと試行停止せずに、
それに必要なものは何か、何があればそれを実現できるのかを考えます。
「何があれば」・・・当然、この時点では存在しません。
ならば、それを作る為に必要なものは何かを考える。
ずーと掘り下げていくと、自分で作れるものに到達します。
そうしたら、掘り下げてきた道を、逆にたどりながら、一つずつ作っていくのです。

決して易しい道のりではありません。
試行錯誤とデバッグの繰り返しになります。
自身の考えをVBAコードにしていくのは、
手本がこの世に存在しないのです。
もちろん、記述方法や文法の参考書はありますが、
その考え方は、その人独自のものになります。
だからこそ、自分の考えをVBAコードで実現できた時の喜びはひとしおだと思います。


今回作成した、
PC5
これくらいですと、初心者は苦労すると思います。
それでも、ある程度やっている人なら、まだ負けることはないと思います。
とりあえず私は、よほど油断をして適当に打たない限り負けなかったです。

次回は、
PC対PCを何十回と対戦させる機能を実装します。
もちろん、結果の記録も必要になります。
実際に、どの程度強さに違いがあるのかを検証します。


№12へ続きます。
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第12回です。前回は、相手の応手を判定して、自分の置く場所を決められるようにしました。オセロソフトとしては、そこそこの強さになりました。

全体の目次
はじめに
ExcelマクロVBAでオセロ(リバーシ)を作っていきながら、マクロVBAを学んで行きましょう。目的は、マクロVBAの学習であり、思考を整理しVBAでプログラミングする学習です。従って、強いソフトを作ることが目的ではありませんので、最近流行のAIなんちゃら…なんていうのは考えるつもりはありません。
№1.シートの用意と標準モジュールの挿入
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第1回です。まずはシートを用意しなければなりません、このシートの作り方で、その後の手間が随分と変わってきますので、しっかりと作ります。とはいえ、まずは一般的な感じで作ってみます、今後必要に応じて追加・変更していきます。
№2.ブックを開いたときの処理と初期配置
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第2回です。前回でシートの準備と標準モジュールを挿入しましたので、今回からは、マクロVBAをどんどん書き足していきます。まずは、イベントプロシージャーを作っていきます。
№3.自分の石を置ける場所の判定の整理
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第3回です。いよいよ自分の石を置いて、相手の石をひっくり返す処理に進むのですが、その前に、そもそも自分の石を置ける場所はどこなのか…。クリックしたセルは、自分の石を置いて良いセルなのかの判定が必要です。
№4.自分の石を置ける場所の判定の実装
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第4回です。石を置ける場所の定義を、前回は文章で書きました、今回は、それをもとにマクロVBAのプログラミングをしていきます。考え方は決定しているので、後はVBAに翻訳(コーディング)していくだけです。
№5.シート機能を拡張して今後の準備
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第5回です。前回で石を置ける場所の判定が完成しましたので、これからは、ゲームとしての機能を一つずつ追加していきます。まずは、石を置ける場所の色を変更してわかりやすくしてみます。
№6.黒石白石を交互に打って相手の石をひっくり返す
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第6回です。いよいよ今回は、黒石白石を交互に打てるようにします。もちろん、相手の石を挟んでいる場所は、自分の石に取り替えます。
№7.パス確認、終局確認、石数取得
VBAでオセロ(リバーシ)を作っていきながらVBAを学ぶ第7回です。前回までで、黒石白石を交互に打つことができるようになりましたが、まだまた不都合な点があります。石を打つ場所がない時に、パスが出来ないから先に進まない… 全部石が埋まっても、何も変化がない… そもそも、どっちが勝っているのかもわからない… つまり、
№8.石を置ける場所の表示とアニメーション
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第8回です。前回までで大分ゲームらしくなってきました。そろそろ、PC対戦の機能を入れたいところですが、今回は、はPC対戦の機能を入れる前に、気になる細かい部分を変更しておきます。
№9.PC対戦の実装
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第9回です。前回までで人が打つのであれば不自由のない機能が実装できたと思います。さて、ここからはPC対戦の機能を入れていきます。
№10.置く場所に重みを付けて少しだけ強く
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第10回です。前回は、PCが自動で打つ機能を実装しました。強さはともかく、とにかくPCが勝手に売ってくれるようになりました。
№11.相手の応手を評価してさらに強く
VBAでオセロ(リバーシ)を作っていきながらVBAを学ぶ第11回です。前回の石を置く場所に重みを付けることで、超々初心者なら勝てるかもしれないというレベルにはなりました。ですが、ある程度オセロをやった事のある人なら、まあ負けることはないでしょう 今回は、自分の打つ場所ではなく、
№12.PC対PCの対戦で強さを確認
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第12回です。前回は、相手の応手を判定して、自分の置く場所を決められるようにしました。オセロソフトとしては、そこそこの強さになりました。
№13.パラメーターと重みを調整してさらに強く
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第12回です。前回は、PC1からPC5までの5段階の状態で、総当り対戦で強さを判定しましたが、PC5が最も強い事が確認出来ました。それでもPC5では、まだまだ、ある程度オセロをやった事のある人には勝てないレベルです。
№14.やはり「待った」が欲しい
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第14回です。前回で、6段階の強さのPCオセロが完成しました、一番強いPC6でも、まだまだそれほどの強さとは言えませんが、初心者の人なら苦戦するでしょうし、私も油断すれば負けてしまうくらいの強さにはなっています。
№15.棋譜で対局を再現
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第15回です。棋譜が扱えるようになり、「待った」の実装も出来ました。棋譜が扱えるようになったので、今回は、対局を再現できるようにします。
№16.これまでを振り返りつつ全体のまとめ
ExcelマクロVBAでオセロ(リバーシ)を作っていきながらマクロVBAを学ぶ第16回です。15回に渡って、オセロ作成をしてきました、マクロVBAコードはかなりの量になっています。今回は最終回として、これまでを振り返ってみます。


ここまでのサンプルファイルのダウンロード




新着記事NEW ・・・新着記事一覧を見る

VBAでのCSVの扱い方まとめ|VBAサンプル集(11月9日)
VBAにおける変数のメモリアドレスについて|VBA技術解説(11月8日)
空文字列の扱い方と処理速度について(""とvbNullString)|VBA技術解説(1月7日)
Errオブジェクトとユーザー定義エラー|VBA入門(11月5日)
シングルクォートの削除とコピー(PrefixCharacter)|VBA技術解説(11月4日)
ユーザー定義型の制限とクラスとの使い分け|VBA技術解説(11月3日)
クリップボードに2次元配列を作成してシートに貼り付ける|VBA技術解説(11月1日)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)|VBA技術解説(10月31日)
VBAクラスのAttributeについて(既定メンバーとFor Each)|VBA技術解説(10月19日)
VBAの用語について:ステートメントとは|VBA技術解説(10月16日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.RangeとCellsの使い方|ExcelマクロVBA入門
4.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
5.変数宣言のDimとデータ型|ExcelマクロVBA入門
6.繰り返し処理(For Next)|ExcelマクロVBA入門
7.マクロって何?VBAって何?|ExcelマクロVBA入門
8.ひらがな⇔カタカナの変換|エクセル基本操作
9.空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)|VBA技術解説
10.セルに文字を入れるとは(Range,Value)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
なお、掲載のVBAコードは自己責任で使ってください。万一データ破損等の損害が発生しても責任は負いません。




    このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
    本文下部へ