数独(再挑戦)② 確定マスの配列を編集する

昨日は数独について、構想新たに27×27の配列を準備してみた。
infoment.hatenablog.com

今日は、実際の出題を配列に反映させることに挑戦する。
f:id:Infoment:20191201124711p:plain

まず数独用に、クラスモジュール「Sudoku」を作成した。

クラスモジュール
Option Explicit
' 出題された9×9を格納する配列。
Public SourceArray As Variant

' 解答の過程で使用する27×27の配列。
Public TempArray As Variant

' 解答となる9×9を格納する配列。
Public AnswerArray As Variant

' 真っ新の27×27配列。
Private Property Get BaseArray() As Variant
    Dim r As Long
    Dim c As Long
    Dim arr(1 To 27, 1 To 27) As Variant
        For r = 1 To 27
            For c = 1 To 27
                arr(r, c) = ((r - 1) Mod 3) * 3 + (c - 1) Mod 3 + 1
            Next
        Next
        BaseArray = arr
End Property

Private Sub Class_Initialize()
    ' TempArray初期化。
    TempArray = BaseArray
End Sub

TempArrayなどは本来、経過を格納する一時的なものであるため、Public変数である必要は無い。ただし今は、このクラスを整備しながら経過を確認するために、Public変数としている。

まず、一つのマスから3×3がどこにあるか知る必要がある。
↓の例で言えば、2行3列目のマスは、
f:id:Infoment:20191201125216p:plain

4行7列目から始まっている。
f:id:Infoment:20191201125248p:plain

そこで、この4行7列目の原点となるポイントを取得することにした。
↓図で言えば、3行6列目の「9」が、これにあたる。
f:id:Infoment:20191201125506p:plain

' 原点
Dim r0 As Long
Dim c0 As Long

Enum OriginIndex
    OriginR
    OriginC
End Enum
Private Function OriginPoint(r_index As Long, c_index As Long) As Variant
    ' 9×9で指定した1つのセルを構成する3×3の配列を抽出。
    ' 例えば2行3列目が属する3×3配列の起点の一つ前は、
    ' (2-1)×3=3
    ' (3-1)×3=6
    ' つまり(3,6)が原点(0,0)なる。
    Dim arr(1) As Variant
        arr(OriginIndex.OriginR) = (r_index - 1) * 3
        arr(OriginIndex.OriginC) = (c_index - 1) * 3
        
        OriginPoint = arr
End Function

この情報を利用して、9×9の範囲に於いて指定したセルを構成する3×3の配列について、指定値以外を0にして確定させる。

Private Sub FixedCell(r_index As Long, c_index As Long, fixed_number)

    ' 原点を取得。
    r0 = OriginPoint(r_index, c_index)(OriginIndex.OriginR)
    c0 = OriginPoint(r_index, c_index)(OriginIndex.OriginC)

    Dim r As Long
    Dim c As Long
        For r = 1 To 3
            For c = 1 To 3
                If 3 * (r - 1) + c <> fixed_number Then
                    TempArray(r0 + r, c0 + c) = 0
                End If
            Next
        Next
End Sub

以上のことから、出題内容を反映し、初期状態を作成する。

Public Sub InitArray()
    Dim r As Long
    Dim c As Long
    For r = 1 To 9
        For c = 1 To 9
            If IsEmpty(SourceArray(r, c)) = False Then
                FixedCell r, c, SourceArray(r, c)
            End If
        Next
    Next
End Sub

それでは、↓の出題からTempArrayを作成してみよう。今回は確認のため、別シートに結果を張り付けている。
f:id:Infoment:20191201130312p:plain

Sub test()

    Dim Sudoku As New Sudoku
        Sudoku.SourceArray = Sheet1.Range("A1:I9").Value
        Sudoku.InitArray

        ' 結果確認のため、仮で貼り付け。
        Range("A1").Resize(27, 27) = Sudoku.TempArray

End Sub

意図した結果を取得できた。
f:id:Infoment:20191201131007p:plain

ただし、現時点では未だ不完全。例えば黄色の3が確定している時点で、緑色の3は全て0でなければならない。
f:id:Infoment:20191201130731p:plain

これについては、次回に続きます。

参考まで。