Excelで五目並べを作った

こんにちは、Mottyです。最近業務でVBAを使う機会が増えてきたので、練習の一環として五目並べを作ってみました。

#0.はじめに
ExcelはVBAでプログラム操作が可能であり、マクロ機能と呼ばれております。マクロ機能は大量のデータの一括処理に向いており、うまく使えばデータの自動集計や集約操作など面倒な手作業を全自動/半自動でこなすことができます。また、マクロ機能を用いたゲーム等の報告例も多々あります。![ 2019-05-02 18.40.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/0dd87cce-f382-aca4-8d94-e0ffee2d5976.png)

Excelの開発タブを表示すると、左上にVBAを編集できる画面と、編集したコードをすぐに実行できるマクロ実行画面があります。

##0-1 セルのダブルクリックをトリガーとする機能がある
VBAには実行ボタンを押すと作動するプロジェクトの他に、特定の動作をトリガーにSubプロシージャーを作動させることはできます。トリガー機能のうちの１つがダブルクリックですので、こちらを利用して石打ちおよび勝利判定等々を実装して五目並べをつくっていきます。
![ 2019-05-02 18.44.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/e1afba81-9b4f-9075-fb13-67b6fa22b1dd.png)



#1.碁盤の作成

碁盤を作成していきましょう。タイトルの入力、罫線の描画、背景色の指定などVBAの基本的な動作で可能です。注意点としては列と行の幅が違うことです。例えば列幅=100、行幅=100を入力すると、横に長い長方形ができます。
![ 2019-05-02 19.06.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/6efc29d6-785a-2a08-67ea-f5bd3d898281.png)


代表的な色であればRGB値を使わなくともColorIndexで指定可能。

実装コードはこちら

``` 
Sub Gomoku_Set()
Set board = Range("A1:X20") ' 盤オブジェクトの範囲設定
Set board_out = Range("B2:U20") 'out
Set board_in = Range("C3:T19") 'in


 B_Width = 3 ' 幅
B_Height = 22  ' 高さ

col_out = 30 'カラ-
col_in = 45

 board.ColumnWidth = B_Width
 board_RowHeight = B_Height

board_in.Borders.LineStyle = True
board_in.BorderAround Weight:=xlThick
board_out.BorderAround Weight:=xlThick

 ' 色
board_out.Interior.ColorIndex = col_out
board_in.Interior.ColorIndex = col_in

' タイトル設定
Cells(1, 1).RowHeight = 58
Cells(1, 2).Value = " GOMOKU NARABE"


' 初期化ボタンの配置
    ActiveSheet.Buttons.Add(26, 499, 120, 30).Select
    Selection.OnAction = "Init"
    Selection.Characters.Text = "初期化"
    With Selection.Characters(Start:=1, Length:=3).Font
        .Name = "Yu Gothic"
        .FontStyle = "標準"
        .Size = 12
        .Strikethrough = False
        .Superscript = False
        .Subscript = False
        .OutlineFont = False
        .Shadow = False
        .Underline = xlUnderlineStyleNone
        .ColorIndex = xlAutomatic
    End With
End Sub
```

![error]()

#2.碁石の色(ターン制)の実装

五目並べは先攻が黒・後攻が白なので、1回ダブルクリックが行われるごとに置く碁石を変えなければいけませんので、1回ダブルクリックが行われるごとに１加算される変数(turn_number)を宣言します。
変数が奇数なら黒石、偶数なら白石がセルに代入される条件式を設定。

![ 2019-05-02 19.26.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/7b7ed690-4d41-a1f1-61b0-ffe1b84954fc.png)


#3.五連の判定
一番苦労した部分でした（汗）。
石が打たれるごとに、全ての盤面に対して縦、横、ナナメに同じ石が連続しているか調べる方法もありましたが、如何せんスマートではないなと思ったので、もう少し良い方法があるか調べていました。

ダブルクリックを行うと、クリックされたセル番地がTargetオブジェクトに格納されます。故にターゲットの周辺セルを値指定で取得できるOffset関数を使えば、打った石の周りの環境だけを選択的に調べられそうですね。

![ 2019-05-02 19.31.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/d104b0f8-d0d2-b997-978a-f2d0556ad487.png)

TargetとOffset関数の関係は、このようになっています。打った石に対して、ある方向にどれだけ自分と同じ色の石が続いているのかを調べることができます。プログラミングでは1回に１方向しか調べられないので、地道に全８方向分調べていきます。具体的にはFor文で指定した方向へ進んでいき、<b>自分と同じ色がなかった場合はForループを抜ける</b>という仕組みでいきます。

![ 2019-05-02 19.44.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/eb948344-082b-d266-b143-b0c05ae2cecd.png)

五連続判定システムですが、プログラムに置き換えると以下のような必要十分条件式が成り立ちます。

![ 2019-05-02 19.55.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/71aa5031-acf5-dd29-da43-a45f823adc02.png)

つまり符号を考えず同じ方向に対して4以上進むことができれば、それは5つ以上並んでいるということですね。
この条件式を満たせば、プレイヤーの勝利とメッセージを出し、盤面を初期化します。

![ 2019-05-02 16.56.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302420/32d6a2fd-ad9a-df52-913f-1ee895f47d35.png)
(実際の画面)

