ワークシートでパズルゲーム

# ワークシートでパズルゲーム
#### むかし書き溜めたVBAのTips
ワークシートのセル 4 × 4 マスをダブルクリックで数字を入れ替えながら、左上から順に1～15まで並べるゲーム。セルの範囲はF15:I18で、数字とセルの色付けは先に設定しておく必要があります。いかに少ない手数でクリアできるか！
![puzzle.png](https://qiita-image-store.s3.amazonaws.com/0/78853/50d3beb4-7394-2a4d-77dc-4c0a4b3dd3c5.png)

コードはSheetモジュールに記載。

```
Const RANGE_COUNT As String = "K15"
Const RANGE_PUZZLE As String = "F15:I18"

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

  Dim moveDirection As String

  Cancel = True
  If Intersect(Target, Range(RANGE_PUZZLE)) Is Nothing Then
    Call PuzzleStart
    Exit Sub
  End If
  
  ' 移動方向判定
  moveDirection = IsCanMove(Target)
  
  ' 移動
  Select Case moveDirection
    Case ""
     Exit Sub
    Case "Up"
      Call PieceMove(Target, Target.Offset(-1, 0))
      
    Case "Right"
      Call PieceMove(Target, Target.Offset(0, 1))
    
    Case "Down"
      Call PieceMove(Target, Target.Offset(1, 0))
    
    Case "Left"
      Call PieceMove(Target, Target.Offset(0, -1))
  
  End Select
  
  Range(RANGE_COUNT).Value = Range(RANGE_COUNT).Value + 1
  
  If IsComplete Then
    MsgBox "Complete!!"
    'Range(RANGE_COUNT).Value = 0
  End If
  
End Sub
```
```
' 終了判定
Function IsComplete() As Boolean

  Dim i As Integer
  Dim rngPiece As Range
  
  i = 1
  For Each rngPiece In Range(RANGE_PUZZLE)
    If rngPiece.Value = "" Then Exit For
    If rngPiece.Value <> i Then Exit For
    i = i + 1
  Next

  If i = 16 Then
    IsComplete = True
  Else
    IsComplete = False
  End If

End Function
```
```
' 駒の移動
Sub PieceMove(rngTarget As Range, rngMove As Range)

  rngMove.Value = rngTarget.Value
  rngMove.Interior.ColorIndex = 36
  
  rngTarget.ClearContents
  rngTarget.Interior.ColorIndex = 2

End Sub
```
```
' 移動可能な方向を返す
Function IsCanMove(rngTarget As Range) As String

  IsCanMove = ""
  With rngTarget
    ' 上
    If .Offset(-1, 0) = "" Then
      If Not Intersect(.Offset(-1, 0), Range(RANGE_PUZZLE)) Is Nothing Then
        IsCanMove = "Up"
        Exit Function
      End If
    End If
    
    ' 右
    If .Offset(0, 1) = "" Then
      If Not Intersect(.Offset(0, 1), Range(RANGE_PUZZLE)) Is Nothing Then
        IsCanMove = "Right"
        Exit Function
      End If
    End If
    
    ' 下
    If .Offset(1, 0) = "" Then
      If Not Intersect(.Offset(1, 0), Range(RANGE_PUZZLE)) Is Nothing Then
        IsCanMove = "Down"
        Exit Function
      End If
    End If
    
    ' 左
    If .Offset(0, -1) = "" Then
      If Not Intersect(.Offset(0, -1), Range(RANGE_PUZZLE)) Is Nothing Then
        IsCanMove = "Left"
        Exit Function
      End If
    End If
    
  End With
    
End Function
```
```
' 並びをバラバラにする
Sub PuzzleStart()

  Dim i As Integer
  Dim rngPiece As Range
  Dim strMovePiece As String
  
  ' 100回駒を動かして並びをバラバラにする
  For i = 0 To 100
    For Each rngPiece In Range(RANGE_PUZZLE)
      If rngPiece.Value = "" Then Exit For
    Next
    
    strMovePiece = GetMovePiece(rngPiece)
    
    Select Case strMovePiece
      Case "Up"
        Call PieceMove(rngPiece.Offset(-1, 0), rngPiece)
      
      Case "Right"
        Call PieceMove(rngPiece.Offset(0, 1), rngPiece)
      
      Case "Down"
        Call PieceMove(rngPiece.Offset(1, 0), rngPiece)
      
      Case "Left"
        Call PieceMove(rngPiece.Offset(0, -1), rngPiece)
    End Select
  Next

  Range(RANGE_COUNT).Value = 0

End Sub
```
```
' 移動する駒をランダム選択
Function GetMovePiece(rngTarget As Range) As String

  Dim i As Integer
  Dim j As Integer
  Dim aryMoveDirection() As String

  i = 0
  
  ' 上
  If rngTarget.Offset(-1, 0) <> "" Then
    ReDim Preserve aryMoveDirection(i)
    aryMoveDirection(i) = "Up"
    i = i + 1
  End If
  
  ' 右
  If rngTarget.Offset(0, 1) <> "" Then
    ReDim Preserve aryMoveDirection(i)
    aryMoveDirection(i) = "Right"
    i = i + 1
  End If
  
  ' 下
  If rngTarget.Offset(1, 0) <> "" Then
    ReDim Preserve aryMoveDirection(i)
    aryMoveDirection(i) = "Down"
    i = i + 1
  End If
  
  ' 左
  If rngTarget.Offset(0, -1) <> "" Then
    ReDim Preserve aryMoveDirection(i)
    aryMoveDirection(i) = "Left"
    i = i + 1
  End If

  Randomize
  j = Int(((UBound(aryMoveDirection) + 1) * Rnd))
  GetMovePiece = aryMoveDirection(j)

End Function
```