昨日は、数独をVBAで解くコードに新たなロジックを追加してみた。
infoment.hatenablog.com
今日は、答えが出るまで繰り返し処理を行わせることに挑戦する。

単純に繰り返すなら、For ~ Next ループが手っ取り早い。
Sub Sample()
Dim i As Long
For i = 1 To 10
Call 何某かの処理
Next
End Sub
この方法の場合、ループ変数iは、何某かの処理と全く無関係でも構わない。でも今回は、再帰呼び出しで行ってみた。
まず、総処理数を数えるために、カウンターを一つ準備する。加えて、最大処理数用にも変数を準備。
Dim TotalCounter As Long
Dim MaxTry As Long
これらの変数は、クラスモジュールの初期化時に初期設定する。
Private Sub Class_Initialize()
Application.ScreenUpdating = False
TotalCounter = 0
MaxTry = 100
End Sub
解析用モジュールに、再帰呼び出し箇所を追加。
Public Sub Analize()
Dim r As Long
Dim c As Long
For r = 1 To 9
For c = 1 To 9
CheckNumber r, c
Next
Next
If TotalCounter = MaxTry Then
Exit Sub
End If
For r = 1 To 9
For c = 1 To 9
If ArrNum(r, c) = 0 Then
TotalCounter = TotalCounter + 1
Call Analize
Exit Sub
End If
Next
Next
End Sub
最後に、処理回数の表示を追加する。
Private Sub Class_Terminate()
TableRange = ArrNum
TableRange.Replace 0, vbNullString
Application.ScreenUpdating = True
If TotalCounter = MaxTry Then
MsgBox "試行が規定回数(" & MaxTry & "回)に達したため、処理を中断します。"
Else
MsgBox "繰り返し回数:" & TotalCounter
End If
End Sub
以上を組み込んだところまでで、某サイトの問題を解いてみると、中級編まではほぼ問題なく全てのマスを埋めることが出来るようになった。

しかし現時点では、まだまだ上級レベルには太刀打ちできない。
Option Explicit
Dim ArrAll(1 To 9, 1 To 9) As Variant
Dim ArrNum(1 To 9, 1 To 9) As Variant
Dim TableRange As Range
Dim TotalCounter As Long
Dim MaxTry As Long
Private Sub Class_Initialize()
Application.ScreenUpdating = False
TotalCounter = 1
MaxTry = 100
End Sub
Public Sub init(table_range As Range)
Set TableRange = table_range
Dim rng As Range
For Each rng In TableRange
If rng = vbNullString Then
rng.Font.Color = 192
Else
rng.Font.Color = vbBlack
End If
Next
Dim r As Long
Dim c As Long
Dim i As Long
Dim v(9) As Variant
For r = 1 To 9
For c = 1 To 9
If table_range.Cells(r, c) = vbNullString Then
ArrAll(r, c) = "2222222222"
ArrNum(r, c) = 0
Else
v(0) = 1
For i = 1 To 9
If i = table_range.Cells(r, c) Then
v(i) = 1
Else
v(i) = 0
End If
Next
ArrAll(r, c) = Join(v, vbNullString)
ArrNum(r, c) = table_range.Cells(r, c)
End If
Next
Next
End Sub
Public Sub Analize()
Dim r As Long
Dim c As Long
For r = 1 To 9
For c = 1 To 9
CheckNumber r, c
Next
Next
If TotalCounter = MaxTry Then
Exit Sub
End If
For r = 1 To 9
For c = 1 To 9
If ArrNum(r, c) = 0 Then
TotalCounter = TotalCounter + 1
Call Analize
Exit Sub
End If
Next
Next
End Sub
Private Sub CheckNumber(r_target As Long, c_target As Long)
If Left(ArrAll(r_target, c_target), 1) = 1 Then Exit Sub
Dim r As Long
Dim c As Long
Dim v(9) As Variant
Dim i As Long
For i = 0 To 9
v(i) = Mid(ArrAll(r_target, c_target), i + 1, 1)
Next
Dim index As Long
For c = 1 To 9
index = ArrNum(r_target, c)
If index <> 0 Then
v(index) = 0
End If
Next
For r = 1 To 9
index = ArrNum(r, c_target)
If index <> 0 Then
v(index) = 0
End If
Next
Dim sr As Long
sr = WorksheetFunction.RoundUp(r_target / 3, 0) * 3 - 2
Dim sc As Long
sc = WorksheetFunction.RoundUp(c_target / 3, 0) * 3 - 2
For r = sr To sr + 2
For c = sc To sc + 2
index = ArrNum(r, c)
If index <> 0 Then
v(index) = 0
End If
Next
Next
If CountNumber(v, 0) = 8 Then
v(0) = 1
For i = 1 To 9
If v(i) <> 0 Then
v(i) = 1
Exit For
End If
Next
ArrNum(r_target, c_target) = i
Else
Dim j As Long
For j = 1 To 9
If GetNumber(r_target, c_target, j) <> 0 Then
For i = 0 To 9
Select Case i
Case 0: v(i) = 1
Case j: v(i) = 1
Case Else: v(i) = 0
End Select
Next
ArrNum(r_target, c_target) = j
Exit For
End If
Next
End If
ArrAll(r_target, c_target) = Join(v, vbNullString)
End Sub
Private Function CountNumber(arr As Variant, target_number As Long) As Long
Dim i As Long
Dim counter As Long
For i = 1 To 9
If arr(i) = target_number Then
counter = counter + 1
End If
Next
CountNumber = counter
End Function
Private Function GetNumber(r_target, c_target, index) As Long
Dim r As Long
Dim c As Long
Dim v(1 To 9) As Variant
For c = 1 To 9
v(c) = Mid(ArrAll(r_target, c), index + 1, 1)
Next
If CountNumber(v, 0) = 8 And v(c_target) = 2 Then
GetNumber = index
Exit Function
End If
For r = 1 To 9
v(r) = Mid(ArrAll(r, c_target), index + 1, 1)
Next
If CountNumber(v, 0) = 8 And v(r_target) = 2 Then
GetNumber = index
Exit Function
End If
Dim sr As Long
sr = WorksheetFunction.RoundUp(r_target / 3, 0) * 3 - 2
Dim sc As Long
sc = WorksheetFunction.RoundUp(c_target / 3, 0) * 3 - 2
Dim i As Long
i = 1
For r = sr To sr + 2
For c = sc To sc + 2
v(i) = Mid(ArrAll(r, c), index + 1, 1)
i = i + 1
Next
Next
Dim myIndex As Long
myIndex = (r_target - sr) * 3 + (c_target - sc) + 1
If CountNumber(v, 0) = 8 And v(myIndex) = 2 Then
GetNumber = index
End If
End Function
Private Sub Class_Terminate()
TableRange = ArrNum
TableRange.Replace 0, vbNullString
Application.ScreenUpdating = True
If TotalCounter = MaxTry Then
MsgBox "試行が規定回数(" & MaxTry & "回)に達したため、処理を中断します。"
Else
MsgBox "繰り返し回数:" & TotalCounter
End If
End Sub
次回に続きます。
参考まで。