昨日は数独の出題を、27×27の配列に反映してみた。
infoment.hatenablog.com
今日は、確定マスの情報を、行と列全体に反映させてみる。
例えば下記のように、黄色のマスが「3」で確定している場合。
このとき、同じ行にある「緑で塗りつぶされた3」は、既に可能性を絶たれているため、「0」に上書きする必要がある。
そのためにまず、9行9列における任意の指定マスの確定値を取得する関数を作成する。例えば、↓ の出題に於いて、f(3,2)の戻り値が7になる関数だ。
※未確定の場合、戻り値は0とする。
' 9×9の任意セルの数値が確定している場合、その数値を返す。 ' 確定していない場合は0を返す。 Private Function FixedNumber(r_index As Long, c_index As Long) As Long Dim arr As Variant arr = DetailArray(r_index, c_index) ' 0の数を数えることで、確定しているか否かを判別する。 If ArrayCountIF(arr, 0) = 8 Then ' 確定値以外は0なので、3×3の範囲の和で求まる。 FixedNumber = WorksheetFunction.Sum(arr) Else FixedNumber = 0 End If End Function ' 9×9の任意のセルを、27×27の配列から抜き出す関数。 Private Function DetailArray(r_index As Long, c_index As Long) ' 原点を取得。 r0 = OriginPoint(r_index, c_index)(OriginIndex.OriginR) c0 = OriginPoint(r_index, c_index)(OriginIndex.OriginC) Dim arr(1 To 3, 1 To 3) As Variant Dim r As Long Dim c As Long For r = 1 To 3 For c = 1 To 3 arr(r, c) = TempArray(r0 + r, c0 + c) Next Next DetailArray = arr End Function ' CountIFの配列版。 ' 指定配列内の指定値を数えて返している。 Private Function ArrayCountIF(source_array As Variant, _ search_criteria As Variant) As Long Dim counter As Long Dim a As Variant On Error Resume Next For Each a In source_array If a = search_criteria Then counter = counter + 1 End If Next On Error GoTo 0 ArrayCountIF = counter End Function
次に必要なのが、9×9の位置と確定値を引数として、その行と列、更には3×3のエリアから確定値を除去する関数だ。例えば一番左上(黄色)が「3」で確定しているなら、太い赤枠で囲った領域の「3」(緑色」を0にする関数。
今回は取り掛かりなので、まず行方向(横方向)で試しに作成してみる。
Private Sub DeleteNumber(r_index As Long, _ c_index As Long, _ delete_number As Long) Dim r As Long Dim c As Long ' 行方向。 r0 = OriginPoint(r_index, c_index)(OriginIndex.OriginR) For r = 1 To 3 For c = 1 To 27 If TempArray(r0 + r, c) = delete_number Then If WorksheetFunction.RoundUp(c / 3, 0) <> c_index Then TempArray(r0 + r, c) = 0 End If End If Next Next End Sub
最後に、これを実行するのがこちら。
Public Sub UpdateArray() Dim r As Long Dim c As Long Dim n As Long Dim SumAll As Long Dim counter As Long ' 編集の有無を確認するために、予め配列の合計値を求めておく。 ' ※何らかの編集があれば、1以上の値が0に置き換わり、合計も変わる。 SumAll = WorksheetFunction.Sum(TempArray) ' 編集の前後で合計値の差が無くなるまで、つまり何も置き換わらなくなる ' まで繰り返す。 Do For r = 1 To 9 For c = 1 To 9 n = FixedNumber(r, c) If n <> 0 Then DeleteNumber r, c, n End If Next Next If SumAll = WorksheetFunction.Sum(TempArray) Then Exit Do Else SumAll = WorksheetFunction.Sum(TempArray) End If ' 念のため、仮の措置として、30回繰り返したらループを抜ける。 counter = counter + 1 If counter >= 30 Then Exit Do Loop End Sub
それでは、確認してみよう。
Sub test() Dim Sudoku As New Sudoku Sudoku.SourceArray = Sheet1.Range("A1:I9").Value Sudoku.InitArray Sudoku.UpdateArray ' 結果確認のため、仮で貼り付け。 Sheet2.Range("A1").Resize(27, 27) = Sudoku.TempArray End Sub
結果、先程の例で言えば、行方向の3(緑色)を0に置き換えることが出来た。
明日に続きます。
参考まで。