#4.変数の宣言
VBAはスコープ（名前空間）のルールが少し厳しいように思えました。（プロシージャーの外で宣言しないと、異なるプロジェクト間で使い回せないようです。）
なので、最初にPublic||で変数を宣言しておく必要があります。
以上を踏まえてコードは以下のようになります。

モジュール上のコード

```Module1.xlsm
' 盤の定義
Public board, board_out, board_in As Range ' レンジ
Public B_Width, B_Height As Long ' 高さx幅
Dim col_out, col_in  As Long ' カラー
 '
 'ヒットカウント
 Public hit_count As Long  ' Hit
 Public Turn_Cell As Range ' ターン

Public suc_num(7) As Long ' 判定ナンバーの配列
Public now_turn As String ' ターンの白黒判定

 
Sub Gomoku_Set()
Set board = Range("A1:X20") ' 盤オブジェクトの範囲設定
Set board_out = Range("B2:U20") 'out
Set board_in = Range("C3:T19") 'in


 B_Width = 3 ' 幅
B_Height = 22  ' 高さ

col_out = 30 'カラ-
col_in = 45

 board.ColumnWidth = B_Width
 board_RowHeight = B_Height

board_in.Borders.LineStyle = True
board_in.BorderAround Weight:=xlThick
board_out.BorderAround Weight:=xlThick

 ' 色
board_out.Interior.ColorIndex = col_out
board_in.Interior.ColorIndex = col_in

' タイトル設定
Cells(1, 1).RowHeight = 58
Cells(1, 2).Value = " GOMOKU NARABE"


' 初期化ボタンの配置
    ActiveSheet.Buttons.Add(26, 499, 120, 30).Select
    Selection.OnAction = "Init"
    Selection.Characters.Text = "初期化"
    With Selection.Characters(Start:=1, Length:=3).Font
        .Name = "Yu Gothic"
        .FontStyle = "標準"
        .Size = 12
        .Strikethrough = False
        .Superscript = False
        .Subscript = False
        .OutlineFont = False
        .Shadow = False
        .Underline = xlUnderlineStyleNone
        .ColorIndex = xlAutomatic
    End With
End Sub

Sub Init()
 hit_count = 1
 Set board_in = Range("C3:T19")
 board_in.Value = "" ' 盤の石の初期化
Set Turn_Cell = Range("M22")
Turn_Cell.Value = ("黒のターンです")
  Turn_Cell.Font.Size = 24
 End Sub
 ```

シート上のコード

```sheet1.xlsm
Private Sub Worksheet_BeforeDoubleClick _
(ByVal Target As Range, Cancel As Boolean)

' ヒットカウントの更新

If hit_count Mod 2 = 1 Then
    stone = "●"
    stone_str = "黒"
Else
    stone = "○"
     stone_str = "白"
 End If

 ' クリックしたセルに石を打つ
 
    With Target
       If .Column > 2 And .Column < 21 And .Row > 2 And .Row < 20 Then
            If IsEmpty(.Value) Then
                .Value = stone
                hit_count = hit_count + 1
                Cancel = True
            End If
        End If
    End With
    
' 連番数の判定
For i = 0 To 7
suc_num(i) = 0
Next


 ' 0番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(-i, 0).Value Then
        suc_num(0) = suc_num(0) + 1
    Else
        Exit For
    End If
  Next
  
   '  1番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(-i, i).Value Then
        suc_num(1) = suc_num(1) + 1
    Else
        Exit For
    End If
  Next
  
     '  2番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(0, i).Value Then
        suc_num(2) = suc_num(2) + 1
    Else
        Exit For
    End If
  Next
  
     '  3番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(i, i).Value Then
        suc_num(3) = suc_num(3) + 1
    Else
        Exit For
    End If
  Next
  
     '  4番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(i, 0).Value Then
        suc_num(4) = suc_num(4) + 1
    Else
        Exit For
    End If
  Next
     '  5番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(i, -i).Value Then
        suc_num(5) = suc_num(5) + 1
    Else
        Exit For
    End If
  Next
     '  6番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(0, -i).Value Then
        suc_num(6) = suc_num(6) + 1
    Else
        Exit For
    End If
  Next
     '  7番方向
  For i = 1 To 30
    If Target.Value = Target.Offset(-i, -i).Value Then
        suc_num(7) = suc_num(7) + 1
    Else
        Exit For
    End If
  Next

 '勝ち負けの判定
For i = 0 To 3
If suc_num(i) + suc_num(i + 4) >= 4 Then
MsgBox " おめでとうございます。" & stone_str & "の勝利です"
 MsgBox " 初期化します"
  Call Init
  
End If
Next
' ダブルクリックプロシージャ〜で設定した値等の初期化
With Cells(22, 13)
If hit_count Mod 2 = 1 Then
    .Value = "黒のターンです"
 Else
    .Value = "白のターンです "
  End If
End With

' 連番カウント変数の初期化
For i = 0 To 7
suc_num(i) = 0
Next

End Sub
```
#.まとめ
今回はVBAの初作品でした。
ダブルクリックのプロシージャーも関数に分けてスッキリしたコードを書きたかったのですが、Targetオブジェクトを他の関数内では使えなかったので、次書くときははこのあたりの問題を解決させて、もう少し綺麗なものを書いていきたいです。